From 38f8390bfb5b0e1df1948a8a6fc50afc97b71dd0 Mon Sep 17 00:00:00 2001 From: Jan Pluskal Date: Sat, 15 Aug 2026 20:38:26 +0200 Subject: [PATCH 1/2] docs: describe battery cell struct registers and bat_mng_struct profiles Add description= strings to the registry entries for battery.cells[0..6], battery.cells_resist[0..6], battery.cells_stat[0..6] and the four bat_mng_struct.profile_* object IDs, and add a short "Struct-type registers" section to the registry documentation page summarizing the payload layouts. All layouts were verified live against a Power Storage 6.0 (BMS software 5615, 4 battery modules) via the port 8899 protocol: - battery.cells[N]: 96 bytes = 24 x (temperature uint8 [degC], voltage uint16 LE [mV], status uint8); cross-checked against battery.min_cell_voltage / battery.max_cell_voltage (matches to 0.1 mV) and battery.temperature. - battery.cells_resist[N]: 96 bytes = 24 x (resistance uint16 BE [1/256 mOhm], 2 bytes padding); format by @sfudeus (cell_resist). - battery.cells_stat[N]: 48 bytes = u_min/u_max/t_min/t_max, each (cell index uint32 LE, unix timestamp uint32 LE, value float32 LE); format by @sfudeus (battery_data_decoding); values match the granular battery.cells_stat[N].* registers. - bat_mng_struct.profile_pdc / profile_pdc_max / profile_load / profile_pext: 192 bytes = <48f LE, one Watt value per 30-minute slot starting at 00:00; first described by @SaxonWood in #49. Verified to produce plausible daily curves. Documentation only, no functional changes. Co-Authored-By: Claude Fable 5 --- docs/inverter_registry.rst | 25 +++++++++++++++++++ src/rctclient/registry.py | 50 +++++++++++++++++++------------------- 2 files changed, 50 insertions(+), 25 deletions(-) diff --git a/docs/inverter_registry.rst b/docs/inverter_registry.rst index d44faa1..121bdca 100644 --- a/docs/inverter_registry.rst +++ b/docs/inverter_registry.rst @@ -20,6 +20,31 @@ as an instance ``REGISTRY`` in the ``registry`` module and can be imported where The following list is a complete index of all the object IDs currently maintained by the registry for use with official vendor devices. +Struct-type registers +===================== + +Some object IDs return binary structures instead of scalar values. The following layouts were determined by +reverse-engineering and verified live against a *Power Storage 6.0* (BMS software ``5615``, 4 battery modules) using +the port 8899 protocol: + +* ``battery.cells[N]``: 96 bytes, 24 records of 4 bytes each: temperature (uint8, °C), cell voltage (uint16 + little-endian, mV) and a status byte. Decoded by :func:`~rctclient.utils.decode_value` as ``BATTERY_MODULE_STATUS``. + Verified against ``battery.min_cell_voltage`` / ``battery.max_cell_voltage`` (matches to 0.1 mV) and + ``battery.temperature``. +* ``battery.cells_resist[N]``: 96 bytes, 24 records of 4 bytes each: per-cell internal resistance (uint16 big-endian, + unit 1/256 mOhm) followed by 2 bytes of padding. Decoded as ``BATTERY_MODULE_RESISTANCE``. Format determined by + `@sfudeus `__ (branch ``cell_resist``). +* ``battery.cells_stat[N]``: 48 bytes, four records ``u_min``, ``u_max``, ``t_min`` and ``t_max``, each consisting of + the cell index (uint32 little-endian, 0-based), a UNIX timestamp (uint32 little-endian) and the value (float32 + little-endian). Decoded as ``BATTERY_MODULE_STATISTICS``. Format determined by `@sfudeus + `__ (branch ``battery_data_decoding``); the values match the granular + ``battery.cells_stat[N].*`` registers. +* ``bat_mng_struct.profile_pdc``, ``profile_pdc_max``, ``profile_load`` and ``profile_pext``: 192 bytes each, + 48 float32 little-endian values (``struct`` format ``<48f``), one Watt value per 30-minute slot of the day starting + at 00:00. These are the device's internal estimates based on historic data, first described by `@SaxonWood + `__ in `#49 `__. No decoder is + implemented for these yet. + .. the following tables are generated from the registry in registry.py using generate_registry.py acc_conv diff --git a/src/rctclient/registry.py b/src/rctclient/registry.py index 93d653a..4a2c54e 100644 --- a/src/rctclient/registry.py +++ b/src/rctclient/registry.py @@ -289,7 +289,7 @@ def name_max_length(self) -> int: ObjectInfo(group=ObjectGroup.TEMPERATURE, object_id=0x90B53336, index=520, request_data_type=DataType.FLOAT, unit='°C', name='temperature.sink_temp_power_reduction', description='Heat sink temperature target'), ObjectInfo(group=ObjectGroup.TEMPERATURE, object_id=0xA7447FC4, index=595, request_data_type=DataType.FLOAT, unit='°C', name='temperature.bat_temp_power_reduction', description='Battery actuator temperature target'), - ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x1676FA6, index=3, request_data_type=DataType.BATTERY_MODULE_STATISTICS, name='battery.cells_stat[3]'), + ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x1676FA6, index=3, request_data_type=DataType.BATTERY_MODULE_STATISTICS, name='battery.cells_stat[3]', description='Cell statistics of battery module 3: u_min, u_max, t_min, t_max, each: cell index uint32 LE, unix timestamp uint32 LE, value float32 LE'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x3D9C51F, index=10, request_data_type=DataType.FLOAT, name='battery.cells_stat[0].u_max.value'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x56162CA, index=15, request_data_type=DataType.UINT32, name='battery.cells_stat[4].u_min.time'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x56417DF, index=16, request_data_type=DataType.UINT8, name='battery.cells_stat[3].t_max.index'), @@ -299,11 +299,11 @@ def name_max_length(self) -> int: ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x86C75B0, index=30, request_data_type=DataType.UINT32, name='battery.stack_software_version[3]', description='Software version stack 3'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x9923C1E, index=35, request_data_type=DataType.UINT8, name='battery.cells_stat[3].t_min.index'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xCFA8BC4, index=47, request_data_type=DataType.UINT16, name='battery.stack_cycles[1]'), - ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xDACF21B, index=49, request_data_type=DataType.BATTERY_MODULE_STATISTICS, name='battery.cells_stat[4]'), + ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xDACF21B, index=49, request_data_type=DataType.BATTERY_MODULE_STATISTICS, name='battery.cells_stat[4]', description='Cell statistics of battery module 4: u_min, u_max, t_min, t_max, each: cell index uint32 LE, unix timestamp uint32 LE, value float32 LE'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xDE3D20D, index=51, request_data_type=DataType.INT32, name='battery.status2', description='Battery extra status'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xEF60C7E, index=58, request_data_type=DataType.FLOAT, name='battery.cells_stat[3].u_max.value'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x120EC3B4, index=68, request_data_type=DataType.UINT8, name='battery.cells_stat[4].u_min.index'), - ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x1348AB07, index=71, request_data_type=DataType.BATTERY_MODULE_STATUS, name='battery.cells[4]'), + ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x1348AB07, index=71, request_data_type=DataType.BATTERY_MODULE_STATUS, name='battery.cells[4]', description='Cell status of battery module 4: 24 records of 4 bytes each: temperature [°C] uint8, cell voltage [mV] uint16 LE, status uint8'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x162491E8, index=76, request_data_type=DataType.STRING, name='battery.module_sn[5]', description='Module 5 Serial Number'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x16A1F844, index=78, request_data_type=DataType.STRING, name='battery.bms_sn', description='BMS Serial Number'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x18D1E9E0, index=87, request_data_type=DataType.UINT8, name='battery.cells_stat[5].u_max.index'), @@ -312,7 +312,7 @@ def name_max_length(self) -> int: ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x1E5FCA70, index=102, request_data_type=DataType.FLOAT, unit='A', name='battery.maximum_charge_current', description='Max. charge current'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x1F73B6A4, index=104, request_data_type=DataType.UINT32, name='battery.cells_stat[3].t_max.time'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x21961B58, index=113, request_data_type=DataType.FLOAT, unit='A', name='battery.current', description='Battery current'), - ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x23E55DA0, index=125, request_data_type=DataType.BATTERY_MODULE_STATISTICS, name='battery.cells_stat[5]'), + ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x23E55DA0, index=125, request_data_type=DataType.BATTERY_MODULE_STATISTICS, name='battery.cells_stat[5]', description='Cell statistics of battery module 5: u_min, u_max, t_min, t_max, each: cell index uint32 LE, unix timestamp uint32 LE, value float32 LE'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x257B5945, index=132, request_data_type=DataType.UINT8, name='battery.cells_stat[2].u_min.index'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x257B7612, index=133, request_data_type=DataType.STRING, name='battery.module_sn[3]', description='Module 3 Serial Number'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x26363AAE, index=135, request_data_type=DataType.UINT8, name='battery.cells_stat[1].t_max.index'), @@ -323,14 +323,14 @@ def name_max_length(self) -> int: ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x2BC1E72B, index=153, request_data_type=DataType.FLOAT, unit='Ah', name='battery.discharged_amp_hours', description='Total charge flow from battery'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x331D0689, index=169, request_data_type=DataType.FLOAT, name='battery.cells_stat[2].t_max.value'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x336415EA, index=170, request_data_type=DataType.UINT32, name='battery.cells_stat[0].t_max.time'), - ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x34A164E7, index=173, request_data_type=DataType.BATTERY_MODULE_STATISTICS, name='battery.cells_stat[0]'), + ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x34A164E7, index=173, request_data_type=DataType.BATTERY_MODULE_STATISTICS, name='battery.cells_stat[0]', description='Cell statistics of battery module 0: u_min, u_max, t_min, t_max, each: cell index uint32 LE, unix timestamp uint32 LE, value float32 LE'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x34E33726, index=174, request_data_type=DataType.UINT8, name='battery.cells_stat[2].u_max.index'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x3503B92D, index=177, request_data_type=DataType.UINT32, name='battery.cells_stat[3].u_max.time'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x381B8BF9, index=187, request_data_type=DataType.FLOAT, unit='%', name='battery.soh', description='SOH (State of Health)'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x3A7D5F53, index=198, request_data_type=DataType.FLOAT, name='battery.cells_stat[1].u_max.value'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x3BA1B77B, index=206, request_data_type=DataType.FLOAT, name='battery.cells_stat[3].t_min.value'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x3F98F58A, index=218, request_data_type=DataType.UINT8, name='battery.cells_stat[5].t_max.index'), - ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x40FF01B7, index=222, request_data_type=DataType.BATTERY_MODULE_STATUS, name='battery.cells[6]'), + ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x40FF01B7, index=222, request_data_type=DataType.BATTERY_MODULE_STATUS, name='battery.cells[6]', description='Cell status of battery module 6: 24 records of 4 bytes each: temperature [°C] uint8, cell voltage [mV] uint16 LE, status uint8'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x41B11ECF, index=224, request_data_type=DataType.UINT8, name='battery.cells_stat[3].u_min.index'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x428CCF46, index=225, request_data_type=DataType.FLOAT, name='battery.cells_stat[5].u_min.value'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x442A3409, index=233, request_data_type=DataType.UINT32, name='battery.cells_stat[4].t_min.time'), @@ -339,7 +339,7 @@ def name_max_length(self) -> int: ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x4CB7C0DC, index=261, request_data_type=DataType.FLOAT, name='battery.min_cell_voltage'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x4D985F33, index=263, request_data_type=DataType.FLOAT, name='battery.cells_stat[5].u_max.value'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x4E699086, index=271, request_data_type=DataType.STRING, name='battery.module_sn[4]', description='Module 4 Serial Number'), - ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x501A162D, index=280, request_data_type=DataType.BATTERY_MODULE_RESISTANCE, name='battery.cells_resist[5]'), + ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x501A162D, index=280, request_data_type=DataType.BATTERY_MODULE_RESISTANCE, name='battery.cells_resist[5]', description='Cell internal resistance of battery module 5: 24 records of 4 bytes each: resistance uint16 BE [1/256 mOhm], 2 bytes padding'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x50514732, index=281, request_data_type=DataType.UINT8, name='battery.cells_stat[6].u_min.index'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x518C7BBE, index=285, request_data_type=DataType.UINT32, name='battery.cells_stat[5].u_min.time'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x537C719F, index=289, request_data_type=DataType.FLOAT, name='battery.cells_stat[0].t_max.value'), @@ -353,14 +353,14 @@ def name_max_length(self) -> int: ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x60749E5E, index=333, request_data_type=DataType.UINT32, name='battery.cells_stat[6].u_min.time'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x61EAC702, index=336, request_data_type=DataType.FLOAT, name='battery.cells_stat[0].t_min.value'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x6213589B, index=337, request_data_type=DataType.FLOAT, name='battery.cells_stat[6].u_min.value'), - ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x62D645D9, index=340, request_data_type=DataType.BATTERY_MODULE_STATUS, name='battery.cells[5]'), + ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x62D645D9, index=340, request_data_type=DataType.BATTERY_MODULE_STATUS, name='battery.cells[5]', description='Cell status of battery module 5: 24 records of 4 bytes each: temperature [°C] uint8, cell voltage [mV] uint16 LE, status uint8'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x6388556C, index=344, request_data_type=DataType.UINT32, name='battery.stack_software_version[0]', description='Software version stack 0'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x6445D856, index=345, request_data_type=DataType.UINT8, name='battery.cells_stat[1].u_min.index'), - ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x649B10DA, index=347, request_data_type=DataType.BATTERY_MODULE_RESISTANCE, name='battery.cells_resist[0]'), + ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x649B10DA, index=347, request_data_type=DataType.BATTERY_MODULE_RESISTANCE, name='battery.cells_resist[0]', description='Cell internal resistance of battery module 0: 24 records of 4 bytes each: resistance uint16 BE [1/256 mOhm], 2 bytes padding'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x4E04DD55, index=266, request_data_type=DataType.FLOAT, name='battery.soc_update_since'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x65EED11B, index=353, request_data_type=DataType.FLOAT, unit='V', name='battery.voltage', description='Battery voltage'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x6974798A, index=369, request_data_type=DataType.UINT32, name='battery.stack_software_version[6]', description='Software version stack 6'), - ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x69B8FF28, index=371, request_data_type=DataType.BATTERY_MODULE_STATUS, name='battery.cells[2]'), + ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x69B8FF28, index=371, request_data_type=DataType.BATTERY_MODULE_STATUS, name='battery.cells[2]', description='Cell status of battery module 2: 24 records of 4 bytes each: temperature [°C] uint8, cell voltage [mV] uint16 LE, status uint8'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x6DB1FDDC, index=385, request_data_type=DataType.FLOAT, name='battery.cells_stat[4].u_min.value'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x6E24632E, index=388, request_data_type=DataType.UINT32, name='battery.cells_stat[5].u_max.time'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x6E491B50, index=390, request_data_type=DataType.FLOAT, unit='V', name='battery.maximum_charge_voltage', description='Max. charge voltage'), @@ -369,10 +369,10 @@ def name_max_length(self) -> int: ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x5847E59E, index=306, request_data_type=DataType.FLOAT, unit='V', name='battery.maximum_charge_voltage_constant_u', description='Max. charge voltage'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x71196579, index=400, request_data_type=DataType.UINT8, name='battery.cells_stat[5].t_min.index'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x71765BD8, index=404, request_data_type=DataType.INT32, name='battery.status', description='Battery status'), - ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x71CB0B57, index=406, request_data_type=DataType.BATTERY_MODULE_RESISTANCE, name='battery.cells_resist[1]'), + ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x71CB0B57, index=406, request_data_type=DataType.BATTERY_MODULE_RESISTANCE, name='battery.cells_resist[1]', description='Cell internal resistance of battery module 1: 24 records of 4 bytes each: resistance uint16 BE [1/256 mOhm], 2 bytes padding'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x7268CE4D, index=409, request_data_type=DataType.UINT32, name='battery.inv_cmd'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x73489528, index=412, request_data_type=DataType.STRING, name='battery.module_sn[2]', description='Module 2 Serial Number'), - ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x74FD4609, index=415, request_data_type=DataType.BATTERY_MODULE_STATISTICS, name='battery.cells_stat[2]'), + ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x74FD4609, index=415, request_data_type=DataType.BATTERY_MODULE_STATISTICS, name='battery.cells_stat[2]', description='Cell statistics of battery module 2: u_min, u_max, t_min, t_max, each: cell index uint32 LE, unix timestamp uint32 LE, value float32 LE'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x770A6E7C, index=422, request_data_type=DataType.UINT8, name='battery.cells_stat[0].u_max.index'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x7E590128, index=455, request_data_type=DataType.UINT32, name='battery.cells_stat[0].u_max.time'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x7F42BB82, index=457, request_data_type=DataType.UINT8, name='battery.cells_stat[6].u_max.index'), @@ -387,7 +387,7 @@ def name_max_length(self) -> int: ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x8BB08839, index=498, request_data_type=DataType.UINT32, name='battery.cells_stat[6].t_min.time'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x8DFFDD33, index=504, request_data_type=DataType.UINT32, name='battery.cells_stat[3].u_min.time'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x8EC23427, index=507, request_data_type=DataType.UINT32, name='battery.cells_stat[4].u_max.time'), - ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x8EF6FBBD, index=509, request_data_type=DataType.BATTERY_MODULE_STATUS, name='battery.cells[1]'), + ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x8EF6FBBD, index=509, request_data_type=DataType.BATTERY_MODULE_STATUS, name='battery.cells[1]', description='Cell status of battery module 1: 24 records of 4 bytes each: temperature [°C] uint8, cell voltage [mV] uint16 LE, status uint8'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x8EF9C9B8, index=510, request_data_type=DataType.UINT32, name='battery.cells_stat[6].t_max.time'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x902AFAFB, index=513, request_data_type=DataType.FLOAT, unit='°C', name='battery.temperature', description='Battery temperature'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x90832471, index=518, request_data_type=DataType.UINT32, name='battery.cells_stat[1].u_max.time'), @@ -395,7 +395,7 @@ def name_max_length(self) -> int: ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x91FB68CD, index=527, request_data_type=DataType.FLOAT, name='battery.cells_stat[6].t_max.value'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x959930BF, index=545, request_data_type=DataType.FLOAT, unit='%', name='battery.soc', description='SOC (State of charge)'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x99396810, index=558, request_data_type=DataType.STRING, name='battery.module_sn[1]', description='Module 1 Serial Number'), - ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x993C06F6, index=559, request_data_type=DataType.BATTERY_MODULE_RESISTANCE, name='battery.cells_resist[3]'), + ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x993C06F6, index=559, request_data_type=DataType.BATTERY_MODULE_RESISTANCE, name='battery.cells_resist[3]', description='Cell internal resistance of battery module 3: 24 records of 4 bytes each: resistance uint16 BE [1/256 mOhm], 2 bytes padding'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x9D785E8C, index=569, request_data_type=DataType.UINT32, name='battery.bms_software_version', description='Software version BMS Master'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0x9E314430, index=572, request_data_type=DataType.UINT32, name='battery.cells_stat[2].u_max.time'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xA10D9A4B, index=574, request_data_type=DataType.FLOAT, name='battery.min_cell_temperature'), @@ -422,29 +422,29 @@ def name_max_length(self) -> int: ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xC0DF2978, index=684, request_data_type=DataType.INT32, name='battery.cycles', description='Battery charge / discharge cycles'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xC42F5807, index=695, request_data_type=DataType.UINT8, name='battery.cells_stat[1].u_max.index'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xC6DA81A0, index=704, request_data_type=DataType.UINT32, name='battery.cells_stat[6].u_max.time'), - ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xC8609C8E, index=712, request_data_type=DataType.BATTERY_MODULE_STATUS, name='battery.cells[3]'), + ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xC8609C8E, index=712, request_data_type=DataType.BATTERY_MODULE_STATUS, name='battery.cells[3]', description='Cell status of battery module 3: 24 records of 4 bytes each: temperature [°C] uint8, cell voltage [mV] uint16 LE, status uint8'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xC88EB032, index=713, request_data_type=DataType.UINT32, name='battery.cells_stat[0].u_min.time'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xC8BA1729, index=714, request_data_type=DataType.UINT32, name='battery.stack_software_version[2]', description='Software version stack 2'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xD0C47326, index=736, request_data_type=DataType.FLOAT, name='battery.cells_stat[1].t_min.value'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xD60E7A2F, index=754, request_data_type=DataType.UINT32, name='battery.cells_stat[1].u_min.time'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xDD5930A2, index=773, request_data_type=DataType.UINT8, name='battery.cells_stat[0].t_min.index'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xDE9CBCB0, index=778, request_data_type=DataType.FLOAT, name='battery.cells_stat[5].t_max.value'), - ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xDEE1957F, index=779, request_data_type=DataType.BATTERY_MODULE_RESISTANCE, name='battery.cells_resist[4]'), + ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xDEE1957F, index=779, request_data_type=DataType.BATTERY_MODULE_RESISTANCE, name='battery.cells_resist[4]', description='Cell internal resistance of battery module 4: 24 records of 4 bytes each: resistance uint16 BE [1/256 mOhm], 2 bytes padding'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xDF0A735C, index=780, request_data_type=DataType.FLOAT, unit='A', name='battery.maximum_discharge_current', description='Max. discharge current'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xDFF966E3, index=783, request_data_type=DataType.UINT8, name='battery.cells_stat[6].t_min.index'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xE7177DEE, index=804, request_data_type=DataType.FLOAT, name='battery.cells_stat[2].u_max.value'), - ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xEB4C2597, index=814, request_data_type=DataType.BATTERY_MODULE_RESISTANCE, name='battery.cells_resist[6]'), + ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xEB4C2597, index=814, request_data_type=DataType.BATTERY_MODULE_RESISTANCE, name='battery.cells_resist[6]', description='Cell internal resistance of battery module 6: 24 records of 4 bytes each: resistance uint16 BE [1/256 mOhm], 2 bytes padding'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xEEA3F59B, index=821, request_data_type=DataType.UINT32, name='battery.stack_software_version[5]', description='Software version stack 5'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xEECDFEFC, index=823, request_data_type=DataType.FLOAT, name='battery.cells_stat[2].u_min.value'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xEFD3EC8A, index=825, request_data_type=DataType.UINT32, name='battery.cells_stat[5].t_min.time'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xF044EDA0, index=828, request_data_type=DataType.FLOAT, name='battery.cells_stat[3].t_max.value'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xF257D342, index=842, request_data_type=DataType.FLOAT, name='battery.cells_stat[1].t_max.value'), - ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xF3FD8CE6, index=848, request_data_type=DataType.BATTERY_MODULE_RESISTANCE, name='battery.cells_resist[2]'), + ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xF3FD8CE6, index=848, request_data_type=DataType.BATTERY_MODULE_RESISTANCE, name='battery.cells_resist[2]', description='Cell internal resistance of battery module 2: 24 records of 4 bytes each: resistance uint16 BE [1/256 mOhm], 2 bytes padding'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xF54BC06D, index=854, request_data_type=DataType.FLOAT, name='battery.cells_stat[4].u_max.value'), - ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xF8C0D255, index=864, request_data_type=DataType.BATTERY_MODULE_STATUS, name='battery.cells[0]'), - ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xF99E8CC8, index=866, request_data_type=DataType.BATTERY_MODULE_STATISTICS, name='battery.cells_stat[6]'), + ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xF8C0D255, index=864, request_data_type=DataType.BATTERY_MODULE_STATUS, name='battery.cells[0]', description='Cell status of battery module 0: 24 records of 4 bytes each: temperature [°C] uint8, cell voltage [mV] uint16 LE, status uint8'), + ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xF99E8CC8, index=866, request_data_type=DataType.BATTERY_MODULE_STATISTICS, name='battery.cells_stat[6]', description='Cell statistics of battery module 6: u_min, u_max, t_min, t_max, each: cell index uint32 LE, unix timestamp uint32 LE, value float32 LE'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xFA3276DC, index=868, request_data_type=DataType.UINT32, name='battery.cells_stat[3].t_min.time'), - ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xFB796780, index=874, request_data_type=DataType.BATTERY_MODULE_STATISTICS, name='battery.cells_stat[1]'), + ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xFB796780, index=874, request_data_type=DataType.BATTERY_MODULE_STATISTICS, name='battery.cells_stat[1]', description='Cell statistics of battery module 1: u_min, u_max, t_min, t_max, each: cell index uint32 LE, unix timestamp uint32 LE, value float32 LE'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xFBF6D834, index=877, request_data_type=DataType.STRING, name='battery.module_sn[0]', description='Module 0 Serial Number'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xFDBD9EE9, index=889, request_data_type=DataType.UINT8, name='battery.cells_stat[3].u_max.index'), ObjectInfo(group=ObjectGroup.BATTERY, object_id=0xFE44BA26, index=892, request_data_type=DataType.UINT8, name='battery.cells_stat[0].u_min.index'), @@ -874,11 +874,11 @@ def name_max_length(self) -> int: enum_map={0: 'Modbus slave', 1: 'Modbus master'}), ObjectInfo(group=ObjectGroup.MODBUS, object_id=0x6C243F71, index=378, request_data_type=DataType.UINT8, name='modbus.address', description='RS485 address'), - ObjectInfo(group=ObjectGroup.BAT_MNG_STRUCT, object_id=0x3B0C6A53, index=203, request_data_type=DataType.STRING, name='bat_mng_struct.profile_pdc_max'), - ObjectInfo(group=ObjectGroup.BAT_MNG_STRUCT, object_id=0x9DC927AA, index=570, request_data_type=DataType.UNKNOWN, name='bat_mng_struct.profile_load'), + ObjectInfo(group=ObjectGroup.BAT_MNG_STRUCT, object_id=0x3B0C6A53, index=203, request_data_type=DataType.STRING, name='bat_mng_struct.profile_pdc_max', description='Estimated maximum PV (DC) power profile [W]: 48 x float32 LE, one value per 30-minute slot of the day starting at 00:00'), + ObjectInfo(group=ObjectGroup.BAT_MNG_STRUCT, object_id=0x9DC927AA, index=570, request_data_type=DataType.UNKNOWN, name='bat_mng_struct.profile_load', description='Estimated load power profile [W]: 48 x float32 LE, one value per 30-minute slot of the day starting at 00:00'), ObjectInfo(group=ObjectGroup.BAT_MNG_STRUCT, object_id=0xB2FB9A90, index=633, request_data_type=DataType.FLOAT, name='bat_mng_struct.k_trust', description='How fast the actual prediction can be trusted [0..10]'), - ObjectInfo(group=ObjectGroup.BAT_MNG_STRUCT, object_id=0xDE68F62D, index=777, request_data_type=DataType.STRING, name='bat_mng_struct.profile_pext'), - ObjectInfo(group=ObjectGroup.BAT_MNG_STRUCT, object_id=0xDF6EA121, index=781, request_data_type=DataType.STRING, name='bat_mng_struct.profile_pdc'), + ObjectInfo(group=ObjectGroup.BAT_MNG_STRUCT, object_id=0xDE68F62D, index=777, request_data_type=DataType.STRING, name='bat_mng_struct.profile_pext', description='Estimated external power profile [W]: 48 x float32 LE, one value per 30-minute slot of the day starting at 00:00'), + ObjectInfo(group=ObjectGroup.BAT_MNG_STRUCT, object_id=0xDF6EA121, index=781, request_data_type=DataType.STRING, name='bat_mng_struct.profile_pdc', description='Estimated PV (DC) power profile [W]: 48 x float32 LE, one value per 30-minute slot of the day starting at 00:00'), ObjectInfo(group=ObjectGroup.BAT_MNG_STRUCT, object_id=0xF0A03A20, index=831, request_data_type=DataType.FLOAT, name='bat_mng_struct.k', description='Forecast correction'), ObjectInfo(group=ObjectGroup.BAT_MNG_STRUCT, object_id=0xF644DCA7, index=856, request_data_type=DataType.FLOAT, name='bat_mng_struct.k_reserve', description='Main reservation coefficient [0..2]'), ObjectInfo(group=ObjectGroup.BAT_MNG_STRUCT, object_id=0xFB57BA65, index=872, request_data_type=DataType.STRING, name='bat_mng_struct.count'), From a5d5ce2fc6912cb2b53c27a26218fcede3fa30ba Mon Sep 17 00:00:00 2001 From: Jan Pluskal Date: Sun, 16 Aug 2026 13:47:08 +0200 Subject: [PATCH 2/2] registry: type profile_* payloads as UNKNOWN, not STRING The four bat_mng_struct.profile_* registers carry binary 48 x float32 LE payloads (documented in this PR); decoding them as STRING truncates at the first NUL or raises UnicodeDecodeError in the CLI. profile_load was already UNKNOWN - align the remaining three so the CLI falls back to a hexdump (cli.py handles DataType.UNKNOWN that way) until a dedicated decoder type exists. Suggested by Copilot review on PR #53. Co-Authored-By: Claude Fable 5 --- src/rctclient/registry.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rctclient/registry.py b/src/rctclient/registry.py index 4a2c54e..5e6116f 100644 --- a/src/rctclient/registry.py +++ b/src/rctclient/registry.py @@ -874,11 +874,11 @@ def name_max_length(self) -> int: enum_map={0: 'Modbus slave', 1: 'Modbus master'}), ObjectInfo(group=ObjectGroup.MODBUS, object_id=0x6C243F71, index=378, request_data_type=DataType.UINT8, name='modbus.address', description='RS485 address'), - ObjectInfo(group=ObjectGroup.BAT_MNG_STRUCT, object_id=0x3B0C6A53, index=203, request_data_type=DataType.STRING, name='bat_mng_struct.profile_pdc_max', description='Estimated maximum PV (DC) power profile [W]: 48 x float32 LE, one value per 30-minute slot of the day starting at 00:00'), + ObjectInfo(group=ObjectGroup.BAT_MNG_STRUCT, object_id=0x3B0C6A53, index=203, request_data_type=DataType.UNKNOWN, name='bat_mng_struct.profile_pdc_max', description='Estimated maximum PV (DC) power profile [W]: 48 x float32 LE, one value per 30-minute slot of the day starting at 00:00'), ObjectInfo(group=ObjectGroup.BAT_MNG_STRUCT, object_id=0x9DC927AA, index=570, request_data_type=DataType.UNKNOWN, name='bat_mng_struct.profile_load', description='Estimated load power profile [W]: 48 x float32 LE, one value per 30-minute slot of the day starting at 00:00'), ObjectInfo(group=ObjectGroup.BAT_MNG_STRUCT, object_id=0xB2FB9A90, index=633, request_data_type=DataType.FLOAT, name='bat_mng_struct.k_trust', description='How fast the actual prediction can be trusted [0..10]'), - ObjectInfo(group=ObjectGroup.BAT_MNG_STRUCT, object_id=0xDE68F62D, index=777, request_data_type=DataType.STRING, name='bat_mng_struct.profile_pext', description='Estimated external power profile [W]: 48 x float32 LE, one value per 30-minute slot of the day starting at 00:00'), - ObjectInfo(group=ObjectGroup.BAT_MNG_STRUCT, object_id=0xDF6EA121, index=781, request_data_type=DataType.STRING, name='bat_mng_struct.profile_pdc', description='Estimated PV (DC) power profile [W]: 48 x float32 LE, one value per 30-minute slot of the day starting at 00:00'), + ObjectInfo(group=ObjectGroup.BAT_MNG_STRUCT, object_id=0xDE68F62D, index=777, request_data_type=DataType.UNKNOWN, name='bat_mng_struct.profile_pext', description='Estimated external power profile [W]: 48 x float32 LE, one value per 30-minute slot of the day starting at 00:00'), + ObjectInfo(group=ObjectGroup.BAT_MNG_STRUCT, object_id=0xDF6EA121, index=781, request_data_type=DataType.UNKNOWN, name='bat_mng_struct.profile_pdc', description='Estimated PV (DC) power profile [W]: 48 x float32 LE, one value per 30-minute slot of the day starting at 00:00'), ObjectInfo(group=ObjectGroup.BAT_MNG_STRUCT, object_id=0xF0A03A20, index=831, request_data_type=DataType.FLOAT, name='bat_mng_struct.k', description='Forecast correction'), ObjectInfo(group=ObjectGroup.BAT_MNG_STRUCT, object_id=0xF644DCA7, index=856, request_data_type=DataType.FLOAT, name='bat_mng_struct.k_reserve', description='Main reservation coefficient [0..2]'), ObjectInfo(group=ObjectGroup.BAT_MNG_STRUCT, object_id=0xFB57BA65, index=872, request_data_type=DataType.STRING, name='bat_mng_struct.count'),