@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs:  <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl:   <http://www.w3.org/2002/07/owl#> .
@prefix xsd:   <http://www.w3.org/2001/XMLSchema#> .
@prefix lorav: <https://www.w3.org/2026/wot/lorawan#> .

#
# LoRaWAN WoT binding vocabulary.
#
# These terms describe how a value carried in a LoRaWAN uplink is located in the
# raw application payload and how the raw bytes are converted into a typed value.
# A converter maps each term onto the MultiTech / LoRa Alliance Payload Schema
# language so a reference codec can be generated and executed.
#
# Uplinked values are modelled as WoT *events*, not properties: a LoRaWAN device
# transmits on its own schedule and cannot be polled, so there is no value to
# read on demand. Accordingly the form-level terms below sit on an event's form,
# and what the decoded value *means* (its unit, range and enumeration) is left to
# TD core's data-schema terms on the event's `data`.
#

lorav: a owl:Ontology ;
    rdfs:label "LoRaWAN Binding Vocabulary" ;
    rdfs:comment "Vocabulary for describing LoRaWAN payload bindings in WoT Thing Descriptions." ;
    owl:versionInfo "0.3.0" .

# --- Thing-level terms -------------------------------------------------------

lorav:payloadLayout a owl:DatatypeProperty ;
    rdfs:label "payload layout" ;
    rdfs:comment "Overall structure of the device payload: 'fixed', 'ports', 'tlv' or 'ctv'. Usage: core, required on every Thing." ;
    rdfs:range xsd:string .

lorav:tagFields a owl:DatatypeProperty ;
    rdfs:label "tag fields" ;
    rdfs:comment "For 'tlv'/'ctv' layouts, the leading tag field(s) (e.g. channel + type) that select a value. Usage: core for tlv/ctv devices (e.g. Milesight, Elsys)." .

# --- Form-level terms --------------------------------------------------------

lorav:endian a owl:DatatypeProperty ;
    rdfs:label "byte order" ;
    rdfs:comment "Byte order of this multi-byte value: 'big' (default) or 'little'. This describes byte order only; the binding has no byte-swap or word-swap mode. Usage: core; big-endian dominates, little-endian is used by Milesight, Digital Matter and Radionode." ;
    rdfs:range xsd:string .

lorav:fPort a owl:DatatypeProperty ;
    rdfs:label "frame port" ;
    rdfs:comment "LoRaWAN FPort used to route or branch the payload. Usage: core for 'ports' layouts." ;
    rdfs:range xsd:integer .

lorav:wireType a owl:DatatypeProperty ;
    rdfs:label "wire data type" ;
    rdfs:comment "Data type of the value *on the wire*, named with an xsd:* alias or a native type (e.g. s16, u8). Distinct from the event data schema's 'type', which gives the type of the decoded value. Usage: core, required on every form." .

lorav:multiplier a owl:DatatypeProperty ;
    rdfs:label "multiplier" ;
    rdfs:comment "Factor multiplied with the raw value to obtain the engineering value. Mutually exclusive with lorav:divisor. Usage: common (e.g. Decentlab, Digital Matter, MClimate)." ;
    rdfs:range xsd:double .

lorav:divisor a owl:DatatypeProperty ;
    rdfs:label "divisor" ;
    rdfs:comment "Factor the raw value is divided by to obtain the engineering value. Preferred over lorav:multiplier for decimal scaling, since a divisor such as 100 is exactly representable in binary floating point while the equivalent multiplier 0.01 is not. Mutually exclusive with lorav:multiplier. Usage: core, the most common scaling term across vendors." ;
    rdfs:range xsd:double .

lorav:addend a owl:DatatypeProperty ;
    rdfs:label "addend" ;
    rdfs:comment "Constant added to the value after scaling. Named 'addend' rather than 'offset' to avoid confusion with lorav:byteOffset, which is a payload position rather than an arithmetic term. Usage: common (Decentlab, MClimate, RadioBridge)." ;
    rdfs:range xsd:double .

