Monitoring the health of our plants with Home Assistant

In this chapter we will learn how to control the health of our plants in a simple and cheap way. Where we can know parameters such as the humidity it has, Temperature, The amount of light, or fertilizer among others.

GOOD, I think almost all of you have heard these sensors at some point, I think there are many brands and they are usually the same thing in the end, I know it as Xiaomi Mi Plant, although you can find it with any name such as Flower Care… in Banggood They usually put a discount and for approximately €10 you have it.

The usual use would be with the app of the manufacturer on duty that collects the data by bluetooth every time it is seen with your mobile. In this case by having a Home Assistant that does not have bluetooth, since in my case it runs like a virtual machine because it is difficult to talk to the sensor. So by having several Raspberry Pis scattered around the house, on each Raspberry Pi that is closest to the plant in question, that will be the one that connects with the sensor, Collect data via Bluetooth, and store them in a database I have lying around from MySQL/MariaDB; it is also true that if you want to skip the database you could send the data by MQTT, But I'm legacy, of storing things in traditional 🙂 BD It depends a little on the idea you have I'm sure this post will help you.

sudo did useless tool lescan LE Scan ...
0To:4D:49:BA:19:XX (unknown)
0To:4D:49:BA:19:XX (unknown)
54:5F:A0:C4:A5:XX (unknown)
0To: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)
...

Now we're going to install on that Raspberry Pi the bookshop Xiaomi's Mi plant sensor for Python, And while you're at it, the MySQL client that we'll need:

Sudo PiP3 Install Miflora Sudo PiP3 Install MySQLCLIENT

And now that's it, All that's left is for you to download this script, I don't remember where I got it from to thank him eternally. If you look closely at the line 47 I added a few lines so that the data it collects is stored on that MySQL server, Enter your details, as well as the name of the plant.

#!/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:AC:[0-9F]{2}:[0-9F]{2}:[0-9F]{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))
    return mac  def poll(args):
    """Poll data from the sensor."""
    backend = _get_backend(args)
    poller = MiFloraPoller(args.mac, backend)
    #print("Getting data from Mi Flora")
    #print("FW: {}".format(poller.firmware_version()))
    print("Name: {}".format(poller.name()))
    print("Temperature: {}".format(poller.parameter_value(MI_TEMPERATURE)))
    print("Moisture: {}".format(poller.parameter_value(MI_MOISTURE)))
    print("Light: {}".format(poller.parameter_value(MI_LIGHT)))
    print("Conductivity: {}".format(poller.parameter_value(MI_CONDUCTIVITY)))
    print("Battery: {}".format(poller.parameter_value(MI_BATTERY)))
    nombre = (poller.name())
    temperatura = (poller.parameter_value(MI_TEMPERATURE))
    humedad = (poller.parameter_value(MI_MOISTURE))
    luz = (poller.parameter_value(MI_LIGHT))
    conductividad = (poller.parameter_value(MI_CONDUCTIVITY))
    bateria = (poller.parameter_value(MI_BATTERY))
    print (name)
    print (temperature)
    print (humidity)
    print (light)
    print (conductividad)
    print (Battery)
    nombre = "NOMBRE_DE_LA_PLANTA"
    db = MySQLdb.connect("DIRECCION_IP_SERVIDOR_MYSQL","USUARIO_BD","CONTRASEÑA_BD","NOMBRE_BD")
    cursor = db.cursor()
    cursor.execute("""INSERT INTO plantas (name,temperature,humidity,light,conductividad,Battery) VALUES (%s,%s,%s,%s,%s,%s) """,(name,temperature,humidity,light,conductividad,Battery))
    db.commit()

def scan(args):
    """Scan for sensors."""
    backend = _get_backend(args)
    print('Scanning for 10 seconds...')
    devices = miflora_scanner.scan(backend, 10)
    print('Found {} devices:'.format(len(devices)))
    for device in devices:
        print('  {}'.format(device))

def _get_backend(args):
    """Extract the backend class from the command line arguments."""
    if args.backend == 'gatttool':
        backend = GatttoolBackend
    elif args.backend == 'bluepy':
        backend = BluepyBackend
    elif args.backend == 'pygatt':
        backend = PygattBackend     else:
        raise Exception('unknown backend: {}'.format(args.backend))
    return backend   def list_backends(_):
    """List all available backends."""
    backends = [b.__name__ for b in available_backends()]
    print('\n'.join(backends))


