Automatizziamo la raccolta differenziata con AppDaemon su Home Assistant

Leggi questo articolo grazie alle donazioni di Andrea Ippolito, Stefano Maria Meconi, Luca Capelletti, Gregorio Seidita, Andrea Giuseppe Calabrò, Cesare Munari, Stefano Silveri.
♥ Partecipa anche tu alle donazioni: sostieni SaggiaMente, sostieni le tue passioni!

Per coloro che si definiscono appassionati di domotica arriva sempre il momento in cui accadono due fatidiche cose:

  1. ci viene chiesto di fare un esempio di applicazione pratica per cui valga la pena implementare (ergo “spendere soldi per”) questo fantastico sistema che abbiamo appena finito di descrivere;
  2. ci riteniamo abbastanza sicuri di poter affermare che il nostro sistema di sviluppo e costruzione di automazioni cominci a starci stretto, o percepiamo una necessità di snellire un po’ il codice che abbiamo già scritto.

Mi rendo conto che è probabile che questi due problemi non sorgano nello stesso momento, ma lo scopo di questo articolo è quello di cercare di risolverli insieme, gratificando noi stessi e chi ci sta ad ascoltare nel primo punto.

Atto primo: la raccolta differenziata

In molti comuni italiani esiste una programmazione abbastanza serrata riguardo alla raccolta porta a porta dei rifiuti: vivendo in uno di questi ho subito pensato di inserire un qualche tipo di reminder per sapere quale contenitore esporre nelle giornate corrette.

Ho scelto di utilizzare il custom_component Garbage_Collection di bruxy70 perché è facilmente installabile tramite HACS (Home Assistant Community Store), e ha una metodologia di configurazione molto completa.

Se non conoscete HACS vi consiglio caldamente di installarlo, seguendo le istruzioni che trovate sul sito: rende la gestione dei custom_components facile e comoda.

Una volta installato Garbage_Collection ho aggiunto la riga

garbage_collection: !include garbage_collection.yaml

al mio configuration.yaml ed ho creato il file garbage_collection.yaml nella mia cartella radice delle configurazioni di Home Assistant (ho preferito separare la configurazione del componente per motivi di ordine e leggibilità del codice, ma è un passaggio facoltativo).

La mia programmazione di raccolta rifiuti è così strutturata:

  • indifferenziato settimanale, martedì mattina, effettuata anche in caso di festività;
  • organico bisettimanale, mercoledì e sabato mattina, effettuata anche in caso di festività;
  • carta quindicinale, martedì pomeriggio, posticipata al giorno successivo in caso di festività;
  • plastica quindicinale, giovedì pomeriggio, posticipata al giorno successivo in caso di festività;
  • vetro quindicinale, giovedì pomeriggio, posticipata al giorno successivo in caso di festività.

Dal momento che per i commercianti il vetro è settimanale, per le famiglie la raccolta è alternata: una settimana vetro e una settimana plastica.

Come potete vedere la periodicità in questione non è di immediata trascrizione in forma di automazione classica di Home Assistant, mentre utilizzando il componente Garbage_Collection si fa presto impostando così il garbage_collection.yaml:

  sensors:
  - name: raccolta_indifferenziato_mattino
    frequency: "weekly"
    collection_days:
    - tue
  - name: raccolta_organico_mattino
    frequency: "weekly"
    collection_days:
    - wed
    - sat
  - name: raccolta_carta_pomeriggio
    frequency: "even-weeks"
    collection_days:
    - tue
    move_country_holidays: IT
  - name: raccolta_plastica_pomeriggio
    frequency: "even-weeks"
    collection_days:
    - thu
    move_country_holidays: IT
  - name: raccolta_vetro_pomeriggio
    frequency: "odd-weeks"
    collection_days:
    - thu
    move_country_holidays: IT

l’opzione frequency indica la frequenza della periodicità, che più essere decisa con un elevato livello di discrezione: per indicare l’alternanza delle settimane per il vetro e la plastica ho utilizzato odds (pari) e evens (dispari) weeks, e l’opzione move_country_holidays: IT permette di riconoscere le festività del paese in cui ci troviamo e spostare la raccolta automaticamente il giorno seguente.

Salvando e riavviando Home Assistant ci troveremo i nuovi sensori appena creati:

come potete notare lo stato di questi sensori è un numero compreso tra zero e due, secondo lo schema seguente:

Stato Significato
0 La raccolta è oggi
1 La raccolta è domani
2 La raccolta è più avanti

Più interessante (per il nostro scopo) sono gli attributi di questi sensori:

Attributo Significato
next_date Giorno successivo di raccolta programmata
days Numero di giorni mancanti alla prossima raccolta

Dall’immagine sopra i più attenti fra voi avranno notato che è presente anche l’attributo friendly_name: questo perché ho aggiunto al mio customize.yaml dei nomi personalizzati per questi sensori, per facilitarmi la rappresentazione nella vista panoramica di HA:

