Ac infinity controller 67 Bluetooth temp, humidity, fan pwm

Wow, amazing. you folks are kicking ass!
Abou the the WIFI BLu Pro model controller, at the beginning of a new setup after connecting to the controller by bluetooth, app asks if you want to use WIFI or bluetooth to connect.

Perhaps need to remove the controller on the app, and add it back but use WIFI as your selection, then it uses wifi and not Bluetooth. This allows you to update firmware and connect from anywhere.

I suppose bluetooth is used by all controllers so it makes sense to use that. I am new to HA, and only learned smartthings ā€œAPIā€ if you will they ended a couple of years ago.

I have a huge learning curve to even try to get this working. my mobo has bluetooth. I have HA installed, discovered a few devices and stopped due to time. any advice is greatly appreciated.

I am interested in using the MLX90614 as well, but obviously, that is another hurdle.

your rightā€¦ temp is sent in the message in kelvin, they had it posted right in the codeā€¦

public static float getFah(float f) {
return ((float) Math.round((((f * 9.0f) / 5.0f) + 32.0f) * 100.0f)) / 100.0f;

this lead me to figuring out:::
66F 39%rh
1eff0209031c0201076a0f63007d000000051d4c0001ffff0001ffff0001ffff0001

Temp = 076a = 1898
temp = f
1898 * 9 / 5 ) + 3200) / 100 = 66.164

Humidity = 0f63 = 3939 move the decimal point 39.39% rh

VPD = 007d = 125 move decimal point 1.25 kPa

i prefer the bluetooth over wifi, easier to sniff atleast for me

have found humidity on the 67 by sending

A500000302e17e04000102032078e9 to uuid 70d510012c7f4e75ae8ad758951ce4e0

while listening for notifications on uuid 70d510022c7f4e75ae8ad758951ce4e0

value returned is a510001102e1575d000102050a0c4d1130030500000000002001009fe2

a510001102e1575d000102050a 0c4d 1130 030500000000002001009fe2

0c4d is Temperature
1130 is humidity

09:57:43.763 - Characteristic (70D51001-2C7F-4E75-AE8A-D758951CE4E0) wrote new value: <a5000003 034979d7 00010203 2078e9> 
09:57:43.815 - Characteristic (70D51001-2C7F-4E75-AE8A-D758951CE4E0) read: (null) 
09:57:43.816 - Characteristic (70D51002-2C7F-4E75-AE8A-D758951CE4E0) notified: <a5100011 0349508e 00010205 0007bd0f 3c030500 00000000 20010093 15> 

GAME SET MATCH

youll have to do the same notification then send command but after that, go back and read the notification uuid to retrieve infoā€¦

THIS WORKS

#!/usr/bin/expect -f

set prompt "#"
set address f0:74:59:49:c0:45
spawn bluetoothctl
expect -re $prompt
send "connect $address\r"
expect "Connection successful"
send "list-attributes\r"
send "menu gatt\r"
send "select-attribute /org/bluez/hci0/dev_F0_74_59_49_C0_45/service001b/char001e\r"
send "notify on\r"
send "select-attribute /org/bluez/hci0/dev_F0_74_59_49_C0_45/service001b/char001c\r"
send "write \"0xa5 0x00 0x00 0x03 0x03 0x49 0x79 0xd7 0x00 0x01 0x02 0x03 0x20 0x78 0xe9\"\r" 
send "select-attribute /org/bluez/hci0/dev_F0_74_59_49_C0_45/service001b/char001e\r"
send "read\r"
sleep 2
send "quit\r"
expect eof

Temp in C

/config/./FILENAME.sh | grep a5 | awk 'NR==2 { print "0x" $16 $17 }' | xargs printf "%d\n"

this will show temp in F from terminal

/config/./FILENAME.sh | grep a5 | awk 'NR==2 { print "0x" $16 $17 }' | xargs printf "%d\n" |xargs -n 1 bash -c 'echo $(($1 * 9/5 + 3200 ))' args | xargs -n 1 bash -c 'echo $(($1 /100))' args

Humidity

