MQTT
Follow values over MQTT, switch devices and run scenes
The controller runs its own MQTT broker and is connected to it itself. Through it, the controller publishes every change of a device value and accepts switching commands and scene calls. A third-party system only needs an MQTT client for this, no knowledge of the Socket.io API.
You switch the broker on in the configuration interface, which is also where you set the credentials for your client: MQTT in the integrator handbook. Since version 3.0.0 the controller only uses its own broker; an external broker can no longer be configured.
DANGER
MQTT has no user permissions.
Whoever may publish to the broker can switch every device and run every scene. The room and scene permissions of nomos users do not apply over MQTT.
- Always run the broker with authentication. Without it, the controller's security check reports CVE-24-0012 "MQTT broker has no authentication configured".
- Only hand the credentials to systems you would trust like an administrator.
- The broker listens unencrypted on port 1883. Only use it within your own, trusted network.
Connection
| Address | mqtt://<controller-ip>:1883 |
| Authentication | user name and password from the MQTT settings |
| Encryption | none (no TLS) |
The configuration interface shows the address for your client as "Broker URL". Over the API, getMQTTStatus returns it in the field clientBrokerUrl, together with the three topic prefixes and the controller's own connection state:
curl -s -X POST http://<controller-ip>/api/v1/getMQTTStatus \
-H 'auth-username: <user>' -H 'auth-password: <password>'{"statusTopic":"nomos/status","subscriberTopic":"nomos/in","publisherTopic":"nomos/out",
"brokerUrl":"mqtt://127.0.0.1:1883","clientBrokerUrl":"mqtt://192.168.0.199:1883",
"status":"Connected","connected":true}brokerUrl is the address the controller connects to itself; your client uses clientBrokerUrl. How to authenticate against the HTTP API is described under HTTP API.
Topics
Every topic contains the controller's serial number (SID), for example NS123456789. Every component returned by getAllComponents carries it in the field sid. CIDs and property names are explained in the data model.
| Direction | Topic | Payload |
|---|---|---|
| controller → client | nomos/out/<SID>/component/<cid>/<property> | new value as text |
| client → controller | nomos/in/<SID>/component/<cid>/<property> | value to set |
| client → controller | nomos/in/<SID>/scene/<id> | anything, not evaluated |
| controller → client | nomos/status/<SID> | online or offline |
The controller publishes with QoS 0 and without the retain flag. A client that connects later therefore gets no last known state, only the changes from the moment it subscribed. Fetch the current state of all values once over the API, for example with getAllComponents.
Following values
Every change of a value, such as state, level or temperature, appears on nomos/out/<SID>/component/<cid>/<property>. The value is text: true/false for switching states, numbers such as 21.5. Values with a unit come in the units chosen in the system settings, as in the app.
Setting values
A message on nomos/in/<SID>/component/<cid>/<property> sets the property like componentUpdate. The payload is read as JSON when that succeeds (true, 42, "text"), otherwise as text. Which properties a component accepts, and which values, is part of its description from getAllComponents.
There is no reply. Whether the command arrived shows in the changed value on nomos/out/…. Messages with a different SID or an incomplete topic are dropped by the controller without an answer.
Running scenes
A message on nomos/in/<SID>/scene/<id> runs the scene with that ID. The payload does not matter. getScenes returns the IDs.
Controller status
When it connects, the controller publishes online on nomos/status/<SID>. offline is registered as last will: the broker sends it when the controller's connection breaks off. When the controller closes the connection in an orderly way, for instance when the skill is switched off, no offline is sent. Since neither message is retained, the topic is only useful to clients that stay connected.
Examples
The examples use mosquitto_sub and mosquitto_pub from the Mosquitto clients. Replace address, credentials, SID and CID with your own.
Follow all values and the status:
mosquitto_sub -h <controller-ip> -u <mqtt-user> -P <mqtt-password> -v \
-t 'nomos/out/<SID>/#' -t 'nomos/status/<SID>'Switch a device on and off again:
mosquitto_pub -h <controller-ip> -u <mqtt-user> -P <mqtt-password> \
-t 'nomos/in/<SID>/component/C5/state' -m true
mosquitto_pub -h <controller-ip> -u <mqtt-user> -P <mqtt-password> \
-t 'nomos/in/<SID>/component/C5/state' -m falsemosquitto_sub shows:
nomos/out/NS123456789/component/C5/state true
nomos/out/NS123456789/component/C5/state falseRun a scene:
mosquitto_pub -h <controller-ip> -u <mqtt-user> -P <mqtt-password> \
-t 'nomos/in/<SID>/scene/17' -nLimits
- MQTT only knows components and scenes. Rooms, automations, users and settings are reached through the Socket.io and HTTP API.
- There are no replies and no error messages. If you need a confirmation, follow the value on
nomos/out/…. - Other topics on the broker, such as
nomos/wiser/…,nomos/zeptrion/…or the topics of MQTT devices, belong to the controller's integrations. They are not an interface for your own applications.