Un sistema che permette la misurazione della temperatura, della CO2 e dell'umidità dell'ambiente in cui viene installato. I dati ottenuti verranno poi inviati ad un foglio Google così da poter poi essere visualizzati e analizzati.
Questo progetto è open source, chiunque può scaricare i file necessari, ricreare il progetto e contribuire al suo miglioramento. Non ci sono restrizioni di licenza d'uso, ma si invita a citare che è stato realizzato dagli studenti ASIRID.
Il progetto è stato concepito per misurare la concentrazione di CO2, la temperatura e l'umidità ambientali. L'impiego di termocarboigrometri in specifici ambienti assicura il monitoraggio delle condizioni, mantenendole ideali per il benessere e la salute. I dati raccolti vengono trasmessi a un foglio di calcolo Google per un'efficace supervisione dei valori rilevati. Nell'ambito del progetto, sono state progettate e successivamente prodotte, mediante l'uso di una stampante 3D, delle custodie atte a contenere tutti i circuiti necessari.
- PCB (Printed Circuit Board): Cliccare qui per realizzare il circuito stampato
- Stazione di saldatura
- NodeMCU ESP8266: Chip con modulo Wi-Fi integrato
- DHT22 AM2302: Sensore di temperatura e umidità compatibile con ESPHome
- MH-Z19: Sensore di CO2 a infrarossi compatibile con ESPHome
- Display 16x2 LCD con Interfaccia I2C 2x16
- Mini Modulo di Alimentazione: Il modulo trasforma la corrente alternata in corrente continua stabilizzata
- Spina C8 maschio a 2 pin: Presa di corrente
- Cavo Alimentazione IEC320 C7 a 2 pin: Cavo di alimentazione compatibile con presa C8.
- Home Assistant.
- ESPHome (componente aggiuntivo di Home Assistant): cliccare qui per la procedura di installazione.
- Repository Gitlab: {terbocarboigrometro}.
ESPHome utilizza le Substitutions per creare un unico file sorgente generico, che può essere adattato automaticamente per tutti i nodi di uno specifico tipo. Questo consente di sostituire parti del file con espressioni specifiche per ciascun sensore.
Per creare il file sorgente, segui questi passaggi:
- Accedere a File Editor e vai alla cartella epshome.
- Creare la cartella Sensor.
- All'interno della cartella Sensor, creare il file common_HTC.yaml.
Per iniziare a configurare il file YAML per ESPHome, segui questi passaggi:
-
Sezione delle substitutions ed esphome: In questa sezione si definiscono coppie chiave-valore che ESPHome sostituirà automaticamente nel file di configurazione con i valori specificati. Queste substitutions permettono di semplificare il codice, mantenendo le configurazioni chiare e riutilizzabili. Uno degli elementi essenziali da specificare è il nome che identifica univocamente il sensore, garantendo una corretta gestione e tracciabilità dei dati rilevati. Questa tecnica consente di personalizzare rapidamente il file di configurazione adattandolo a sensori e dispositivi specifici.
substitutions: device_name: "sostituisci_con_il_nome_del_dispositivo" update_interval: "60s" esphome: name: termocarboigrometro-${device_name} esp8266: board: d1
-
Sezione globals: Definisce la variabile intera
display_page
utilizzata per alternare automaticamente tra diverse pagine di informazioni sul display. Coninitial_value
impostato a0
, si parte dalla prima pagina. Questa variabile viene incrementata periodicamente, permettendo di mostrare diverse tipologie di dati ogni 5 secondi senza intervento manuale. Il ciclo continuo tra le informazioni migliora l'usabilità del display, fornendo una visualizzazione sequenziale di tutti i dati rilevanti in modo dinamico e automatico.globals: - id: display_page type: int restore_value: no initial_value: "0"
Nella sezione sensor, sono definiti i sensori impiegati nel progetto:
-
Sezione sensor:
-
Sensore DHT22: Rileva umidità e temperatura, collegato al pin GPIO0. I parametri hanno nomi e ID univoci, e l'aggiornamento dei valori avviene ogni 30 secondi.
-
Sensore di CO2: Configurato con un intervallo di aggiornamento di 30 secondi, ha nomi e ID univoci. La calibrazione automatica è abilitata e si ripete ogni 24 ore dall'accensione del dispositivo, garantendo letture accurate.
-
Sensore Uptime: Monitora la durata di funzionamento del dispositivo, indicando il tempo trascorso dall'ultimo avvio, utile per monitorare la stabilità del sistema.
sensor: - platform: dht pin: GPIO0 model: DHT22 temperature: name: "Temperatura ${device_name}" id: "${device_name}_temperatura" humidity: name: "Umidita ${device_name}" id: "${device_name}_umidita" update_interval: ${update_interval} - platform: mhz19 co2: name: "CO2 ${device_name}" id: "${device_name}_co2" update_interval: ${update_interval} automatic_baseline_calibration: true - platform: uptime name: "Uptime ${device_name}"
-
-
Sezione button: è stato configurato un pulsante che permette di riavviare il dispositivo in caso di malfunzionamento. Questa funzionalità è essenziale per garantire che l'utente possa facilmente resettare il sistema senza dover ricorrere a interventi più complessi.
button: - platform: restart name: "Restart ${device_name}"
-
Sezione uart: sono stati configurati i pin TX (trasmissione) e RX (ricezione) utilizzati per la comunicazione con il sensore di CO2. La configurazione specifica i pin GPIO a cui il sensore è connesso e imposta il baud rate, ovvero la velocità di trasmissione dei dati, a 9600 bps (bit per secondo).
uart: tx_pin: GPIO12 rx_pin: GPIO14 baud_rate: 9600
-
Sezione display: Configura e gestisce il display LCD collegato tramite un expander PCF8574.
-
Sezione I2C: Imposta la frequenza della comunicazione I2C a 400kHz, garantendo una trasmissione rapida e stabile dei dati tra il microcontrollore e il display.
-
Sezione PCF8574: Configura l'expander I/O PCF8574, specificando l'indirizzo I2C per il corretto funzionamento del display.
-
Sezione interval: Definisce un'azione da eseguire ogni 5000 millisecondi (5 secondi) per aggiornare le informazioni visualizzate.
-
Sezione display: Configura l’LCD, inclusi aggiornamento, dimensioni e indirizzo, utilizzando una lambda function per mostrare alternativamente temperatura, umidità, livello di CO2 e l’indirizzo IP del dispositivo, migliorando la leggibilità e l’interazione utente.
i2c: frequency: 400khz pcf8574: id: "pcf8574_hub" address: 0x27 pcf8575: false interval: - interval: 5000ms then: - lambda: |- id(display_page) += 1; if (id(display_page) > 2) id(display_page) = 0; display: - platform: lcd_pcf8574 update_interval: 100ms dimensions: 20x4 address: 0x27 lambda: |- it.print(0, 0, " "); if (id(display_page) == 0) { it.printf(0, 0, "Temp: %.1f C", id(${device_name}_temperatura).state); } else if (id(display_page) == 1) { it.printf(0, 0, "Umidita': %.1f%%", id(${device_name}_umidita).state); } else if (id(display_page) == 2) { it.printf(0, 0, "CO2: %.0f ppm", id(${device_name}_co2).state); } it.printf(0, 1, "IP: %s", WiFi.localIP().toString().c_str());
- Andare in Esplora File e digitare sulla barra di ricerca \\IP_HOME_ASSISTANT>\addons
- Creare la cartella ScritturaGoogle
- Questa è la struttura della cartella:
ScritturaGoogle/
├── Dockerfile
├── build.yaml
├── config.yaml
├── CHANGELOG.md
├── README.md
├── icon.png
├── rootfs
│ ├── scritturagoogle
│ │ ├── main.py
│ │ ├── googleConf.py
│ │ ├── logger_module.py
│ │ ├── smtp_sender_email.py
│ │ └── interface.sh
│ └── etc
│ └── services.d
│ └── interface
│ └── run
Un Dockerfile è un file di testo che definisce come creare un’immagine di Docker, ovvero una “foto” del contenitore che contiene l’applicazione e le sue dipendenze. Il Dockerfile può contenere istruzioni per copiare i file dell’applicazione, installare dipendenze e configurare l’ambiente.
ARG BUILD_FROM
FROM ${BUILD_FROM}
RUN apt-get update -y && apt upgrade -y
RUN apt-get install -y \
coreutils \
wget \
curl \
python3 \
python3-dev \
python3-pip
RUN pip3 install requests==2.24.0
RUN pip3 install colorlog==4.6.2
COPY rootfs /
- Utilizza l'immagine di base specificata dalla variabile BUILD_FROM.
- Aggiorna l'elenco dei pacchetti e aggiorna i pacchetti installati all'ultima versione.
- Installa i pacchetti necessari per il funzionamento dell'Add-On.
- Installa i moduli Python specifici utilizzando pip3.
- Copia il contenuto della directory rootfs nel file system del container.
Questo file di configurazione fornisce la basi per l'integrazione dell'Add-On con Home Assistant, specificando le caratteristiche e i requisiti essenziali
---
version: "1.0.0"
name: Scrittura Google
description: Scrittura periodica sui fogli di Google
slug: scritturagoogle
arch:
- aarch64
- amd64
- armhf
- armv7
- i386
init: false
homeassistant_api: true
ingress: true
panel_admin: false
panel_title: Scrittura Google
panel_icon: mdi:google-spreadsheet
map:
- share:rw
options:
devices:
- deviceid: ""
frequency_update: 5
receiver_email: ""
sensor: []
sender_email: ""
base_url_app_script: ""
schema:
devices:
- deviceid: str
frequency_update: "int(1,5)"
receiver_email: email
sensor:
- str
sender_email: email
base_url_app_script: url
-
version: "1.0.0"
- Specifica la versione corrente dell'addon per la gestione degli aggiornamenti.
-
name: Scrittura Google
- Nome visualizzato dell'addon nell'interfaccia utente di Home Assistant.
-
description:
- Descrive brevemente le funzionalità, fornendo una panoramica rapida.
-
slug: scritturagoogle
- Identificatore univoco usato internamente da Home Assistant.
-
arch:
- Indica le architetture hardware supportate.
-
init: false
- Specifica se l'addon richiede un processo di inizializzazione.
-
homeassistant_api: true
- Abilita l'accesso alle API di Home Assistant.
-
panel_admin: false
- Definisce se l'addon dovrebbe avere un pannello amministrativo.
-
panel_title:
- Titolo del pannello nell'interfaccia utente.
-
panel_icon:
- Icona del pannello utilizzando la libreria Material Design Icons.
-
map:
- Monta la directory
share
con permessi di lettura e scrittura.
- Monta la directory
---
build_from:
aarch64: ghcr.io/hassio-addons/debian-base/aarch64:5.3.1
amd64: ghcr.io/hassio-addons/debian-base/amd64:5.3.1
armhf: ghcr.io/hassio-addons/debian-base/armhf:5.3.1
armv7: ghcr.io/hassio-addons/debian-base/armv7:5.3.1
i386: ghcr.io/hassio-addons/debian-base/i386:5.3.1
codenotary:
base_image: notary@home-assistant.io
signer: notary@home-assistant.io
Questo file contiene le immagini Docker che devono essere utilizzate per costruire l'Add-On. Durante la fase di build, Docker scaricherà le immagini specificate e le utilizzerà per costruire l'Add-On
Attraverso il seguente codice otteniamo dall'istanza di Home Assistant il token di autenticazione (SUPERVISOR_TOKEN
) e l'indirizzo URL (HASSIO_URL
) necessari per utilizzare le API, recuperandoli dalle variabili d'ambiente del sistema.
import os
SUPERVISOR_TOKEN = os.environ["SUPERVISOR_TOKEN"]
HASSIO_URL = os.environ.get("HASSIO_URL","http://hassio/homeassistant/api")
Il seguente script è suddiviso nelle seguenti funzioni:
-
check_entity_availability
: Questa funzione verifica lo stato di una specifica entità su Home Assistant. Effettua una richiesta HTTP GET per ottenere lo stato attuale dientity
. Se la chiamata viene effettuata con successo (HTTP_STATUS_CODE == 200), controlla che lo stato dell’entità non siaunavailable
ounknown
. In questi casi viene inviata una notifica email e viene registrato l'avviso sul logger. Altrimenti vengono restituiti i dati dell'entità. Se la richiesta ritorna un errore 404, il logger registra un errore, poiché l’entità specificata non è stata trovata. -
fetch_and_send_data
: Questa funzione raccoglie i dati delle entità di un dispositivo e li invia a un URL Google Apps Script a intervalli regolari. Per ogni entità si verifica la disponibilità, se disponibile, si costruisce un URL con i parametrideviceid
,tag
(classe del dispositivo) evalue
(stato corrente). Successivamente viene effettuata una richiesta GET all'URL con i dati dell’entità. Se l'endpoint conferma la ricezione (Wrote è presente nella risposta), registra l'invio con successo. In caso contrario, invia una notifica email e registra un errore. La funzione attende 5 secondi tra le chiamate per ogni entità e, dopo aver inviato tutti i dati, attende il tempo di aggiornamento specificato (frequency_update
) per il dispositivo. In caso di eccezioni, registra l'errore tramite il logger. -
main
: Crea e avvia un thread per ogni dispositivo specificato nella configurazione, invocandofetch_and_send_data
con i parametri del dispositivo.
import googleConf, smtp_sender_email
import json
import requests
import time
import threading
from logger_module import setup_logger
token = googleConf.SUPERVISOR_TOKEN
url_ha = googleConf.HASSIO_URL
headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'}
with open('/data/options.json') as f:
config = json.load(f)
logger = setup_logger()
def check_entity_availability(entity, receiver_email):
"""Check if the entity is available and not in 'unavailable' or 'unknown' state."""
url_states = f"{url_ha}/states/{entity}"
response = requests.get(url_states, headers=headers)
if response.status_code == 200:
data = response.json()
state = data.get('state')
sender_email = config['sender_email']
if state == 'unavailable':
title = f"Entity {entity} is unavailable"
message = f"Entity {entity} is unavailable (state: unavailable). Please check the entity_id."
smtp_sender_email.send_email(sender_email, receiver_email, title, message)
logger.warning(message)
return None
elif state == 'unknown':
title = f"Entity {entity} is in an unknown state"
message = f"Entity {entity} is in an unknown state (state: unknown)."
smtp_sender_email.send_email(sender_email, receiver_email, title, message)
logger.warning(message)
return None
else:
return data
elif response.status_code == 404:
logger.error(f"Entity {entity} not found (404 error). Please check the entity_id.")
return None
else:
logger.error(f"Failed to fetch data for entity {entity}: {response.status_code}")
return None
def fetch_and_send_data(deviceid, entities, frequency_update, receiver_email, base_url_app_script):
"""Fetch data from Home Assistant and send it to Google Apps Script."""
while True:
try:
for entity in entities:
data = check_entity_availability(entity, receiver_email)
if data:
tag = data['attributes'].get('device_class', 'unknown')
value = data['state']
url_app_script = f"{base_url_app_script}?deviceid={deviceid}&tag={tag}&value={value}"
response = requests.get(url_app_script)
data = response.text
if ('Wrote' in data):
logger.info("Data sent: [device_id: {:<5} | tag: {:<15} | value: {:<5}]".format(deviceid, tag, value))
else:
logger.error(f"Failed to send data: {deviceid} {tag} {value}")
title = f"Failed to send data for sensor: {deviceid}"
message = f"Failed to send data: {deviceid} {tag} {value}" + time.strftime("%Y-%m-%d %H:%M:%S")
smtp_sender_email.send_email(config['sender_email'], receiver_email, title, message)
time.sleep(5)
time.sleep((frequency_update * 60) - (5 * (len(entities) - 1)))
except Exception as e:
logger.error(f"Error: {e}")
if __name__ == '__main__':
if not config['devices'] or not config['sender_email'] or not config['base_url_app_script']:
logger.error("Configuration not found")
exit(1)
base_url_app_script = config['base_url_app_script']
for device in config['devices']:
deviceid = device['deviceid']
frequency_update = device['frequency_update']
receiver_email = device['receiver_email']
threading.Thread(target=fetch_and_send_data, args=(deviceid, device['sensor'], frequency_update, receiver_email, base_url_app_script)).start()
La funzione send_email
ha lo scopo di inviare un'email utilizzando il server SMTP di Google, configurato come relay senza autenticazione.
La funzione prende quattro parametri: l'indirizzo email del mittente, quello del destinatario, l'oggetto dell'email (title
), e il contenuto del messaggio (message
). Con queste informazioni, prepara un'email che verrà inviata tramite il server di Google, smtp-relay.gmail.com, sulla porta 587, tipicamente usata per connessioni sicure SMTP con TLS.
La funzione costruisce l’email partendo dai dettagli forniti. Utilizza MIMEMultipart
, un oggetto che consente di combinare facilmente vari componenti dell’email. Imposta il mittente, il destinatario e l’oggetto dell’email per rendere il messaggio completo e comprensibile. Poi aggiunge il contenuto del messaggio, che è inviato in formato plain text, quindi come semplice testo senza formattazione complessa.
Una volta preparato il messaggio, la funzione si collega al server di Google sulla porta 587, una porta standard per connessioni sicure. Per proteggere la trasmissione dei dati, la funzione avvia una connessione TLS che cripta i dati trasmessi tra il client e il server.
Una volta completato l'invio del messaggio, la connessione viene chiusa con il server SMTP.
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from logger_module import setup_logger
logger = setup_logger()
def send_email(sender_email, receiver_email, title, message):
"""Send an email with the specified message from sender to receiver."""
try:
smtp_server = "smtp-relay.gmail.com"
smtp_port = 587
msg = MIMEMultipart()
msg['From'] = sender_email
msg['To'] = receiver_email
msg['Subject'] = title
msg.attach(MIMEText(message, 'plain'))
server = smtplib.SMTP(smtp_server, smtp_port)
server.starttls()
server.send_message(msg)
server.quit()
except Exception as e:
logger.error(f"Failed to send email: {e}")
Questo script esegue il file Python main.py
, gestendo l’esecuzione principale del programma.
#!/usr/bin/with-contenv bashio
# ==============================================================================
# Home Assistant Add-on: ScritturaGoogle
# ==============================================================================
bashio::log.info "Running interface.sh"
python3 /scritturagoogle/main.py
Questo script esegue l'interfaccia interface.sh
, che a sua volta avvia lo script Python.
#!/usr/bin/with-contenv bashio
# ==============================================================================
# Home Assistant Add-on: ScritturaGoogle
# ==============================================================================
bashio::log.info "Starting service.d [Interface]"
bashio::log.info "Executing interface script..."
/scritturagoogle/interface.sh
La sequenza organizza correttamente il flusso di esecuzione, con run.sh
che chiama interface.sh
, che a sua volta esegue main.py
.
Cliccare sul pulsante per installare l'Add-On oppure seguire la procedura indicata di seguito.
- Una volta configurato l'Add-On, riavvia Home Assistant.
- Clicca su Impostazioni > Componenti Aggiuntivi.
- Seleziona l'Add-On e poi clicca su Installa.
- Configurare l'addon nel modo giusto per il corretto funzionamento dell'addon.
- Una volta configurato, fai clic su Avvia.
- Dopo l'avvio, sarà possibile vedere tutti i messaggi segnalati dall'Add-On nella sezione Registro.
Nella sezione di configurazione specificare l'URL base dell'App Script, l'email del mittente e le informazioni sui dispositivi. Per queste ultime informazioni guardare l'esempio riportato di seguito:
- deviceid: <device_id>
frequency_update: <int_update_interval>
receiver_email: <email>
sensor:
- <entity_id_device_1>
- <entity_id_device_2>
...
- <entity_id_device_n>
- deviceid: <device_id>
frequency_update: <int_update_interval>
receiver_email: <email>
sensor:
- <entity_id_device_1>
- <entity_id_device_2>
...
- <entity_id_device_n>
Il progetto è stato pubblicato a Marzo 2024, nella sua prima versione ed è attualmente funzionante. Sviluppi futuri da definire.
Francesco Sparascio, Francesco Rinaldi