sensor.raccolta_indifferenziato_mattino:
  friendly_name: Indifferenziato - Mattino
sensor.raccolta_carta_pomeriggio:
  friendly_name: Carta - Pomeriggio
sensor.raccolta_organico_mattino:
  friendly_name: Organico - Mattino
sensor.raccolta_plastica_pomeriggio:
  friendly_name: Plastica - Pomeriggio
sensor.raccolta_vetro_pomeriggio:
  friendly_name: Vetro - Pomeriggio

Lo stesso risultato è ottenibile via UI passando da Impostazioni – Personalizzazione, dove è anche possibile modificare le icone dei sensori.

Per la visualizzazione di queste entità nel frontend ci sono varie possibilità: personalmente utilizzo la custom_card lovelace-multiple-entity-row (trovate anch’essa su HACS) perché mi permette di creare una vista semplice ma efficace delle informazioni cruciali (non tanto lo stato effettivo, ma soprattutto l’attributo days che trovo più interessante).

entities:
  - entities:
      - attribute: days
        entity: sensor.raccolta_indifferenziato_mattino
        name: Mancano
        unit: Giorni
    entity: sensor.raccolta_indifferenziato_mattino
    name: Indifferenziato - Mattino
    secondary_info:
    show_state: false
    type: 'custom:multiple-entity-row'
  - entities:
      - attribute: days
        entity: sensor.raccolta_organico_mattino
        name: Mancano
        unit: Giorni
    entity: sensor.raccolta_organico_mattino
    name: Organico - Mattino
    secondary_info:
    show_state: false
    type: 'custom:multiple-entity-row'
  - entities:
      - attribute: days
        entity: sensor.raccolta_carta_pomeriggio
        name: Mancano
        unit: Giorni
    entity: sensor.raccolta_carta_pomeriggio
    name: Carta - Pomeriggio
    secondary_info:
    show_state: false
    type: 'custom:multiple-entity-row'
  - entities:
      - attribute: days
        entity: sensor.raccolta_plastica_pomeriggio
        name: Mancano
        unit: Giorni
    entity: sensor.raccolta_plastica_pomeriggio
    name: Plastica - Pomeriggio
    secondary_info:
    show_state: false
    type: 'custom:multiple-entity-row'
  - entities:
      - attribute: days
        entity: sensor.raccolta_vetro_pomeriggio
        name: Mancano
        unit: Giorni
    entity: sensor.raccolta_vetro_pomeriggio
    name: Vetro - Pomeriggio
    secondary_info:
    show_state: false
    type: 'custom:multiple-entity-row'
type: entities

A questo punto la maggior parte di voi avrà già pensato di mettere a frutto quanto imparato nelle nostre puntate precedenti sulla scrittura delle automazioni, e scriverne qualcuna per farsi notificare queste informazioni al momento e sul dispositivo giusto. Tuttavia nell’ottica di soddisfare il secondo dei problemi esposti all’inizio, vediamo come impostare gli automatismi che ci interessano nella maniera più snella ed ottimizzata possibile introducendo AppDaemon.

Atto secondo: AppDaemon

AppDaemon è la modalità di scrittura di automazioni ed in generale di gestione delle informazioni che arrivano da Home Assistant (ma non solo) più versatile e flessibile che gli sviluppatori di questo software offrano. È nato per soddisfare le esigenze delle persone che chiedevano di poter scrivere automazioni più complesse ed integrate senza legarsi alla verbosità che la struttura YAML classica impone: permette di sfruttare il linguaggio di programmazione Python (uno tra i più diffusi in ambito didattico per la sua semplicità e versatilità) per soddisfare questo tipo di esigenze.
La documentazione ufficiale è completa e ben sviluppata (e già questo è un punto di inizio importante, come citava Maurizio su Jinja2) e si prodiga in diversi esempi chiarificatori.

Detto questo, un minimo di conoscenza di Python è indispensabile per poter scrivere App con questo sistema: se non avete mai avuto a che fare con questo linguaggio vi consiglio di farvene anche solo un’infarinatura, con una delle migliaia di risorse gratuite che trovate sul web a questo scopo. Posso consigliare ThinkPython in Italiano, o uno degli altri consigliati sul sito italiano ufficiale di Python.

AppDaemon va installato sulla macchina su cui opereremo: se utilizzate hassio (come il sottoscritto) trovate un’addon ufficiale mantenuto da Frenk. Vi sarà utile anche se decideste di utilizzare HADashboard, una dashboard configurabile che offre questo sistema.
AD riconosce le modifiche che eseguiamo sui nostri files e li ricarica di conseguenza, permettendo di fare debug molto in fretta, non essendo così costretti a ricaricare le automazioni ogni volta (o peggio, a riavviare Home Assistant).