/config/./FILENAME.sh | grep '20 01\| a5' | awk 'NR==1 {print "0x" $18};NR==2 {print $3}' | awk 'NR%2{printf "%s",$0;next;}1' | xargs printf "%d\n"

call these proof of concept stringsā€¦ if we can make it work from terminal we can make it work from lovelace

This is accurate. It reverts on update. I updated to the Feb update and the script stopped working. I re-ran your docker exec line thing and it works again immediately.

1 Like

trying to follow this reverse engineering ble devices guide and so far have this, not working codeā€¦

the crc is correct now, i made sure it only calculating the command and param portion to get correct checksum at the endā€¦

i know we also need to notify the 70d510022c7f4e75ae8ad758951ce4e0 uuid but ā€¦ i may have to get it working on the 69 and come back to the harder 67

import asyncio
from bleak import BleakClient
from crccheck.crc import Crc16CcittFalse

address = " f0:74:59:49:c0:45"

async def main(address):
    async with BleakClient(address) as client:

        header = bytes.fromhex("a5000006011dee34")
        command = bytes.fromhex("00")
        params = bytes.fromhex("310010212010")
        crcinst = Crc16CcittFalse
        crcinst.process(command)
        crcinst.process(params)
        model_number = await client.write_gatt_char("70d51001-2c7f-4e75-ae8a-d758951ce4e0", header + command + params + crcinst.finalbytes())

asyncio.run(main(address))

alright so the crazy part is if i break apart the header and params theres 2 checksums on this codeā€¦

a5000006011d checksum ā†’ ee34
000310010212010 checksum ā†’ 0b79

so my question is do we then add it something like header + crcinst.finalbytes() + command + params + crcinst2?.finalbytes())

how would that work?

10 on -     a5000006011d  ee34       000310010212010a  0b79
9 on-       a5000006013a  bab1       0003100102120109  3b1a
8 on -      a50000060146  05aa       0003100102120108  2b3b
7 on-       a50000060151  677c       0003100102120107  dad4
6 on-       a5000006015d  a6f0       0003100102120106  caf5
5 on -      a50000060169  d027       0003100102120105  fa96
4 on -      a50000060176  33f9       0003100102120104  eab7
3 on -      a50000060184  fca4       0003100102120103  9a50
2 on -      a50000060190  ae11       0003100102120102  8a71
1 on -      a5000006019b  1f7a       0003100102120101  ba12
0 on -      a500000601a8  194a       0003100102120100  aa33

something like ?>

import asyncio
from bleak import BleakClient
from crccheck.crc import Crc16CcittFalse

address = " f0:74:59:49:c0:45"

async def main(address):
    async with BleakClient(address) as client:

        header = bytes.fromhex("a5000006011d")
        command = bytes.fromhex("00")
        params = bytes.fromhex("03100102120100")
        crcinst = Crc16CcittFalse
        crcinst2 = Crc16CcittFalse 
        crcinst.process(header)
        crcinst2.process(command)
        crcinst2.process(params)
        model_number = await client.write_gatt_char("70d51001-2c7f-4e75-ae8a-d758951ce4e0", header + crcinst.finalbytes() + command + params + crcinst2.finalbytes())

asyncio.run(main(address))

working python code for 69ā€¦ ill come back to 67 unless someone else figures out last bit on their ownā€¦

was able to get 69 to turn on with

import asyncio
from bleak import *
from bleak import BleakClient
from crccheck.crc import Crc16CcittFalse
address = "34:85:18:6a:52:52"

async def main(address):
    async with BleakClient(address) as client:

        header = bytes.fromhex("a5000008013bb191")
        command = bytes.fromhex("00")
        params = bytes.fromhex("0310010212010aff01209a")
        crcinst = Crc16CcittFalse()
        crcinst.process(header)
        crcinst.process(command)
        crcinst.process(params)
        model_number = await client.write_gatt_char("70d51001-2c7f-4e75-ae8a-d758951ce4e0", header + command + params + crcinst.finalbytes())

asyncio.run(main(address))