lorav:bitmask a owl:DatatypeProperty ;
    rdfs:label "bitmask" ;
    rdfs:comment "Hexadecimal mask selecting a contiguous range of bits from the raw value. Usage: common, for status/flag bytes (Dragino, Digital Matter, MClimate, RadioBridge, RAKwireless)." ;
    rdfs:range xsd:string .

lorav:valueMap a owl:DatatypeProperty ;
    rdfs:label "value map" ;
    rdfs:comment "Maps each integer on the wire to the value it denotes, as a list of {wireValue, value} pairs. The allowed values themselves are stated by the event's data schema, through 'enum' or through a self-enumerating type such as boolean; what TD core cannot express is which encoding yields which of them, and that correspondence is a fact about the transfer rather than about the value's meaning. Compare bacv:hasValueMap with bacv:hasProtocolVal and bacv:hasLogicalVal in the WoT BACnet binding, which keeps the same split. Usage: rare, for status and state readings (MClimate, RadioBridge, Makerfabs)." .

lorav:byteOffset a owl:DatatypeProperty ;
    rdfs:label "byte offset" ;
    rdfs:comment "Zero-based byte position of the value within a fixed payload layout. Usage: core for 'fixed' and 'ports' layouts." ;
    rdfs:range xsd:integer .

lorav:byteLength a owl:DatatypeProperty ;
    rdfs:label "byte length" ;
    rdfs:comment "Byte length for variable-length wire types (string, bytes, hex, base64). Named 'byteLength' to state the unit of the count. Usage: rare (RadioBridge)." ;
    rdfs:range xsd:integer .

lorav:tag a owl:DatatypeProperty ;
    rdfs:label "tag" ;
    rdfs:comment "For 'tlv'/'ctv' layouts, the tag value(s) (e.g. [channel, type]) selecting this value. Usage: core for tlv/ctv devices (Milesight, Elsys)." .

# --- Grouping / conditional-presence terms -----------------------------------

lorav:slot a owl:DatatypeProperty ;
    rdfs:label "group slot" ;
    rdfs:comment "Zero-based order of this event within its group (multi-field TLV case, flagged group or match case). Usage: core wherever a group holds more than one value." ;
    rdfs:range xsd:integer .

lorav:padBefore a owl:DatatypeProperty ;
    rdfs:label "padding before" ;
    rdfs:comment "Number of reserved bytes consumed before this event's value within its group (e.g. a skip inside a match case). Usage: rare (RadioBridge)." ;
    rdfs:range xsd:integer .

lorav:presentWhen a owl:DatatypeProperty ;
    rdfs:label "presence condition" ;
    rdfs:comment "Condition gating whether this value appears in the payload at all, as an object {field, bit | value}: 'field' names the value that decides, 'bit' selects a flag bit of it, and 'value' selects a match case of it. At most one of 'bit' or 'value' applies, which is why these are one object rather than four independent terms whose valid combinations could not be stated. Usage: common (Decentlab for flag bits, Dragino/RadioBridge/Radionode for match cases)." .

lorav:alias a owl:DatatypeProperty ;
    rdfs:label "reference alias" ;
    rdfs:comment "Name under which other blocks reference this value as $alias, needed when a match block's name for its discriminator differs from the event name. Kept separate from lorav:presentWhen because it describes a value that other values are gated by, rather than a value that is itself gated. Usage: rare (RadioBridge, Radionode)." ;
    rdfs:range xsd:string .

# --- Computed / derived-value terms ------------------------------------------
#
# A derived value (lorav:wireType 'number') carries no payload bytes; the
# reference interpreter computes it from other already-decoded values.

lorav:derived a owl:DatatypeProperty ;
    rdfs:label "derived value" ;
    rdfs:comment "Describes a value computed from other decoded values rather than read off the wire, as an object with any of: 'ref' (a $name reference to the input value), 'polynomial' (coefficients [c0, c1, ...] evaluated as c0 + c1*x + c2*x^2 + ... over the input), 'compute' ({op, a, b}, a binary operation combining two values), 'guard' ({when, else}, a predicate selecting between values) and 'transform' (ordered add/div/mult post-processing). Grouped into one object because these keys are meaningful only together and only for a derived value. Usage: rare (MClimate, Decentlab, Digital Matter)." .