Per quanto ci riguarda in questa sede, vi basti sapere che ogni App di AppDaemon (cioè ogni file .py dentro cui scriveremo del codice Python) necessita anche di essere registrata all’interno del file apps.yaml (una sorta di registro di tutte le App caricate) che viene creato in fase di registrazione di AppDaemon.

Come vedete nell’immagine a lato, dentro la cartella appdaemon che viene creata esisterà una cartella apps, dove dovranno essere posizionate le cartelle dentro cui sono contenuti i files di cui hanno bisogno le apps per funzionare.
Nel mio caso il file Utilities.py è posizionato dentro la cartella Utilities, a sua volta dentro la cartella apps: questa ramificazione non è obbligatoria ma consigliata, per confinare la cache di esecuzione di ogni app all’interno della propria cartella e per organizzare in maniera razionale lo spazio occupato dai singoli files.

Ecco il contenuto del mio Utilities.py:

import hassapi as hass
import datetime
# App per la notifica della raccolta differenziata
class Differenziata(hass.Hass):

    def initialize(self):
        orario = self.parse_time(self.args["orario_notifica"])
        self.run_daily(self.principale, orario)

    def componi_messaggio(self, nome, days):
        if days in self.args["messaggi"]:
            messaggio = self.args["messaggi"].get(days).format(nome, days)
        else:
            messaggio = "Esporre {} tra {} giorni.".format(nome, days)
        return messaggio

    def principale(self, kwargs):
        # if self.args["arrivo_a_casa"]:
        for sensor in self.args["sensori_rifiuti"]:
            days = self.get_state(sensor, attribute="days")
            nome = self.get_state(sensor, attribute="friendly_name")
            if days == self.args["giorni_mancanti"]:
                for servizio_notifica in self.args["servizi_notifica"]:
                    service = "notify/" + servizio_notifica
                    self.call_service(service, title = "Raccolta Differenziata", message = self.componi_messaggio(nome, days))

Questa è di fatto già la mia app, definita come la classe python dal nome Differenziata. Ecco invece la parte di configurazione, presente nel mio file apps.yaml:

Differenziata:
  module: Utilities
  class: Differenziata
  orario_notifica: "18:00:00"
  giorni_mancanti: 1
  sensori_rifiuti:
    - sensor.raccolta_indifferenziato_mattino
    - sensor.raccolta_carta_pomeriggio
    - sensor.raccolta_organico_mattino
    - sensor.raccolta_plastica_pomeriggio
    - sensor.raccolta_vetro_pomeriggio
  servizi_notifica:
    - mobile_app_iphone_di_alby
    - mobile_app_iphone_di_alessandra
  messaggi:
    2: "Esporre {} dopodomani."
    1: "Esporre {} domani."
    0: "Esporre {} oggi."

L’app Differenziata si compone di tre semplici funzioni:

  • inizialize(self) dice ad AppDaemon che la funzione “principale” della nostra app dovrà essere eseguita tutti i giorni all’orario specificato nella voce di configurazione orario_notifica. Questa funzione di inizializzazione è obbligatoria in tutte le apps scritte con questo sistema;
  • componi_messaggio(self, nome, days) costruisce il messaggio che verrà consegnato come notifica con la relativa frase modello specificata in messaggi in base al numero di giorni mancanti, o in sua assenza usa una frase standard;
  • principale(self, kwargs) contiene la logica principale di funzionamento dell’app. In sostanza per ogni sensore indicato nell’elenco sensori_rifiuti viene controllato che manchino il numero di giorni_mancanti specificati in configurazione: se la risposta è positiva viene consegnato ad ogni servizio di notifica indicato nell’elenco servizi_notifica il messaggio composto dalla funzione componi_messaggio vista in precedenza, personalizzata con il friendly_name dell’entità in questione.

Questa App è volutamente semplice perché io stesso non sono un esperto di Python e perché le mie esigenze erano abbastanza limitate: su GitHub sono disponibili un gran numero di esempi con gradi di complessità crescenti, molto utili per farsi le ossa.

Lo scopo di questo mio articolo era di introdurvi ad AppDaemon, fornendo un caso di applicazione pratico di questo software e delle sue potenzialità: con 20 righe scarse di codice siamo stati in grado di produrre qualcosa di dinamico, configurabile ed integrato facilmente in Home Assistant.

Autore:
Alberto Zamboni (@albyzambo)

Revisori:
Vincenzo Lapenta (@lapy) – con ringraziamento speciale per il suo contributo al codice
Maurizio Natali (@simplemal)
Cristian (@smorgagno)
Massimiliano Latella (@lamax)

SmartHome Channel

Special Team - I SaggiUtenti del canale #SmartHome sul nostro Slack uniti per creare guide e recensioni dedicate all'automazione. Partecipa anche tu entrando nel gruppo effettuando una donazione dal SaggioSupporto o discutendo con noi nel Canale Telegram SmartHome Italia