# Specifiche OHMq

Per facilitare l'integrazione di dispositivi di terze parti, compatibili con il protocollo *MQTT*, sono state definite delle specifiche che permettono facilmente di comunicare con un Sistema Domotico *Ohm Automation*.

## Protocollo MQTT

La comunicazione avviene nella rete locale sfruttando il protocollo *MQTT*, e richiede di rispettare un preciso formato per *topic* e *payload*.

### Topic

I topic sono in generale definiti come segue:

```
ohmq/<device_id>/<message_type>/<category>/<item_id>
```

Dove:

* `ohmq`: prefisso necessario.
* `device_id`: ID del dispositivo che desideriamo integrare e che deve essere univoco, per questo è preferibile usare il *MAC address* senza i `:`.
* `message_type`: definisce la tipologia del messaggio, se ad esempio si tratta di un messaggio di stato (`state`) o comando (`command`).
* `category`: se il messaggio è indirizzato ad un elemento specifico, indica la sua categoria (es. `device`, `output`, ecc.).
* `item_id`: ID dell'elemento della scheda a cui è riferito il messaggio.

Ad esempio il topic `ohmq/a861a504ab/command/output/R1` si riferisce ad un messaggio per eseguire un comando sull'output con ID `R1` appartenente al dispositivo `a861a504ab` (in seguito un esempio più specifico).

{% hint style="info" %}
`item_id` indica l'ID dell'elemento, che di fatto viene fatto coincidere con la codifica assegnatagli (scritta magari sul morsetto); non ci sono particolari vincoli sul formato dell'ID, ma deve essere univoco sul dispositivo. Se l'output rappresenta il primo relé della scheda, la sua codifica (quindi il suo ID) potrebbe essere `R1`.
{% endhint %}

Esiste inoltre un topic "universale" a cui si devono sottoscrivere tutte le schede che supportano le specifiche *OHMq*:

```
ohmq/universe
```