with this one it allows open command line , progress? maybe lots of errors which slows this code down now (fixed errors, moving onto seeing if i can create a full working code to integrate into home assistant using this as the starting point. By only needing to change single value (command) we can control the speed of the fan.

we can

import asyncio
from bleak import *
from bleak import BleakClient
from crccheck.crc import Crc16CcittFalse

address = "34:85:18:6a:52:52"

async def main(address):
    async with BleakClient(address) as client:

        aciuni = bytes.fromhex("a5000008013bb191")
        header = bytes.fromhex("00031001021201")
        command = bytes.fromhex("00") 
        params = bytes.fromhex("ff01")
        
        crcinst = Crc16CcittFalse()
        crcinst.process(header)
        crcinst.process(command)
        crcinst.process(params)
        model_number = await client.write_gatt_char("70d51001-2c7f-4e75-ae8a-d758951ce4e0",aciuni + header + command + params + crcinst.finalbytes())

asyncio.run(main(address))

heres what i have so far

init.py

"""ACI Universal COntroller"""

ACI_UNI.py

import asyncio
from bleak import BleakClient, BleakScanner
from crccheck.crc import Crc16CcittFalse
import logging

WRITE_UUID = "70d51001-2c7f-4e75-ae8a-d758951ce4e0"
LOGGER = logging.getLogger(__name__)

async def discover():
    """Discover Bluetooth LE devices."""
    devices = await BleakScanner.discover()
    LOGGER.debug("Discovered devices: %s", [{"address": device.address, "name": device.name} for device in devices])
    return [device for device in devices if device.name.startswith("4GRDH")]

class ACIInstance:
    def __init__(self, mac: str) -> None:
        self._mac = mac
        self._device = BleakClient(self._mac)
        self._is_on = None
        self._connected = None
        self._speed = None

    async def _send(self, data: bytearray):
        LOGGER.debug(''.join(format(x, ' 03x') for x in data))
        
        if (not self._connected):
            await self.connect()
        
        crcinst = Crc16CcittFalse()
        crcinst.process(data)
        await self._device.write_gatt_char(WRITE_UUID, data + crcinst.finalbytes())

    @property
    def mac(self):
        return self._mac

    @property
    def is_on(self):
        return self._is_on

    @property
    def speed(self):
        return self._speed

    async def set_speed(self, velocity: int):
        aciuni = bytes.fromhex("a5000008013bb191")
        header = bytes.fromhex("00031001021201")
        command = bytes.fromhex(s)([velocity])
        params = bytes.fromhex("ff01")

        await self._send(aciuni + header + command + params)

        self._speed = velocity

    async def turn_on(self):
        aciuni = bytes.fromhex("a5000008013bb191")
        header = bytes.fromhex("00031001021201")
        command = bytes.fromhex([velocity])
        params = bytes.fromhex("ff01")

        await self._send(aciuni + header + command + params)
        self._is_on = True

    async def turn_off(self):
        aciuni = bytes.fromhex("a500000801ee2a49")
        header = bytes.fromhex("00031001011101")
        command = bytes.fromhex([velocity])
        params = bytes.fromhex("ff01")

        await self._send(aciuni + header + command + params)
        self._is_on = False

    async def connect(self):
        await self._device.connect(timeout=20)
        await asyncio.sleep(1)
        self._connected = True

    async def disconnect(self):
        if self._device.is_connected:
            await self._device.disconnect()

fan.py

"""Provides functionality to interact with fans."""
from __future__ import annotations

import logging
from.ACI_Uni import ACIInstance
import voluptuous as vol
from pprint import pformat

import homeassistant.helpers.config_validation as config_validation
from homeassistant.components.fan import (    SERVICE_TOGGLE,SUPPORT_SET_SPEED,
    SERVICE_TURN_OFF,
    SERVICE_TURN_ON,
    STATE_ON, PLATFORM_SCHEMA)
from homeassistant.const import CONF_NAME , CONT_MAC
from homeassistant.core import homeassistant
from homeassistant.helpers.entity_platform import AddEntitieCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryType

_LOGGER = logging.getLogger("ACI")

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Optional(CONF_NAME): cv.string,
    vol.Required(CONF_MAC): cv.sring,
})

def setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    add_entities: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None
) -> None:
    """Set up the ACI_Universal fan platform."""
    # Add devices
    _LOGGER.info(pformat(config))
    
    Fan = {
        "name": config[CONF_NAME],
        "mac": config[CONF_MAC]
    }
    
    add_entities([ACI_UniversalController(Fan)])

from homeassistant.util.percentage import int_states_in_range, ranged_value_to_percentage, percentage_to_ranged_value

SPEED_RANGE = (1, 10)

percentage = ranged_value_to_percentage(SPEED_RANGE, 10)

value_in_range = math.ceil(percentage_to_ranged_value(SPEED_RANGE, 1))



class FanEntity(ToggleEntity):

    def turn_on(self, speed: Optional[str] = None, percentage: Optional[int] = None, preset_mode: Optional[str] = None, **kwargs: Any) -> None:
        """Turn on the fan."""

    def turn_off(self, **kwargs: Any) -> None:
        """turn the fan off"""

    @property
    def percentage(self) -> Optional[int]:
        """Return the current speed percentage."""
        return ranged_value_to_percentage(SPEED_RANGE, current_speed)

    @property
    def speed_count(self) -> int:
        """Return the number of speeds the fan supports."""
        return int_states_in_range(SPEED_RANGE)

manifest.json

{
    "domain": "ACI Universal Controller",
    "name": "ACI 69 Controller",
    "requirements": ["bleak==0.19.5", "crccheck==1.3.0"],
    "iot_class": "assumed_state",
    "version": "0.0"
}

id like to be able to call velocity from a slider to be able to adjust fan speed see how that goes

broke original code sent further , you dont have to use it this long way it just shows what its for so far

import asyncio
from bleak import *
from bleak import BleakClient
from crccheck.crc import Crc16CcittFalse

address = "34:85:18:6a:52:52"

async def main(address):
    async with BleakClient(address) as client:

        aciuni = bytes.fromhex("a5000008013bb191")
        header = bytes.fromhex("00031001")
        power = bytes.fromhex("0111")
        direction = bytes.fromhex("01")
        velocity = bytes.fromhex("04") 
        params = bytes.fromhex("ff01")
        
        crcinst = Crc16CcittFalse()
        crcinst.process(header)
        crcinst.process(power)
        crcinst.process(direction)
        crcinst.process(velocity)
        crcinst.process(params)
        model_number = await client.write_gatt_char("70d51001-2c7f-4e75-ae8a-d758951ce4e0",aciuni + header + power + direction +  velocity + params + crcinst.finalbytes())

asyncio.run(main(address))

you can use all the same code but by changing the power to 0212 it will turn on at the velocity value you set. wit knowing you only have to change this value we can easily configure the message sent to the device a bit easier

Just been reading through the thread and find it hard to work out where to start from in doing this? Could someone give me a pointed please?

1 Like

sorry if you follow jayrama on post 65 for the 67 it will get you setup with a shell script to control variable fan speedā€¦

im still working out coding the other output but you could put it into a shh file and then use a command line sensor to parse the data?

.sh file for temp/humidity

#!/usr/bin/expect -f

set prompt "#"
set address f0:74:59:49:c0:45
spawn bluetoothctl
expect -re $prompt
send "connect $address\r"
expect "Connection successful"
send "list-attributes\r"
send "menu gatt\r"
send "select-attribute /org/bluez/hci0/dev_F0_74_59_49_C0_45/service001b/char001e\r"
send "notify on\r"
send "select-attribute /org/bluez/hci0/dev_F0_74_59_49_C0_45/service001b/char001c\r"
send "write \"0xa5 0x00 0x00 0x03 0x03 0x49 0x79 0xd7 0x00 0x01 0x02 0x03 0x20 0x78 0xe9\"\r" 
send "select-attribute /org/bluez/hci0/dev_F0_74_59_49_C0_45/service001b/char001e\r"
send "read\r"
sleep 2
send "quit\r"
expect eof

command flow to follow to find values

Temp in C

