## Intro

## Represented Devices

Some topics in an MQTT tree really represent a device — a handful of sub-topics that, taken together, describe one physical thing (its name, its IP, whether it is online, …). Masqott can recognise such groups and collapse them into a single, readable summary line on the parent topic instead of making you drill into every sub-topic.

Which groups count as a device, and how their summary is rendered, is described by device definitions. Masqott ships with a built-in set, and you can add your own by creating the file

    ~/Library/Containers/com.bitart.Masqott/Data/Library/Application Support/Masqott/RepresentedDevices.json

(Masqott is sandboxed, so its Application Support folder lives inside the app's container rather than directly under `~/Library`.)

Your definitions are merged with the built-in ones and take precedence: recognition tries each definition in turn — your own first, then the built-in ones — and uses the first one that matches. So giving one of your definitions the same structure as a built-in lets you override how that device is rendered. The file is read once at startup; restart Masqott after editing it.

### File structure

The file is a JSON array of device-definition objects:

    [
        {
            "identifier": "my-device",
            "matchPaths": ["status", "info"],
            "format": "[c:%@][s:gearshape.fill][c:] %@",
            "fields": [
                { "path": "status", "valueKeyPath": "latestMessage.payload",
                  "map": { "online": "LimeGreen", "default": "FireBrick" }, "optional": true },
                { "path": "info", "valueKeyPath": "latestMessage.json.name" }
            ]
        }
    ]

### Device definition

Key | Type | Description
--- | --- | ---
`identifier` | string | A unique name for this device type. Used for your own reference.
`matchPaths` | array of string | Sub-topic paths (relative to the candidate topic, `/`-separated) that must all exist for the topic to be recognised as this device. Recognition is purely structural — it only checks that the sub-topics are present, not their content.
`format` | string | The summary template. Each `%@` placeholder is replaced, in order, by the value of the corresponding entry in `fields`. Supports the markup described below.
`fields` | array | Ordered list of value definitions; the first fills the first `%@`, and so on.

### Field definition

Key | Type | Description
--- | --- | ---
`path` | string | Sub-topic path (relative to the device topic) of the topic the value is read from.
`valueKeyPath` | string | A key path evaluated on that sub-topic to obtain the value (see "Value key paths").
`map` | object | Optional. Translates the extracted value to the text that is shown. The (lower-cased) value is looked up among the keys; if no key matches, the `default` key is used; if there is no `map`, the raw value is shown as-is. Handy for turning a status into a colour, an enum into a label, etc.
`optional` | boolean | Optional, default `false`. When `false` (required), a missing or empty value cancels the whole summary and the topic falls back to its normal display. When `true`, a missing value is allowed — typically paired with a `map` `default`.

### Value key paths

`valueKeyPath` is evaluated against the sub-topic named by `path`. The most useful starting points are:

Key path | Yields
--- | ---
`latestMessage.payload` | The topic's latest message payload, as text.
`latestMessage.payloadFirstTerm` | The first whitespace-separated word of the payload (e.g. a version string without trailing build info).
`latestMessage.json.KEY` | A value from a JSON payload. Nest deeper with `latestMessage.json.KEY.SUBKEY`.
`latestMessage.homeAssistantSummary` | The Home Assistant discovery summary (only present on `…/config` topics).

A key path that cannot be resolved simply yields no value (treated the same as a missing value), so a typo or an unexpected payload will never crash the summary — at worst the field is empty.

### Markup in `format` and `map`

The `format` string — and any text produced by a `map` — may use Masqott's inline markup:

Markup | Effect
--- | ---
`[c:NAME]` | Set the text colour. `NAME` may be a colour name (`LimeGreen`, `FireBrick`, …) or a hex value (`#19bcf2`).
`[c:]` | Reset to the default colour.
`[s:SYMBOL]` | Insert a symbol. `SYMBOL` is an SF Symbol name (`gearshape.fill`, `antenna.radiowaves.left.and.right`, …) or one of the app's named symbols (`hass`).

A common pattern is to drive a symbol's colour from an online/status field: make the first field produce a colour via `map`, and begin the format with `[c:%@][s:…][c:]`.

### Worked example

    {
        "identifier": "openmqttgateway",
        "matchPaths": ["SYStoMQTT"],
        "format": "[c:%@][s:gearshape.fill][c:] %@ — %@",
        "fields": [
            { "path": "LWT", "valueKeyPath": "latestMessage.payload",
              "map": { "online": "LimeGreen", "default": "FireBrick" }, "optional": true },
            { "path": "SYStoMQTT", "valueKeyPath": "latestMessage.json.ip" },
            { "path": "SYStoMQTT", "valueKeyPath": "latestMessage.json.version" }
        ]
    }

This recognises any topic that has a `SYStoMQTT` sub-topic. It then shows a gear icon coloured green when the `LWT` topic reads `online` (red otherwise, including when `LWT` is absent), followed by the `ip` and `version` pulled from the JSON published under `SYStoMQTT`, separated by an em-dash. Because `ip` and `version` are required (not `optional`), the device line only appears once that JSON is available; until then the topic shows its normal summary.

Copied to clipboard