Compare commits

...
19 Commits
Author SHA1 Message Date
Patrick762 7da392d85a Merge pull request #54 from Patrick762/dev
[WIP] Release
2024-05-31 17:05:16 +02:00
Patrick762 03ea349cbf set new version to 0.1 2024-05-31 17:04:41 +02:00
Patrick762 5a762266c0 Merge pull request #104 from Patrick762/values-fix-2
Values and pack polling fix
2024-05-31 17:02:07 +02:00
Patrick762 12a59346e9 fix device type info 2024-05-31 16:59:46 +02:00
Patrick762 206099ab64 increase version number 2024-05-31 14:23:43 +02:00
Patrick762 6be2abaaf2 fix pack polling 2024-05-31 14:23:07 +02:00
Patrick762 c3bfad0ea6 add pack sensors if available 2024-05-31 13:46:09 +02:00
Patrick762 b548528085 updated pack polling 2024-05-31 12:55:46 +02:00
Patrick762 6e433e3551 fix total_battery_voltage 2024-05-31 12:14:51 +02:00
Patrick762 b34d0a3039 fix ac300 and ac500 solar string values and renamed entity 2024-05-31 12:06:21 +02:00
Patrick762 2922c5cb90 add multiplier to decimal field 2024-05-31 12:05:09 +02:00
Patrick762 9af3d67ec6 removed binary sensors for disabled control entities to prevent duplicates 2024-05-31 11:34:25 +02:00
Patrick762 221da9a38d fix internal_power sensors for ac300 and ac500 2024-05-31 11:24:20 +02:00
Patrick762 7f576df84b Merge pull request #58 from Patrick762/Patrick762-patch-1
Create LICENSE
2024-02-18 20:15:44 +01:00
Patrick762 8bc8ba4a4b Create LICENSE 2024-02-18 20:15:19 +01:00
Patrick762 213ee82e63 Merge pull request #56 from Patrick762/Patrick762-patch-1
Update versions in bug_report template
2024-02-18 17:22:21 +01:00
Patrick762 c08099ca4f Update versions in bug_report template 2024-02-18 17:21:36 +01:00
Patrick762 4008dc844e Merge pull request #53 from Patrick762/Patrick762-patch-1
Create FUNDING.yml
2024-02-18 12:43:17 +01:00
Patrick762 e27c28b5c8 Create FUNDING.yml 2024-02-18 12:41:25 +01:00
18 changed files with 100 additions and 67 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
github: Patrick762
+5
View File
@@ -20,6 +20,11 @@ body:
label: Version label: Version
description: What version of our software are you running? description: What version of our software are you running?
options: options:
- 0.0.17
- 0.0.16
- 0.0.15
- 0.0.14
- 0.0.13
- 0.0.11 - 0.0.11
- 0.0.10 - 0.0.10
- 0.0.9 - 0.0.9
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Patrick762
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -36,7 +36,7 @@ async def async_setup_entry(
device_name = entry.data.get(CONF_NAME) device_name = entry.data.get(CONF_NAME)
address = entry.data.get(CONF_ADDRESS) address = entry.data.get(CONF_ADDRESS)
use_controls = entry.data.get(CONF_USE_CONTROLS) use_controls = entry.data.get(CONF_USE_CONTROLS, False)
if address is None: if address is None:
_LOGGER.error("Device has no address") _LOGGER.error("Device has no address")
@@ -52,12 +52,7 @@ async def async_setup_entry(
for field_key, field_config in all_fields.items(): for field_key, field_config in all_fields.items():
if bluetti_device.has_field(field_key): if bluetti_device.has_field(field_key):
if field_config.type == FieldType.BOOL: if field_config.type == FieldType.BOOL:
category = None
if field_config.setter is True: if field_config.setter is True:
category = EntityCategory.DIAGNOSTIC
# Ignore setters if controls enabled
if field_config.setter is True and use_controls is True:
continue continue
sensors_to_add.append( sensors_to_add.append(
@@ -67,7 +62,6 @@ async def async_setup_entry(
address, address,
field_key, field_key,
field_config.name, field_config.name,
category=category,
) )
) )
@@ -84,7 +78,6 @@ class BluettiBinarySensor(CoordinatorEntity, BinarySensorEntity):
address, address,
response_key: str, response_key: str,
name: str, name: str,
category: EntityCategory | None = None,
): ):
"""Init battery entity.""" """Init battery entity."""
super().__init__(coordinator) super().__init__(coordinator)
@@ -98,7 +91,6 @@ class BluettiBinarySensor(CoordinatorEntity, BinarySensorEntity):
self._attr_name = name self._attr_name = name
self._attr_available = False self._attr_available = False
self._attr_unique_id = get_unique_id(e_name) self._attr_unique_id = get_unique_id(e_name)
self._attr_entity_category = category
@property @property
def available(self) -> bool: def available(self) -> bool:
@@ -42,6 +42,11 @@ class BluettiDevice:
def writable_ranges(self) -> List[range]: def writable_ranges(self) -> List[range]:
"""The address ranges that are writable""" """The address ranges that are writable"""
return [] return []
@property
def pack_num_field(self) -> List[ReadHoldingRegisters]:
"""The address 'range' of the pack num result. Matches pack_num_result"""
return []
def has_field(self, field: str): def has_field(self, field: str):
return any(f.name == field for f in self.struct.fields) return any(f.name == field for f in self.struct.fields)
@@ -56,3 +56,9 @@ class ProtocolV1Device(BluettiDevice):
@property @property
def writable_ranges(self) -> List[range]: def writable_ranges(self) -> List[range]:
return [range(3006, 3009)] return [range(3006, 3009)]
@property
def pack_num_field(self) -> List[ReadHoldingRegisters]:
return [
ReadHoldingRegisters(96, 1),
]
@@ -97,20 +97,20 @@ class DeviceReader:
_LOGGER.warning("Got a parse exception") _LOGGER.warning("Got a parse exception")
# Execute pack polling commands # Execute pack polling commands
if len(pack_commands) > 0: if len(pack_commands) > 0 and len(self.bluetti_device.pack_num_field) == 1:
_LOGGER.debug("Polling battery packs") _LOGGER.debug("Polling battery packs")
for pack in range(1, self.bluetti_device.pack_num_max + 1): for pack in range(1, self.bluetti_device.pack_num_max + 1):
_LOGGER.debug("Setting pack_num to %i", pack)
# Set current pack number # Set current pack number
await self._async_send_command( command = self.bluetti_device.build_setter_command(
self.bluetti_device.build_setter_command( "pack_num", pack
"pack_num", pack
)
) )
body = command.parse_response(
# We need to wait after switching packs for the data to be available await self._async_send_command(command)
await asyncio.sleep(5) )
_LOGGER.debug("Raw data set: %s", body)
pack_ok = False # b'\x00\x02' b'\x00\x03' here (seems like result)
for command in pack_commands: for command in pack_commands:
# Request & parse result for each pack # Request & parse result for each pack
@@ -123,20 +123,6 @@ class DeviceReader:
) )
_LOGGER.debug("Parsed data: %s", parsed) _LOGGER.debug("Parsed data: %s", parsed)
if not pack_ok: # Only check this once
pack_number = parsed.get("pack_num")
if (
not isinstance(pack_number, int)
or pack_number != pack
):
_LOGGER.debug(
"Parsed pack_num(%s) does not match expected '%s'",
pack_number,
pack,
)
continue
pack_ok = True
for key, value in parsed.items(): for key, value in parsed.items():
parsed_data.update({key + str(pack): value}) parsed_data.update({key + str(pack): value})
@@ -187,9 +173,9 @@ class DeviceReader:
return cast(bytes, res) return cast(bytes, res)
except TimeoutError: except TimeoutError:
_LOGGER.warning("Polling single command timed out") _LOGGER.debug("Polling single command timed out")
except ModbusError as err: except ModbusError as err:
_LOGGER.warning( _LOGGER.debug(
"Got an invalid request error for %s: %s", "Got an invalid request error for %s: %s",
command, command,
err, err,
@@ -24,7 +24,7 @@ class AC200L(ProtocolV1Device):
# Battery packs # Battery packs
self.struct.add_uint_field("pack_num_max", 91) # internal self.struct.add_uint_field("pack_num_max", 91) # internal
self.struct.add_decimal_field("total_battery_voltage", 92, 1) self.struct.add_decimal_field("total_battery_voltage", 92, 1)
self.struct.add_uint_field("pack_num", 96) # internal self.struct.add_uint_field("pack_num_result", 96) # internal
self.struct.add_decimal_field("pack_voltage", 98, 2) # Full pack voltage self.struct.add_decimal_field("pack_voltage", 98, 2) # Full pack voltage
self.struct.add_uint_field("pack_battery_percent", 99) self.struct.add_uint_field("pack_battery_percent", 99)
self.struct.add_decimal_array_field("cell_voltages", 105, 16, 2) # internal self.struct.add_decimal_array_field("cell_voltages", 105, 16, 2) # internal
@@ -42,6 +42,7 @@ class AC200L(ProtocolV1Device):
return super().polling_commands + [ return super().polling_commands + [
ReadHoldingRegisters(70, 5), ReadHoldingRegisters(70, 5),
ReadHoldingRegisters(86, 3), ReadHoldingRegisters(86, 3),
ReadHoldingRegisters(92, 1),
ReadHoldingRegisters(3060, 2), ReadHoldingRegisters(3060, 2),
] ]
@@ -24,7 +24,7 @@ class AC200M(ProtocolV1Device):
# Battery packs # Battery packs
self.struct.add_uint_field("pack_num_max", 91) # internal self.struct.add_uint_field("pack_num_max", 91) # internal
self.struct.add_decimal_field("total_battery_voltage", 92, 1) self.struct.add_decimal_field("total_battery_voltage", 92, 1)
self.struct.add_uint_field("pack_num", 96) # internal self.struct.add_uint_field("pack_num_result", 96) # internal
self.struct.add_decimal_field("pack_voltage", 98, 2) # Full pack voltage self.struct.add_decimal_field("pack_voltage", 98, 2) # Full pack voltage
self.struct.add_uint_field("pack_battery_percent", 99) self.struct.add_uint_field("pack_battery_percent", 99)
self.struct.add_decimal_array_field("cell_voltages", 105, 16, 2) # internal self.struct.add_decimal_array_field("cell_voltages", 105, 16, 2) # internal
@@ -42,6 +42,7 @@ class AC200M(ProtocolV1Device):
return super().polling_commands + [ return super().polling_commands + [
ReadHoldingRegisters(70, 5), ReadHoldingRegisters(70, 5),
ReadHoldingRegisters(86, 3), ReadHoldingRegisters(86, 3),
ReadHoldingRegisters(92, 1),
ReadHoldingRegisters(3060, 2), ReadHoldingRegisters(3060, 2),
] ]
@@ -15,22 +15,22 @@ class AC300(ProtocolV1Device):
self.struct.add_enum_field("ac_output_mode", 70, OutputMode) self.struct.add_enum_field("ac_output_mode", 70, OutputMode)
self.struct.add_decimal_field("internal_ac_voltage", 71, 1) self.struct.add_decimal_field("internal_ac_voltage", 71, 1)
self.struct.add_decimal_field("internal_current_one", 72, 1) self.struct.add_decimal_field("internal_current_one", 72, 1)
self.struct.add_uint_field("internal_power_one", 73) self.struct.add_int_field("internal_power_one", 73)
self.struct.add_decimal_field("internal_ac_frequency", 74, 2) self.struct.add_decimal_field("internal_ac_frequency", 74, 2)
self.struct.add_decimal_field("internal_current_two", 75, 1) self.struct.add_decimal_field("internal_current_two", 75, 1)
self.struct.add_uint_field("internal_power_two", 76) self.struct.add_int_field("internal_power_two", 76)
self.struct.add_decimal_field("ac_input_voltage", 77, 1) self.struct.add_decimal_field("ac_input_voltage", 77, 1)
self.struct.add_decimal_field("internal_current_three", 78, 1, (0, 100)) self.struct.add_decimal_field("internal_current_three", 78, 1, (0, 100))
self.struct.add_uint_field("internal_power_three", 79) self.struct.add_int_field("internal_power_three", 79)
self.struct.add_decimal_field("ac_input_frequency", 80, 2) self.struct.add_decimal_field("ac_input_frequency", 80, 2)
self.struct.add_uint_field("internal_dc_input_voltage", 86, scale=.1) self.struct.add_uint_field("pv_input_voltage1", 86, multiplier=.1)
self.struct.add_decimal_field("internal_dc_input_power", 87, 1) self.struct.add_decimal_field("pv_input_power1", 87, 1, multiplier=10)
self.struct.add_decimal_field("internal_dc_input_current", 88, 2) self.struct.add_decimal_field("pv_input_current1", 88, 2, multiplier=10)
# Battery packs # Battery packs
self.struct.add_uint_field("pack_num_max", 91) # internal self.struct.add_uint_field("pack_num_max", 91) # internal
self.struct.add_decimal_field("total_battery_voltage", 92, 1) self.struct.add_decimal_field("total_battery_voltage", 92, 1)
self.struct.add_uint_field("pack_num", 96) # internal self.struct.add_uint_field("pack_num_result", 96) # internal
self.struct.add_decimal_field("pack_voltage", 98, 2) # Full pack voltage self.struct.add_decimal_field("pack_voltage", 98, 2) # Full pack voltage
self.struct.add_uint_field("pack_battery_percent", 99) self.struct.add_uint_field("pack_battery_percent", 99)
self.struct.add_decimal_array_field("cell_voltages", 105, 16, 2) # internal self.struct.add_decimal_array_field("cell_voltages", 105, 16, 2) # internal
@@ -57,6 +57,7 @@ class AC300(ProtocolV1Device):
return super().polling_commands + [ return super().polling_commands + [
ReadHoldingRegisters(70, 11), ReadHoldingRegisters(70, 11),
ReadHoldingRegisters(86, 3), ReadHoldingRegisters(86, 3),
ReadHoldingRegisters(92, 1),
ReadHoldingRegisters(3001, 1), ReadHoldingRegisters(3001, 1),
ReadHoldingRegisters(3004, 2), ReadHoldingRegisters(3004, 2),
ReadHoldingRegisters(3011, 1), ReadHoldingRegisters(3011, 1),
@@ -15,22 +15,22 @@ class AC500(ProtocolV1Device):
self.struct.add_enum_field("ac_output_mode", 70, OutputMode) self.struct.add_enum_field("ac_output_mode", 70, OutputMode)
self.struct.add_decimal_field("internal_ac_voltage", 71, 1) self.struct.add_decimal_field("internal_ac_voltage", 71, 1)
self.struct.add_decimal_field("internal_current_one", 72, 1) self.struct.add_decimal_field("internal_current_one", 72, 1)
self.struct.add_uint_field("internal_power_one", 73) self.struct.add_int_field("internal_power_one", 73)
self.struct.add_decimal_field("internal_ac_frequency", 74, 2) self.struct.add_decimal_field("internal_ac_frequency", 74, 2)
self.struct.add_decimal_field("internal_current_two", 75, 1) self.struct.add_decimal_field("internal_current_two", 75, 1)
self.struct.add_uint_field("internal_power_two", 76) self.struct.add_int_field("internal_power_two", 76)
self.struct.add_decimal_field("ac_input_voltage", 77, 1) self.struct.add_decimal_field("ac_input_voltage", 77, 1)
self.struct.add_decimal_field("internal_current_three", 78, 1, (0, 100)) self.struct.add_decimal_field("internal_current_three", 78, 1, (0, 100))
self.struct.add_uint_field("internal_power_three", 79) self.struct.add_int_field("internal_power_three", 79)
self.struct.add_decimal_field("ac_input_frequency", 80, 2) self.struct.add_decimal_field("ac_input_frequency", 80, 2)
self.struct.add_uint_field("internal_dc_input_voltage", 86, scale=.1) self.struct.add_uint_field("pv_input_voltage1", 86, multiplier=.1)
self.struct.add_decimal_field("internal_dc_input_power", 87, 1) self.struct.add_decimal_field("pv_input_power1", 87, 1, multiplier=10)
self.struct.add_decimal_field("internal_dc_input_current", 88, 2) self.struct.add_decimal_field("pv_input_current1", 88, 2, multiplier=10)
# Battery packs # Battery packs
self.struct.add_uint_field("pack_num_max", 91) # internal self.struct.add_uint_field("pack_num_max", 91) # internal
self.struct.add_decimal_field("total_battery_voltage", 92, 1) self.struct.add_decimal_field("total_battery_voltage", 92, 1)
self.struct.add_uint_field("pack_num", 96) # internal self.struct.add_uint_field("pack_num_result", 96) # internal
self.struct.add_decimal_field("pack_voltage", 98, 2) # Full pack voltage self.struct.add_decimal_field("pack_voltage", 98, 2) # Full pack voltage
self.struct.add_uint_field("pack_battery_percent", 99) self.struct.add_uint_field("pack_battery_percent", 99)
self.struct.add_decimal_array_field("cell_voltages", 105, 16, 2) # internal self.struct.add_decimal_array_field("cell_voltages", 105, 16, 2) # internal
@@ -56,6 +56,7 @@ class AC500(ProtocolV1Device):
return super().polling_commands + [ return super().polling_commands + [
ReadHoldingRegisters(70, 11), ReadHoldingRegisters(70, 11),
ReadHoldingRegisters(86, 3), ReadHoldingRegisters(86, 3),
ReadHoldingRegisters(92, 1),
ReadHoldingRegisters(3001, 1), ReadHoldingRegisters(3001, 1),
ReadHoldingRegisters(3004, 2), ReadHoldingRegisters(3004, 2),
ReadHoldingRegisters(3011, 1), ReadHoldingRegisters(3011, 1),
@@ -30,7 +30,7 @@ class EP500(ProtocolV1Device):
# Battery packs # Battery packs
self.struct.add_uint_field("pack_num_max", 91) # internal self.struct.add_uint_field("pack_num_max", 91) # internal
self.struct.add_decimal_field("total_battery_voltage", 92, 1) self.struct.add_decimal_field("total_battery_voltage", 92, 1)
self.struct.add_uint_field("pack_num", 96) # internal self.struct.add_uint_field("pack_num_result", 96) # internal
self.struct.add_decimal_field("pack_voltage", 98, 2) # Full pack voltage self.struct.add_decimal_field("pack_voltage", 98, 2) # Full pack voltage
self.struct.add_uint_field("pack_battery_percent", 99) self.struct.add_uint_field("pack_battery_percent", 99)
self.struct.add_decimal_array_field("cell_voltages", 105, 16, 2) # internal self.struct.add_decimal_array_field("cell_voltages", 105, 16, 2) # internal
@@ -52,6 +52,7 @@ class EP500(ProtocolV1Device):
return super().polling_commands + [ return super().polling_commands + [
ReadHoldingRegisters(70, 11), ReadHoldingRegisters(70, 11),
ReadHoldingRegisters(86, 3), ReadHoldingRegisters(86, 3),
ReadHoldingRegisters(92, 1),
ReadHoldingRegisters(3001, 1), ReadHoldingRegisters(3001, 1),
ReadHoldingRegisters(3004, 2), ReadHoldingRegisters(3004, 2),
ReadHoldingRegisters(3011, 1), ReadHoldingRegisters(3011, 1),
@@ -30,7 +30,7 @@ class EP500P(ProtocolV1Device):
# Battery packs # Battery packs
self.struct.add_uint_field("pack_num_max", 91) # internal self.struct.add_uint_field("pack_num_max", 91) # internal
self.struct.add_decimal_field("total_battery_voltage", 92, 1) self.struct.add_decimal_field("total_battery_voltage", 92, 1)
self.struct.add_uint_field("pack_num", 96) # internal self.struct.add_uint_field("pack_num_result", 96) # internal
self.struct.add_decimal_field("pack_voltage", 98, 2) # Full pack voltage self.struct.add_decimal_field("pack_voltage", 98, 2) # Full pack voltage
self.struct.add_uint_field("pack_battery_percent", 99) self.struct.add_uint_field("pack_battery_percent", 99)
self.struct.add_decimal_array_field("cell_voltages", 105, 16, 2) # internal self.struct.add_decimal_array_field("cell_voltages", 105, 16, 2) # internal
@@ -52,6 +52,7 @@ class EP500P(ProtocolV1Device):
return super().polling_commands + [ return super().polling_commands + [
ReadHoldingRegisters(70, 11), ReadHoldingRegisters(70, 11),
ReadHoldingRegisters(86, 3), ReadHoldingRegisters(86, 3),
ReadHoldingRegisters(92, 1),
ReadHoldingRegisters(3001, 1), ReadHoldingRegisters(3001, 1),
ReadHoldingRegisters(3004, 2), ReadHoldingRegisters(3004, 2),
ReadHoldingRegisters(3011, 1), ReadHoldingRegisters(3011, 1),
@@ -53,6 +53,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 match is None or len(match) < 2: if match is None:
return "Unknown" return "Unknown"
return match[1] return match[1]
@@ -30,15 +30,15 @@ class DeviceField:
class UintField(DeviceField): class UintField(DeviceField):
def __init__(self, name: str, address: int, range: Optional[Tuple[int, int]], scale: int): def __init__(self, name: str, address: int, range: Optional[Tuple[int, int]], multiplier: float):
self.range = range self.range = range
self.scale = scale self.multiplier = multiplier
super().__init__(name, address, 1) super().__init__(name, address, 1)
def parse(self, data: bytes) -> int: def parse(self, data: bytes) -> int:
val = struct.unpack("!H", data)[0] val = struct.unpack("!H", data)[0]
return val * self.scale return val * self.multiplier
def in_range(self, val: int) -> bool: def in_range(self, val: int) -> bool:
if self.range is None: if self.range is None:
@@ -82,15 +82,16 @@ class EnumField(DeviceField):
class DecimalField(DeviceField): class DecimalField(DeviceField):
def __init__( def __init__(
self, name: str, address: int, scale: int, range: Optional[Tuple[int, int]] self, name: str, address: int, scale: int, range: Optional[Tuple[int, int]], multiplier: float
): ):
self.scale = scale self.scale = scale
self.range = range self.range = range
self.multiplier = multiplier
super().__init__(name, address, 1) super().__init__(name, address, 1)
def parse(self, data: bytes) -> Decimal: def parse(self, data: bytes) -> Decimal:
val = Decimal(struct.unpack("!H", data)[0]) val = Decimal(struct.unpack("!H", data)[0])
return val / 10 ** self.scale return (val / 10 ** self.scale) * self.multiplier
def in_range(self, val: Decimal) -> bool: def in_range(self, val: Decimal) -> bool:
if self.range is None: if self.range is None:
@@ -147,8 +148,8 @@ class DeviceStruct:
def __init__(self): def __init__(self):
self.fields = [] self.fields = []
def add_uint_field(self, name: str, address: int, range: Tuple[int, int] = None, scale: int = 1): def add_uint_field(self, name: str, address: int, range: Tuple[int, int] = None, multiplier: float = 1):
self.fields.append(UintField(name, address, range, scale)) self.fields.append(UintField(name, address, range, multiplier))
def add_int_field(self, name: str, address: int, range: Tuple[int, int] = None): def add_int_field(self, name: str, address: int, range: Tuple[int, int] = None):
self.fields.append(IntField(name, address, range)) self.fields.append(IntField(name, address, range))
@@ -160,9 +161,9 @@ class DeviceStruct:
self.fields.append(EnumField(name, address, enum)) self.fields.append(EnumField(name, address, enum))
def add_decimal_field( def add_decimal_field(
self, name: str, address: int, scale: int, range: Tuple[int, int] = None self, name: str, address: int, scale: int, range: Tuple[int, int] = None, multiplier: float = 1
): ):
self.fields.append(DecimalField(name, address, scale, range)) self.fields.append(DecimalField(name, address, scale, range, multiplier))
def add_decimal_array_field(self, name: str, address: int, size: int, scale: int): def add_decimal_array_field(self, name: str, address: int, size: int, scale: int):
self.fields.append(DecimalArrayField(name, address, size, scale)) self.fields.append(DecimalArrayField(name, address, size, scale))
+11 -1
View File
@@ -14,7 +14,7 @@ from homeassistant.components.bluetooth import (
BluetoothServiceInfoBleak, BluetoothServiceInfoBleak,
async_discovered_service_info, async_discovered_service_info,
) )
from homeassistant.const import CONF_ADDRESS, CONF_NAME from homeassistant.const import CONF_ADDRESS, CONF_NAME, CONF_TYPE
from homeassistant.data_entry_flow import FlowResult from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers import selector from homeassistant.helpers import selector
@@ -48,11 +48,20 @@ class BluettiConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
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()
if isinstance(discovery_info.name, str):
name = re.sub("[^A-Z0-9]+", "", discovery_info.name)
discovery_info.manufacturer_data = {
CONF_TYPE: get_type_by_bt_name(name)
}
# Get device type if needed # Get device type if needed
if isinstance(discovery_info.name, str) and discovery_info.name.startswith("PBOX"): if isinstance(discovery_info.name, str) and discovery_info.name.startswith("PBOX"):
bleak_device = BleakClient(discovery_info.device) bleak_device = BleakClient(discovery_info.device)
device_type = await recognize_device(bleak_device, self.hass.loop.create_future) device_type = await recognize_device(bleak_device, self.hass.loop.create_future)
_LOGGER.info("Device identified as %s", device_type) _LOGGER.info("Device identified as %s", device_type)
discovery_info.manufacturer_data = {
CONF_TYPE: device_type.strip(),
}
discovery_info.name = discovery_info.name.replace("PBOX", device_type.strip()) discovery_info.name = discovery_info.name.replace("PBOX", device_type.strip())
self._discovery_info = discovery_info self._discovery_info = discovery_info
@@ -76,6 +85,7 @@ class BluettiConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
data={ data={
CONF_ADDRESS: discovery_info.address, CONF_ADDRESS: discovery_info.address,
CONF_NAME: name, CONF_NAME: name,
CONF_TYPE: discovery_info.manufacturer_data.get(CONF_TYPE, "Unknown"),
}, },
) )
+1 -1
View File
@@ -23,5 +23,5 @@
"iot_class": "local_polling", "iot_class": "local_polling",
"issue_tracker": "https://github.com/Patrick762/hassio-bluetti-bt/issues", "issue_tracker": "https://github.com/Patrick762/hassio-bluetti-bt/issues",
"requirements": [], "requirements": [],
"version": "0.0.24" "version": "0.1.0"
} }
+2 -2
View File
@@ -57,7 +57,7 @@ async def async_setup_entry(
all_fields.update({name+str(pack): field}) all_fields.update({name+str(pack): field})
for field_key, field_config in all_fields.items(): for field_key, field_config in all_fields.items():
if bluetti_device.has_field(field_key): if bluetti_device.has_field(field_key) or (len(bluetti_device.pack_polling_commands) > 0 and field_key.startswith("pack_")):
category = None category = None
if field_config.setter is True or field_key in DIAGNOSTIC_FIELDS: if field_config.setter is True or field_key in DIAGNOSTIC_FIELDS:
category = EntityCategory.DIAGNOSTIC category = EntityCategory.DIAGNOSTIC
@@ -156,7 +156,7 @@ class BluettiSensor(CoordinatorEntity, SensorEntity):
response_data = self.coordinator.data.get(self._response_key) response_data = self.coordinator.data.get(self._response_key)
if response_data is None: if response_data is None:
_LOGGER.warning("No data for available for (%s)", self._response_key) _LOGGER.debug("No data for available for (%s)", self._response_key)
self._attr_available = False self._attr_available = False
self.async_write_ha_state() self.async_write_ha_state()
return return