/config/./FILENAME.sh | grep a5 | awk 'NR==2 { print "0x" $16 $17 }' | xargs printf "%d\n"

this will show temp in F from terminal

/config/./FILENAME.sh | grep a5 | awk 'NR==2 { print "0x" $16 $17 }' | xargs printf "%d\n" |xargs -n 1 bash -c 'echo $(($1 * 9/5 + 3200 ))' args | xargs -n 1 bash -c 'echo $(($1 /100))' args

Humidity

/config/./FILENAME.sh | grep '20 01\| a5' | awk 'NR==1 {print "0x" $18};NR==2 {print $3}' | awk 'NR%2{printf "%s",$0;next;}1' | xargs printf "%d\n"

.sh file for controlling pwm of fan

#!/bin/bash

macaddress="c9:e4:xx:xx:xx:xx"
devstring=dev_$(echo ${macaddress^^} | tr ":" _)

if [[ $1 == 0* ]]; then speed="0x00 0xaa 0x33"; fi
if [[ $1 == 1* ]]; then speed="0x01 0xba 0x12"; fi
if [[ $1 == 2* ]]; then speed="0x02 0x8a 0x71"; fi
if [[ $1 == 3* ]]; then speed="0x03 0x9a 0x50"; fi
if [[ $1 == 4* ]]; then speed="0x04 0xea 0xb7"; fi
if [[ $1 == 5* ]]; then speed="0x05 0xfa 0x96"; fi
if [[ $1 == 6* ]]; then speed="0x06 0xca 0xf5"; fi
if [[ $1 == 7* ]]; then speed="0x07 0xda 0xd4"; fi
if [[ $1 == 8* ]]; then speed="0x08 0x2b 0x3b"; fi
if [[ $1 == 9* ]]; then speed="0x09 0x3b 0x1a"; fi
if [[ $1 == 10* ]]; then speed="0x0a 0x0b 0x79"; fi

/usr/bin/expect <(cat <<'EOF'
set prompt "#"
set speed [lindex $argv 0]
set devstring [lindex $argv 1]
set address [lindex $argv 2]
spawn bluetoothctl
expect -re $prompt
send "connect $address\r"
expect "Connection successful"
send "menu gatt\r"
send "select-attribute /org/bluez/hci0/$devstring/service001b/char001e\r"
send "notify on\r"
send "select-attribute /org/bluez/hci0/$devstring/service001b/char001c\r"
send "write \"0xa5 0x00 0x00 0x06 0x00 0x67 0x02 0xd8 0x00 0x03 0x10 0x01 0x02 0x12 0x01 $speed\"\r"
send "quit\r"
expect eof
EOF
) "$speed" "$devstring" "$macaddress"

config.yaml

shell_command:
  set_acinfinity_speed: '/config/ac.sh {{ states("input_number.acinfinity_speed") }}'

input_number:
  acinfinity_speed:
    name: acinfinity_speed
    initial: 5
    min: 0
    max: 10
    step: 1

automation:
  - alias: "run_set_acinfinity"
    trigger:
      platform: state
      entity_id: input_number.acinfinity_speed
    action:
      service: shell_command.set_acinfinity_speed
1 Like

is there any way of using an ESP32 as a bluetooth proxy with this? Now the proxy can control active connections ( Bluetooth Proxy ā€” ESPHome )

I tried the script and it always ā€œDevice ((MAC Address)) not availableā€ but i never use the native bluetooth of the RaspberryPI i use the proxies (using a SSD with the Rpi that kills my bluetooth)

Thank you!

1 Like

did you change the mac address to yours?

id get a dongle for rPi, i use rPi4b

Yes i did changed to my one but i donā€™t know if is trying with its bluetooth or with the proxies. I believe with the internal bluetooth. I will do a scan to see what i find (i have many bluetooth devices)

Hit hciconfig and send readout from your terminal

it tells me command not found been searching on google how to get it working but couldnā€™t make it work.

Scanning devices I can find 15 devices but none of them is the AC controller (with bluetoothctl and then scan on) the controller is far away from the Rpi4 but close to a BT-Proxy

