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
A thing model can serve as input to storage mapping, API contracts, and UI-control generation, but those artifacts appear automatically only where the platform implements the corresponding generators. IoT DC3 manages Profiles and their point definitions through /profile, while device metadata references a Profile so the platform knows the data points and read/write properties of that device class. A new device still needs a protocol Driver, packet parsing, point mapping, unit conversion, credentials, and field acceptance; declaring a Profile ID does not create zero-adaptation onboarding. A thing model defines the versioned capability contract of a device class rather than one device's current state. It reduces repeated protocol interpretation in upper-layer applications, but compatibility still requires contract tests.
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.
In data interoperability, the thing model is first a semantic contract. A device shadow is a separate runtime pattern that may reuse that contract; the two must not be treated as one capability. The following three positions clarify their engineering relationship.
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.
A device shadow can optionally decouple synchronization. A shadow commonly reuses thing-model fields to maintain desired and reported state. While a device is offline, an application writes a versioned desired value with an expiry; after reconnecting, the device pulls and validates it, executes it, and reports actual state. This turns synchronous waiting into an explicit asynchronous state machine, while introducing stale commands, conflict merging, duplicate execution, and recovery-order problems. A shadow is not an inherent feature of a thing model, nor does it make device offline state irrelevant. The platform and Driver must state whether they implement it and define versioning, idempotency, receipts, and human takeover. The current IoT DC3 implementation should not be described as providing a universal Device Shadow.
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 the interoperability boundary clearer: a temperature sensor reports a raw message → a Driver maps it through the Profile into "temperature = 25.3 °C, humidity = 60.2%RH" with time and quality context → Data stores the point values → an upper-layer application reads them through a unified interface. The application no longer handles every vendor's byte format, but it must still understand units, sampling intervals, quality, and freshness. If a device shadow is implemented separately, it belongs on the command-and-state synchronization path rather than being a required stage in this acquisition path. IoT DC3 manages Profiles through the /profile REST APIs, and devices of the same kind can reuse a definition. A later AI layer may only read point values through authorized Tools or submit an Action pending confirmation; a unified thing model is not unconditional permission to read or write a device.