# MQTT API

## Requisiti

Affinché possiate integrare i vostri dispositivi è necessario aver configurato come scheda *Master* una delle nostre schede o un nostro dispositivo Gateway.

## MQTT

**MQTT** è l’acronimo di **Message Queuing Telemetry Transport** e indica un protocollo di trasmissione dati TCP/IP basato su un modello di pubblicazione e sottoscrizione che opera attraverso un apposito *Message Broker*.

La scheda *Master* del Sistema (o il Gateway che forniamo in impianti privi di schede) fa le veci del *Broker*, quindi il dispositivo è ad essa che si deve connettere.

### Connessione al Broker

Dall'applicazione tecnico potete ottenere l'indirizzo IP della scheda *Master*, quindi per la connessione al *Broker* è sufficiente definire:

* **Porta:** 1883
* **Indirizzo Server:** IP scheda *Master* o `ohmb-master.local`

{% hint style="warning" %}
Consigliamo di impostare un indirizzo **IP statico** per la scheda *Master*, per evitare che questo cambi in seguito a riavvii della scheda o del router.
{% endhint %}

### Configurazione Elemento

Per poter integrare output e input via MQTT è necessario, in fase di configurazione di un elemento, definire **MQTT** come tipo di collegamento.

Di seguito gli elementi che supportano questo tipo di connessione:

* Output
* Input
* Sensori
* Led RGB
* Variabili Virtuali

Questi i parametri che possono essere definiti:

* Topic Lettura
* Topic Scrittura (solo Output e Led RGB)
* Path Messaggio (payload)

#### Path Messaggio

Tale parametro va definito solo nel caso in qui il messaggio (o *payload*) contenga un **JSON**. Il *path* permette infatti di definire il percorso per l'ottenimento del valore.

Se abbiamo ad esempio il seguente JSON:

```json
{
    "temp": {
        "value": 22.5,
        "unity": "C"
    },
    "hum": {
        "value": 46,
        "unity": "%"
    }
}
```

i *path* per ottenere i valori di temperatura e umidità saranno:

* Temperatura: `temp/value`
* Umidità: `hum/value`

Se il JSON contenesse un array (lista) come in questo caso:

```json
{
    "sensors": [
        {
            "id": "abc",
            "value": 22.5,
            "type": "temperature"
        },
        {
            "id": "xyz",
            "value": 46,
            "type": "humidity"
        }
    ]
}
```

i *path* da definire saranno:

* Temperatura: `sensors/0/value`
* Umidità: `sensors/1/value`

### Publish

I topic per la pubblicazione degli stati non devono avere un formato particolare, ma è importante che siano univoci per ciascun input e output che desiderate integrare; è invece importante che il messaggio contenga soltanto il valore.&#x20;

Quindi se vogliamo integrare un sensore di temperatura, il topic potrebbe essere `test/temperatura` e il messaggio il valore numerico che vogliamo comunicare.

Non ci sono vincoli sull'origine del valore numerico, potrebbe ad esempio trattarsi di un valore di temperatura ottenuto tramite internet sfruttando qualche API Meteo, o in seguito alla digitazione di un valore da tastiera.

Di seguito il formato del valore da inviare a seconda del tipo di elemento:

* *Sensore*: invio del valore numerico con `.` come separatore decimale (es. `23.4`).
* *Input*: inviare **0** nel caso di input non verificato, **1** se verificato.
* *Output Digitale (relè)*: **0** se disattivo, **1** se attivo.
* *Output Analogico*: valore tra **1** (o valore minimo di dimmerazione) e **255** (o valore massimo di dimmerazione) se attivo; se è disattivo va inviato `0:x`, dove `x` è il valore prima di essere disattivato (es. `0:130`).
* *Variabile Virtuale*: numero intero o decimale (con il `.` come separatore decimale).

### Subscribe

Nel caso si voglia integrare un output va definito anche il topic per la ricezione delle richieste. Anche in questo caso non ci sono vincoli, se non la definizione di topic univoci per ciascun output.

Il topic per la ricezione delle richieste potrebbe essere ad esempio `out_1/comando`, e a seconda del tipo di richiesta il messaggio sarà:

* `on`: per l'attivazione dell'output
* `off`: per la disattivazione
* Valore tra `0` e `255`: se l'uscita è analogica viene inviato un valore numerico (`0` corrisponde alla minima intensità, `255` alla massima)

Anche in questo caso non è obbligatorio che il comando sia inviato ad un output reale, o comunque connesso fisicamente al dispositivo che volete integrare, ma tale valore potrebbe essere allegato ad una richiesta HTTP o ad un messaggio via bus seriale.

## Esempi

Il codice sottostante (in *Python*) è stato testato su un *Raspberry Pi*, e permette di integrare un sensore *DHT*, per l'invio di temperatura e umidità:

```python
import Adafruit_DHT
import time
import uuid
import paho.mqtt.client as mqtt


disconnected = False
last_message_sent = time.time()


class SuperClient(mqtt.Client):
	
	def loop(self, timeout=1.0, max_packets=1):
		time.sleep(0.001)
		return super(SuperClient, self).loop()


class DHTSensor(object):

	def __init__(self, pin):
		self.pin = pin
		self.temperature = 0.0
		self.humidity = 0.0
		self.old_temperature = 0.0
		self.old_humidity = 0.0
		self.last_update = 0

	def check(self) -> bool:
		return self.old_humidity != self.humidity or self.old_temperature != self.temperature

	def upate(self) -> bool:
		if time.time() - self.last_update > 5:
			self.old_humidity = self.humidity
			self.old_temperature = self.temperature
			humidity, temperature = Adafruit_DHT.read(sensor=Adafruit_DHT.DHT22, pin=self.pin)
			if None not in [humidity, temperature]:
				self.humidity = round(float(humidity), 1)
				self.temperature = round(float(temperature), 1)
			self.last_update = time.time()
			return self.check()
		return False

# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
	global disconnected
	disconnected = False


# The callback for when the client receives a CONNACK response from the server.
def on_disconnect(client, userdata, rc):
	global disconnected
	disconnected = True


def set_client(host):
	try:
		global mqtt_client
		client_id = "rasp-" + str(uuid.uuid4())
		mqtt_client = SuperClient(client_id=client_id, clean_session=True)
		# Set callbacks methods
		mqtt_client.on_connect = on_connect
		mqtt_client.on_disconnect = on_disconnect
		mqtt_client._host = host
		mqtt_client._port = 1883
		mqtt_client._keepalive = 60

		#mqtt_client.max_queued_messages_set(queue_size=1)

		# Start connection
		mqtt_client.loop_start()
		mqtt_client.reconnect()
	except:
		pass


def update_via_mqtt(sensor: DHTSensor):
	global mqtt_client
	try:
		mqtt_client.publish(topic=f"rasp/dht/humidity/{sensor.pin}", payload=f"{sensor.humidity}", qos=0, retain=True)
		mqtt_client.publish(topic=f"rasp/dht/temperature/{sensor.pin}", payload=f"{sensor.temperature}", qos=0, retain=True)
	except:
		pass
		
		
set_client(host="x.x.x.x")

sensors = [DHTSensor(pin=19), DHTSensor(pin=26)]

# Set client loop
while True:
	for sensor in sensors:
		if sensor.upate():
			update_via_mqtt(sensor=sensor)

	if time.time() - last_message_sent > 10:
		for sensor in sensors:
			update_via_mqtt(sensor=sensor)
		last_message_sent = time.time()
	
	time.sleep(0.1)
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.ohmautomation.io/tech/mqtt-api.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
