bluetti_mqtt
bluetti_mqtt copied to clipboard
added slightly expanded AC180 support
I identified the following registers:
device type: 1101 serial_number: 1107 battery type: 6101 battery serial number: 6107 bcu version: 6175 battery percent: 102 output mode: 123 (returns 32 when both loads off, 40 when AC is on, 48 when DC is on, 56 when both on. This is accurate, but may not be exhaustive if there are other additional modes) dc output power: 140 ac output power: 142 dc input power: 144 (this is an educated guess and not tested, because I didn't have a PV module handy) ac input power: 146 power generation might be 1202 or 154, but I can figure it out once I connect a module.
@alexnathanson I incorporated your changes into my fork: https://github.com/ftrueck/bluetti_mqtt/ I hope that @ZachGoldberg will give it a try to build a woking HA addon with it. As long as @warhammerkid is MIA we could at least keep the library up and working.
Thanks for hunting these registers down. there is no "output mode" in mqtt_client.py So for me, this value is not sent out.
Hope you dont mind me putting it in this way. I have no inclination to create a fork, maybe a spoon :) I have the controls for AC and DC working in Home Assistant now. Nothing clever here I jusy noticed the AC60 had very similar controls. It needs a bit of tidying up. I will experiment a but more, but I am away till next week now, but thought I would share it for now.
AC180.py ..................................................
from enum import Enum, unique # //???????????????????????
# from alexnathanson
# now put in ac60 similars...29/5/2
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()
#Core
self.struct.add_swap_string_field('device_type', 110, 6) # went back to these
self.struct.add_sn_field('serial_number', 116)
#self.struct.add_swap_string_field('device_type', 1101, 6) #nl
#self.struct.add_sn_field('serial_number', 1107) #nl
#Battery Data
self.struct.add_uint_field('total_battery_percent', 102) #y
self.struct.add_decimal_field('total_battery_voltage', 6003, 2)
#Power IO
# self.struct.add_bool_field('ac_output_on',123) #y? #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) #y
self.struct.add_uint_field('ac_output_power', 142) #y
self.struct.add_uint_field('dc_input_power', 144) #y
self.struct.add_uint_field('ac_input_power', 146) #y #this is a guess because I didn't have a PV module handy to test
# Controls (2000)
self.struct.add_bool_field('ac_output_on', 2011)
self.struct.add_bool_field('dc_output_on', 2012)
#History
#self.struct.add_decimal_field('power_generation', 154, 1) # try this one Total power generated since last reset (kwh)
#and back again with new polling range
self.struct.add_decimal_field('power_generation', 1202, 1) #y unknown # Total power generated since last reset (kwh)
# this is usefull for investigating the available data............. how???
# 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)
@property
def polling_commands(self) -> List[ReadHoldingRegisters]:
return [
ReadHoldingRegisters(100, 62),
ReadHoldingRegisters(1200, 90),
ReadHoldingRegisters(2000, 67),
]
@property
def logging_commands(self) -> List[ReadHoldingRegisters]:
return [
ReadHoldingRegisters(0,21),
ReadHoldingRegisters(100, 67),
ReadHoldingRegisters(700,6),
ReadHoldingRegisters(720,49),
ReadHoldingRegisters(1100, 51),
ReadHoldingRegisters(1200, 90),
ReadHoldingRegisters(1300, 31),
ReadHoldingRegisters(1400, 48),
ReadHoldingRegisters(1500, 30),
ReadHoldingRegisters(2000, 67),
ReadHoldingRegisters(2200, 29),
ReadHoldingRegisters(3000, 27),
ReadHoldingRegisters(6000, 31),
ReadHoldingRegisters(6100, 100),
ReadHoldingRegisters(6300, 52),
ReadHoldingRegisters(7000,5)
]
@property
def writable_ranges(self) -> List[range]:
return [range(2000, 2225)]
....................................................................................
T
Just want to add I tested this code on AC180P and can confirm it works great:
{'battery_type': 'AC180P', 'battery_serial_number': 23440040765XX, 'bcu_version': Decimal('1039.01')} {} {} {} {'total_battery_percent': 72, 'output_mode': 40, 'dc_output_power': 0, 'ac_output_power': 56, 'dc_input_power': 0, 'ac_input_power': 0} {} {} {'device_type': 'AC180P', 'serial_number': 23440040765XX} {'power_generation': Decimal('0')}
Thanks!
Glad it help. I know I was so relieved when it all started to work as intended. I've been able to automate switch on and switch off, and it ticks along nicely. I do wonder if I could get away from having to have the bluetti-mqtt running on the PC, maybe in HA, or trying to get the esp versions working, and I did try them, but without success. So just sticking with what works for now.