3.7 Thing Model and Device Abstraction
3.7.1 The Thing-Model Concept and Profile Implementation
The temperature-humidity sensors in a smart greenhouse, the RFID readers in a warehouse, the vibration monitors on a shop floor — these devices come from different vendors, each with its own interface protocol and a completely different format for reported data. Company A's temperature sensor reports {"temp": 25.3, "unit": "C"} in JSON, while Company B's device of the same kind uses binary messages whose parsing depends on a 300-page protocol document. When you build an IoT platform, a large share of the effort goes into "translating" this device data. Whenever a new brand or a new model of device is connected, the adaptation code has to be written all over again. This state of affairs makes interoperability between heterogeneous devices extremely expensive and slows the pace of project deployment.
The core idea for solving this problem is to give each class of device a "capability card" — stating its data types, its control interfaces, and the events it can report, all in a description language that machines can understand. The industry generally calls this kind of capability description a thing model; IoT DC3 carries the capability definitions of a class of devices in a Profile (template). A thing model describes the capability contract of a device type, which is a different concept from the "device shadow" that records the running state of an individual device — the shadow is a runtime snapshot of state, while the thing model is a permanent blueprint of capability.
A thing model aggregates the properties, services, and events shared by devices of the same model, describing "what this class of device can collect, what it can control, and what it will report." One device belongs to exactly one thing model, and many devices can reuse the same thing model. A batch of 100 temperature-humidity sensors, for example, shares a single thing-model definition — their basic capabilities are identical, and only their IDs and current values differ. A thing model does not care about the instantaneous state of any single device; it describes only the possible behaviors of the device class.
Properties, services, and events are the three basic elements of a thing model. A property is a state value of the device, either readable and writable or read-only — for example, the current temperature of a temperature sensor, the on/off state of a smart plug, or the battery percentage of a battery. A service (called a "command" or "action" in DC3) is an executable operation the device exposes to the outside, such as remotely restarting a gateway, calibrating a sensor's zero point, or setting an alarm threshold. An event is a signal the device emits on its own initiative, usually indicating some state change or anomaly, such as a temperature-excursion alarm, a device-offline notification, or a periodic heartbeat. Defining these three elements is, in essence, abstracting the behavior of a physical device into a programmable interface. An application-layer engineer only needs to know that "there is a property called temperature and I can read its value" — not whether that temperature value comes out of a Modbus register or straight from the chip over the I²C bus.
The mainstream thing-model standards each have their own emphasis, but their core idea is the same. The Web of Things (WoT) Thing Description (TD) proposed by the W3C (World Wide Web Consortium) is the more mature open specification: it describes a device as a set of properties, actions, and events, and it supports defining input and output data schemas with JSON Schema, defining security schemes (OAuth2, PSK, and so on), and protocol bindings (HTTP, CoAP, MQTT). Another important standards contributor is oneM2M, which faces cellular IoT scenarios, defines operations such as the resource model, subscription, and notification in finer detail, and stresses consistency of hierarchy and semantics. Whichever standard you choose, the core design principle is the same: strip "device capability" away from "device implementation" — a thing model defines "what it can do," not "how it is done." This abstraction lets application-layer developers concern themselves only with property values, service calls, and event reception, without having to understand whether the layer below is Modbus RTU or CoAP.
In open-source platforms such as IoT DC3, the thing-model concept is implemented in practice under the name Profile. The platform provides a set of RESTful APIs to manage thing models: add, update, query, and delete. A device instance binds to the Profile of its model, so when the application layer accesses a device it no longer faces the raw protocol; it reads standardized property values or triggers services through the Profile interface. This echoes exactly the direction of sensing-layer evolution proposed at the start of this chapter — from "collecting data" to "abstracting capability." The thing model condenses the endless variety of the physical world into a set of programmable interfaces, so that application-layer engineers can interact with physical devices the way they call a function, without understanding the communication details behind every kind of sensor.
A DC3-style Profile can be as compact as a dozen or so lines of JSON. Taking the temperature sensor used repeatedly throughout this chapter as an example, its minimal skeleton is as follows:
{
"name": "Wireless Temperature Sensor T-100",
"description": "A battery-powered temperature sensor for cold-chain warehousing, accuracy ±0.1°C",
"properties": [
{ "name": "currentTemperature", "type": "double", "unit": "℃", "accessMode": "r" },
{ "name": "maxAlarmThreshold", "type": "double", "unit": "℃", "accessMode": "rw" }
],
"services": [
{ "name": "calibrateSensor", "invocation": "async", "input": { "referenceTemperature": "double" } }
],
"events": [
{ "name": "overTemperatureAlarm", "data": { "currentTemperature": "double", "timestamp": "string" } }
]
}Reading it side by side with the W3C WoT TD makes the correspondence clear: the two express the same capability contract — a Profile's properties correspond to TD properties, services to actions, and events to events. The difference lies in the level of detail: WoT TD uses fields such as @context, forms, and security to carry semantic annotations, protocol bindings, and security schemes, aiming at cross-platform interoperability; the DC3 Profile targets management within the platform and keeps only the minimal required fields — a property is a point, a service is a command the platform can dispatch, and an event hooks into the alarm channel. invocation: "async" marks the service's asynchronous invocation mode, a point Section 3.7.2 takes up again.
The thing model is not icing on the cake. Without it, connecting every new device category to the platform is like solving a fresh puzzle; with it, device onboarding becomes a matter of filling in a form — the vendor simply maps its device capabilities onto an existing thing-model template, or adds a new template for a new model. This is the engineering cornerstone of deploying IoT systems at scale: it brings the interoperability cost down from "custom work per device" to "model once, reuse without limit." What is given above is only the minimal skeleton of a Profile; what other design considerations does a complete thing-model document involve in engineering? Section 3.7.2 will use a concrete temperature sensor example to demonstrate how to define a Profile JSON document. As for how the thing model serves as the interface through which AI agents interact with the physical world, we will explore that in depth in Chapter 7, on AIoT and agent applications.
3.7.2 A Thing-Model Design Example: The Temperature Sensor
Let us get hands-on and define a thing model for a common IoT device — the temperature sensor. This gives you a direct look at how the concepts from the previous section land in practice.
Suppose you are responsible for designing the thing model for Model-T-100, a wireless temperature sensor for cold-chain warehousing. It reports the temperature every 30 seconds with 0.1 °C accuracy, supports remote calibration, and proactively reports an alarm when the temperature moves outside a preset range. This scenario is a good vehicle for demonstrating the core structure of a thing model.
Properties, Services, and Events: A Device Capability Card
A thing model is, in essence, a "device capability card." Referring to the industry-mainstream W3C Web of Things Thing Description (WoT TD) specification, and to the way IoT DC3 defines thing models, this card needs to describe three kinds of capability:
- Properties: state variables of the device that can be read or set. For example,
Current Temperature(read-only) andMin/Max Temperature Alarm Thresholds(writable). - Services: remote operations the device can execute. For example,
Calibrate SensorandReset to Factory Defaults. These are usually processes that may take some time and return an execution result. - Events: messages the device emits on its own initiative to notify that a condition has been triggered. For example,
Temperature Excursion Alarm— the moment the sensor reading leaves the range, a message is pushed to the platform.
The value of the thing model is this: it defines the widely differing capabilities of devices uniformly, through these three standard kinds of interface, as a machine-parseable template. A platform developer who can read this template can interact with any device that conforms to it, without caring about the device's hardware differences.
Below, the thing model for the Model-T-100 is written in JSON (JavaScript Object Notation), based on the core structure of the W3C WoT TD with some simplification:
{
"@context": "https://www.w3.org/2019/wot/td/v1",
"id": "urn:dev:profile:temperature-sensor:t-100:v1",
"title": "Wireless Temperature Sensor T-100",
"description": "A battery-powered temperature sensor for cold chain monitoring, accuracy ±0.1°C.",
"@type": "TemperatureSensor",
"properties": {
"currentTemperature": {
"title": "Current Temperature",
"type": "number",
"unit": "celsius",
"readOnly": true,
"minimum": -40,
"maximum": 85
},
"minAlarmThreshold": {
"title": "Minimum Alarm Threshold",
"type": "number",
"unit": "celsius",
"readOnly": false,
"minimum": -40,
"maximum": 85
},
"maxAlarmThreshold": {
"title": "Maximum Alarm Threshold",
"type": "number",
"unit": "celsius",
"readOnly": false,
"minimum": -40,
"maximum": 85
},
"batteryLevel": {
"title": "Battery Level",
"type": "integer",
"unit": "percent",
"readOnly": true,
"minimum": 0,
"maximum": 100
}
},
"actions": {
"calibrateSensor": {
"title": "Calibrate Sensor",
"description": "One-point calibration using a reference temperature. The device compares its reading with the provided value and adjusts the offset.",
"input": {
"type": "object",
"properties": { "referenceTemperature": { "type": "number" } },
"required": ["referenceTemperature"]
},
"output": {
"type": "object",
"properties": {
"status": { "type": "string", "enum": ["success", "failure"] },
"adjustedOffset": { "type": "number" }
}
}
},
"resetToFactory": {
"title": "Reset to Factory Defaults",
"input": { "type": "null" },
"output": {
"type": "object",
"properties": { "status": { "type": "string", "enum": ["success", "failure"] } }
}
}
},
"events": {
"overTemperatureAlarm": {
"title": "Over-Temperature Alarm",
"data": {
"type": "object",
"properties": {
"currentTemperature": { "type": "number" },
"thresholdValue": { "type": "number" },
"timestamp": { "type": "string", "format": "date-time" }
}
}
},
"underTemperatureAlarm": {
"title": "Under-Temperature Alarm",
"data": {
"type": "object",
"properties": {
"currentTemperature": { "type": "number" },
"thresholdValue": { "type": "number" },
"timestamp": { "type": "string", "format": "date-time" }
}
}
}
},
"links": {
"properties": "mqtt://broker.iot.example.com/devices/t-100-001/properties",
"actions": "mqtt://broker.iot.example.com/devices/t-100-001/actions",
"events": "mqtt://broker.iot.example.com/devices/t-100-001/events"
}
}This JSON file defines clearly:
- The sensor has 4 properties, of which
currentTemperatureandbatteryLevelare read-only and the two alarm thresholds are writable. The platform can change the device's behavior by modifying these properties. - It supports 2 services:
calibrateSensortakes a reference temperature as input and returns the calibration result;resetToFactoryneeds no input and returns a status after execution. - It can proactively report 2 events: an over-temperature and an under-temperature alarm. Each event carries the temperature at that moment, the threshold, and a timestamp.
The figure below shows the relationship between the thing model as template and device instances, and the interaction patterns of the three kinds of capability on the platform side.
Engineering Trade-offs: Three Design-Time Decisions
The example above looks straightforward, but in real projects the following trade-offs need careful thought.
1. Choosing property granularity
Should each threshold stand as its own property, or should all configuration items be merged into one JSON object property? In the example, minAlarmThreshold and maxAlarmThreshold are defined separately; the benefit is that the platform can modify one of them alone, without reading and writing the whole configuration object. If there are very many configuration items (a dozen or so, say), defining them separately makes the property list unwieldy, and a composite property (such as alarmConfig, of type object) can be considered for managing them. The key point: frequent read/write operations should use fine-grained properties, while low-frequency bulk configuration suits composite properties.
2. Synchronous and asynchronous services
The calibrateSensor in the example has both input and output, so it looks synchronous. In many IoT scenarios, however, executing a service may take seconds or longer, and the device cannot return the result in real time. The command model in IoT DC3 is asynchronous by design: after the platform issues a command, the device replies with the execution result on a separate, independent channel. When designing a service, you must mark its invocation mode explicitly. You can add an extension field to the actions definition, such as "invocation": "async", and document the timeout and the callback mechanism.
3. The data payload of events
The excursion alarm events carry three fields: currentTemperature, thresholdValue, and timestamp. If events carry too much data, network overhead and platform load grow. You need to judge which facts the downstream alarm system must know immediately, and which can be fetched later through follow-up interfaces. An alarm event, for example, could carry only deviceId, eventType, and a timestamp, while the device caches the detailed temperature trend data locally for the platform to pull later through properties. This is a classic bandwidth vs. real-time trade-off.
From Thing Model to Platform Interaction
Once the thing-model definition is complete, the platform can generate the data storage model, the API interfaces, and the UI controls from it automatically. IoT DC3 provides a corresponding /profile API to manage thing models (add, query, delete, and so on). When a device connects, it only has to declare the ID of the thing model it belongs to (such as urn:dev:profile:temperature-sensor:t-100:v1), and the platform automatically knows which properties the device has, which services it supports, and which events it can report — no extra adaptation code is needed. Designing a thing model is not describing "the state of one particular device at this moment"; it is defining "everything this class of device can do." A well-designed thing model makes upper-layer application development simpler, and it lets the platform, when onboarding a new device model, parse one new "capability card" instead of rewriting a whole set of adaptation code. This idea will come through even more clearly in the next section, on cross-platform data integration.
As for how upper-layer AI agents invoke the thing model, we will discuss that in depth in Chapter 7.
3.7.3 The Thing Model in Practice: Data Interoperability
The previous section defined a capability card of properties, services, and events for the temperature sensor, giving the devices of one model a unified description. In real projects, however, it is rare to connect only one kind of device: Vendor A's temperature-humidity transmitters speak Modbus RTU, with the temperature expressed in hexadecimal in bytes 3–4; Vendor B's air-conditioning controllers use the KNX bus, where the temperature setpoint corresponds to a communication object number; Vendor C's smart meters follow the DL/T645 protocol, with data identifiers nested layer upon layer. Every new brand that comes in means the application team must learn a proprietary protocol, write parsing code, and debug point mappings over and over. The core problem the thing model is really meant to solve is exactly this "unification of heterogeneous data" — letting physical quantities from disparate sources converge into one semantic space.
The thing model plays three engineering roles in data interoperability.
The semantic adaptation layer closes the protocol gap. The thing model abstracts device capabilities into three categories — properties (Property, i.e. point values), services (Service), and events (Event) — which is close to the logic the W3C Web of Things Thing Description uses to classify device capabilities. Vendor A's sensor outputs the hexadecimal frame "00 64"; the adaptation layer concatenates the two bytes big-endian into 0x0064 — decimal 100 — and multiplies by the 0.1 coefficient field to obtain 10.0 °C. Vendor B's air conditioner's KNX data point "9.001" likewise expresses a standard floating-point temperature value. Through the thing model, the "temperature" property of both is assigned to the same point. When the upper-layer application reads a temperature value, it does not need to know at all whether the raw data came from a Modbus register, a KNX communication object, or a DL/T645 data identifier. This adaptation is usually implemented once, on the edge gateway or in the device driver layer; later devices of the same model reuse the same mapping set, with no repeated coding.
The device shadow dissolves synchronous coupling. IoT devices are inevitably offline at times — low-power nodes sleep for most of their life, or field network jitter breaks the connection. If every command had to wait for the device to be online, business processes would be dragged to a standstill. The device shadow is the buffer: the platform holds the device's latest thing-model state; the application layer performs a write on one of the shadow's properties (say, "setpoint temperature"), and the shadow records the desired value. When the device next comes online, it actively pulls the desired value from the shadow, compares it with its current state, and synchronizes whenever it finds a difference. Writes are no longer blocked by the device's online status, and the synchronization problem becomes asynchronous state management. This is the most direct engineering payoff of the thing model for platform decoupling — the application is unaware of the device's online status, and the device is unaware of the application's call timing.
It reduces application coupling to protocols. Suppose a building energy-optimization strategy needs to read the supply-air temperature on every floor. If different devices map to compatible thing models, the core computation can be reused. Units, accuracy, sampling intervals, quality marks, and writable ranges may still differ, however, so contract and field validation remain mandatory before deployment. A thing model reduces protocol-adaptation code; it does not "completely decouple" business logic from infrastructure.
One flow makes this data interoperability easier to see: a temperature sensor reports its raw message → the thing-model adapter on the edge gateway parses out "temperature = 25.3 °C, humidity = 60.2%RH" and updates the device shadow → the cloud platform application reads the property values from the shadow through the same thing model. Throughout the process, the application never touches any proprietary communication detail; the data carries a clear semantic label all the way up from the sensing layer. Real-time monitoring panels, alarm rules, and energy reports can all converse directly within this unified semantic space, with no separate handling for each vendor's private format. In platforms such as IoT DC3, the thing model is managed through the /profile family of REST APIs, follows the design principle that "one device belongs to one thing model, and multiple devices of the same model reuse one thing model," and integrates the adaptation-layer logic into the device access module, making the thing model the semantic anchor of the entire data flow. When the AI layer later needs to invoke device capabilities, it too reads and writes properties and calls services through the thing model, instead of dealing with protocol fragmentation all over again — this is the key engineering foundation for a unified data model from the sensing layer to the intelligence layer.