From 5562e340b45a2ab4d38636f541a52f8158729399 Mon Sep 17 00:00:00 2001 From: AlexNathanson Date: Wed, 3 Apr 2024 13:58:24 -0400 Subject: [PATCH 1/5] Create ac180.py --- bluetti_mqtt/core/devices/ac180.py | 44 ++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 bluetti_mqtt/core/devices/ac180.py diff --git a/bluetti_mqtt/core/devices/ac180.py b/bluetti_mqtt/core/devices/ac180.py new file mode 100644 index 0000000..0171623 --- /dev/null +++ b/bluetti_mqtt/core/devices/ac180.py @@ -0,0 +1,44 @@ +from typing import List +from ..commands import ReadHoldingRegisters +from .bluetti_device import BluettiDevice +from .struct import DeviceStruct + + +class AC180(BluettiDevice): + def __init__(self, address: str, sn: str): + self.struct = DeviceStruct() + + self.struct.add_uint_field('total_battery_percent', 102) + self.struct.add_swap_string_field('device_type', 110, 6) + self.struct.add_sn_field('serial_number', 116) + self.struct.add_decimal_field('power_generation', 154, 1) # Total power generated since last reset (kwh) + self.struct.add_swap_string_field('device_type', 1101, 6) + self.struct.add_sn_field('serial_number', 1107) + self.struct.add_decimal_field('power_generation', 1202, 1) # Total power generated since last reset (kwh) + self.struct.add_swap_string_field('battery_type', 6101, 6) + self.struct.add_sn_field('battery_serial_number', 6107) + self.struct.add_version_field('bcu_version', 6175) + + super().__init__(address, 'AC180', sn) + + @property + def polling_commands(self) -> List[ReadHoldingRegisters]: + return [ + ReadHoldingRegisters(100, 62), + ] + + @property + def logging_commands(self) -> List[ReadHoldingRegisters]: + return [ + ReadHoldingRegisters(100, 62), + ReadHoldingRegisters(1100, 51), + ReadHoldingRegisters(1200, 90), + ReadHoldingRegisters(1300, 31), + ReadHoldingRegisters(1400, 48), + ReadHoldingRegisters(1500, 30), + ReadHoldingRegisters(2000, 67), + ReadHoldingRegisters(2200, 29), + ReadHoldingRegisters(6000, 31), + ReadHoldingRegisters(6100, 100), + ReadHoldingRegisters(6300, 52), + ] From 179613572ed3e09047fa35b940ce30c68c1e1596 Mon Sep 17 00:00:00 2001 From: AlexNathanson Date: Wed, 3 Apr 2024 14:00:43 -0400 Subject: [PATCH 2/5] Update __init__.py --- bluetti_mqtt/bluetooth/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bluetti_mqtt/bluetooth/__init__.py b/bluetti_mqtt/bluetooth/__init__.py index 5d5bea8..6d76da4 100644 --- a/bluetti_mqtt/bluetooth/__init__.py +++ b/bluetti_mqtt/bluetooth/__init__.py @@ -3,13 +3,13 @@ from typing import Set from bleak import BleakScanner from bleak.backends.device import BLEDevice -from bluetti_mqtt.core import BluettiDevice, AC200M, AC300, AC500, AC60, EP500, EP500P, EP600, EB3A +from bluetti_mqtt.core import BluettiDevice, AC200M, AC300, AC500, AC180, AC60, EP500, EP500P, EP600, EB3A from .client import BluetoothClient from .exc import BadConnectionError, ModbusError, ParseError from .manager import MultiDeviceManager -DEVICE_NAME_RE = re.compile(r'^(AC200M|AC300|AC500|AC60|EP500P|EP500|EP600|EB3A)(\d+)$') +DEVICE_NAME_RE = re.compile(r'^(AC200M|AC300|AC500|AC60|AC180|EP500P|EP500|EP600|EB3A)(\d+)$') async def scan_devices(): @@ -33,6 +33,8 @@ def build_device(address: str, name: str): return AC500(address, match[2]) if match[1] == 'AC60': return AC60(address, match[2]) + if match[1] == 'AC180': + return AC180(address, match[2]) if match[1] == 'EP500': return EP500(address, match[2]) if match[1] == 'EP500P': From 294a6ccc02a3eb516e397da6582f83a23502116f Mon Sep 17 00:00:00 2001 From: AlexNathanson Date: Wed, 3 Apr 2024 17:21:07 -0400 Subject: [PATCH 3/5] Update __init__.py with ac180 --- bluetti_mqtt/core/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bluetti_mqtt/core/__init__.py b/bluetti_mqtt/core/__init__.py index 2d16d41..ac79132 100644 --- a/bluetti_mqtt/core/__init__.py +++ b/bluetti_mqtt/core/__init__.py @@ -7,6 +7,7 @@ from .devices.ep500p import EP500P from .devices.ep600 import EP600 from .devices.eb3a import EB3A +from .devices.ac180 import AC180 from .commands import ( DeviceCommand, ReadHoldingRegisters, From fe06d33d13c60b2efc0b7ba3cd3c873c50c2dd37 Mon Sep 17 00:00:00 2001 From: AlexNathanson Date: Wed, 3 Apr 2024 21:14:47 -0400 Subject: [PATCH 4/5] Update ac180.py Added support for AC & DC input and output power --- bluetti_mqtt/core/devices/ac180.py | 37 ++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/bluetti_mqtt/core/devices/ac180.py b/bluetti_mqtt/core/devices/ac180.py index 0171623..292e260 100644 --- a/bluetti_mqtt/core/devices/ac180.py +++ b/bluetti_mqtt/core/devices/ac180.py @@ -2,22 +2,40 @@ from ..commands import ReadHoldingRegisters from .bluetti_device import BluettiDevice from .struct import DeviceStruct - +from enum import Enum, unique class AC180(BluettiDevice): def __init__(self, address: str, sn: str): self.struct = DeviceStruct() - self.struct.add_uint_field('total_battery_percent', 102) - self.struct.add_swap_string_field('device_type', 110, 6) - self.struct.add_sn_field('serial_number', 116) - self.struct.add_decimal_field('power_generation', 154, 1) # Total power generated since last reset (kwh) + #Core + # self.struct.add_swap_string_field('device_type', 110, 6) + # self.struct.add_sn_field('serial_number', 116) self.struct.add_swap_string_field('device_type', 1101, 6) self.struct.add_sn_field('serial_number', 1107) - self.struct.add_decimal_field('power_generation', 1202, 1) # Total power generated since last reset (kwh) + + #Battery Data self.struct.add_swap_string_field('battery_type', 6101, 6) self.struct.add_sn_field('battery_serial_number', 6107) self.struct.add_version_field('bcu_version', 6175) + self.struct.add_uint_field('total_battery_percent', 102) + + #Power IO + self.struct.add_uint_field('output_mode',123) #32 when both loads off, 40 when AC is on, 48 when DC is on, 56 when both on + self.struct.add_uint_field('dc_output_power', 140) + self.struct.add_uint_field('ac_output_power', 142) + self.struct.add_uint_field('dc_input_power', 144) + self.struct.add_uint_field('ac_input_power', 146) #this is a guess because I didn't have a PV module handy to test + + #History + # self.struct.add_decimal_field('power_generation', 154, 1) # Total power generated since last reset (kwh) + self.struct.add_decimal_field('power_generation', 1202, 1) # Total power generated since last reset (kwh) + + # this is usefule for investigating the available data + # registers = {0:21,100:67,700:6,720:49,1100:51,1200:90,1300:31,1400:48,1500:30,2000:67,2200:29,3000:27,6000:31,6100:100,6300:52,7000:5} + # for k in registers: + # for v in range(registers[k]): + # self.struct.add_uint_field('testI' + str(v+k), v+k) super().__init__(address, 'AC180', sn) @@ -30,7 +48,10 @@ def polling_commands(self) -> List[ReadHoldingRegisters]: @property def logging_commands(self) -> List[ReadHoldingRegisters]: return [ - ReadHoldingRegisters(100, 62), + ReadHoldingRegisters(0,21), + ReadHoldingRegisters(100, 67), + ReadHoldingRegisters(700,6), + ReadHoldingRegisters(720,49), ReadHoldingRegisters(1100, 51), ReadHoldingRegisters(1200, 90), ReadHoldingRegisters(1300, 31), @@ -38,7 +59,9 @@ def logging_commands(self) -> List[ReadHoldingRegisters]: ReadHoldingRegisters(1500, 30), ReadHoldingRegisters(2000, 67), ReadHoldingRegisters(2200, 29), + ReadHoldingRegisters(3000, 27), ReadHoldingRegisters(6000, 31), ReadHoldingRegisters(6100, 100), ReadHoldingRegisters(6300, 52), + ReadHoldingRegisters(7000,5) ] From 5f3a2c9ec606c59dab7193319f4b71dd98293041 Mon Sep 17 00:00:00 2001 From: AlexNathanson Date: Wed, 3 Apr 2024 21:15:21 -0400 Subject: [PATCH 5/5] Update ac180.py - removed Enum class --- bluetti_mqtt/core/devices/ac180.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bluetti_mqtt/core/devices/ac180.py b/bluetti_mqtt/core/devices/ac180.py index 292e260..9f538e5 100644 --- a/bluetti_mqtt/core/devices/ac180.py +++ b/bluetti_mqtt/core/devices/ac180.py @@ -2,7 +2,6 @@ from ..commands import ReadHoldingRegisters from .bluetti_device import BluettiDevice from .struct import DeviceStruct -from enum import Enum, unique class AC180(BluettiDevice): def __init__(self, address: str, sn: str):