Appearance
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 itselfAfter=mosquitto.service. muxen-boat≥ 5.0.0, which is what turns MUXEN CAN frames into thedevice/…MQTT topics the daemon reads. It is aRecommends:of the package, not a hard dependency — the daemon starts and serves an empty list without it.muxen-udsonPATH, 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 providesmuxen.target, and themuxensystem 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-alarmsThat pulls in muxen-alarms-database of the same version — the two are pinned to each other — and installs:
| Path | What |
|---|---|
/usr/bin/muxen-alarmsd | the daemon |
/usr/bin/muxen-dtc | the command-line viewer |
/usr/bin/muxen-alarms-test-audio | the speaker test |
/usr/lib/systemd/system/muxen-alarms.service | the unit |
/etc/nginx/snippets/muxen-api-alarms.conf | the REST proxy snippet |
/etc/nginx/snippets/muxen-ws-alarms.conf | the WebSocket proxy snippet |
/usr/share/muxen-alarms/dtc.json | the alarm code catalogue |
/usr/share/muxen-alarms/functions.json | the 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-mdnsIt 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-alarmsThe 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 connectedThose 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/dtcs4. 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:
activeandmutedare the two numbers a screen needs.activeis the count that is not silenced; a client that wants "is anything wrong" reads that one.deviceIdidentifies the box.functionCodeandinstanceare the two halves of it —deviceId = functionCode * 64 + instance— andfunctionNameis the readable form of the function, in English;functionName.FR,functionName.DE, … are the same in other languages (shortened here to two).creationTimeis when the fault started, not when it was last seen.expireTimeis 15 seconds after the last report; it moves forward as long as the fault persists.alarmDescriptionandseveritycome from the catalogue.unknownin 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 themThe 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:
| File | Effect |
|---|---|
/usr/share/muxen-alarms/dtc.json | the code catalogue. Shipped by muxen-alarms-database; edit it only to test |
/etc/muxen/deploy.json | the 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
- What an alarm does from arrival to disappearance — The life of an alarm
- The 65000 and 65001 alarms — Devices that go quiet, and the daemon's own health
- What the codes mean — The alarm catalogue
- When something does not work — Troubleshooting