I messaggi inviati su questo topic devono essere gestiti da tutti e in particolare tutte le schede devono supportare il [Discovery](#discovery).

### Payload

Il *payload* è in generale in formato JSON e il suo contenuto varia a seconda del tipo di messaggio.

Di seguito le due principali tipologie di messaggio:

* `state`: messaggio di stato.
* `command`: messaggio di comando.

#### **Messaggi di Comando**

I messaggi di comando devono contenere tutti il campo `action`, che permette di definire l'operazione da eseguire, ad esempio per accendere un output avremo:

```json
{
    "action": "on"
}
```

{% hint style="warning" %}
Importante I messaggi di comando devono essere sempre inviati con l'impostazione `retain` su `false`, in quanto non devono essere assolutamente conservati, ma hanno valore solo nell'istante in cui vengono inviati.
{% endhint %}

#### **Messaggi di Stato**

Questi messaggi devono essere inviati per avvisare il Sistema del cambio di stato di un dato elemento, quindi in seguito all'accensione di una luce verrà ad esempio inviato il messaggio:

```json
{
    "on": true,
    "value": 255
}
```

Alla pressione di un pulsante avremo invece:

```json
{
    "value": 1
}
```

{% hint style="warning" %}
Importante I messaggi di stato richiedono che l'impostazione `retain` sia `true`, così da permettere di recuperare lo stato anche in un secondo momento.
{% endhint %}

## Discovery

Tutte le schede devono supportare questa funzionalità, perché rappresenta il modo ufficiale per permettere al sistema di individuare le schede.

Per garantire questa funzione è necessario che le schede effettuino il *subscribe* al topic `ohmq/universe` e rispondano con la propria configurazione al messaggio seguente:

```json
{
    "action": "announce"
}
```

La configurazione dovrà essere inviata in formato JSON e deve essere qualcosa del tipo:

```json
{
    "id": "a861a504ab",
    "name": "Example",
    "model": "ESP32",
    "ip": "192.168.0.x",
    "mac": "00:00:00:00:00",
    "supported_commands": ["find", "set_network", "set_name", "restart"],
    "outputs": [{"address": "R1", "name": "Out 1"}],
    "inputs": [{"address": "I1", "name": "In 1"}],
    "sensors": [{"address": "S1", "name": "Sensor 1"}],
    "virtualInputs": [{"address": "ABC"}]
}
```

Tra i campi quelli che meritano attenzione sono:

* `supported_commands`: indica la tipologia di comandi supportati dalla scheda (es. `find` indica la possibilità di essere localizzata visivamente, magari con un led che lampeggia o cambia colore).
* `outputs` / `inputs`: la lista di output/input gestiti dalla scheda.

Il messaggio contenente la configurazione dovrà essere inviato al topic seguente:

```
ohmq/<device_id>/hi
```

### mDNS

Per permettere una più rapida e facile implementazione è presente il supporto al servizio `_mqtt._tcp.local.` di *mDNS*, così da permettere la connessione al broker usando come hostname `ohmb-master.local` e `1883` come porta.

## Categorie

Queste sono le categorie principali:

* `device`: rappresenta il dispositivo/scheda.
* `output`: per la gestione degli output (uscite).
* `input`: per la gestione degli input digitali (ingressi binari).
* `sensor`: per la gestione degli input analogici.
* `virtualInput`: per la gestione di input virtuali (quindi qualsiasi elemento non propriamente hardware).

### `device`

Il topic che permette di inviare comandi alla scheda è il seguente:

```
ohmq/<device_id>/command/device
```

I comandi devono sempre contenere il parametro `action`, che definisce l'operazione da eseguire. Di seguito le principali operazioni e il relativo *payload*.

#### **`find`**

Questa operazione permette di localizzare fisicamente la scheda, utile se ci sono numerosi dispositivi ed è necessario individuare quello corretto per il cablaggio.

Il messaggio che verrà inviato alla scheda sarà semplicemente:

```json
{
    "action": "find"
}
```

In seguito alla ricezione di questo messaggio è possibile effettuare qualsiasi operazione che permetta l'identificazione visiva del dispositivo, è possibile ad esempio far lampeggiare un led dedicato.

#### **`restart`**

Questa operazione richiede il riavvio della scheda e il messaggio inviato sarà:

```json
{
    "action": "restart"
}
```

#### **`set_name`**

Per garantire una facile identificazione della scheda nell'app di configurazione, è consigliato assegnare un nome che permetta di distinguere facilmente le schede, quindi la scheda deve supportare il comando `set_name` e prevedere una strategia di salvataggio del nome assegnato.

Il messaggio ricevuto dalla scheda sarà una cosa del tipo:

```json
{
    "action": "set_name",
    "name": "Scheda 1"
}
```

### `output`

Ogni output viene definito come segue:

```json
{
    "address": "R1",
    "name": "Luce"
}
```

Dove `address` indica la codifica visibile all'utente e indicata ad esempio sul morsetto, mentre `name` rappresenta il nome che permette una rappresentazione più *user-friendly* nell'app di configurazione.

Quando viene inviata la configurazione della scheda è fondamentale allegare perlomeno il parametro `address`, in quanto viene usato per la comunicazione via MQTT.

#### **Comandi**

Il topic relativo ai comandi inviati ad un output è il seguente:

```
ohmq/<device_id>/command/output/<item_id>
```

Per semplicità e convenienza la scheda può effettuare un subscribe al topic `ohmq/<device_id>/command/output/+`, sfruttando il *wildcard* `+`.

Come nel caso della scheda, i comandi vanno inviati in formato JSON definendo il parametro `action`, e sono:

* `on`: per l'attivazione.
* `off`: per la disattivazione.
* `toggle`: per il cambio di stato.
* `set`: per il set di un valore tra `0` e `255` nel caso di output analogico.
* `preset`: per impostare il valore di un output analogico senza attivarlo, così da assumere il valore desiderato solo in seguito ad una successiva accensione.

Quindi ad esempio:

```json
{
    "action": "set",
    "value": 120
}
```

#### **Stato**

Lo stato dell'output deve essere inviato al topic:

```
ohmq/<device_id>/state/output/<item_id> 
```

{% hint style="warning" %}
È fondamentale inviare lo stato ogniqualvolta l'elemento cambia di valore.
{% endhint %}

Lo stato deve essere inviato come segue:

```json
{
    "is_on": false,
    "value": 210
}
```

Nel caso di output digitale (binario) il valore indicato dal parametro `value` sarà `0` (quando disattivo) o `255` (se attivo).

Il parametro `is_on` indica se l'output è attivo o disattivo. Il parametro `value` va assolutamente inviato nel caso di output analogico, anche quando l'elemento è disattivo, in quanto permette di riabilitare lo stato precedente alla riaccensione.

### `input`

Ogni input viene definito come segue:

```json
{
    "address": "I1",
    "name": "Pulsante"
}
```

Dove `address` indica la codifica visibile all'utente e indicata ad esempio sul morsetto, mentre `name` rappresenta il nome che permette una rappresentazione più *user-friendly* nell'app di configurazione.

Quando viene inviata la configurazione della scheda è fondamentale allegare perlomeno il parametro `address`, in quanto viene usato per la comunicazione via MQTT.

#### **Stato**

Lo stato dell'input deve essere inviato al topic:

```
ohmq/<device_id>/state/input/<item_id>
```

{% hint style="warning" %}
È fondamentale inviare lo stato ogniqualvolta l'elemento cambia di valore.
{% endhint %}

Lo stato deve essere inviato come segue:

```json
{
    "value": 1
}
```

Gli elementi della categoria `input` vanno intesi come ingressi digitali (binari), quindi assumono solo i valori `0` e `1`. Per input analogici bisogna riferirsi alla categoria `sensor`.

### `sensor`

Ogni sensore (input analogico) viene definito come segue:

```json
{
    "address": "S1",
    "name": "Temperatura"
}
```

Dove `address` indica la codifica visibile all'utente e indicata ad esempio sul morsetto, mentre `name` rappresenta il nome che permette una rappresentazione più *user-friendly* nell'app di configurazione.

Quando viene inviata la configurazione della scheda è fondamentale allegare perlomeno il parametro `address`, in quanto viene usato per la comunicazione via MQTT.

#### **Stato**

Lo stato del sensore deve essere inviato al topic:

```
ohmq/<device_id>/state/sensor/<item_id>
```

{% hint style="warning" %}
Nel caso dei sensori è consigliabile prevedere dei limiti nell'invio dei dati, così da evitare di sovraccaricare il Broker MQTT. Ad esclusione di alcune tipologie di sensori (es. sensori di allarme) in genere non c'è alcun vantaggio nell'inviare cambi di stato in tempo reale. È quindi opportuno prevedere dei filtri (es. livellamento esponenziale).
{% endhint %}

Lo stato deve essere inviato come segue:

```json
{
    "value": 22.5
}
```

### `virtualInput`

Ogni input virtuale viene definito come segue:

```json
{
    "address": "RFID",
    "name": "Lettore RFID"
}
```

Dove `address` indica la codifica visibile all'utente e indicata ad esempio sul morsetto, mentre `name` rappresenta il nome che permette una rappresentazione più *user-friendly* nell'app di configurazione.

Quando viene inviata la configurazione della scheda è fondamentale allegare perlomeno il parametro `address`, in quanto viene usato per la comunicazione via MQTT.

#### **Stato**

Lo stato dell'input virtuale deve essere inviato al topic:

```
ohmq/<device_id>/state/virtualInput/<item_id>
```

{% hint style="warning" %}
È fondamentale inviare lo stato ogniqualvolta l'elemento cambia di valore.
{% endhint %}

Lo stato deve essere inviato come segue nel caso in cui si tratti di un valore numerico:

```json
{
    "value": 1
}
```

Se il valore dell'input virtuale contiene testo, va inviato il seguente JSON:

```json
{
    "text": 1
}
```