make sure your phone is not connected to the app, if so force shutdown the app

also try unplugging the fan/controller from wall and replug it inā€¦ i know it sounds simple but sometimes the device gets hung up

it also will only allow a single connection

67 will show up as ACI Universal Controller

69 will show up as 4GRDH

I canā€™t find the mac address from the Rpi4. I can find it with an android phone i have nearby ACI Universal Controller with its mac addressā€¦ I guess itā€™s using the local BT of the Rpi and not the proxy. the thing is the proxyā€™s log doesnā€™t tell me the devices found i donā€™t really know how they work

At the moment i have an android phone nearby it so i can remote access to it and then control the AC app from it (Just waiting to receive the 69 pro to avoid this and control independently everything)

You ever get a dongle to fix your connection issue?

Hi everyone, I found this post during the weekend and got hands on implementing. Progress looks very promising so far but I found that the BT commands fail to execute some times and Iā€™m not so comfortable with that so I reverted to the app Auto version. I would be interesting in finding the commands to set the temp/humidity triggers for the App and let auto mode do its thing based on that.

In the meantime, I created a few automations and sensor that feel would come very handy to you.

Calculating environment and leaf VPD (leaf temperature is coming from a helper but can be changed to a sensor):

  - platform: template
    sensors:
      tent_vpd:
        entity_id: sensor.tent_vpd
        friendly_name: "VPD Carpa"
        icon_template: mdi:waves-arrow-up
        value_template: >-
          {% set T = states('sensor.temperatura_indoor')|float %}
          {% set RH = states('sensor.humedad_indoor')|float %}
          {% set SVP = 0.61078 * e ** (T / (T + 237.3) * 17.2694) %}
          {% set VPD = SVP * ((100 - RH) / 100) %}
          {{-VPD | round(2) -}}
        unit_of_measurement: 'kPa'

  - platform: template
    sensors:
      leaf_vpd:
        friendly_name: "VPD Hoja"
        entity_id: sensor.leaf_vpd
        icon_template: mdi:waves-arrow-up
        value_template: >-
         {% set T = states('sensor.temperatura_indoor')|float %}
         {% set RH = states('sensor.humedad_indoor')|float %}
         {% set LTO = states('input_number.leaf_temperature_offset')|float %}
         {% set LT = T + LTO %}
         {% set ASVP = 0.61078 * e ** (T / (T + 237.3) * 17.2694) %}
         {% set LSVP = 0.61078 * e ** (LT / (LT + 237.3) * 17.2694) %}
         {% set LVPD = LSVP - (ASVP * RH / 100) %}
         {{-LVPD | round(2) -}}
        unit_of_measurement: "kPa"

Calculating the recommended, min and max humidity based on recommended VPD per stage (Seedling, Veg or Flower) - This is where sending the humidity trigger to AC App would be handy.

  - platform: template
    sensors:
      target_humidity:
        friendly_name: "Humedad Objetivo"
        entity_id: sensor.target_humidity
        icon_template: mdi:cloud-percent
        value_template: >-
         {% set T = states('sensor.temperatura_indoor')|float %}
         {% set LTO = states('input_number.leaf_temperature_offset')|float %}
         {% set LT = T + LTO %}
         {% set ASVP = 0.61078 * e ** (T / (T + 237.3) * 17.2694) %}
         {% set LSVP = 0.61078 * e ** (LT / (LT + 237.3) * 17.2694) %}
         {% set LVPD = states('input_number.vpd_obetivo')|float %}
         {% set RH = ((LSVP - LVPD) * 100) / ASVP %}
         {{-RH | round(0) -}}
        unit_of_measurement: "%"
        
  - platform: template
    sensors:
      min_humidity:
        friendly_name: "Humedad Minima"
        entity_id: sensor.min_humidity
        icon_template: mdi:cloud-percent
        value_template: >-
         {% set T = states('sensor.temperatura_indoor')|float %}
         {% set LTO = states('input_number.leaf_temperature_offset')|float %}
         {% set LT = T + LTO %}
         {% set ASVP = 0.61078 * e ** (T / (T + 237.3) * 17.2694) %}
         {% set LSVP = 0.61078 * e ** (LT / (LT + 237.3) * 17.2694) %}
         {% set LVPD = states('input_number.vpd_recomendado_max')|float %}
         {% set RH = ((LSVP - LVPD) * 100) / ASVP %}
         {{-RH | round(0) -}}
        unit_of_measurement: "%"
        
  - platform: template
    sensors:
      max_humidity:
        friendly_name: "Humedad Maxima"
        entity_id: sensor.max_humidity
        icon_template: mdi:cloud-percent
        value_template: >-
         {% set T = states('sensor.temperatura_indoor')|float %}
         {% set LTO = states('input_number.leaf_temperature_offset')|float %}
         {% set LT = T + LTO %}
         {% set ASVP = 0.61078 * e ** (T / (T + 237.3) * 17.2694) %}
         {% set LSVP = 0.61078 * e ** (LT / (LT + 237.3) * 17.2694) %}
         {% set LVPD = states('input_number.vpd_recomendado_min')|float %}
         {% set RH = ((LSVP - LVPD) * 100) / ASVP %}
         {{-RH | round(0) -}}
        unit_of_measurement: "%"

