Skip to content
AI-driventedge-dot is developed largely by AI coding agents, and it is alpha software. Check what it does before you rely on it, and report what you find.

A fleet of identical devices

Every ACME meter of the same firmware has the same registers; only its address differs. Writing the full point list into every [[device]] means copying it 50 times and fixing it 50 times. A point library keeps that list in one file, and each device references it.

What you’ll build: a library for one device type, a configuration where each instance is four lines long, a site-specific adjustment that survives package upgrades, and a device added at runtime from its address alone.

A point library is the point list of one device type, in its own TOML file, with no connection information at all. It holds only a [library] table and [[point]] entries. A device references one or more libraries with points_from, and declares only what is per-instance:

[[device]]
name = "plc-7"
protocol_address = { transport = "tcp", host = "192.168.0.17", port = 502, unit_id = 1 }
points_from = ["acme-meter-v2"] # points.d/modbus/acme-meter-v2.toml

References are resolved when the configuration is loaded. A protocol module, a flow and the samples see no difference between an inherited point and one written inline.

  1. Start from the [library] table. protocol must match the connector that loads it, and type names the device type the points describe. This is the header of the demo Modbus library that ships with the package:

    demo/points.d/modbus/demo-sim.toml
    [library]
    protocol = "modbus"
    # The device *type* this list describes (§3.1): it names the parameter sets of every
    # device that references this library, which is what keeps two device types on the
    # same protocol from sharing a (tenant-wide) Cumulocity identifier.
    type = "modbus-demo-sim"
    description = "pymodbus simulator, as seeded by demo/docker-compose.yaml's modbus-sim"
    version = "1"
    View demo/points.d/modbus/demo-sim.toml on GitHub
  2. Add the points, with exactly the fields a [[device.point]] takes. Give them a name and a description: declared once here, they reach every instance, the Cumulocity parameter UI and the connector’s retained capability descriptor. A sensible reporting policy for the device type belongs here too:

    demo/points.d/modbus/demo-sim.toml
    [[point]]
    id = "temp_scaled"
    name = "Boiler temperature"
    description = "Measured boiler loop temperature, scaled to °C"
    datatype = "uint16"
    address = { table = "holding", address = 3, count = 1 }
    transform = { decimal_shift = -3 }
    unit = "°C"
    report = { deadband = 0.5, min_interval = "10s" }
    View demo/points.d/modbus/demo-sim.toml on GitHub
  3. Install it where the connector looks. A name in points_from resolves to <dir>/<protocol>/<name>.toml, in each directory of the search path, first match winning:

    DirectoryFor
    /etc/tedge/plugins/ot/points.dyour own libraries; they shadow packaged ones of the same name
    /usr/share/tedge-dot/points.dlibraries shipped by a package

    So a site’s ACME library goes to /etc/tedge/plugins/ot/points.d/modbus/acme-meter-v2.toml.

Each device needs a name, an address and the reference. The packaged Modbus default config documents the pattern, including a per-instance adjustment:

packaging/config/modbus.toml (commented example)
# ...or, when several devices share one device type's point list, keep that list
# in a POINT LIBRARY and give the instance only its address:
#
# [[device]]
# name = "plc-2"
# type = "acme-meter-v2" # optional here: the library below declares it
# protocol_address = { transport = "tcp", host = "127.0.0.1", port = 502, unit_id = 2 }
# points_from = ["acme-meter-v2"] # points.d/modbus/acme-meter-v2.toml
#
# # An instance may still add or adjust points without touching the library:
# # a repeated id patches it, and only the fields named here change.
# [[device.point]]
# id = "boiler_temp"
# unit = "K"
View packaging/config/modbus.toml on GitHub

The demo configs work exactly this way: demo/config/modbus.toml gives its device an address and points_from = ["demo-sim"], and nothing else.

Points are collected in order: each library in points_from order, then the device’s own [[device.point]] entries. A definition whose id already exists patches the earlier one:

  • meta, transform and report merge key by key, so one field changes without restating the rest;
  • map is replaced whole (map = {} removes an inherited map);
  • every other field, address included, is replaced when the override declares it;
  • { id = "<point-id>", enabled = false } switches an inherited point off.
[[device]]
name = "meter-3"
points_from = ["acme-meter-v2"]
protocol_address = { transport = "tcp", host = "192.168.0.13", port = 502, unit_id = 1 }
[[device.point]]
id = "flow_rate"
name = "Inlet flow" # only the label changes

Because the device wins over its libraries, a packaged list can be extended and adjusted while the file the package owns, which an upgrade replaces, stays untouched. To edit a packaged library itself, copy it to /etc/tedge/plugins/ot/points.d/ under the same name: the packaged copy is then no longer used. Give it another name if you want both.

The library’s type is inherited by every device that references it (from the first library in points_from that declares one; a type on the [[device]] wins). It does two jobs in the cloud:

  • Parameter set names. Writable points are Cumulocity device parameters, grouped into sets named <device type>_<group>_parameters, for example acme_meter_v2_control_parameters. These names are tenant-wide, so deriving them from the type is what lets two device types on the same protocol coexist. A device without a type falls back to <protocol>_control_parameters, and tedge-dot describe warns about it.
  • Entity type. The registered child device shows up as an acme-meter-v2, not a generic modbus-device.

A point picks its set with meta.parameter.group (relative to the type, and it may be a list of groups), or names a set outright with meta.parameter.set. See contract §5.2 and RFC 0005.

Because the runtime keeps the reference rather than the expanded points, the define-device management command can add an instance from its address alone. Whatever finds devices on your network (mDNS, a subnet scan, an asset inventory) publishes one small command per instance:

Terminal window
tedge mqtt pub -r te/device/main/service/tedge-dot-modbus/ot/cmd/define-device/d1 '{
"status": "init",
"device": {
"name": "plc-7",
"protocol_address": { "transport": "tcp", "host": "192.168.0.17", "port": 502, "unit_id": 1 },
"points_from": ["acme-meter-v2"]
}
}'

The runtime patches the configuration file, which still says points_from = ["acme-meter-v2"] rather than 200 expanded points, and applies it. A reference introduced by a command must be a name, never a path. An unresolvable reference fails loudly, naming every path it looked in: a device never comes up quietly with zero points.