Skip to content

Getting started ​

On a Brain that came from the yard, this is already installed, already running, and already feeding the alarm page. This chapter is for a Brain being commissioned, or for confirming that a working one really is working.

Prerequisites ​

  • An MQTT broker on 127.0.0.1:1883. The daemon connects there and nowhere else, and the unit orders itself After=mosquitto.service.
  • muxen-boat ≥ 5.0.0, which is what turns MUXEN CAN frames into the device/… MQTT topics the daemon reads. It is a Recommends: of the package, not a hard dependency — the daemon starts and serves an empty list without it.
  • muxen-uds on PATH, for the once-a-minute offline scan. Without it the scan fails and logs a line; nothing else breaks.
  • muxen-systemd, a hard dependency: it provides muxen.target, and the muxen system user the daemon runs as.
  • nginx, if screens or off-Brain clients are to reach the API. The daemon binds the loopback interface only.

Install ​

sh
sudo apt install muxen-alarms

That pulls in muxen-alarms-database of the same version — the two are pinned to each other — and installs:

PathWhat
/usr/bin/muxen-alarmsdthe daemon
/usr/bin/muxen-dtcthe command-line viewer
/usr/bin/muxen-alarms-test-audiothe speaker test
/usr/lib/systemd/system/muxen-alarms.servicethe unit
/etc/nginx/snippets/muxen-api-alarms.confthe REST proxy snippet
/etc/nginx/snippets/muxen-ws-alarms.confthe WebSocket proxy snippet
/usr/share/muxen-alarms/dtc.jsonthe alarm code catalogue
/usr/share/muxen-alarms/functions.jsonthe equipment names in the other languages
/usr/share/muxen-alarms/swagger/the Swagger UI

The mDNS announcement is a separate package, and optional:

sh
sudo apt install muxen-alarms-mdns

It installs /etc/avahi/services/muxen-alarms.service and pulls in avahi-daemon and avahi-autoipd. Install it on a Brain that must be findable by a client on the same network — a laptop, a tablet — with no DHCP server and no configured address.

Enable and start ​

sh
sudo systemctl enable --now muxen-alarms.service
systemctl status muxen-alarms

The unit is WantedBy=muxen.target and PartOf=muxen.target, so on a Brain where that target is already in use, enabling it is enough and the usual MUXEN restart applies. The package also activates the muxen-restart-target dpkg trigger, so installing or upgrading it restarts muxen.target by itself, and the nginx-reload trigger, so a running nginx picks up the new snippets at the same time.

Verify it is working ​

1. The daemon started and knows where its catalogue is.

sh
$ journalctl -u muxen-alarms -n 20 --no-pager
config: verbose = 0
config: wsPort = 12001
config: data source = mqtt
config: dtc = /usr/share/muxen-alarms/dtc.json
config: functions = /usr/share/muxen-alarms/functions.json
config: swaggerFolder = /usr/share/muxen-alarms/swagger
main: mqtt connected

Those config: lines are printed on every start. main: mqtt connected is the one to look for: without it the daemon is running but blind, and it will be raising alarm 65001 against itself.

2. The API answers.

sh
$ curl -s http://127.0.0.1:12001/api/dtcs
{
  "type": "dtcs",
  "dtcs": [],
  "active": 0,
  "muted": 0
}

An empty list with active: 0 on a healthy boat is the expected answer. An empty list is not proof of health on its own — check that 65001 is absent, which is the same thing as checking active is 0.

3. It is reachable through nginx. The two snippets have to be included by a site configuration; they are not active on their own.

sh
curl -s http://127.0.0.1/api/alarms/dtcs

4. The live feed works. Point any WebSocket client at the daemon's root — ws://127.0.0.1:12001/ locally, ws://<brain>/ws/alarms through nginx. The daemon sends the whole list to a client as it connects, then to every connected client on each change and every 5 seconds, and reads nothing back: the socket is one-way.

Read the list ​

GET /api/alarms/dtcs returns one envelope:

json
{
  "type": "dtcs",
  "dtcs": [
    {
      "alarmCode": 1,
      "alarmDescription": "Low voltage",
      "alarmDescription.FR": "Sous-tension",
      "severity": "warning",
      "deviceId": 320,
      "functionCode": 5,
      "functionName": "Power source battery",
      "functionName.FR": "Batterie",
      "functionName.DE": "Batterie",
      "instance": 0,
      "creationTime": "2026-08-16T09:14:02Z",
      "expireTime": "2026-08-16T09:14:31Z",
      "muted": false
    }
  ],
  "active": 1,
  "muted": 0
}

Reading it:

  • active and muted are the two numbers a screen needs. active is the count that is not silenced; a client that wants "is anything wrong" reads that one.
  • deviceId identifies the box. functionCode and instance are the two halves of it — deviceId = functionCode * 64 + instance — and functionName is the readable form of the function, in English; functionName.FR, functionName.DE, … are the same in other languages (shortened here to two).
  • creationTime is when the fault started, not when it was last seen. expireTime is 15 seconds after the last report; it moves forward as long as the fault persists.
  • alarmDescription and severity come from the catalogue. unknown in both means the code is not in it — see The alarm catalogue.

The full field list is The HTTP and WebSocket API, and the machine -readable form is dtc-schema.json.

Watch alarms arrive, from a shell ​

muxen-dtc prints alarm messages as they land on MQTT. It is the fastest way to tell "the daemon is not showing it" from "nothing is sending it".

sh
$ muxen-dtc -f
config: verbose = 0
config: flow = 1
config: expired = 0
Topic                   Code    Date                            Device type
device/5/0/error/1      1       2026-08-16T09:14:02Z            (Battery)

Without -f it listens for one second and exits, which is what a script wants. -e also prints reports whose own expireAfterSec has already passed — useful when the Brain's clock is suspect.

Place a first mute ​

Muting is a PUT with at least one of deviceId, functionCode or code. An omitted field matches anything.

sh
# silence alarm code 1 from device 320 for an hour (the default)
curl -X PUT http://127.0.0.1:12001/api/filter \
     -H 'Content-Type: application/json' \
     -d '{"deviceId": 320, "code": 1}'
sh
# every alarm from every battery, for ten minutes
curl -X PUT http://127.0.0.1:12001/api/filter \
     -H 'Content-Type: application/json' \
     -d '{"functionCode": 5, "duration": 600}'

The alarm stays in dtcs[], gains "muted": true and a filterBy object naming the filter that silenced it, and moves from the active count to the muted count.

List and remove:

sh
curl -s     http://127.0.0.1:12001/api/filter
curl -X DELETE http://127.0.0.1:12001/api/filter/320-000-000001
curl -X DELETE http://127.0.0.1:12001/api/filter          # all of them

The identifier is derived from the three fields, not random — see The life of an alarm, which also explains why two different-looking mutes can end up sharing one.

The minimum configuration ​

There is almost none. The daemon takes no configuration file of its own, and the shipped unit passes it a single flag, --mqtt. Two files elsewhere on the Brain change what it does:

FileEffect
/usr/share/muxen-alarms/dtc.jsonthe code catalogue. Shipped by muxen-alarms-database; edit it only to test
/etc/muxen/deploy.jsonthe device list the offline scan compares against, and the per-device DisableOfflineAlarm opt-out

Offline detection is the one part that needs commissioning attention: the scan raises an alarm for every device in deploy.json that does not answer, so a stale deployment file produces alarms for equipment that was never fitted. Devices that go quiet, and the daemon's own health covers it.

Where to go next ​

Integration of multiplexed solutions
MUXEN and the MUXEN logo are trademarks of MUXEN SAS.