Automations to adjust helpers (Auto VPD recommended values and managing a humidifier based on min/max values obtained early). - You would need to create any required helpers (mostly input numbers)

- id: '1676181510184'
  alias: VPD Recomendado Semilla
  description: ''
  trigger:
  - platform: state
    entity_id:
    - input_select.etapa
    to: Semilla
  condition: []
  action:
  - service: input_number.set_value
    data:
      value: 0.4
    target:
      entity_id: input_number.vpd_recomendado_min
  - service: input_number.set_value
    data:
      value: 0.8
    target:
      entity_id: input_number.vpd_recomendado_max
  - service: input_number.set_value
    data:
      value: 0.6
    target:
      entity_id: input_number.vpd_obetivo
  mode: single
- id: '1676181609393'
  alias: VPD Recomendado VegetaciĆ³n
  description: ''
  trigger:
  - platform: state
    entity_id:
    - input_select.etapa
    to: VegetaciĆ³n
  condition: []
  action:
  - service: input_number.set_value
    data:
      value: 0.8
    target:
      entity_id: input_number.vpd_recomendado_min
  - service: input_number.set_value
    data:
      value: 1.2
    target:
      entity_id: input_number.vpd_recomendado_max
  - service: input_number.set_value
    data:
      value: 1
    target:
      entity_id: input_number.vpd_obetivo
  mode: single
- id: '1676181641198'
  alias: VPD Recomendado FloraciĆ³n
  description: ''
  trigger:
  - platform: state
    entity_id:
    - input_select.etapa
    to: FloraciĆ³n
  condition: []
  action:
  - service: input_number.set_value
    data:
      value: 1.2
    target:
      entity_id: input_number.vpd_recomendado_min
  - service: input_number.set_value
    data:
      value: 1.6
    target:
      entity_id: input_number.vpd_recomendado_max
  - service: input_number.set_value
    data:
      value: 1.4
    target:
      entity_id: input_number.vpd_obetivo
  mode: single
- id: '1676188661977'
  alias: Humidificador ON
  description: ''
  trigger:
  - platform: template
    value_template: '{{ (float(states(''sensor.humedad_indoor''))) <= (float(states(''sensor.min_humidity''))
      + 2) }}'
  condition: []
  action:
  - type: turn_on
    device_id: a72395ffd9a67c1bca890c64aa309d57
    entity_id: switch.humidificador_socket_1
    domain: switch
  mode: single
- id: '1676189308835'
  alias: Humidificador OFF
  description: ''
  trigger:
  - platform: template
    value_template: '{{ (float(states(''sensor.humedad_indoor''))) >= (float(states(''sensor.max_humidity''))
      - 2) }}'
  condition: []
  action:
  - type: turn_off
    device_id: a72395ffd9a67c1bca890c64aa309d57
    entity_id: switch.humidificador_socket_1
    domain: switch
  mode: single

Hopes this comes handy to someone!

1 Like