Monitoritzant la salut de les nostres plantes amb Home Assistant

En aquest capítol aprendrem a com controlar la salut de les nostres plantes d'una manera senzilla i barata. On podrem conèixer paràmetres com la humitat que té, la temperatura, la quantitat de llum, o fertilitzant entre d' altres.

Bé, crec que ja gairebé tots haureu escoltat alguna vegada aquests sensors, crec que n'hi ha de moltes marques i solen ser el mateix caixet al final, jo el conec com Xiaomi Mi Plant, encara que t'ho podràs trobar amb qualsevol nom tipus Flower Care… en Banggood solen posar algun descompte i per aproximadament 10€ el tens.

L'ús habitual seria amb l'app de torn del fabricant de torn que per bluetooth recull les dades cada vegada que es vegi amb el teu mòbil. En aquest cas en tenir un Home Assistant que no té bluetooth, ja que en el meu cas corre com una màquina virtual doncs difícil té per parlar-se amb el sensor de fallades. Així que en tenir diverses Raspberry Pi repartides per casa, a cada Raspberry Pi que quedi més a prop de la planta en qüestió, aquesta serà la que connecti amb el sensor, reculli les dades per bluetooth, i els emmagatzema en una BD que tinc per aquí de MySQL/MariaDB; també és cert que si et vols saltar la BD podries enviar les dades per MQTT, però sóc legacy, d'anar emmagatzemant les coses en BD tradicional 🙂 Depèn una mica la idea que tinguis estic segur aquest post t'ajuda.

sudo hcitool lescan LE Scan ...
0A:4D:49:BA:19:XX (unknown)
0A:4D:49:BA:19:XX (unknown)
54:5F:A0:C4:A5:XX (unknown)
0A:4D:49:BA:19:XX (unknown)
C4:7C:8D:6B:25:XX (unknown)
C4:7C:8D:6B:25:XX Flower care 0A:4D:49:BA:19:XX (unknown)
54:5F:A0:C4:A5:XX (unknown)
...

Ara anem a instal·lar en aquesta Raspberry Pi la llibreria de Xiaomi Mi plant sensor para Python, i ja de pas el client de mysql que ho necessitarem:

sudo pip3 install miflora sudo pip3 install mysqlclient

I ara llest, només queda que us descarregueu aquest script, que no recordo bé d'on el treu per agrair-s'ho eternament. Si us fixeu bé a la línia 47 li afegiu unes línies perquè les dades que reculli els emmagatzeme en aquest servidor de MySQL, poseu les vostres dades, així com el nom de la planta.

#!/usr/bin/env python3
"""Demo file showing how to use the miflora library."""

import argparse import re import logging import sys import MySQLdb from btlewrap import available_backends, BluepyBackend, GatttoolBackend, PygattBackend from miflora.miflora_poller import MiFloraPoller, \
    MI_CONDUCTIVITY, MI_MOISTURE, MI_LIGHT, MI_TEMPERATURE, MI_BATTERY from miflora import miflora_scanner def valid_miflora_mac(mac, pat = re.compile(r"80:EA:CA:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}")):
    """Check for valid mac adresses."""
    if not pat.match(mac.upper()):
        raise argparse. ArgumentTypeError('The MAC address "{}" seems to be in the wrong format'.format(mac))
    Retorna l'enquesta de def de Mac(Arguments):
    """Enquesteu les dades del sensor."""
    backend = _get_backend(Arguments)
    poller = MiFloraPoller(args.mac (anglès), Backend)
    #print("Obtenir dades de Mi Flora")
    #print("FW: {}".format(poller.firmware_version()))
    print("Name: {}".format(poller.name()))
    print("Temperatura: {}".format(poller.parameter_value(MI_TEMPERATURE)))
    print("Humitat: {}".format(poller.parameter_value(MI_MOISTURE)))
    print("Llum: {}".format(poller.parameter_value(MI_LIGHT)))
    print("Conductivitat: {}".format(poller.parameter_value(MI_CONDUCTIVITY)))
    print("Bateria: {}".format(poller.parameter_value(MI_BATTERY)))
    nombre = (poller.name())
    temperatura = (poller.parameter_value(MI_TEMPERATURE))
    humedad = (poller.parameter_value(MI_MOISTURE))
    llum = (poller.parameter_value(MI_LIGHT))
    conductivitat = (poller.parameter_value(MI_CONDUCTIVITY))
    bateria = (poller.parameter_value(MI_BATTERY))
    print (nom)
    print (temperatura)
    print (humitat)
    print (llum)
    print (conductivitat)
    print (bateria)
    nombre = "NOMBRE_DE_LA_PLANTA"
    db = MySQLdb.connect("DIRECCION_IP_SERVIDOR_MYSQL","USUARIO_BD","CONTRASEÑA_BD","NOMBRE_BD")
    cursor = db.cursor()
    cursor.execute("""INSEREIX EN plantes (nom,temperatura,humitat,llum,conductivitat,bateria) VALUES (%s,%s,%s,%s,%s,%s) """,(nom,temperatura,humitat,llum,conductivitat,bateria))
    db.commit()

