mariolukas / Bluetti_ESP32_Bridge

Bluetti Power Station ESP32 Bluetooth to MQTT Bridge
GNU General Public License v3.0
88 stars 29 forks source link

[Feature] Discovery for Home Assistant would be fine ;-) #21

Closed giovanne123 closed 1 year ago

giovanne123 commented 1 year ago

Compared to "bluetti_mqtt" (https://github.com/warhammerkid/bluetti_mqtt/blob/main/bluetti_mqtt/mqtt_client.py, _send_discovery_message) it would be fine to have auto discovery for Home Assistant implemented into Bluetti_ESP32_Bridge for easy setup in HA.

That would be the desired result for the EB3A: (Sensors "Unknown" because I haven't the bluetti_mqtt python script running at the moment) image

mariolukas commented 1 year ago

I am not using Home Assistent, i have to check what we need to do. Maybe somebody else who is using Home Assistant can implement this?

areyoufserious commented 1 year ago

HA autodiscovery is very needed functionality!

giovanne123 commented 1 year ago

Need to understand the HA config messages:

I think the related parts which needs to be converted for the ESP32 are in https://github.com/warhammerkid/bluetti_mqtt/blob/main/bluetti_mqtt/mqtt_client.py:

@dataclass(frozen=True)
class MqttFieldConfig:
    type: MqttFieldType
    setter: bool
    advanced: bool  # Do not export by default to Home Assistant
    home_assistant_extra: dict
    id_override: Optional[str] = None  # Used to override Home Assistant field id

COMMAND_TOPIC_RE = re.compile(r'^bluetti/command/(\w+)-(\d+)/([a-z_]+)$')
NORMAL_DEVICE_FIELDS = {
    'dc_input_power': MqttFieldConfig(
        type=MqttFieldType.NUMERIC,
        setter=False,
        advanced=False,
        home_assistant_extra={
            'name': 'DC Input Power',
            'unit_of_measurement': 'W',
            'device_class': 'power',
            'state_class': 'measurement',
            'force_update': True,
        }
    ),
    'ac_input_power': MqttFieldConfig(
        type=MqttFieldType.NUMERIC,
        setter=False,
        advanced=False,
        home_assistant_extra={
            'name': 'AC Input Power',
            'unit_of_measurement': 'W',
            'device_class': 'power',
            'state_class': 'measurement',
            'force_update': True,
        }
    ),
    'ac_output_power': MqttFieldConfig(
        type=MqttFieldType.NUMERIC,
        setter=False,
        advanced=False,
        home_assistant_extra={
            'name': 'AC Output Power',
            'unit_of_measurement': 'W',
            'device_class': 'power',
            'state_class': 'measurement',
            'force_update': True,
        }
    ),
    'dc_output_power': MqttFieldConfig(
        type=MqttFieldType.NUMERIC,
        setter=False,
        advanced=False,
        home_assistant_extra={
            'name': 'DC Output Power',
            'unit_of_measurement': 'W',
            'device_class': 'power',
            'state_class': 'measurement',
            'force_update': True,
        }
    ),
    'power_generation': MqttFieldConfig(
        type=MqttFieldType.NUMERIC,
        setter=False,
        advanced=False,
        home_assistant_extra={
            'name': 'Total Power Generation',
            'unit_of_measurement': 'kWh',
            'device_class': 'energy',
            'state_class': 'total_increasing',
        }
...
    async def _send_discovery_message(self, client: Client):
        def payload(id: str, device: BluettiDevice, field: MqttFieldConfig) -> str:
            ha_id = id if not field.id_override else field.id_override
            payload_dict = {
                'state_topic': f'bluetti/state/{device.type}-{device.sn}/{id}',
                'device': {
                    'identifiers': [
                        f'{device.sn}'
                    ],
                    'manufacturer': 'Bluetti',
                    'name': f'{device.type} {device.sn}',
                    'model': device.type
                },
                'unique_id': f'{device.sn}_{ha_id}',
                'object_id': f'{device.type}_{ha_id}',
            }
            if field.setter:
                payload_dict['command_topic'] = f'bluetti/command/{device.type}-{device.sn}/{id}'
            payload_dict.update(field.home_assistant_extra)

            return json.dumps(payload_dict, separators=(',', ':'))

        # Loop through devices
        for d in self.devices:
            # Publish normal fields
            for name, field in NORMAL_DEVICE_FIELDS.items():
                # Skip fields not supported by the device
                if not d.has_field(name):
                    continue

                # Skip advanced fields if not enabled
                if field.advanced and self.home_assistant_mode != 'advanced':
                    continue

                # Figure out Home Assistant type
                if field.type == MqttFieldType.NUMERIC:
                    type = 'number' if field.setter else 'sensor'
                elif field.type == MqttFieldType.BOOL:
                    type = 'switch' if field.setter else 'binary_sensor'
                elif field.type == MqttFieldType.ENUM:
                    type = 'select' if field.setter else 'sensor'
                elif field.type == MqttFieldType.BUTTON:
                    type = 'button'

                # Publish config
                await client.publish(
                    f'homeassistant/{type}/{d.sn}_{name}/config',
                    payload=payload(name, d, field).encode(),
                    retain=True
                )

            # Publish battery pack configs
            for pack in range(1, d.pack_num_max + 1):
                fields = self._battery_pack_fields(pack)
                for field in fields:
                    await client.publish(
                        f'homeassistant/sensor/{d.sn}_{field.id_override}/config',
                        payload=payload(f'pack_details{pack}', d, field).encode(),
                        retain=True
                    )

            # Publish DC input config
            if d.has_field('internal_dc_input_voltage'):
                for name, field in DC_INPUT_FIELDS.items():
                    await client.publish(
                        f'homeassistant/sensor/{d.sn}_{name}/config',
                        payload=payload(name, d, field).encode(),
                        retain=True
                    )

            logging.info(f'Sent discovery message of {d.type}-{d.sn} to Home Assistant')
Dengor1984 commented 1 year ago

HA autodiscovery is very needed functionality!

It's will be GREAT because in this time you need manually add all sensors to home assistant...(

giovanne123 commented 1 year ago

Until it is directly implemented you maybe can use e.g. a workarounnd to manually created the entities in HA. I use a simple python code to publish the needed stuff to mqtt:

https://github.com/giovanne123/EB3A_Bluetti_ESP32_HA/blob/main/Bluetti_ESP32/Device_EB3A_ha_discover_config.py

https://github.com/giovanne123/EB3A_Bluetti_ESP32_HA/blob/main/Bluetti_ESP32/Device_EB3A_ha_discover_config_TEMPLATE.json

It is in general working for me (some restrictions) so that I can use the EB3A in HA until we have implemented it to the ESP32...

Dengor1984 commented 1 year ago

Until it is directly implemented you maybe can use e.g. a workarounnd to manually created the entities in HA. I use a simple python code to publish the needed stuff to mqtt:

https://github.com/giovanne123/EB3A_Bluetti_ESP32_HA/blob/main/Bluetti_ESP32/Device_EB3A_ha_discover_config.py

https://github.com/giovanne123/EB3A_Bluetti_ESP32_HA/blob/main/Bluetti_ESP32/Device_EB3A_ha_discover_config_TEMPLATE.json

It is in general working for me (some restrictions) so that I can use the EB3A in HA until we have implemented it to the ESP32...

Thanks! First file - is a pyton script. Second file - is it a template sensors? Or Where i must add this? Sorry for stupid questions...

giovanne123 commented 1 year ago

Yes, Python Script to run. An in the second file is the Input (HA config) for the Python Script. The Python will parse the input and publish to mqtt Broker the needed config for HA... You need to search & replace in the input file to insert your Bluetti ID ( or what you want to have in the topics) ...

Dengor1984 commented 1 year ago

Yes, Python Script to run. An in the second file is the Input (HA config) for the Python Script. The Python will parse the input and publish to mqtt Broker the needed config for HA... You need to search & replace in the input file to insert your Bluetti ID ( or what you want to have in the topics) ...

Do i need to change this line in config.py? paho = mqtt.Client("esp32_bluetti_config_ha_discover_py") My mqtt client is Bluetti_ESP32

giovanne123 commented 1 year ago

No, this is only the client name in mqtt broker - can stay as ist...

Only path/name to input file in: file1 = open('Bluetti_ESP32\Device_EB3A_ha_discover_config.json

Dengor1984 commented 1 year ago

No, this is only the client name in mqtt broker - can stay as ist...

Only path/name to input file in: file1 = open('Bluetti_ESP32\Device_EB3A_ha_discover_config.json

I do ALL with this files, but mqtt doesn't add my Bluetti...(

giovanne123 commented 1 year ago

After Script is running successful, first there should be the entries created in MQTT-Broker in Homeassistant Section for the HA autodiscovery config: e.g. a Switch: image

And after that you should find the Device in HA - Settings - Devices&Services - MQTT: image And with something like this for the device: https://user-images.githubusercontent.com/16689453/218101746-88c26f85-9ce7-4484-99e3-6ec1f2127e5b.png https://user-images.githubusercontent.com/16689453/217889994-d51393c4-65a5-4eb5-ba86-e19727586bd3.png

Later when sending in MQTT Broker it will appear like: https://user-images.githubusercontent.com/16689453/217887074-5e2c0094-dbd3-400c-a1f6-35d7add38de4.png

Dengor1984 commented 1 year ago

Maybe something with script...

![Uploading 5762E199-041B-4244-9EF0-411E438C474E.jpeg…]()

My script not running - unknown error

i have create a folder Bluetti_ESP32 and put here a file with my Bluetti parameters in all lines /config/Bluetti_ESP32/Device_AC200M_ha_discover_config_TEMPLATE.json

Dengor1984 commented 1 year ago

My script:

!/usr/bin/env python3

import subprocess import time import paho.mqtt.client as mqtt

broker = "10.0.1.7" port = int("1883") user = "MQTT" password = "DG18"

def on_connect(client, userdata, result): """Pass publish.""" pass

def on_publish(client, userdata, result): """Pass publish.""" pass

def send_ha_config_message():

file1 = open('Bluetti_ESP32\Device_AC200M_ha_discover_config_TEMPLATE.json', 'r')
lines = file1.readlines()

paho = mqtt.Client("esp32_bluetti_config_ha_discover_py")
paho.username_pw_set(user, password=password)
paho.on_connect = on_connect
paho.connect(broker, port)

count = 0
# Strips the newline character
for line in lines:
    try:
        count += 1
        #print("Line{}: {}".format(count, line.strip()))
        l = line.strip()
        #am ersten Blank splitten
        n = l.split(" ", 1)
        #print("-------------------")
        #print(n[0])
        #print("-------------------")
        print(n[1])
        paho.publish(topic=n[0], payload = n[1], qos=1, retain=True,)
    except Exception as e:
        print('An error was produced while processing ' + str(line) + ' with exception: ' + str(e))
        raise
paho.disconnect()

def on_connect(client, userdata, result): """Pass publish.""" pass

def on_publish(client, userdata, result): """Pass publish.""" pass

if name == 'main':

send_ha_config_message()
Dengor1984 commented 1 year ago

It seems problem is with home assistant os - it haven't paho-mqtt and i can't install it from HA OS. Later i will try to install HA from docker and try again

giovanne123 commented 1 year ago

The script does not require to be executed/ integrated in HA. The script can executed from everywhere where python with required moduls (e.g. paho-mqtt) is available (Windows PC, other linux/rpi box, ...) So no need to install additional HA in docker - if you want docker to execute the script a linux image/container with python is enough...

You have a lot of effort only to get the workarounnd running ;-) Would be faster to implement it directly in ESP instead ;-)

Dengor1984 commented 1 year ago

Now i have an error :

Logger: homeassistant.components.python_script Source: components/python_script/init.py:112 Integration: Python Scripts (documentation, issues) First occurred: 20:13:02 (11 occurrences) Last logged: 20:49:20

Error loading script device_ac200m_ha_discoverconfig.py: Line 58: "name" is an invalid variable name because it starts with "" Error loading script device_ac200m_ha_discover_config.py: Line 60: IndentationError: unexpected indent at statement: 'send_ha_config_message()' Error loading script device_ac200m_ha_discover_config.py: Line 60: IndentationError: expected an indented block after 'if' statement on line 58 at statement: 'send_ha_config_message()'

giovanne123 commented 1 year ago

I don't know what you are trying... as mentioned before the script itself is not intendend to be used in HA directly. It only creates the config entries in mqtt broker which HA is using then for autodiscovering and creating the mqtt device.

Dengor1984 commented 1 year ago

I don't know what you are trying... as mentioned before the script itself is not intendend to be used in HA directly. It only creates the config entries in mqtt broker which HA is using then for autodiscovering and creating the mqtt device.

I followed the instructions carefully: created a folder for the script; created a folder for the config; I inserted my values for the broker and for bluetti in the files; I'm trying to run the script and I get this error...

giovanne123 commented 1 year ago

But the error is from HomeAssistant Log and not from running it in python directly...

Dengor1984 commented 1 year ago

After Script is running successful, first there should be the entries created in MQTT-Broker in Homeassistant Section for the HA autodiscovery config: e.g. a Switch: image

And after that you should find the Device in HA - Settings - Devices&Services - MQTT: image And with something like this for the device: https://user-images.githubusercontent.com/16689453/218101746-88c26f85-9ce7-4484-99e3-6ec1f2127e5b.png https://user-images.githubusercontent.com/16689453/217889994-d51393c4-65a5-4eb5-ba86-e19727586bd3.png

Later when sending in MQTT Broker it will appear like: https://user-images.githubusercontent.com/16689453/217887074-5e2c0094-dbd3-400c-a1f6-35d7add38de4.png

Can you please show a mqtt.yaml sample with your all sensors and switches?

giovanne123 commented 1 year ago

There are no entries in my mqtt.yaml I think you haven't understud HA/MQTT AutoDiscovery .

My config is the already linked file: https://github.com/giovanne123/EB3A_Bluetti_ESP32_HA/blob/main/Bluetti_ESP32/Device_EB3A_ha_discover_config_TEMPLATE.json

which creates the needed configs for HA in the MQTT broker.

Maybe it is simpler for you to create the sensors manually in HA until someone has implemented it directly in ESP firmware.

smai86 commented 1 year ago

@giovanne123 can you help me to get it autodiscovered by ha?

Your Version cant get compiled by Arduino IDE... I went in different errors. So can u help me to modify this version?

giovanne123 commented 1 year ago

@giovanne123 can you help me to get it autodiscovered by ha?

Your Version cant get compiled by Arduino IDE... I went in different errors. So can u help me to modify this version?

So far I only used a workaround to publish/created the HA config in mqtt broker: https://github.com/giovanne123/EB3A_Bluetti_ESP32_HA/blob/main/Bluetti_ESP32/Device_EB3A_ha_discover_config.py

HA autodiscovery isn't in my firmware integrated so far... Maybe there will come a direct integration in this firmware version here? But I don't follow it anymore because for my EB3A the workaround is working still fine and for future I am switching to Ecoflow devices with their Powerstream :-)

giovanne123 commented 1 year ago

.