# --- Thing-level OTAA activation / identity terms ----------------------------
#
# DevEUI and JoinEUI are identifiers and may appear in the TD. The OTAA root keys
# AppKey (1.0.x / 1.1.x) and NwkKey (1.1.x only) are secrets and are NOT given a
# vocabulary term: they are declared as WoT 'apikey' security schemes (with the
# scheme 'name' set to "appKey" / "nwkKey") and injected at runtime, never stored
# as values in the Thing Description.

lorav:devEUI a owl:DatatypeProperty ;
    rdfs:label "DevEUI" ;
    rdfs:comment "8-byte LoRaWAN device identifier (16 hexadecimal characters)." ;
    rdfs:range xsd:string .

lorav:joinEUI a owl:DatatypeProperty ;
    rdfs:label "JoinEUI" ;
    rdfs:comment "8-byte LoRaWAN join/application identifier, formerly AppEUI (16 hexadecimal characters)." ;
    rdfs:range xsd:string .

lorav:macVersion a owl:DatatypeProperty ;
    rdfs:label "LoRaWAN MAC version" ;
    rdfs:comment "LoRaWAN MAC version such as '1.0.3' or '1.1.0'. Version 1.1.x additionally requires a NwkKey." ;
    rdfs:range xsd:string .

# --- Thing-level onboarding / device-repository metadata ---------------------
#
# The manufacturer and part number are schema.org's job (schema:manufacturer,
# schema:mpn) and so are the hardware and firmware revisions (schema:version,
# schema:softwareVersion), so only the two terms that are genuinely
# LoRaWAN-specific are defined here.

lorav:region a owl:DatatypeProperty ;
    rdfs:label "region" ;
    rdfs:comment "Regulatory region / profile of the device, e.g. 'EU868', 'US915'." ;
    rdfs:range xsd:string .

lorav:frequencyPlan a owl:DatatypeProperty ;
    rdfs:label "frequency plan" ;
    rdfs:comment "LoRaWAN Network Server frequency plan identifier, e.g. 'EU_863_870_TTN'." ;
    rdfs:range xsd:string .

# --- Withdrawn terms ---------------------------------------------------------
#
# A published vocabulary is a contract: deleting an IRI outright would leave
# already-published Thing Descriptions pointing at something undefined. These
# terms are therefore retained and marked owl:deprecated, each naming what
# replaced it. The converter rejects them by name with the same migration hint,
# so an old Thing Description fails loudly instead of decoding the wrong bytes.

lorav:type a owl:DatatypeProperty ;
    rdfs:label "payload data type (withdrawn)" ;
    rdfs:comment "Renamed to lorav:wireType, to separate the wire type from the decoded value's data-schema 'type'." ;
    owl:deprecated true .

lorav:offset a owl:DatatypeProperty ;
    rdfs:label "offset (withdrawn)" ;
    rdfs:comment "Renamed to lorav:addend, to separate the scaling constant from the payload position lorav:byteOffset." ;
    owl:deprecated true .

lorav:length a owl:DatatypeProperty ;
    rdfs:label "length (withdrawn)" ;
    rdfs:comment "Renamed to lorav:byteLength, to state the unit of the count." ;
    owl:deprecated true .

lorav:presenceField a owl:DatatypeProperty ;
    rdfs:label "presence field (withdrawn)" ;
    rdfs:comment "Replaced by the 'field' key of lorav:presentWhen." ;
    owl:deprecated true .

lorav:presenceBit a owl:DatatypeProperty ;
    rdfs:label "presence bit (withdrawn)" ;
    rdfs:comment "Replaced by the 'bit' key of lorav:presentWhen." ;
    owl:deprecated true .