def history(args):
    """Read the history from the sensor."""
    backend = _get_backend(args)
    print('Getting history from sensor...')
    poller = MiFloraPoller(args.mac, backend)
    history_list = poller.fetch_history()
    print('History returned {} entries.'.format(len(history_list)))
    for entry in history_list:
        print('History from {}'.format(entry.wall_time))
        print("    Temperature: {}".format(entry.temperature))
        print("    Moisture: {}".format(entry.moisture))
        print("    Light: {}".format(entry.light))
        print("    Conductivity: {}".format(entry.conductivity))


def clear_history(args):
    """Clear the sensor history."""
    backend = _get_backend(args)
    print('Deleting sensor history data...')
    poller = MiFloraPoller(args.mac, backend)
    poller.clear_history()


def main():
    """Main function.

    Mostly parsing the command line arguments.
    """
    parser = argparse. ArgumentParser()
    parser.add_argument('--backend', choices=['gatttool', 'bluepy', 'pygatt'], default='gatttool')
    parser.add_argument('-v', '--verbose', action='store_const', const=True)
    subparsers = parser.add_subparsers(help='sub-command help', )

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

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

    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', type=valid_miflora_mac)
    parser_history.set_defaults(func=history)

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

    args = parser.parse_args()

    if args.verbose:
        logging.basicConfig(level=logging. DEBUG)

    if not hasattr(args, "func"):
        parser.print_help()
        sys.exit(0)

    args.func(args)

if __name__ == '__main__':
    main()

Si queréis, os dejo el código que necesitaréis en MySQL para crear esta tabla, que al final es muy sencilla, Has 7 Fields, donde se almacena el nombre de la planta, Temperature, la humedad, la luz en luxes, la conductividad, lo que le queda de batería y la fecha de cuando hizo el chequeo.

CREATE TABLE `plantas` (
    'name' CHAR(20) NULL COLLATE 'utf8mb4_general_ci',
    'temperature' FLOAT NULL,
    'moisture' FLOAT NULL,
    'light' FLOAT NULL,
    'conductivity' FLOAT NULL,
    'battery' FLOAT NULL,
    'date' TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)
COLLATE='utf8mb4_general_ci'
ENGINE=InnoDB
ROW_FORMAT=COMPACT
;

Total, that what we'll do is program on the Raspberry Pi to run this script with a scheduled task, for example, with which it collects the data every 1 Now it doesn't matter to me, We add in cron using 'crontab -e', and at the end the bluetooth MAC address of your 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:AC:88:E4:XX

Now we need to collect this data with Home Assistant, ¿how? Very simple, With MySQL queries! So in our configuration.yaml we can add the following queries for each floor:

sensor:
...
  - Platform: SQL db_url: Mysql://USUARIO_BD:CONTRASEÑA_BD@DIRECCION_IP_SERVIDOR_MYSQL/NOMBRE_DE_BD     queries:
      - 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     queries:
      - Name: "Ciclamen - Humedad"
        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     queries:
      - Name: "Ciclamen - Luz"
        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     queries:
      - Name: "Ciclamen - Conductividad"
        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     queries:
      - Name: "Ciclamen - Battery"
        query: "SELECT bateria FROM plantas WHERE nombre = 'Ciclamen' ORDER BY fecha DESC LIMIT 1;"
        column: 'bateria'
        unit_of_measurement: '%'
...

And now the time has come to add the Plant component to integrate these values that we are collecting, and indicating what they correspond to, So we'd add something like the following in configuration.yaml:

Plant:
  # Cyclamen cyclamen:
    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

GOOD, And what values correspond to those of our plant?? Here I leave you a Google Spreadsheet sheet where you can get info about a plant, if not approximately you can indicate them by searching a little on San Google.

As usual, after tapping configuration file, we restart our Home Assistant and from the Lovelace UI we can add our cards with our plants or flowers, or garden or tree or whatever we want to control. We choose a 'Plant Status' card’ and we indicate the entity of the plant and! Work done. We will now be able to know the status of our plants at all times.

And now we have the icing on the cake, when something happens that alerts us, For example, if humidity is low, Well, that's because the plant is drying up, what better than to send a Telegram or an alert through our home speakers to tell us.

As usual, Thank you if you have made it to the end and hope to have been able to help, And thank you above all for sharing on social networks and those likes!

Recommended Posts

Author

nheobug@bujarra.com
Autor del blog Bujarra.com Cualquier necesidad que tengas, Do not hesitate to contact me, I will try to help you whenever I can, Sharing is living ;) . Enjoy documents!!!