added device type detection to config_flow

This commit is contained in:
Patrick762 committed 2024-02-18 16:37:37 +01:00
1 parent 9b7fd82591
commit a50deaae37
4 files changed
+73 -17

No files matched your search

@@ -12,7 +12,7 @@ class ProtocolV2Device(BluettiDevice):
self.struct = DeviceStruct() self.struct = DeviceStruct()
# Device info # Device info
self.struct.add_string_field("device_type", 110, 6) self.struct.add_swap_string_field("device_type", 110, 6)
self.struct.add_sn_field("serial_number", 116) self.struct.add_sn_field("serial_number", 116)
# Power IO # Power IO
@@ -0,0 +1,59 @@
"""Device recognizer."""
import asyncio
import logging
from typing import Any, Callable
from bleak import BleakClient
from custom_components.bluetti_bt.bluetti_bt_lib.utils.commands import (
ReadHoldingRegisters,
)
from ..base_devices.ProtocolV2Device import ProtocolV2Device
from ..bluetooth.device_reader import DeviceReader
_LOGGER = logging.getLogger(__name__)
async def recognize_device(
bleak_client: BleakClient,
future_builder_method: Callable[[], asyncio.Future[Any]],
) -> str:
# Since we don't know the type we use the base device
bluetti_device = ProtocolV2Device("Unknown", "Unknown", "Unknown")
# Create device builder
device_reader = DeviceReader(bleak_client, bluetti_device, future_builder_method)
# Retry a few times to get data
for _ in range(1, 50):
# We only need 6 registers to get the device type
data = await device_reader.read_data(
[
ReadHoldingRegisters(110, 6),
]
)
if data is not None:
break
await asyncio.sleep(0.1)
if data is None:
# Should not happen
return "Unknown"
field_data = data.get("device_type")
if field_data is None:
# We have a problem
_LOGGER.error("No data in device type field_datas. Maybe it's a V1 device?")
return "Unknown"
if not isinstance(field_data, str):
# We have a problem
_LOGGER.error("Invalid data in device type field_datas")
return "Unknown"
return field_data
@@ -41,6 +41,6 @@ def build_device(address: str, name: str):
def get_type_by_bt_name(bt_name: str): def get_type_by_bt_name(bt_name: str):
match = DEVICE_NAME_RE.match(bt_name) match = DEVICE_NAME_RE.match(bt_name)
if len(match) > 0: if match is None or len(match) < 2:
return match[1]
return "Unknown" return "Unknown"
return match[1]
+11 -14
View File
@@ -5,6 +5,7 @@ from __future__ import annotations
import re import re
import logging import logging
from typing import Any from typing import Any
from bleak import BleakClient
import voluptuous as vol import voluptuous as vol
@@ -13,11 +14,12 @@ from homeassistant.components.bluetooth import (
BluetoothServiceInfoBleak, BluetoothServiceInfoBleak,
async_discovered_service_info, async_discovered_service_info,
) )
from homeassistant.const import CONF_ADDRESS, CONF_TYPE, CONF_NAME from homeassistant.const import CONF_ADDRESS, CONF_NAME
from homeassistant.data_entry_flow import FlowResult from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers import selector from homeassistant.helpers import selector
from .bluetti_bt_lib.utils.device_builder import get_type_by_bt_name from .bluetti_bt_lib.utils.device_builder import get_type_by_bt_name
from .bluetti_bt_lib.bluetooth.device_recognizer import recognize_device
from .const import ( from .const import (
CONF_MAX_RETRIES, CONF_MAX_RETRIES,
@@ -26,7 +28,6 @@ from .const import (
CONF_POLLING_TIMEOUT, CONF_POLLING_TIMEOUT,
CONF_USE_CONTROLS, CONF_USE_CONTROLS,
DOMAIN, DOMAIN,
SUPPORTED_MODELS,
) )
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@@ -46,6 +47,14 @@ class BluettiConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle bluetooth discovery.""" """Handle bluetooth discovery."""
await self.async_set_unique_id(discovery_info.address) await self.async_set_unique_id(discovery_info.address)
self._abort_if_unique_id_configured() self._abort_if_unique_id_configured()
# Get device type if needed
if isinstance(discovery_info.name, str) and discovery_info.name.startswith("PBOX"):
bleak_device = BleakClient(discovery_info.device)
device_type = await recognize_device(bleak_device, self.hass.loop.create_future)
_LOGGER.info("Device identified as %s", device_type)
discovery_info.name = discovery_info.name.replace("PBOX", device_type.strip())
self._discovery_info = discovery_info self._discovery_info = discovery_info
self.context["title_placeholders"] = {"name": discovery_info.name} self.context["title_placeholders"] = {"name": discovery_info.name}
return await self.async_step_user() return await self.async_step_user()
@@ -60,18 +69,12 @@ class BluettiConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
discovery_info = self._discovered_devices[address] discovery_info = self._discovered_devices[address]
await self.async_set_unique_id(address, raise_on_progress=False) await self.async_set_unique_id(address, raise_on_progress=False)
self._abort_if_unique_id_configured() self._abort_if_unique_id_configured()
dev_type = get_type_by_bt_name(discovery_info.name)
name = re.sub("[^A-Z0-9]+", "", discovery_info.name) name = re.sub("[^A-Z0-9]+", "", discovery_info.name)
if dev_type == "Unknown":
dev_type = user_input[CONF_TYPE]
name = name.replace("PBOX", dev_type)
return self.async_create_entry( return self.async_create_entry(
title=name, title=name,
data={ data={
CONF_ADDRESS: discovery_info.address, CONF_ADDRESS: discovery_info.address,
CONF_TYPE: dev_type,
CONF_NAME: name, CONF_NAME: name,
}, },
) )
@@ -97,12 +100,6 @@ class BluettiConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
for service_info in self._discovered_devices.values() for service_info in self._discovered_devices.values()
} }
), ),
vol.Optional(CONF_TYPE): vol.In(
{
model: model
for model in SUPPORTED_MODELS
}
),
} }
) )
return self.async_show_form( return self.async_show_form(