improvement: Automatically build polling commands basing on the fields
This commit is contained in:
1 parent
0e5c1a0b44
commit
a1e9c180b3
10 files changed
+69
-113
No files matched your search
@@ -53,6 +53,8 @@ class DeviceReader:
|
||||
if filter_registers is not None:
|
||||
polling_commands = filter_registers
|
||||
pack_commands = []
|
||||
_LOGGER.info("Pooling comands: " + ",".join([f"{c.starting_address}-{c.starting_address + c.quantity - 1}" for c in polling_commands]))
|
||||
_LOGGER.info("Pack comands: " + ",".join([f"{c.starting_address}-{c.starting_address + c.quantity - 1}" for c in pack_commands]))
|
||||
|
||||
parsed_data: dict = {}
|
||||
|
||||
@@ -142,7 +144,7 @@ class DeviceReader:
|
||||
_LOGGER.warning("Got a parse exception...")
|
||||
|
||||
except TimeoutError:
|
||||
_LOGGER.warning("Polling timed out. Trying again later")
|
||||
_LOGGER.warning(f"Polling timed out ({self.polling_timeout}s). Trying again later")
|
||||
return None
|
||||
except BleakError as err:
|
||||
_LOGGER.error("Bleak error: %s", err)
|
||||
|
||||
@@ -28,6 +28,7 @@ class AC200L(ProtocolV1Device):
|
||||
self.struct.add_decimal_field("pack_voltage", 98, 2) # Full pack voltage
|
||||
self.struct.add_uint_field("pack_battery_percent", 99)
|
||||
self.struct.add_decimal_array_field("cell_voltages", 105, 16, 2) # internal
|
||||
self.struct.add_version_field("pack_bms_version", 201)
|
||||
|
||||
# Controls
|
||||
self.struct.add_bool_field("power_off", 3060)
|
||||
@@ -39,12 +40,9 @@ class AC200L(ProtocolV1Device):
|
||||
|
||||
@property
|
||||
def polling_commands(self) -> List[ReadHoldingRegisters]:
|
||||
return super().polling_commands + [
|
||||
ReadHoldingRegisters(70, 5),
|
||||
ReadHoldingRegisters(86, 3),
|
||||
ReadHoldingRegisters(92, 1),
|
||||
ReadHoldingRegisters(3060, 2),
|
||||
]
|
||||
return self.struct.get_read_holding_registers(
|
||||
filter=lambda address: address <= 90 or address >= 256 # pack_polling_commands
|
||||
)
|
||||
|
||||
@property
|
||||
def writable_ranges(self) -> List[range]:
|
||||
@@ -53,8 +51,6 @@ class AC200L(ProtocolV1Device):
|
||||
@property
|
||||
def pack_polling_commands(self) -> List[ReadHoldingRegisters]:
|
||||
return [
|
||||
ReadHoldingRegisters(91, 2),
|
||||
ReadHoldingRegisters(96, 1),
|
||||
ReadHoldingRegisters(98, 2),
|
||||
ReadHoldingRegisters(105, 16)
|
||||
ReadHoldingRegisters(91, 37),
|
||||
ReadHoldingRegisters(201, 2),
|
||||
]
|
||||
@@ -28,6 +28,7 @@ class AC200M(ProtocolV1Device):
|
||||
self.struct.add_decimal_field("pack_voltage", 98, 2) # Full pack voltage
|
||||
self.struct.add_uint_field("pack_battery_percent", 99)
|
||||
self.struct.add_decimal_array_field("cell_voltages", 105, 16, 2) # internal
|
||||
self.struct.add_version_field("pack_bms_version", 201)
|
||||
|
||||
# Controls
|
||||
self.struct.add_bool_field("power_off", 3060)
|
||||
@@ -39,12 +40,9 @@ class AC200M(ProtocolV1Device):
|
||||
|
||||
@property
|
||||
def polling_commands(self) -> List[ReadHoldingRegisters]:
|
||||
return super().polling_commands + [
|
||||
ReadHoldingRegisters(70, 5),
|
||||
ReadHoldingRegisters(86, 3),
|
||||
ReadHoldingRegisters(92, 1),
|
||||
ReadHoldingRegisters(3060, 2),
|
||||
]
|
||||
return self.struct.get_read_holding_registers(
|
||||
filter=lambda address: address <= 90 or address >= 256 # pack_polling_commands
|
||||
)
|
||||
|
||||
@property
|
||||
def writable_ranges(self) -> List[range]:
|
||||
@@ -53,8 +51,6 @@ class AC200M(ProtocolV1Device):
|
||||
@property
|
||||
def pack_polling_commands(self) -> List[ReadHoldingRegisters]:
|
||||
return [
|
||||
ReadHoldingRegisters(91, 2),
|
||||
ReadHoldingRegisters(96, 1),
|
||||
ReadHoldingRegisters(98, 2),
|
||||
ReadHoldingRegisters(105, 16)
|
||||
ReadHoldingRegisters(91, 37),
|
||||
ReadHoldingRegisters(201, 2),
|
||||
]
|
||||
@@ -54,17 +54,9 @@ class AC300(ProtocolV1Device):
|
||||
|
||||
@property
|
||||
def polling_commands(self) -> List[ReadHoldingRegisters]:
|
||||
return super().polling_commands + [
|
||||
ReadHoldingRegisters(70, 11),
|
||||
ReadHoldingRegisters(86, 3),
|
||||
ReadHoldingRegisters(92, 1),
|
||||
ReadHoldingRegisters(3001, 1),
|
||||
ReadHoldingRegisters(3004, 2),
|
||||
ReadHoldingRegisters(3011, 1),
|
||||
ReadHoldingRegisters(3013, 1),
|
||||
ReadHoldingRegisters(3015, 2),
|
||||
ReadHoldingRegisters(3061, 1),
|
||||
]
|
||||
return self.struct.get_read_holding_registers(
|
||||
filter=lambda address: address <= 90 or address >= 256 # pack_polling_commands
|
||||
)
|
||||
|
||||
@property
|
||||
def writable_ranges(self) -> List[range]:
|
||||
@@ -76,9 +68,6 @@ class AC300(ProtocolV1Device):
|
||||
@property
|
||||
def pack_polling_commands(self) -> List[ReadHoldingRegisters]:
|
||||
return [
|
||||
ReadHoldingRegisters(91, 2),
|
||||
ReadHoldingRegisters(96, 1),
|
||||
ReadHoldingRegisters(98, 2),
|
||||
ReadHoldingRegisters(105, 16),
|
||||
ReadHoldingRegisters(91, 37),
|
||||
ReadHoldingRegisters(201, 2),
|
||||
]
|
||||
@@ -34,6 +34,7 @@ class AC500(ProtocolV1Device):
|
||||
self.struct.add_decimal_field("pack_voltage", 98, 2) # Full pack voltage
|
||||
self.struct.add_uint_field("pack_battery_percent", 99)
|
||||
self.struct.add_decimal_array_field("cell_voltages", 105, 16, 2) # internal
|
||||
self.struct.add_version_field('pack_bms_version', 201)
|
||||
|
||||
# Controls
|
||||
self.struct.add_enum_field("ups_mode", 3001, UpsMode)
|
||||
@@ -53,17 +54,9 @@ class AC500(ProtocolV1Device):
|
||||
|
||||
@property
|
||||
def polling_commands(self) -> List[ReadHoldingRegisters]:
|
||||
return super().polling_commands + [
|
||||
ReadHoldingRegisters(70, 11),
|
||||
ReadHoldingRegisters(86, 3),
|
||||
ReadHoldingRegisters(92, 1),
|
||||
ReadHoldingRegisters(3001, 1),
|
||||
ReadHoldingRegisters(3004, 2),
|
||||
ReadHoldingRegisters(3011, 1),
|
||||
ReadHoldingRegisters(3013, 1),
|
||||
ReadHoldingRegisters(3015, 2),
|
||||
ReadHoldingRegisters(3061, 1),
|
||||
]
|
||||
return self.struct.get_read_holding_registers(
|
||||
filter=lambda address: address <= 90 or address >= 256 # pack_polling_commands
|
||||
)
|
||||
|
||||
@property
|
||||
def writable_ranges(self) -> List[range]:
|
||||
@@ -75,8 +68,6 @@ class AC500(ProtocolV1Device):
|
||||
@property
|
||||
def pack_polling_commands(self) -> List[ReadHoldingRegisters]:
|
||||
return [
|
||||
ReadHoldingRegisters(91, 2),
|
||||
ReadHoldingRegisters(96, 1),
|
||||
ReadHoldingRegisters(98, 2),
|
||||
ReadHoldingRegisters(105, 16),
|
||||
ReadHoldingRegisters(91, 37),
|
||||
ReadHoldingRegisters(201, 2),
|
||||
]
|
||||
@@ -25,13 +25,7 @@ class EB3A(ProtocolV1Device):
|
||||
|
||||
@property
|
||||
def polling_commands(self) -> List[ReadHoldingRegisters]:
|
||||
return super().polling_commands + [
|
||||
ReadHoldingRegisters(77, 1),
|
||||
ReadHoldingRegisters(86, 1),
|
||||
ReadHoldingRegisters(3034, 1),
|
||||
ReadHoldingRegisters(3060, 1),
|
||||
ReadHoldingRegisters(3063, 5),
|
||||
]
|
||||
return self.struct.get_read_holding_registers()
|
||||
|
||||
@property
|
||||
def writable_ranges(self) -> List[range]:
|
||||
|
||||
@@ -34,6 +34,7 @@ class EP500(ProtocolV1Device):
|
||||
self.struct.add_decimal_field("pack_voltage", 98, 2) # Full pack voltage
|
||||
self.struct.add_uint_field("pack_battery_percent", 99)
|
||||
self.struct.add_decimal_array_field("cell_voltages", 105, 16, 2) # internal
|
||||
self.struct.add_version_field("pack_bms_version", 201)
|
||||
|
||||
# Controls
|
||||
self.struct.add_enum_field("ups_mode", 3001, UpsMode)
|
||||
@@ -49,17 +50,9 @@ class EP500(ProtocolV1Device):
|
||||
|
||||
@property
|
||||
def polling_commands(self) -> List[ReadHoldingRegisters]:
|
||||
return super().polling_commands + [
|
||||
ReadHoldingRegisters(70, 11),
|
||||
ReadHoldingRegisters(86, 3),
|
||||
ReadHoldingRegisters(92, 1),
|
||||
ReadHoldingRegisters(3001, 1),
|
||||
ReadHoldingRegisters(3004, 2),
|
||||
ReadHoldingRegisters(3011, 1),
|
||||
ReadHoldingRegisters(3013, 1),
|
||||
ReadHoldingRegisters(3015, 2),
|
||||
ReadHoldingRegisters(3061, 1),
|
||||
]
|
||||
return self.struct.get_read_holding_registers(
|
||||
filter=lambda address: address <= 90 or address >= 256 # pack_polling_commands
|
||||
)
|
||||
|
||||
@property
|
||||
def writable_ranges(self) -> List[range]:
|
||||
@@ -71,8 +64,6 @@ class EP500(ProtocolV1Device):
|
||||
@property
|
||||
def pack_polling_commands(self) -> List[ReadHoldingRegisters]:
|
||||
return [
|
||||
ReadHoldingRegisters(91, 2),
|
||||
ReadHoldingRegisters(96, 1),
|
||||
ReadHoldingRegisters(98, 2),
|
||||
ReadHoldingRegisters(105, 16),
|
||||
ReadHoldingRegisters(91, 37),
|
||||
ReadHoldingRegisters(201, 2),
|
||||
]
|
||||
@@ -34,6 +34,7 @@ class EP500P(ProtocolV1Device):
|
||||
self.struct.add_decimal_field("pack_voltage", 98, 2) # Full pack voltage
|
||||
self.struct.add_uint_field("pack_battery_percent", 99)
|
||||
self.struct.add_decimal_array_field("cell_voltages", 105, 16, 2) # internal
|
||||
self.struct.add_version_field("pack_bms_version", 201)
|
||||
|
||||
# Controls
|
||||
self.struct.add_enum_field("ups_mode", 3001, UpsMode)
|
||||
@@ -49,17 +50,9 @@ class EP500P(ProtocolV1Device):
|
||||
|
||||
@property
|
||||
def polling_commands(self) -> List[ReadHoldingRegisters]:
|
||||
return super().polling_commands + [
|
||||
ReadHoldingRegisters(70, 11),
|
||||
ReadHoldingRegisters(86, 3),
|
||||
ReadHoldingRegisters(92, 1),
|
||||
ReadHoldingRegisters(3001, 1),
|
||||
ReadHoldingRegisters(3004, 2),
|
||||
ReadHoldingRegisters(3011, 1),
|
||||
ReadHoldingRegisters(3013, 1),
|
||||
ReadHoldingRegisters(3015, 2),
|
||||
ReadHoldingRegisters(3061, 1),
|
||||
]
|
||||
return self.struct.get_read_holding_registers(
|
||||
filter=lambda address: address <= 90 or address >= 256 # pack_polling_commands
|
||||
)
|
||||
|
||||
@property
|
||||
def writable_ranges(self) -> List[range]:
|
||||
@@ -71,8 +64,6 @@ class EP500P(ProtocolV1Device):
|
||||
@property
|
||||
def pack_polling_commands(self) -> List[ReadHoldingRegisters]:
|
||||
return [
|
||||
ReadHoldingRegisters(91, 2),
|
||||
ReadHoldingRegisters(96, 1),
|
||||
ReadHoldingRegisters(98, 2),
|
||||
ReadHoldingRegisters(105, 16),
|
||||
ReadHoldingRegisters(91, 37),
|
||||
ReadHoldingRegisters(201, 2),
|
||||
]
|
||||
@@ -97,28 +97,4 @@ class EP600(ProtocolV2Device):
|
||||
|
||||
@property
|
||||
def polling_commands(self) -> List[ReadHoldingRegisters]:
|
||||
return super().polling_commands + [
|
||||
ReadHoldingRegisters(2022, 2),
|
||||
ReadHoldingRegisters(2213, 5),
|
||||
ReadHoldingRegisters(1212, 3),
|
||||
ReadHoldingRegisters(1220, 3),
|
||||
ReadHoldingRegisters(1228, 3),
|
||||
ReadHoldingRegisters(1236, 3),
|
||||
ReadHoldingRegisters(1244, 3),
|
||||
ReadHoldingRegisters(1300, 1),
|
||||
ReadHoldingRegisters(1313, 3),
|
||||
ReadHoldingRegisters(1319, 3),
|
||||
ReadHoldingRegisters(1325, 3),
|
||||
ReadHoldingRegisters(1500, 1),
|
||||
ReadHoldingRegisters(1510, 3),
|
||||
ReadHoldingRegisters(1517, 3),
|
||||
ReadHoldingRegisters(1524, 3),
|
||||
ReadHoldingRegisters(152, 1),
|
||||
ReadHoldingRegisters(156, 1),
|
||||
ReadHoldingRegisters(158, 1),
|
||||
# KM - Consumption
|
||||
ReadHoldingRegisters(1430, 15),
|
||||
# KM - Totals
|
||||
ReadHoldingRegisters(142, 5),
|
||||
|
||||
]
|
||||
return self.struct.get_read_holding_registers()
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
from decimal import Decimal
|
||||
from enum import Enum
|
||||
from .commands import ReadHoldingRegisters
|
||||
import struct
|
||||
from typing import Any, List, Optional, Tuple, Type
|
||||
|
||||
@@ -180,6 +181,35 @@ class DeviceStruct:
|
||||
def add_sn_field(self, name: str, address: int):
|
||||
self.fields.append(SerialNumberField(name, address))
|
||||
|
||||
def get_read_holding_registers(self, tolerance=20, filter=lambda address: True):
|
||||
address_set = set()
|
||||
for field in self.fields:
|
||||
for address in range(field.address, field.address + field.size):
|
||||
address_set.add(address)
|
||||
# Filter out addresses that we specificly
|
||||
# specified to exclude.
|
||||
for address in set(address_set):
|
||||
if not filter(address):
|
||||
address_set.remove(address)
|
||||
# Fast path: no addresses -> no commands.
|
||||
if not address_set:
|
||||
return []
|
||||
|
||||
commands = []
|
||||
|
||||
address_list = sorted(address_set)
|
||||
start = address_list[0]
|
||||
last = address_list[0]
|
||||
for address in address_list[1:]:
|
||||
# Create a new group if there's a gap
|
||||
# in the addresses.
|
||||
if address >= last + tolerance:
|
||||
commands.append(ReadHoldingRegisters(start, last - start + 1))
|
||||
start = address
|
||||
last = address
|
||||
commands.append(ReadHoldingRegisters(start, last - start + 1))
|
||||
return commands
|
||||
|
||||
def parse(self, starting_address: int, data: bytes) -> dict:
|
||||
# Offsets and size are counted in 2 byte chunks, so for the range we
|
||||
# need to divide the byte size by 2
|
||||
|
||||
Reference in new issue
Block a user