Escaneig de Def(Arguments):
    """Cerca de sensors."""
    backend = _get_backend(Arguments)
    print('Scanning for 10 seconds...')
    dispositius = miflora_scanner.scan(Backend, 10)
    print('Found {} Dispositius:'.format(len(Dispositius)))
    per a dispositius en dispositius:
        print('  {}'.format(device))

def _get_backend(Arguments):
    """Extreu la classe de fons dels arguments de la línia d'ordres."""
    if args.backend == 'gatttool':
        backend = GatttoolBackend
    elif args.backend == 'bluepy':
        backend = BluepyBackend
    elif args.backend == 'pygatt':
        backend = PygattBackend else:
        raise Excepció('unknown backend: {}'.format(args.backend))
    retorn backend def list_backends(_):
    """Llista tots els dorsals disponibles."""
    dorsals = [b.__name__ per b a available_backends()]
    print('\n'.join(Backends))


Història de DEF(Arguments):
    """Llegiu l'historial del sensor."""
    backend = _get_backend(Arguments)
    print('Getting history from sensor...')
    poller = MiFloraPoller(args.mac (anglès), Backend)
    history_list = poller.fetch_history()
    print('History returned {} entries.'.format(len(history_list)))
    per a l'entrada a history_list:
        print('History from {}'.format(entry.wall_time))
        print("    Temperatura: {}".format(entrada.temperatura))
        print("    Humitat: {}".format(entrada.humitat))
        print("    Llum: {}".format(entrada.llum))
        print("    Conductivitat: {}".format(entrada.conductivitat))


def clear_history(Arguments):
    """Esborreu l'historial del sensor."""
    backend = _get_backend(Arguments)
    print('Deleting sensor history data...')
    poller = MiFloraPoller(args.mac (anglès), Backend)
    poller.clear_history()


def principal():
    """Funció principal.

    Principalment analitzant els arguments de la línia d'ordres.
    """
    analitzador = argparse. ArgumentParser()
    parser.add_argument('--backend', opcions =['gatttool', 'bluepy', 'pygatt'], default='gatttool')
    parser.add_argument('-v', '--verbose', action='store_const', const=Cert)
    subanalitzadors = parser.add_subparsers(help='sub-command help', )

    parser_poll = subparsers.add_parser('poll', help='poll data from a sensor')
    parser_poll.add_argument('mac', tipus = valid_miflora_mac)
    parser_poll.set_defaults(func=poll)

    parser_scan = subparsers.add_parser('scan', help='scan for devices')
    parser_scan.set_defaults(func=escaneig)

    parser_scan = subparsers.add_parser('backends', help='list the available backends')
    parser_scan.set_defaults(func=list_backends)

    parser_history = subparsers.add_parser('history', help='get device history')
    parser_history.add_argument('mac', tipus = valid_miflora_mac)
    parser_history.set_defaults(func=història)

    parser_history = subparsers.add_parser('clear-history', help='clear device history')
    parser_history.add_argument('mac', tipus = valid_miflora_mac)
    parser_history.set_defaults(func=clear_history)

    args = parser.parse_args()

    if args.verbose:
        logging.basicConfig(nivell=registre. DEPURAR)

    si no hasattr(Arguments, "func"):
        parser.print_help()
        sys.exit(0)

    args.func(Arguments)

if __name__ == '__main__':
    principal()

Si queréis, os dejo el codi que necessitaréis en MySQL para crear esta tabla, que al final és molt senzill, té 7 camps, on s'emmagatzema el nombre de la planta, la temperatura, la humedad, La Llum en Luxes, La conductivitat, el que li queda de bateria i la data de quan va fer la revisió mèdica.

CREATE TABLE 'plantes' (
    'nom' CHAR(20) NULL COLLATE 'utf8mb4_general_ci',
    'temperatura' FLOAT NULL,
    'humitat' FLOAT NULL,
    'llum' FLOAT NULL,
    'conductivitat' FLOAT NULL,
    'bateria' FLOAT NULL,
    'fecha' TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)
COLLATE='utf8mb4_general_ci'
ENGINE=InnoDB
ROW_FORMAT=COMPACT
;

Total, que el que farem és programar a la Raspberry Pi que executi aquest script amb una tasca programada, per exemple amb què reculli les dades cada 1 hora a mi em val, afegim en cron mitjançant 'crontab -e', i al final la direcció MAC bluetooth del vostre sensor:

0 * * * * python3 /home/pi/miflora_jardin.py -v poll C4:7C:8D:6B:1E:XX
0 * * * * python3 /home/pi/miflora_portulaca.py -v poll 80:EA:CA:88:E4:XX

Ara hem de recollir aquestes dades amb Home Assistant, com? Molt senzill, amb consultes mysql! Així que en el nostre configuration.yaml podem afegir de cada planta les següents consultes:

sensor:
...
  - platform: sql db_url: mysql://USUARIO_BD:CONTRASEÑA_BD@DIRECCION_IP_SERVIDOR_MYSQL/NOMBRE_DE_BD voleu:
      - name: "Ciclamen - Temperatura"
        query: "SELECT temperatura FROM plantas WHERE nombre = 'Ciclamen' ORDER BY fecha DESC LIMIT 1;"
        column: 'temperatura'
        unit_of_measurement: 'ºC'

  - platform: sql db_url: mysql://USUARIO_BD:CONTRASEÑA_BD@DIRECCION_IP_SERVIDOR_MYSQL/NOMBRE_DE_BD voleu:
      - name: "Ciclamen - Humitat"
        query: "SELECT humedad FROM plantas WHERE nombre = 'Ciclamen' ORDER BY fecha DESC LIMIT 1;"
        column: 'humedad'
        unit_of_measurement: '%'

  - platform: sql db_url: mysql://USUARIO_BD:CONTRASEÑA_BD@DIRECCION_IP_SERVIDOR_MYSQL/NOMBRE_DE_BD voleu:
      - name: "Ciclamen - Llum"
        query: "SELECT luz FROM plantas WHERE nombre = 'Ciclamen' ORDER BY fecha DESC LIMIT 1;"
        column: 'luz'
        unit_of_measurement: 'lux'

  - platform: sql db_url: mysql://USUARIO_BD:CONTRASEÑA_BD@DIRECCION_IP_SERVIDOR_MYSQL/NOMBRE_DE_BD voleu:
      - name: "Ciclamen - Conductivitat"
        query: "SELECT conductividad FROM plantas WHERE nombre = 'Ciclamen' ORDER BY fecha DESC LIMIT 1;"
        column: 'conductividad'
        unit_of_measurement: 'Ω'

  - platform: sql db_url: mysql://USUARIO_BD:CONTRASEÑA_BD@DIRECCION_IP_SERVIDOR_MYSQL/NOMBRE_DE_BD voleu:
      - name: "Ciclamen - Bateria"
        query: "SELECT bateria FROM plantas WHERE nombre = 'Ciclamen' ORDER BY fecha DESC LIMIT 1;"
        column: 'bateria'
        unit_of_measurement: '%'
...

I ara sí que va arribar el moment d'afegir el component de Plant per integrar aquests valors que estem recollint, i indicant a què li corresponen, així que afegiríem alguna cosa com el següent en configuration.yaml:

plant:
  # Cyclamen ciclamen:
    sensors:
      moisture: sensor.ciclamen_humedad battery: sensor.ciclamen_bateria temperature: sensor.ciclamen_temperatura conductivity: sensor.ciclamen_conductividad brightness: sensor.ciclamen_luz min_moisture: 15
    max_moisture: 75
    min_battery: 10
    min_conductivity: 250
    max_conductivity: 2000
    min_temperature: 1
    max_temperature: 35
    min_brightness: 2000
    max_brightness: 40000
    check_days: 3

Bé, i quins valors corresponen amb els de la nostra planta? Aquí us deixo un full de Google Spreadsheet on podeu obtenir info d'alguna planta, si no podeu indicar-los buscant una mica a Sant Google.

Com sempre, després de tocar fitxer de configuració, reiniciem el nostre Home Assistant i des de la UI de Lovelace podrem afegir ja les nostres targetes amb les nostres plantes o flors, o jardí o arbre o el que vulguem controlar. Escollim una targeta d'Estat de la planta’ i indiquem l'entitat de la planta i ¡zas! treball realitzat. Ja podrem conèixer l'estat en tot moment de les nostres plantes.

I ara ens queda la guinda del pastís, vagi que quan passi alguna cosa que ens alerti, per exemple, si la humitat és baixa, doncs això és que la planta s'està assecant, que millor que manar un Telegram o una alerta pels nostres altaveus de casa per indicar-nos-ho.

Com sempre, agrair-vos si heu arribat fins al final i esperant haver pogut ajudar, i gràcies sobretot per compartir en xarxes socials i aquests likes!

Posts recomanats

Autor

nheobug@bujarra.com
Autor del blog Bujarra.com Cualquier necesidad que tengas, no dubtis a contactar amb mi, us intentareu ajudar sempre que pugui, compartir és viure ;) . Gaudir dels documents!!!