lorav:switchField a owl:DatatypeProperty ;
    rdfs:label "switch field (withdrawn)" ;
    rdfs:comment "Replaced by the 'field' key of lorav:presentWhen." ;
    owl:deprecated true .

lorav:switchValue a owl:DatatypeProperty ;
    rdfs:label "switch value (withdrawn)" ;
    rdfs:comment "Replaced by the 'value' key of lorav:presentWhen." ;
    owl:deprecated true .

lorav:var a owl:DatatypeProperty ;
    rdfs:label "discriminator alias (withdrawn)" ;
    rdfs:comment "Renamed to lorav:alias." ;
    owl:deprecated true .

lorav:ref a owl:DatatypeProperty ;
    rdfs:label "input reference (withdrawn)" ;
    rdfs:comment "Replaced by the 'ref' key of lorav:derived." ;
    owl:deprecated true .

lorav:polynomial a owl:DatatypeProperty ;
    rdfs:label "polynomial (withdrawn)" ;
    rdfs:comment "Replaced by the 'polynomial' key of lorav:derived." ;
    owl:deprecated true .

lorav:transform a owl:DatatypeProperty ;
    rdfs:label "transform (withdrawn)" ;
    rdfs:comment "Replaced by the 'transform' key of lorav:derived." ;
    owl:deprecated true .

lorav:compute a owl:DatatypeProperty ;
    rdfs:label "compute (withdrawn)" ;
    rdfs:comment "Replaced by the 'compute' key of lorav:derived." ;
    owl:deprecated true .

lorav:guard a owl:DatatypeProperty ;
    rdfs:label "guard (withdrawn)" ;
    rdfs:comment "Replaced by the 'guard' key of lorav:derived." ;
    owl:deprecated true .

lorav:validRange a owl:DatatypeProperty ;
    rdfs:label "valid range (withdrawn)" ;
    rdfs:comment "Replaced by TD core's 'minimum' and 'maximum' on the event's data schema." ;
    owl:deprecated true .

lorav:enum a owl:DatatypeProperty ;
    rdfs:label "enumeration (renamed)" ;
    rdfs:comment "Renamed to lorav:valueMap, whose {wireValue, value} pairs make the wire integer and the decoded value separate fields. Briefly expressed instead as TD core 'oneOf' entries of 'const' plus 'title', which was withdrawn: 'const' held the wire integer inside the schema of the decoded value, making that schema unsatisfiable, and 'title' is a display label whose rewording would have changed decoded payloads." ;
    owl:deprecated true .

lorav:unece a owl:DatatypeProperty ;
    rdfs:label "UN/CEFACT unit code (withdrawn)" ;
    rdfs:comment "Replaced by TD core's 'unit' on the event's data schema; no device in the reference catalog ever carried this term." ;
    owl:deprecated true .

lorav:brand a owl:DatatypeProperty ;
    rdfs:label "end-device brand (withdrawn)" ;
    rdfs:comment "Replaced by schema:manufacturer, which consumers already understand." ;
    owl:deprecated true .

lorav:model a owl:DatatypeProperty ;
    rdfs:label "end-device model (withdrawn)" ;
    rdfs:comment "Replaced by schema:mpn, the manufacturer's own part number for the model." ;
    owl:deprecated true .

lorav:hardwareVersion a owl:DatatypeProperty ;
    rdfs:label "hardware version (withdrawn)" ;
    rdfs:comment "Replaced by schema:version. TD core's 'version' object versions the Thing Description itself, not the device." ;
    owl:deprecated true .

lorav:softwareVersion a owl:DatatypeProperty ;
    rdfs:label "software version (withdrawn)" ;
    rdfs:comment "Replaced by schema:softwareVersion. TD core's 'version' object versions the Thing Description itself, not the firmware running on the device." ;
    owl:deprecated true .

lorav:endDeviceId a owl:DatatypeProperty ;
    rdfs:label "end-device ID (withdrawn)" ;
    rdfs:comment "Replaced by TD core's 'id' and 'title'." ;
    owl:deprecated true .
