From 5a6dbb8d008948f57c411dc6dc3dd56961d862f9 Mon Sep 17 00:00:00 2001 From: Patrick762 Date: Sat, 19 Aug 2023 17:05:59 +0200 Subject: [PATCH 1/6] added "my" link to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e39bf62..f01d517 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![Validate with hassfest](https://github.com/Patrick762/hassio-bluetti-bt/actions/workflows/hassfest_validation.yml/badge.svg)](https://github.com/Patrick762/hassio-bluetti-bt/actions/workflows/hassfest_validation.yml) [![HACS Action](https://github.com/Patrick762/hassio-bluetti-bt/actions/workflows/HACS.yml/badge.svg)](https://github.com/Patrick762/hassio-bluetti-bt/actions/workflows/HACS.yml) +[![Add repo to HACS](https://my.home-assistant.io/badges/hacs_repository.svg)](https://my.home-assistant.io/redirect/hacs_repository/?repository=hassio-bluetti-bt&owner=Patrick762) Bluetti Integration for Home Assistant From 886ab957bb61bc0186c58eccd1319d28040ee482 Mon Sep 17 00:00:00 2001 From: Patrick762 Date: Sat, 19 Aug 2023 17:06:58 +0200 Subject: [PATCH 2/6] added options flow --- custom_components/bluetti_bt/config_flow.py | 56 ++++++++++++++++++- custom_components/bluetti_bt/const.py | 1 + custom_components/bluetti_bt/coordinator.py | 3 +- .../bluetti_bt/translations/de.json | 11 ++++ .../bluetti_bt/translations/en.json | 11 ++++ 5 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 custom_components/bluetti_bt/translations/de.json create mode 100644 custom_components/bluetti_bt/translations/en.json diff --git a/custom_components/bluetti_bt/config_flow.py b/custom_components/bluetti_bt/config_flow.py index 9823ec5..6e818e5 100644 --- a/custom_components/bluetti_bt/config_flow.py +++ b/custom_components/bluetti_bt/config_flow.py @@ -14,9 +14,10 @@ from homeassistant.components.bluetooth import ( ) from homeassistant.const import CONF_ADDRESS, CONF_TYPE, CONF_NAME from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers import selector from . import get_type_by_bt_name -from .const import DOMAIN +from .const import DOMAIN, CONF_USE_CONTROLS _LOGGER = logging.getLogger(__name__) @@ -86,3 +87,56 @@ class BluettiConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): step_id="user", data_schema=data_schema, ) + + @staticmethod + def async_get_options_flow( + config_entry: config_entries.ConfigEntry, + ) -> config_entries.OptionsFlow: + """Create the options flow.""" + return OptionsFlowHandler(config_entry) + + +class OptionsFlowHandler(config_entries.OptionsFlow): + """Handle a option flow.""" + + def __init__(self, config_entry: config_entries.ConfigEntry) -> None: + """Initialize options flow.""" + self.config_entry = config_entry + + async def async_step_init( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Manage the options.""" + if user_input is not None: + changed = self.hass.config_entries.async_update_entry( + self.config_entry, + data={ + **self.config_entry.data, + **{ + CONF_USE_CONTROLS: user_input[CONF_USE_CONTROLS], + }, + }, + ) + if changed is False: + _LOGGER.error( + "Method OptionsFlowHandler.async_step_init: Config entry %s has not been changed", + self.config_entry.entry_id, + ) + return self.async_create_entry( + title="", + data={ + CONF_USE_CONTROLS: user_input[CONF_USE_CONTROLS], + }, + ) + + return self.async_show_form( + step_id="init", + data_schema=vol.Schema( + { + vol.Required( + CONF_USE_CONTROLS, + default=self.config_entry.data.get(CONF_USE_CONTROLS, False), + ): selector.BooleanSelector(), + } + ), + ) diff --git a/custom_components/bluetti_bt/const.py b/custom_components/bluetti_bt/const.py index 3b11800..9848d21 100644 --- a/custom_components/bluetti_bt/const.py +++ b/custom_components/bluetti_bt/const.py @@ -6,6 +6,7 @@ DOMAIN = "bluetti_bt" MANUFACTURER = "Bluetti" CONF_OPTIONS = "options" +CONF_USE_CONTROLS = "use_controls" DIAGNOSTIC_FIELDS = [ "max_ac_input_power", diff --git a/custom_components/bluetti_bt/coordinator.py b/custom_components/bluetti_bt/coordinator.py index f2338b3..85102bb 100644 --- a/custom_components/bluetti_bt/coordinator.py +++ b/custom_components/bluetti_bt/coordinator.py @@ -1,12 +1,11 @@ """Coordinator for Bluetti integration.""" from __future__ import annotations -from typing import List +from typing import List, cast import asyncio from datetime import timedelta import logging -from typing import cast import async_timeout from bleak import BleakClient, BleakError diff --git a/custom_components/bluetti_bt/translations/de.json b/custom_components/bluetti_bt/translations/de.json new file mode 100644 index 0000000..8b1d377 --- /dev/null +++ b/custom_components/bluetti_bt/translations/de.json @@ -0,0 +1,11 @@ +{ + "options": { + "step": { + "init": { + "data": { + "use_controls": "Steuerung aktivieren (auf eigenes Risiko)" + } + } + } + } +} \ No newline at end of file diff --git a/custom_components/bluetti_bt/translations/en.json b/custom_components/bluetti_bt/translations/en.json new file mode 100644 index 0000000..d3fceda --- /dev/null +++ b/custom_components/bluetti_bt/translations/en.json @@ -0,0 +1,11 @@ +{ + "options": { + "step": { + "init": { + "data": { + "use_controls": "Use controls (use at own risk)" + } + } + } + } +} \ No newline at end of file From 8fce8270b2bd53b095f13c45d6f9edfaf1c089d3 Mon Sep 17 00:00:00 2001 From: Patrick762 Date: Sat, 19 Aug 2023 17:38:24 +0200 Subject: [PATCH 3/6] added switches for ac and dc on off --- custom_components/bluetti_bt/__init__.py | 12 +- custom_components/bluetti_bt/const.py | 5 + custom_components/bluetti_bt/switch.py | 135 +++++++++++++++++++++++ 3 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 custom_components/bluetti_bt/switch.py diff --git a/custom_components/bluetti_bt/__init__.py b/custom_components/bluetti_bt/__init__.py index 34f8497..4deeadc 100644 --- a/custom_components/bluetti_bt/__init__.py +++ b/custom_components/bluetti_bt/__init__.py @@ -3,6 +3,7 @@ from __future__ import annotations import re +import logging from homeassistant.components import bluetooth from homeassistant.config_entries import ConfigEntry @@ -11,10 +12,11 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.exceptions import ConfigEntryNotReady -from .const import DOMAIN, MANUFACTURER +from .const import DOMAIN, MANUFACTURER, CONF_USE_CONTROLS from .coordinator import PollingCoordinator PLATFORMS: [Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR] +_LOGGER = logging.getLogger(__name__) async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: @@ -22,6 +24,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: address = entry.data.get(CONF_ADDRESS) device_name = entry.data.get(CONF_NAME) + use_controls = entry.data.get(CONF_USE_CONTROLS) if address is None: return False @@ -37,8 +40,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN].setdefault(entry.entry_id, coordinator) + platforms: list = PLATFORMS + if use_controls is True: + _LOGGER.warning("You are using controls with this integration at your own risk!") + platforms.append(Platform.SWITCH) + # Setup platforms - await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + await hass.config_entries.async_forward_entry_setups(entry, platforms) return True diff --git a/custom_components/bluetti_bt/const.py b/custom_components/bluetti_bt/const.py index 9848d21..2d8d3ad 100644 --- a/custom_components/bluetti_bt/const.py +++ b/custom_components/bluetti_bt/const.py @@ -8,6 +8,11 @@ MANUFACTURER = "Bluetti" CONF_OPTIONS = "options" CONF_USE_CONTROLS = "use_controls" +CONTROL_FIELDS = [ + "ac_output_on", + "dc_output_on", +] + DIAGNOSTIC_FIELDS = [ "max_ac_input_power", "max_ac_input_current", diff --git a/custom_components/bluetti_bt/switch.py b/custom_components/bluetti_bt/switch.py new file mode 100644 index 0000000..e215650 --- /dev/null +++ b/custom_components/bluetti_bt/switch.py @@ -0,0 +1,135 @@ +"""Bluetti BT switches.""" + +from __future__ import annotations + +import logging + +from homeassistant.components.switch import SwitchEntity, SwitchDeviceClass +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant, callback +from homeassistant.const import ( + CONF_ADDRESS, + CONF_NAME, + EntityCategory, +) +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.update_coordinator import ( + CoordinatorEntity, +) + +from bluetti_mqtt.bluetooth import build_device +from bluetti_mqtt.mqtt_client import ( + NORMAL_DEVICE_FIELDS, + DC_INPUT_FIELDS, + MqttFieldType, +) + +from . import device_info as dev_info, get_unique_id +from .const import CONTROL_FIELDS, DOMAIN, ADDITIONAL_DEVICE_FIELDS +from .coordinator import PollingCoordinator, DummyDevice + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: + """Setup switch entities.""" + + device_name = entry.data.get(CONF_NAME) + address = entry.data.get(CONF_ADDRESS) + if address is None: + _LOGGER.error("Device has no address") + + # Generate device info + _LOGGER.info("Creating sensors for device with address %s", address) + device_info = dev_info(entry) + + # Add sensors according to device_info + bluetti_device = build_device(address, device_name) + bluetti_device = DummyDevice(bluetti_device) + + sensors_to_add = [] + all_fields = NORMAL_DEVICE_FIELDS + all_fields.update(DC_INPUT_FIELDS) + all_fields.update(ADDITIONAL_DEVICE_FIELDS) + for field_key, field_config in all_fields.items(): + if bluetti_device.has_field(field_key): + if field_config.type == MqttFieldType.BOOL: + if field_config.setter is True and field_key in CONTROL_FIELDS: + sensors_to_add.append( + BluettiSwitch( + hass.data[DOMAIN][entry.entry_id], + device_info, + address, + field_key, + field_config.home_assistant_extra.get(CONF_NAME, ""), + ) + ) + + async_add_entities(sensors_to_add) + + +class BluettiSwitch(CoordinatorEntity, SwitchEntity): + """Bluetti universal switch.""" + + def __init__( + self, + coordinator: PollingCoordinator, + device_info: DeviceInfo, + address, + response_key: str, + name: str, + category: EntityCategory | None = None, + ): + """Init entity.""" + super().__init__(coordinator) + + e_name = f"{device_info.get('name')} {name}" + self._address = address + self._response_key = response_key + + self._attr_device_info = device_info + self._attr_name = e_name + self._attr_available = False + self._attr_unique_id = get_unique_id(e_name) + self._attr_entity_category = category + self._attr_device_class = SwitchDeviceClass.OUTLET + + @callback + def _handle_coordinator_update(self) -> None: + """Handle updated data from the coordinator.""" + _LOGGER.debug("Updating state of %s", self._attr_unique_id) + if not isinstance(self.coordinator.data, dict): + _LOGGER.error( + "Invalid data from coordinator (sensor.%s)", self._attr_unique_id + ) + self._attr_available = False + return + + response_data = self.coordinator.data.get(self._response_key) + if response_data is None: + self._attr_available = False + return + + if not isinstance(response_data, bool): + _LOGGER.warning( + "Invalid response data type from coordinator (sensor.%s): %s", + self._attr_unique_id, + response_data, + ) + self._attr_available = False + return + + self._attr_available = True + self._attr_is_on = self.coordinator.data[self._response_key] is True + self.async_write_ha_state() + + async def async_turn_on(self, **kwargs): + """Turn the entity on.""" + _LOGGER.warning("Turn on") + + async def async_turn_off(self, **kwargs): + """Turn the entity off.""" + _LOGGER.warning("Turn off") From dd7665f069ef308d333e6996f53410fe7c1ceee0 Mon Sep 17 00:00:00 2001 From: Patrick762 Date: Sat, 19 Aug 2023 21:17:44 +0200 Subject: [PATCH 4/6] added option to turn on and off outputs --- custom_components/bluetti_bt/__init__.py | 12 ++-- custom_components/bluetti_bt/binary_sensor.py | 4 +- custom_components/bluetti_bt/const.py | 3 + custom_components/bluetti_bt/coordinator.py | 14 +++-- custom_components/bluetti_bt/sensor.py | 6 +- custom_components/bluetti_bt/switch.py | 61 +++++++++++++++++-- 6 files changed, 82 insertions(+), 18 deletions(-) diff --git a/custom_components/bluetti_bt/__init__.py b/custom_components/bluetti_bt/__init__.py index 4deeadc..6c5a740 100644 --- a/custom_components/bluetti_bt/__init__.py +++ b/custom_components/bluetti_bt/__init__.py @@ -12,7 +12,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.exceptions import ConfigEntryNotReady -from .const import DOMAIN, MANUFACTURER, CONF_USE_CONTROLS +from .const import DATA_COORDINATOR, DOMAIN, MANUFACTURER, CONF_USE_CONTROLS, DATA_POLLING_RUNNING from .coordinator import PollingCoordinator PLATFORMS: [Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR] @@ -32,13 +32,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: if not bluetooth.async_address_present(hass, address): raise ConfigEntryNotReady("Bluetti device not present") + # Create data structure + hass.data.setdefault(DOMAIN, {}) + hass.data[DOMAIN].setdefault(entry.entry_id, {}) + hass.data[DOMAIN][entry.entry_id].setdefault(DATA_POLLING_RUNNING, False) + # Create coordinator for polling coordinator = PollingCoordinator(hass, address, device_name) await coordinator.async_config_entry_first_refresh() - - # Create data structure - hass.data.setdefault(DOMAIN, {}) - hass.data[DOMAIN].setdefault(entry.entry_id, coordinator) + hass.data[DOMAIN][entry.entry_id].setdefault(DATA_COORDINATOR, coordinator) platforms: list = PLATFORMS if use_controls is True: diff --git a/custom_components/bluetti_bt/binary_sensor.py b/custom_components/bluetti_bt/binary_sensor.py index f2e53d0..67cfe38 100644 --- a/custom_components/bluetti_bt/binary_sensor.py +++ b/custom_components/bluetti_bt/binary_sensor.py @@ -26,7 +26,7 @@ from bluetti_mqtt.mqtt_client import ( ) from . import device_info as dev_info, get_unique_id -from .const import DOMAIN, ADDITIONAL_DEVICE_FIELDS +from .const import DATA_COORDINATOR, DOMAIN, ADDITIONAL_DEVICE_FIELDS from .coordinator import PollingCoordinator, DummyDevice _LOGGER = logging.getLogger(__name__) @@ -63,7 +63,7 @@ async def async_setup_entry( sensors_to_add.append( BluettiBinarySensor( - hass.data[DOMAIN][entry.entry_id], + hass.data[DOMAIN][entry.entry_id][DATA_COORDINATOR], device_info, address, field_key, diff --git a/custom_components/bluetti_bt/const.py b/custom_components/bluetti_bt/const.py index 2d8d3ad..2fdacb7 100644 --- a/custom_components/bluetti_bt/const.py +++ b/custom_components/bluetti_bt/const.py @@ -8,6 +8,9 @@ MANUFACTURER = "Bluetti" CONF_OPTIONS = "options" CONF_USE_CONTROLS = "use_controls" +DATA_COORDINATOR = "coordinator" +DATA_POLLING_RUNNING = "polling_running" + CONTROL_FIELDS = [ "ac_output_on", "dc_output_on", diff --git a/custom_components/bluetti_bt/coordinator.py b/custom_components/bluetti_bt/coordinator.py index 85102bb..0a62d41 100644 --- a/custom_components/bluetti_bt/coordinator.py +++ b/custom_components/bluetti_bt/coordinator.py @@ -26,6 +26,8 @@ from bluetti_mqtt.bluetooth import ( from bluetti_mqtt.core.devices.bluetti_device import BluettiDevice from bluetti_mqtt.core.commands import ReadHoldingRegisters +from .const import DATA_POLLING_RUNNING, DOMAIN + _LOGGER = logging.getLogger(__name__) @@ -150,6 +152,8 @@ class PollingCoordinator(DataUpdateCoordinator): This is the place to pre-process the data to lookup tables so entities can quickly look up their data. """ + self.hass.data[DOMAIN][self.config_entry.entry_id][DATA_POLLING_RUNNING] = True + self.logger.debug("Polling data") device = bluetooth.async_ble_device_from_address(self.hass, self._address) @@ -203,7 +207,7 @@ class PollingCoordinator(DataUpdateCoordinator): parsed_data.update(parsed) except TimeoutError: - self.logger.error( + self.logger.warning( "Polling timed out (address: %s)", self._address ) except ParseError: @@ -216,17 +220,19 @@ class PollingCoordinator(DataUpdateCoordinator): ) except (BadConnectionError, BleakError) as err: self.logger.warning( - "Needed to disconnect due to error: %s", err + "Needed to disconnect due to error: %s (This can also be the case if you used device controls)", err ) except TimeoutError: - self.logger.error("Polling timed out for device %s", self._address) + self.logger.warning("Polling timed out for device %s", self._address) return None except BleakError as err: - self.logger.error("Bleak error: %s", err) + self.logger.warning("Bleak error: %s", err) return None finally: await client.disconnect() + self.hass.data[DOMAIN][self.config_entry.entry_id][DATA_POLLING_RUNNING] = False + # Pass data back to sensors return parsed_data diff --git a/custom_components/bluetti_bt/sensor.py b/custom_components/bluetti_bt/sensor.py index fa32848..902145f 100644 --- a/custom_components/bluetti_bt/sensor.py +++ b/custom_components/bluetti_bt/sensor.py @@ -29,7 +29,7 @@ from bluetti_mqtt.mqtt_client import ( ) from . import device_info as dev_info, get_unique_id -from .const import DOMAIN, CONF_OPTIONS, DIAGNOSTIC_FIELDS, ADDITIONAL_DEVICE_FIELDS +from .const import DATA_COORDINATOR, DOMAIN, CONF_OPTIONS, DIAGNOSTIC_FIELDS, ADDITIONAL_DEVICE_FIELDS from .coordinator import PollingCoordinator, DummyDevice _LOGGER = logging.getLogger(__name__) @@ -65,7 +65,7 @@ async def async_setup_entry( if field_config.type == MqttFieldType.NUMERIC: sensors_to_add.append( BluettiSensor( - hass.data[DOMAIN][entry.entry_id], + hass.data[DOMAIN][entry.entry_id][DATA_COORDINATOR], device_info, address, field_key, @@ -79,7 +79,7 @@ async def async_setup_entry( elif field_config.type == MqttFieldType.ENUM: sensors_to_add.append( BluettiSensor( - hass.data[DOMAIN][entry.entry_id], + hass.data[DOMAIN][entry.entry_id][DATA_COORDINATOR], device_info, address, field_key, diff --git a/custom_components/bluetti_bt/switch.py b/custom_components/bluetti_bt/switch.py index e215650..0d6a83e 100644 --- a/custom_components/bluetti_bt/switch.py +++ b/custom_components/bluetti_bt/switch.py @@ -2,8 +2,13 @@ from __future__ import annotations +import asyncio import logging +import async_timeout +from bleak import BleakClient, BleakError + +from homeassistant.components import bluetooth from homeassistant.components.switch import SwitchEntity, SwitchDeviceClass from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback @@ -19,6 +24,8 @@ from homeassistant.helpers.update_coordinator import ( ) from bluetti_mqtt.bluetooth import build_device +from bluetti_mqtt.bluetooth.client import BluetoothClient +from bluetti_mqtt.core.devices.bluetti_device import BluettiDevice from bluetti_mqtt.mqtt_client import ( NORMAL_DEVICE_FIELDS, DC_INPUT_FIELDS, @@ -26,7 +33,7 @@ from bluetti_mqtt.mqtt_client import ( ) from . import device_info as dev_info, get_unique_id -from .const import CONTROL_FIELDS, DOMAIN, ADDITIONAL_DEVICE_FIELDS +from .const import CONTROL_FIELDS, DATA_COORDINATOR, DATA_POLLING_RUNNING, DOMAIN, ADDITIONAL_DEVICE_FIELDS from .coordinator import PollingCoordinator, DummyDevice _LOGGER = logging.getLogger(__name__) @@ -60,11 +67,13 @@ async def async_setup_entry( if field_config.setter is True and field_key in CONTROL_FIELDS: sensors_to_add.append( BluettiSwitch( - hass.data[DOMAIN][entry.entry_id], + bluetti_device, + hass.data[DOMAIN][entry.entry_id][DATA_COORDINATOR], device_info, address, field_key, field_config.home_assistant_extra.get(CONF_NAME, ""), + entry.entry_id ) ) @@ -76,19 +85,23 @@ class BluettiSwitch(CoordinatorEntity, SwitchEntity): def __init__( self, + bluetti_device: BluettiDevice, coordinator: PollingCoordinator, device_info: DeviceInfo, address, response_key: str, name: str, + entry_id: str, category: EntityCategory | None = None, ): """Init entity.""" super().__init__(coordinator) + self._bluetti_device = bluetti_device e_name = f"{device_info.get('name')} {name}" self._address = address self._response_key = response_key + self._entry_id = entry_id self._attr_device_info = device_info self._attr_name = e_name @@ -128,8 +141,48 @@ class BluettiSwitch(CoordinatorEntity, SwitchEntity): async def async_turn_on(self, **kwargs): """Turn the entity on.""" - _LOGGER.warning("Turn on") + _LOGGER.debug("Turn on %s on %s", self._response_key, self._address) + await self.write_to_device(True) async def async_turn_off(self, **kwargs): """Turn the entity off.""" - _LOGGER.warning("Turn off") + _LOGGER.debug("Turn off %s on %s", self._response_key, self._address) + await self.write_to_device(False) + + async def write_to_device(self, state: bool): + """Write to device.""" + device = bluetooth.async_ble_device_from_address(self.hass, self._address) + if device is None: + _LOGGER.error("Device %s not available", self._address) + return + + command = self._bluetti_device.build_setter_command(self._response_key, state) + + # Wait until polling is done + while self.hass.data[DOMAIN][self._entry_id][DATA_POLLING_RUNNING] is True: + _LOGGER.debug("Waiting for polling to finish...") + asyncio.sleep(1) + + client = BleakClient(device) + try: + async with async_timeout.timeout(15): + await client.connect() + + # Send command + await client.write_gatt_char( + BluetoothClient.WRITE_UUID, bytes(command) + ) + + # Wait until device has changed value, otherwise reading register might reset it + await asyncio.sleep(5) + + except TimeoutError: + _LOGGER.error("Timed out for device %s", self._address) + return None + except BleakError as err: + _LOGGER.error("Bleak error: %s", err) + return None + finally: + await client.disconnect() + + await self.coordinator.async_request_refresh() From 61bc379d3a026496e64c42925fbb842fe7f3257c Mon Sep 17 00:00:00 2001 From: Patrick762 Date: Sat, 19 Aug 2023 21:20:32 +0200 Subject: [PATCH 5/6] updated readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index f01d517..63fcd9e 100644 --- a/README.md +++ b/README.md @@ -24,3 +24,6 @@ Based on [bluetti_mqtt](https://github.com/warhammerkid/bluetti_mqtt). ### Available sensors: All sensors which are available in bluetti_mqtt (Based on [this file](https://github.com/warhammerkid/bluetti_mqtt/blob/main/bluetti_mqtt/mqtt_client.py)) excluding battery pack fields. + +### Available controls: +AC and DC outputs (for now) From 95f2331cc3e312ebf43539583dc745581e18d3f1 Mon Sep 17 00:00:00 2001 From: Patrick762 Date: Sat, 19 Aug 2023 22:38:30 +0200 Subject: [PATCH 6/6] fixed enum sensors --- README.md | 3 ++- custom_components/bluetti_bt/sensor.py | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 63fcd9e..87b1f25 100644 --- a/README.md +++ b/README.md @@ -26,4 +26,5 @@ All sensors which are available in bluetti_mqtt (Based on [this file](https://gi excluding battery pack fields. ### Available controls: -AC and DC outputs (for now) +If enabled in the Integration options (you need to reload the integration if you change this option): +AC and DC outputs diff --git a/custom_components/bluetti_bt/sensor.py b/custom_components/bluetti_bt/sensor.py index 902145f..66ca581 100644 --- a/custom_components/bluetti_bt/sensor.py +++ b/custom_components/bluetti_bt/sensor.py @@ -1,6 +1,7 @@ """Bluetti BT sensors.""" from __future__ import annotations +from enum import Enum import logging from decimal import Decimal @@ -146,6 +147,7 @@ class BluettiSensor(CoordinatorEntity, SensorEntity): and not isinstance(response_data, float) and not isinstance(response_data, complex) and not isinstance(response_data, Decimal) + and not isinstance(response_data, Enum) ): _LOGGER.warning( "Invalid response data type from coordinator (sensor.%s): %s has type %s", @@ -161,11 +163,11 @@ class BluettiSensor(CoordinatorEntity, SensorEntity): # Different for enum and numeric if ( self._options is not None - and isinstance(response_data, int) - and response_data < len(self._options) + and isinstance(response_data, Enum) + and response_data.value < len(self._options) ): # Enum - self._attr_native_value = self._options[response_data] + self._attr_native_value = self._options[response_data.value] else: # Numeric self._attr_native_value = response_data