Compare commits

..

9 Commits

Author SHA1 Message Date
Stefan Allius
3d422f9249 fix link 2025-04-13 16:11:51 +02:00
Stefan Allius
6d4ff0d508 update compose help link 2025-04-10 23:40:51 +02:00
renovate[bot]
31f4f05bed Update ghcr.io/hassio-addons/base Docker tag to v17.2.4 (#355)
* Update ghcr.io/hassio-addons/base Docker tag to v17.2.3

* Update ghcr.io/hassio-addons/base Docker tag to v17.2.4

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Stefan Allius <stefan.allius@t-online.de>
2025-04-08 20:47:11 +02:00
Stefan Allius
8ca91c2fdd define the value 2 for the out status (#356) 2025-04-07 23:47:29 +02:00
Stefan Allius
ea749dcce6 enforce numbered release candidates (#353) 2025-04-06 22:28:32 +02:00
Stefan Allius
af5604d029 add alarm bitfields (#352)
- fix bitfield of the inverter alarms
- add batterie alarms
2025-04-06 20:07:17 +02:00
renovate[bot]
015b6b8db0 Update dependency pytest-cov to v6.1.1 (#346)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-06 01:28:27 +02:00
Stefan Allius
7782a3cb57 Add two states build from the measurements (#351)
* Add two states build from the measurements
- Batterie Status calculated from the batt current
- Power Supply State calc from the out Power

* improve test coverage
2025-04-06 01:21:41 +02:00
Stefan Allius
3d073acc58 Cleanup MQTT json format for DCU batterie (#349)
* Cleanup MQTT json format for DCU batterie
- add hw and sw version
- rename total generation into total charging energy
- rename cell temperature sensors
- restructure json format
- adapt unit tests

* revert changed test packages
2025-04-05 22:30:57 +02:00
12 changed files with 201 additions and 66 deletions

View File

@@ -141,7 +141,7 @@ No special configuration is required for the Docker container if it is built and
On the host, two directories (for log files and for config files) must be mapped. If necessary, the UID of the proxy process can be adjusted, which is also the owner of the log and configuration files.
A description of the configuration parameters can be found [here](https://github.com/s-allius/tsun-gen3-proxy/wiki/Configuration-toml#docker-compose-environment-variables).
A description of the configuration parameters can be found [here](https://github.com/s-allius/tsun-gen3-proxy/wiki/configuration-env#docker-compose-environment-variables)
## Proxy Configuration

View File

@@ -22,7 +22,16 @@ dev debug:
export IMAGE=$(PRIVAT_CONTAINER_REGISTRY)$(IMAGE) && \
docker buildx bake -f docker-bake.hcl $@
preview rc rel:
rc:
@[ "${RC}" ] || ( echo ">> RC is not set"; exit 1 )
@echo version: $(VERSION) build-date: $(BUILD_DATE) image: $(PUBLIC_CONTAINER_REGISTRY)$(IMAGE)
@echo login at $(PUBLIC_URL) as $(PUBLIC_USER)
@DO_LOGIN="$(shell echo $(PUBLIC_CR_KEY) | docker login $(PUBLIC_URL) -u $(PUBLIC_USER) --password-stdin)"
export VERSION=$(VERSION)-$@$(RC) && \
export IMAGE=$(PUBLIC_CONTAINER_REGISTRY)$(IMAGE) && \
docker buildx bake -f docker-bake.hcl $@
preview rel:
@echo version: $(VERSION) build-date: $(BUILD_DATE) image: $(PUBLIC_CONTAINER_REGISTRY)$(IMAGE)
@echo login at $(PUBLIC_URL) as $(PUBLIC_USER)
@DO_LOGIN="$(shell echo $(PUBLIC_CR_KEY) | docker login $(PUBLIC_URL) -u $(PUBLIC_USER) --password-stdin)"

View File

@@ -1,7 +1,7 @@
flake8==7.2.0
pytest==8.3.5
pytest-asyncio==0.26.0
pytest-cov==6.1.0
pytest-cov==6.1.1
python-dotenv==1.1.0
mock==5.2.0
coverage==7.8.0

View File

@@ -19,6 +19,19 @@ class RegisterFunc:
result += prod
return result
@staticmethod
def cmp_values(info: Infos, params: map) -> None | int:
try:
val = info.get_db_value(params['reg'])
if val < params['cmp_val']:
return params['res'][0]
if val == params['cmp_val']:
return params['res'][1]
return params['res'][2]
except Exception:
pass
return None
class RegisterMap:
# make the class read/only by using __slots__
@@ -136,8 +149,8 @@ class RegisterMap:
0x42010034: {'reg': Register.BATT_PV2_VOLT, 'fmt': '!H', 'ratio': 0.01}, # noqa: E501, DC Voltage PV2
0x42010036: {'reg': Register.BATT_PV2_CUR, 'fmt': '!H', 'ratio': 0.01}, # noqa: E501, DC Current PV2
0x42010038: {'reg': Register.BATT_TOTAL_CHARG, 'fmt': '!L', 'ratio': 0.01}, # noqa: E501
0x4201003c: {'reg': Register.BATT_STATUS_1, 'fmt': '!H'}, # noqa: E501 MPTT-1 Operating Status: 0(Standby), 1(Work)
0x4201003e: {'reg': Register.BATT_STATUS_2, 'fmt': '!H'}, # noqa: E501 MPTT-2 Operating Status: 0(Standby), 1(Work)
0x4201003c: {'reg': Register.BATT_PV1_STATUS, 'fmt': '!H'}, # noqa: E501 MPTT-1 Operating Status: 0(Standby), 1(Work)
0x4201003e: {'reg': Register.BATT_PV2_STATUS, 'fmt': '!H'}, # noqa: E501 MPTT-2 Operating Status: 0(Standby), 1(Work)
0x42010040: {'reg': Register.BATT_VOLT, 'fmt': '!h', 'ratio': 0.01}, # noqa: E501
0x42010042: {'reg': Register.BATT_CUR, 'fmt': '!h', 'ratio': 0.01}, # noqa: E501 => Batterie Status: <0(Discharging), 0(Static), 0>(Loading)
0x42010044: {'reg': Register.BATT_SOC, 'fmt': '!H', 'ratio': 0.01}, # noqa: E501, state of charge (SOC) in percent
@@ -164,7 +177,7 @@ class RegisterMap:
0x4201006e: {'reg': Register.BATT_OUT_CUR, 'fmt': '!H', 'ratio': 0.01}, # noqa: E501 Output Current
0x42010070: {'reg': Register.BATT_OUT_STATUS, 'fmt': '!H'}, # noqa: E501 Output Working Status: 0(Standby), 1(Work)
0x42010072: {'reg': Register.BATT_TEMP_4, 'fmt': '!h'}, # noqa: E50, Environment temp
0x42010074: {'reg': Register.BATT_74, 'fmt': '!H'}, # noqa: E501 Warning Alarmcode 1, Bit 0..15
0x42010074: {'reg': Register.BATT_ALARM, 'fmt': '!H'}, # noqa: E501 Warning Alarmcode 1, Bit 0..15
0x42010076: {'reg': Register.BATT_HW_VERS, 'fmt': '!h'}, # noqa: E501 hardware version
0x42010078: {'reg': Register.BATT_SW_VERS, 'fmt': '!h'}, # noqa: E501 software main version
'calc': {
@@ -173,8 +186,12 @@ class RegisterMap:
[Register.BATT_PV2_VOLT, Register.BATT_PV2_CUR]]},
2: {'reg': Register.BATT_PWR, 'func': RegisterFunc.prod_sum, # noqa: E501
'params': [[Register.BATT_VOLT, Register.BATT_CUR]]},
3: {'reg': Register.BATT_OUT_PWR, 'func': RegisterFunc.prod_sum, # noqa: E501 Supply Power => Power Supply State: 0(Idle), 0>(Power Supply)
3: {'reg': Register.BATT_OUT_PWR, 'func': RegisterFunc.prod_sum, # noqa: E501 Supply Power => Power Supply State: 0(Idle), 0>(Power Supply)
'params': [[Register.BATT_OUT_VOLT, Register.BATT_OUT_CUR]]},
4: {'reg': Register.BATT_PWR_SUPL_STATE, 'func': RegisterFunc.cmp_values, # noqa: E501
'params': {'reg': Register.BATT_OUT_PWR, 'cmp_val': 0, 'res': [0, 0, 1]}}, # noqa: E501
5: {'reg': Register.BATT_STATUS, 'func': RegisterFunc.cmp_values, # noqa: E501
'params': {'reg': Register.BATT_CUR, 'cmp_val': 0.0, 'res': [0, 1, 2]}} # noqa: E501
}
}

View File

@@ -126,8 +126,8 @@ class Register(Enum):
BATT_PV2_VOLT = 1002
BATT_PV2_CUR = 1003
BATT_TOTAL_CHARG = 1005
BATT_STATUS_1 = 1006
BATT_STATUS_2 = 1007
BATT_PV1_STATUS = 1006
BATT_PV2_STATUS = 1007
BATT_VOLT = 1010
BATT_CUR = 1011
BATT_SOC = 1012
@@ -154,12 +154,14 @@ class Register(Enum):
BATT_OUT_CUR = 1033
BATT_OUT_STATUS = 1034
BATT_TEMP_4 = 1035
BATT_74 = 1036
BATT_ALARM = 1036
BATT_HW_VERS = 1037
BATT_SW_VERS = 1038
BATT_PV_PWR = 1040
BATT_PWR = 1041
BATT_OUT_PWR = 1042
BATT_PWR_SUPL_STATE = 1043
BATT_STATUS = 1044
TEST_VAL_0 = 2000
TEST_VAL_1 = 2001
@@ -324,6 +326,7 @@ class Infos:
SOLAR_POWER_VAR = 'mdi:solar-power-variant'
SOLAR_POWER = 'mdi:solar-power'
WIFI = 'mdi:wifi'
ALARM_LIGHT = 'mdi:alarm-light'
UPDATE = 'mdi:update'
DAILY_GEN = 'Daily Generation'
TOTAL_GEN = 'Total Generation'
@@ -377,7 +380,9 @@ class Infos:
__status_type_val_tpl = "{%set inv_status = ['Off-line', 'On-grid', 'Off-grid'] %}{{inv_status[value_json['Inverter_Status']|int(0)]|default(value_json['Inverter_Status'])}}" # noqa: E501
__mppt1_status_type_val_tpl = "{%set mppt_status = ['Standby', 'On', 'Off'] %}{{mppt_status[value_json['pv1']['MPPT-Status']|int(0)]|default(value_json['pv1']['MPPT-Status'])}}" # noqa: E501
__mppt2_status_type_val_tpl = "{%set mppt_status = ['Standby', 'On', 'Off'] %}{{mppt_status[value_json['pv2']['MPPT-Status']|int(0)]|default(value_json['pv2']['MPPT-Status'])}}" # noqa: E501
__out_status_type_val_tpl = "{%set out_status = ['Standby', 'On'] %}{{out_status[value_json['out']['Out_Status']|int(0)]|default(value_json['out']['Out_Status'])}}" # noqa: E501
__supply_status_type_val_tpl = "{%set supply_status = ['Idle', 'Power-Supply'] %}{{supply_status[value_json['out']['Suppl_State']|int(0)]|default(value_json['out']['Suppl_State'])}}" # noqa: E501
__batt_status_type_val_tpl = "{%set batt_status = ['Discharging', 'Static', 'Loading'] %}{{batt_status[value_json['batt']['Batt_State']|int(0)]|default(value_json['batt']['Batt_State'])}}" # noqa: E501
__out_status_type_val_tpl = "{%set out_status = ['Standby', 'On', 'Off'] %}{{out_status[value_json['out']['Out_Status']|int(0)]|default(value_json['out']['Out_Status'])}}" # noqa: E501
__rated_power_val_tpl = "{% if 'Rated_Power' in value_json and value_json['Rated_Power'] != None %}{{value_json['Rated_Power']|string() +' W'}}{% else %}{{ this.state }}{% endif %}" # noqa: E501
__designed_power_val_tpl = '''
{% if 'Max_Designed_Power' in value_json and
@@ -399,52 +404,52 @@ class Infos:
{% set result = 'noAlarm'%}
{%else%}
{% set result = '' %}
{% if val_int | bitwise_and(1)%}
{% if val_int | bitwise_and(0x0001)%}
{% set result = result + 'HBridgeFault, '%}
{% endif %}
{% if val_int | bitwise_and(2)%}
{% if val_int | bitwise_and(0x0002)%}
{% set result = result + 'DriVoltageFault, '%}
{% endif %}
{% if val_int | bitwise_and(3)%}
{% if val_int | bitwise_and(0x0004)%}
{% set result = result + 'GFDI-Fault, '%}
{% endif %}
{% if val_int | bitwise_and(4)%}
{% if val_int | bitwise_and(0x0008)%}
{% set result = result + 'OverTemp, '%}
{% endif %}
{% if val_int | bitwise_and(5)%}
{% if val_int | bitwise_and(0x0010)%}
{% set result = result + 'CommLose, '%}
{% endif %}
{% if val_int | bitwise_and(6)%}
{% if val_int | bitwise_and(0x0020)%}
{% set result = result + 'Bit6, '%}
{% endif %}
{% if val_int | bitwise_and(7)%}
{% if val_int | bitwise_and(0x0040)%}
{% set result = result + 'Bit7, '%}
{% endif %}
{% if val_int | bitwise_and(8)%}
{% if val_int | bitwise_and(0x0080)%}
{% set result = result + 'EEPROM-Fault, '%}
{% endif %}
{% if val_int | bitwise_and(9)%}
{% if val_int | bitwise_and(0x0100)%}
{% set result = result + 'NoUtility, '%}
{% endif %}
{% if val_int | bitwise_and(10)%}
{% if val_int | bitwise_and(0x0200)%}
{% set result = result + 'VG_Offset, '%}
{% endif %}
{% if val_int | bitwise_and(11)%}
{% if val_int | bitwise_and(0x0400)%}
{% set result = result + 'Relais_Open, '%}
{% endif %}
{% if val_int | bitwise_and(12)%}
{% if val_int | bitwise_and(0x0800)%}
{% set result = result + 'Relais_Short, '%}
{% endif %}
{% if val_int | bitwise_and(13)%}
{% if val_int | bitwise_and(0x1000)%}
{% set result = result + 'GridVoltOverRating, '%}
{% endif %}
{% if val_int | bitwise_and(14)%}
{% if val_int | bitwise_and(0x2000)%}
{% set result = result + 'GridVoltUnderRating, '%}
{% endif %}
{% if val_int | bitwise_and(15)%}
{% if val_int | bitwise_and(0x4000)%}
{% set result = result + 'GridFreqOverRating, '%}
{% endif %}
{% if val_int | bitwise_and(16)%}
{% if val_int | bitwise_and(0x8000)%}
{% set result = result + 'GridFreqUnderRating, '%}
{% endif %}
{% endif %}
@@ -461,42 +466,104 @@ class Infos:
{% set result = 'noFault'%}
{%else%}
{% set result = '' %}
{% if val_int | bitwise_and(1)%}
{% if val_int | bitwise_and(0x0001)%}
{% set result = result + 'PVOV-Fault (PV OverVolt), '%}
{% endif %}
{% if val_int | bitwise_and(2)%}
{% if val_int | bitwise_and(0x0002)%}
{% set result = result + 'PVLV-Fault (PV LowVolt), '%}
{% endif %}
{% if val_int | bitwise_and(3)%}
{% if val_int | bitwise_and(0x0004)%}
{% set result = result + 'PV OI-Fault (PV OverCurrent), '%}
{% endif %}
{% if val_int | bitwise_and(4)%}
{% if val_int | bitwise_and(0x0008)%}
{% set result = result + 'PV OFV-Fault, '%}
{% endif %}
{% if val_int | bitwise_and(5)%}
{% if val_int | bitwise_and(0x0010)%}
{% set result = result + 'DC ShortCircuitFault, '%}
{% endif %}
{% if val_int | bitwise_and(6)%}{% set result = result + 'Bit6, '%}
{% if val_int | bitwise_and(0x0020)%}{% set result = result + 'Bit6, '%}
{% endif %}
{% if val_int | bitwise_and(7)%}{% set result = result + 'Bit7, '%}
{% if val_int | bitwise_and(0x0040)%}{% set result = result + 'Bit7, '%}
{% endif %}
{% if val_int | bitwise_and(8)%}{% set result = result + 'Bit8, '%}
{% if val_int | bitwise_and(0x0080)%}{% set result = result + 'Bit8, '%}
{% endif %}
{% if val_int | bitwise_and(9)%}{% set result = result + 'Bit9, '%}
{% if val_int | bitwise_and(0x0100)%}{% set result = result + 'Bit9, '%}
{% endif %}
{% if val_int | bitwise_and(10)%}{% set result = result + 'Bit10, '%}
{% if val_int | bitwise_and(0x0200)%}{% set result = result + 'Bit10, '%}
{% endif %}
{% if val_int | bitwise_and(11)%}{% set result = result + 'Bit11, '%}
{% if val_int | bitwise_and(0x0400)%}{% set result = result + 'Bit11, '%}
{% endif %}
{% if val_int | bitwise_and(12)%}{% set result = result + 'Bit12, '%}
{% if val_int | bitwise_and(0x0800)%}{% set result = result + 'Bit12, '%}
{% endif %}
{% if val_int | bitwise_and(13)%}{% set result = result + 'Bit13, '%}
{% if val_int | bitwise_and(0x1000)%}{% set result = result + 'Bit13, '%}
{% endif %}
{% if val_int | bitwise_and(14)%}{% set result = result + 'Bit14, '%}
{% if val_int | bitwise_and(0x2000)%}{% set result = result + 'Bit14, '%}
{% endif %}
{% if val_int | bitwise_and(15)%}{% set result = result + 'Bit15, '%}
{% if val_int | bitwise_and(0x4000)%}{% set result = result + 'Bit15, '%}
{% endif %}
{% if val_int | bitwise_and(16)%}{% set result = result + 'Bit16, '%}
{% if val_int | bitwise_and(0x8000)%}{% set result = result + 'Bit16, '%}
{% endif %}
{% endif %}
{{ result }}
{% else %}
{{ this.state }}
{% endif %}
'''
__batt_alarm_val_tpl = '''
{% if 'Batterie_Alarm' in value_json and
value_json['Batterie_Alarm'] != None %}
{% set val_int = value_json['Batterie_Alarm'] | int %}
{% if val_int == 0 %}
{% set result = 'noAlarm'%}
{%else%}
{% set result = '' %}
{% if val_int | bitwise_and(0x0001)%}
{% set result = result + 'PV1-OverVoltage, '%}
{% endif %}
{% if val_int | bitwise_and(0x0002)%}
{% set result = result + 'PV2-OverVoltage, '%}
{% endif %}
{% if val_int | bitwise_and(0x0004)%}
{% set result = result + 'EquipmentOverheating, '%}
{% endif %}
{% if val_int | bitwise_and(0x0008)%}
{% set result = result + 'EquipmentLowTemp, '%}
{% endif %}
{% if val_int | bitwise_and(0x0010)%}
{% set result = result + 'BMS-CommFailed, '%}
{% endif %}
{% if val_int | bitwise_and(0x0020)%}
{% set result = result + 'UnderVoltageProt, '%}
{% endif %}
{% if val_int | bitwise_and(0x0040)%}
{% set result = result + 'ChargingHighTemp, '%}
{% endif %}
{% if val_int | bitwise_and(0x0080)%}
{% set result = result + 'ChargingLowTemp, '%}
{% endif %}
{% if val_int | bitwise_and(0x0100)%}
{% set result = result + 'DischargeHighTemp, '%}
{% endif %}
{% if val_int | bitwise_and(0x0200)%}
{% set result = result + 'DischargeLowTemp, '%}
{% endif %}
{% if val_int | bitwise_and(0x0400)%}
{% set result = result + 'BatterieOverVoltage, '%}
{% endif %}
{% if val_int | bitwise_and(0x0800)%}
{% set result = result + 'SingleCorePressureDifferenceIsTooLarge, '%}
{% endif %}
{% if val_int | bitwise_and(0x1000)%}
{% set result = result + 'Bit-12, '%}
{% endif %}
{% if val_int | bitwise_and(0x2000)%}
{% set result = result + 'Bit-13, '%}
{% endif %}
{% if val_int | bitwise_and(0x4000)%}
{% set result = result + 'Bit-14, '%}
{% endif %}
{% if val_int | bitwise_and(0x8000)%}
{% set result = result + 'Bit-15, '%}
{% endif %}
{% endif %}
{{ result }}
@@ -562,8 +629,8 @@ class Infos:
# 0xffffff03: {'name':['proxy', 'Voltage'], 'level': logging.DEBUG, 'unit': 'V', 'ha':{'dev':'proxy', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id':'proxy_volt_', 'fmt':FMT_FLOAT,'name': 'Grid Voltage'}}, # noqa: E501
# events
Register.EVENT_ALARM: {'name': ['events', 'Inverter_Alarm'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'inverter', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'inv_alarm_', 'name': 'Inverter Alarm', 'val_tpl': __inv_alarm_val_tpl, 'icon': 'mdi:alarm-light'}}, # noqa: E501
Register.EVENT_FAULT: {'name': ['events', 'Inverter_Fault'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'inverter', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'inv_fault_', 'name': 'Inverter Fault', 'val_tpl': __inv_fault_val_tpl, 'icon': 'mdi:alarm-light'}}, # noqa: E501
Register.EVENT_ALARM: {'name': ['events', 'Inverter_Alarm'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'inverter', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'inv_alarm_', 'name': 'Inverter Alarm', 'val_tpl': __inv_alarm_val_tpl, 'icon': ALARM_LIGHT}}, # noqa: E501
Register.EVENT_FAULT: {'name': ['events', 'Inverter_Fault'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'inverter', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'inv_fault_', 'name': 'Inverter Fault', 'val_tpl': __inv_fault_val_tpl, 'icon': ALARM_LIGHT}}, # noqa: E501
Register.EVENT_BF1: {'name': ['events', 'Inverter_Bitfield_1'], 'level': logging.INFO, 'unit': ''}, # noqa: E501
Register.EVENT_BF2: {'name': ['events', 'Inverter_bitfield_2'], 'level': logging.INFO, 'unit': ''}, # noqa: E501
# Register.EVENT_409: {'name': ['events', '409_No_Utility'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
@@ -637,10 +704,10 @@ class Infos:
Register.INV_UNKNOWN_1: {'name': ['inv_unknown', 'Unknown_1'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
# Batterie DC-1000: Electricity Genration
Register.BATT_STATUS_1: {'name': ['batterie', 'pv1', 'MPPT-Status'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'status1_', 'name': 'MPPT-1 Status', 'val_tpl': __mppt1_status_type_val_tpl, 'icon': POWER}}, # noqa: E501
Register.BATT_PV1_STATUS: {'name': ['batterie', 'pv1', 'MPPT-Status'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'status1_', 'name': 'MPPT-1 Status', 'val_tpl': __mppt1_status_type_val_tpl, 'icon': POWER}}, # noqa: E501
Register.BATT_PV1_VOLT: {'name': ['batterie', 'pv1', 'Voltage'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'bat_inp_pv1', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_pv1_', 'val_tpl': "{{ (value_json['pv1']['Voltage'] | float)}}", 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501
Register.BATT_PV1_CUR: {'name': ['batterie', 'pv1', 'Current'], 'level': logging.INFO, 'unit': 'A', 'ha': {'dev': 'bat_inp_pv1', 'dev_cla': 'current', 'stat_cla': 'measurement', 'id': 'cur_pv1_', 'val_tpl': "{{ (value_json['pv1']['Current'] | float)}}", 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501
Register.BATT_STATUS_2: {'name': ['batterie', 'pv2', 'MPPT-Status'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'status2_', 'name': 'MPPT-2 Status', 'val_tpl': __mppt2_status_type_val_tpl, 'icon': POWER}}, # noqa: E501
Register.BATT_PV2_STATUS: {'name': ['batterie', 'pv2', 'MPPT-Status'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'status2_', 'name': 'MPPT-2 Status', 'val_tpl': __mppt2_status_type_val_tpl, 'icon': POWER}}, # noqa: E501
Register.BATT_PV2_VOLT: {'name': ['batterie', 'pv2', 'Voltage'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'bat_inp_pv2', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_pv2_', 'val_tpl': "{{ (value_json['pv2']['Voltage'] | float)}}", 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501
Register.BATT_PV2_CUR: {'name': ['batterie', 'pv2', 'Current'], 'level': logging.INFO, 'unit': 'A', 'ha': {'dev': 'bat_inp_pv2', 'dev_cla': 'current', 'stat_cla': 'measurement', 'id': 'cur_pv2_', 'val_tpl': "{{ (value_json['pv2']['Current'] | float)}}", 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501
Register.BATT_PV_PWR: {'name': ['batterie', 'PV_Power'], 'level': logging.INFO, 'unit': 'W', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'pv_power_', 'fmt': FMT_INT, 'name': 'PV Power'}}, # noqa: E501
@@ -648,6 +715,7 @@ class Infos:
Register.BATT_OUT_VOLT: {'name': ['batterie', 'out', 'Voltage'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'out_volt_', 'val_tpl': "{{ (value_json['out']['Voltage'] | float)}}", 'name': 'Output Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501
Register.BATT_OUT_CUR: {'name': ['batterie', 'out', 'Current'], 'level': logging.INFO, 'unit': 'A', 'ha': {'dev': 'batterie', 'dev_cla': 'current', 'stat_cla': 'measurement', 'id': 'out_cur_', 'val_tpl': "{{ (value_json['out']['Current'] | float)}}", 'name': 'Output Current', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501
Register.BATT_OUT_PWR: {'name': ['batterie', 'out', 'Power'], 'level': logging.INFO, 'unit': 'W', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'out_power_', 'val_tpl': "{{ (value_json['out']['Power'] | int)}}", 'name': 'Supply Power'}}, # noqa: E501
Register.BATT_PWR_SUPL_STATE: {'name': ['batterie', 'out', 'Suppl_State'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'status_supply_', 'name': 'Supply State', 'val_tpl': __supply_status_type_val_tpl, 'icon': POWER}}, # noqa: E501
# Batterie DC-1000: Cell Package
Register.BATT_CELL1_VOLT: {'name': ['batterie', 'cell', 'Volt1'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell1_', 'val_tpl': "{{ (value_json['cell']['Volt1'] | float)}}", 'name': 'Cell-01 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501
@@ -673,12 +741,13 @@ class Infos:
Register.BATT_PWR: {'name': ['batterie', 'batt', 'Power'], 'level': logging.INFO, 'unit': 'W', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'power_', 'val_tpl': "{{ (value_json['batt']['Power'] | int)}}", 'name': 'Batterie Power'}}, # noqa: E501
Register.BATT_SOC: {'name': ['batterie', 'batt', 'SOC'], 'level': logging.INFO, 'unit': '%', 'ha': {'dev': 'batterie', 'dev_cla': None, 'stat_cla': 'measurement', 'id': 'soc_', 'val_tpl': "{{ (value_json['batt']['SOC'] | float)}}", 'name': 'State of Charge (SOC)', 'icon': 'mdi:battery-90'}}, # noqa: E501
Register.BATT_TOTAL_CHARG: {'name': ['batterie', 'batt', 'Total_Charging'], 'level': logging.INFO, 'unit': 'kWh', 'ha': {'dev': 'batterie', 'dev_cla': 'energy', 'stat_cla': 'total', 'id': 'total_charg_', 'val_tpl': "{{ (value_json['batt']['Total_Charging'] | float)}}", 'name': TOTAL_CHARG, 'icon': 'mdi:battery-charging', 'must_incr': True}}, # noqa: E501
Register.BATT_STATUS: {'name': ['batterie', 'batt', 'Batt_State'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'status_batt_', 'name': 'Batterie State', 'val_tpl': __batt_status_type_val_tpl, 'icon': POWER}}, # noqa: E501
Register.BATT_TEMP_1: {'name': ['batterie', 'cell', 'Temp_1'], 'level': logging.INFO, 'unit': '°C', 'ha': {'dev': 'batterie', 'dev_cla': 'temperature', 'stat_cla': 'measurement', 'id': 'temp_1_', 'val_tpl': "{{ (value_json['cell']['Temp_1'] | int)}}", 'name': 'Cell Temp-1', 'ent_cat': 'diagnostic'}}, # noqa: E501
Register.BATT_TEMP_2: {'name': ['batterie', 'cell', 'Temp_2'], 'level': logging.INFO, 'unit': '°C', 'ha': {'dev': 'batterie', 'dev_cla': 'temperature', 'stat_cla': 'measurement', 'id': 'temp_2_', 'val_tpl': "{{ (value_json['cell']['Temp_2'] | int)}}", 'name': 'Cell Temp-2', 'ent_cat': 'diagnostic'}}, # noqa: E501
Register.BATT_TEMP_3: {'name': ['batterie', 'cell', 'Temp_3'], 'level': logging.INFO, 'unit': '°C', 'ha': {'dev': 'batterie', 'dev_cla': 'temperature', 'stat_cla': 'measurement', 'id': 'temp_3_', 'val_tpl': "{{ (value_json['cell']['Temp_3'] | int)}}", 'name': 'Cell Temp-3', 'ent_cat': 'diagnostic'}}, # noqa: E501
Register.BATT_TEMP_4: {'name': ['batterie', 'Controller_Temp'], 'level': logging.INFO, 'unit': '°C', 'ha': {'dev': 'batterie', 'dev_cla': 'temperature', 'stat_cla': 'measurement', 'id': 'temp_4_', 'fmt': FMT_INT, 'name': 'Temperature'}}, # noqa: E501
Register.BATT_74: {'name': ['batterie', 'Reg_74'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'batt_74_', 'fmt': FMT_INT, 'ent_cat': 'diagnostic'}}, # noqa: E501
Register.BATT_ALARM: {'name': ['batterie', 'Batterie_Alarm'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'batt_alarm_', 'name': 'Batterie Alarm', 'val_tpl': __batt_alarm_val_tpl, 'icon': ALARM_LIGHT}}, # noqa: E501
Register.BATT_HW_VERS: {'name': ['batterie', 'Hardware_Version'], 'level': logging.INFO, 'unit': ''}, # noqa: E501
Register.BATT_SW_VERS: {'name': ['batterie', 'Software_Version'], 'level': logging.INFO, 'unit': ''}, # noqa: E501

View File

@@ -43,8 +43,8 @@ class Modbus():
0x000a: {'reg': Register.BATT_PV2_VOLT, 'fmt': '!H', 'ratio': 0.01}, # noqa: E501, PV2 voltage
0x000b: {'reg': Register.BATT_PV2_CUR, 'fmt': '!H', 'ratio': 0.01}, # noqa: E501, PV2 current
0x000c: {'reg': Register.BATT_TOTAL_CHARG, 'fmt': '!L', 'ratio': 0.01}, # noqa: E501
0x000e: {'reg': Register.BATT_STATUS_1, 'fmt': '!H'}, # noqa: E501
0x000f: {'reg': Register.BATT_STATUS_2, 'fmt': '!H'}, # noqa: E501
0x000e: {'reg': Register.BATT_PV1_STATUS, 'fmt': '!H'}, # noqa: E501
0x000f: {'reg': Register.BATT_PV2_STATUS, 'fmt': '!H'}, # noqa: E501
0x0010: {'reg': Register.BATT_VOLT, 'fmt': '!h', 'ratio': 0.01}, # noqa: E501
0x0011: {'reg': Register.BATT_CUR, 'fmt': '!h', 'ratio': 0.01}, # noqa: E501
0x0012: {'reg': Register.BATT_SOC, 'fmt': '!H', 'ratio': 0.01}, # noqa: E501, state of charge (SOC) in percent
@@ -71,7 +71,7 @@ class Modbus():
0x0027: {'reg': Register.BATT_OUT_CUR, 'fmt': '!H', 'ratio': 0.01}, # noqa: E501
0x0028: {'reg': Register.BATT_OUT_STATUS, 'fmt': '!H'}, # noqa: E501
0x0029: {'reg': Register.BATT_TEMP_4, 'fmt': '!h'}, # noqa: E501
0x002a: {'reg': Register.BATT_74, 'fmt': '!h'}, # noqa: E501
0x002a: {'reg': Register.BATT_ALARM, 'fmt': '!h'}, # noqa: E501
0x002b: {'reg': Register.BATT_HW_VERS, 'fmt': '!h'}, # noqa: E501
0x002c: {'reg': Register.BATT_SW_VERS, 'fmt': '!h'}, # noqa: E501

View File

@@ -221,7 +221,7 @@ def inv_data_seq2(): # Data indication from the controller
return msg
@pytest.fixture
def inv_data_seq3(): # Inverter indication from MS-2000
def inv_data_seq3(): # Inverter indication from MS-3000
msg = b'\x00\x00\x01\x2c\x00\x00\x00\x64\x53\x00\x00' # | ..^.....,...dS..
msg += b'\x00\x00\x00\xc8\x53\x44\x00\x00\x00\x01\x2c\x53\x00\x00\x00\x00' # | ....SD....,S....

View File

@@ -82,13 +82,25 @@ def batterie_data(): # 0x4210 ftype: 0x01
msg += b'\x00\x00\x00\x0f\x00\x00\x02\x05\x02\x01'
return msg
@pytest.fixture
def batterie_data1(): # 0x4210 ftype: 0x01
msg = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x26\x30\xc7\xde'
msg += b'\x2d\x32\x28\x00\x00\x00\x84\x17\x79\x35\x01\x00\x4c\x12\x00\x00'
msg += b'\x34\x31\x30\x31\x32\x34\x30\x37\x30\x31\x34\x39\x30\x33\x31\x34'
msg += b'\x0d\x3a\x00\x70\x0d\x2c\x00\x00\x00\x00\x08\x20\x00\x00\x00\x00'
msg += b'\x01\x00\x00\x00\x03\xe8\x0c\x89\x0c\x89\x0c\x89\x0c\x8a\x0c\x89'
msg += b'\x0c\x89\x0c\x8a\x0c\x89\x0c\x89\x0c\x8a\x0c\x8a\x0c\x89\x0c\x89'
msg += b'\x0c\x89\x0c\x89\x0c\x88\x00\x0f\x00\x0f\x00\x0f\x0c\x0e\x01\x00'
msg += b'\x00\x00\x00\x0f\x00\x00\x02\x05\x02\x01'
return msg
@pytest.fixture
def batterie_data2(): # 0x4210 ftype: 0x01
msg = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x26\x30\xc7\xde'
msg += b'\x2d\x32\x28\x00\x00\x00\x84\x17\x79\x35\x01\x00\x4c\x12\x00\x00'
msg += b'\x34\x31\x30\x31\x32\x34\x30\x37\x30\x31\x34\x39\x30\x33\x31\x34'
msg += b'\x0d\x3a\x00\x70\x0d\x2c\x00\x00\x00\x00\x08\x20\x00\x00\x00\x00'
msg += b'\x14\x0e\xff\xfe\x03\xe8\x0c\x89\x0c\x89\x0c\x89\x0c\x8a\x0c\x89'
msg += b'\x14\x0e\x02\xfe\x03\xe8\x0c\x89\x0c\x89\x0c\x89\x0c\x8a\x0c\x89'
msg += b'\x0c\x89\x0c\x8a\x0c\x89\x0c\x89\x0c\x8a\x0c\x8a\x0c\x89\x0c\x89'
msg += b'\x0c\x89\x0c\x89\x0c\x88\x00\x0f\x00\x0f\x00\x0f\x00\x0e'
return msg
@@ -157,10 +169,29 @@ def test_parse_4210_3026(batterie_data: bytes):
"inverter": {"Serial_Number": "4101240701490314"},
"batterie": {"pv1": {"Voltage": 33.86, "Current": 1.12, "MPPT-Status": 0},
"pv2": {"Voltage": 33.72, "Current": 0.0, "MPPT-Status": 0},
"batt": {"Total_Charging": 20.8, "Voltage": 51.34, "Current": -0.02, "SOC": 10.0, "Power": -1.0268000000000002},
"batt": {"Total_Charging": 20.8, "Voltage": 51.34, "Current": -0.02, "SOC": 10.0, "Power": -1.0268000000000002, 'Batt_State': 0},
"cell": {"Volt1": 3.21, "Volt2": 3.21, "Volt3": 3.21, "Volt4": 3.21, "Volt5": 3.21, "Volt6": 3.21, "Volt7": 3.21, "Volt8": 3.21, "Volt9": 3.21, "Volt10": 3.21, "Volt11": 3.21, "Volt12": 3.21, "Volt13": 3.21, "Volt14": 3.21, "Volt15": 3.21, "Volt16": 3.21, "Temp_1": 15, "Temp_2": 15, "Temp_3": 15},
"out": {"Voltage": 0.14, "Current": 0.0, "Out_Status": 0, "Power": 0.0},
"Controller_Temp": 15, "Reg_74": 0, "Hardware_Version": 517, "Software_Version": 513,
"out": {"Voltage": 0.14, "Current": 0.0, "Out_Status": 0, "Power": 0.0, "Suppl_State": 0},
"Controller_Temp": 15, "Batterie_Alarm": 0, "Hardware_Version": 517, "Software_Version": 513,
"PV_Power": 37.9232},
})
def test_parse_4210_3026_prod(batterie_data1: bytes):
i = InfosG3P(client_mode=False)
i.db.clear()
for key, update in i.parse (batterie_data1, 0x42, 1, 0x3026):
pass # side effect is calling generator i.parse()
assert json.dumps(i.db) == json.dumps({
"controller": {"Sensor_List": "3026", "Power_On_Time": 4684},
"inverter": {"Serial_Number": "4101240701490314"},
"batterie": {"pv1": {"Voltage": 33.86, "Current": 1.12, "MPPT-Status": 0},
"pv2": {"Voltage": 33.72, "Current": 0.0, "MPPT-Status": 0},
"batt": {"Total_Charging": 20.8, "Voltage": 2.56, "Current": 0.0, "SOC": 10.0, "Power": 0.0, 'Batt_State': 1},
"cell": {"Volt1": 3.21, "Volt2": 3.21, "Volt3": 3.21, "Volt4": 3.21, "Volt5": 3.21, "Volt6": 3.21, "Volt7": 3.21, "Volt8": 3.21, "Volt9": 3.21, "Volt10": 3.21, "Volt11": 3.21, "Volt12": 3.21, "Volt13": 3.21, "Volt14": 3.21, "Volt15": 3.21, "Volt16": 3.21, "Temp_1": 15, "Temp_2": 15, "Temp_3": 15},
"out": {"Voltage": 30.86, "Current": 2.56, "Out_Status": 0, "Power": 79.0016, "Suppl_State": 1},
"Controller_Temp": 15, "Batterie_Alarm": 0, "Hardware_Version": 517, "Software_Version": 513,
"PV_Power": 37.9232},
})
@@ -176,10 +207,10 @@ def test_parse_4210_3026_incomplete(batterie_data2: bytes):
"inverter": {"Serial_Number": "4101240701490314"},
"batterie": {"pv1": {"Voltage": 33.86, "Current": 1.12, "MPPT-Status": 0},
"pv2": {"Voltage": 33.72, "Current": 0.0, "MPPT-Status": 0},
"batt": {"Total_Charging": 20.8, "Voltage": 51.34, "Current": -0.02, "SOC": 10.0, "Power": -1.0268000000000002},
"batt": {"Total_Charging": 20.8, "Voltage": 51.34, "Current": 7.66, "SOC": 10.0, "Power": 393.2644, 'Batt_State': 2},
"cell": {"Volt1": 3.21, "Volt2": 3.21, "Volt3": 3.21, "Volt4": 3.21, "Volt5": 3.21, "Volt6": 3.21, "Volt7": 3.21, "Volt8": 3.21, "Volt9": 3.21, "Volt10": 3.21, "Volt11": 3.21, "Volt12": 3.21, "Volt13": 3.21, "Volt14": 3.21, "Volt15": 3.21, "Volt16": 3.21, "Temp_1": 15, "Temp_2": 15, "Temp_3": 15},
"out": {"Voltage": 0.14, "Current": None, "Out_Status": None, "Power": None},
"Controller_Temp": None, "Reg_74": None, "Hardware_Version": None, "Software_Version": None,
"out": {"Voltage": 0.14, "Current": None, "Out_Status": None, "Power": None, "Suppl_State": None},
"Controller_Temp": None, "Batterie_Alarm": None, "Hardware_Version": None, "Software_Version": None,
"PV_Power": 37.9232},
})

View File

@@ -47,7 +47,16 @@ dev debug: local_add_on
export IMAGE=$(PRIVAT_CONTAINER_REGISTRY)$(IMAGE) && \
docker buildx bake -f docker-bake.hcl $@
rc rel: local_add_on
rc: local_add_on
@[ "${RC}" ] || ( echo ">> RC is not set"; exit 1 )
@echo version: $(VERSION) build-date: $(BUILD_DATE) image: $(PUBLIC_CONTAINER_REGISTRY)$(IMAGE)
@echo login at $(PUBLIC_URL) as $(PUBLIC_USER)
@DO_LOGIN="$(shell echo $(PUBLIC_CR_KEY) | docker login $(PUBLIC_URL) -u $(PUBLIC_USER) --password-stdin)"
export VERSION=$(VERSION)-$@$(RC) && \
export IMAGE=$(PUBLIC_CONTAINER_REGISTRY)$(IMAGE) && \
docker buildx bake -f docker-bake.hcl $@
rel: local_add_on
@echo version: $(VERSION) build-date: $(BUILD_DATE) image: $(PUBLIC_CONTAINER_REGISTRY)$(IMAGE)
@echo login at $(PUBLIC_URL) as $(PUBLIC_USER)
@DO_LOGIN="$(shell echo $(PUBLIC_CR_KEY) | docker login $(PUBLIC_URL) -u $(PUBLIC_USER) --password-stdin)"

View File

@@ -50,7 +50,7 @@ Example add-on configuration after installation:
```yaml
inverters:
- serial: R17E760702080400
- serial: R17E000000000000
node_id: PV-Garage
suggested_area: Garage
modbus_polling: false
@@ -89,7 +89,7 @@ Example add-on configuration for GEN3PLUS energie storages:
```yaml
batteries:
- serial: 4100000000000000
monitor_sn: 2300000000
monitor_sn: 3000000000
node_id: bat_1
suggested_area: Garage
modbus_polling: false
@@ -174,4 +174,4 @@ SOFTWARE.
[AdGuard]: https://github.com/hassio-addons/addon-adguard-home
[repository-badge]: https://img.shields.io/badge/Add%20repository%20to%20my-Home%20Assistant-41BDF5?logo=home-assistant&style=for-the-badge
[repository-url]: https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2Fs-allius%2Fha-addons
[configdetails]: https://github.com/s-allius/tsun-gen3-proxy/wiki/Configuration-toml
[configdetails]: https://github.com/s-allius/tsun-gen3-proxy/wiki/Configuration-addon

View File

@@ -13,7 +13,7 @@
# 1 Build Base Image #
######################
ARG BUILD_FROM="ghcr.io/hassio-addons/base:17.2.3"
ARG BUILD_FROM="ghcr.io/hassio-addons/base:17.2.4"
# hadolint ignore=DL3006
FROM $BUILD_FROM AS base

View File

@@ -153,9 +153,9 @@ def dcu_data_ind_msg(): # 0x4210
msg += b'\x2d\x32\x28\x00\x00\x00\x84\x17\x79\x35\x01\x00\x4c\x12\x00\x00'
msg += get_dcu_no()
msg += b'\x0d\x3a\x00\x0a\x0d\x2c\x00\x00\x00\x00\x08\x20\x00\x00\x00\x00'
msg += b'\x14\x0e\xff\xfe\x03\xe8\x0c\x89\x0c\x89\x0c\x89\x0c\x8a\x0c\x89'
msg += b'\x14\x0e\x05\xfe\x03\xe8\x0c\x89\x0c\x89\x0c\x89\x0c\x8a\x0c\x89'
msg += b'\x0c\x89\x0c\x8a\x0c\x89\x0c\x89\x0c\x8a\x0c\x8a\x0c\x89\x0c\x89'
msg += b'\x0c\x89\x0c\x89\x0c\x88\x00\x0f\x00\x0f\x00\x0f\x00\x0e\x00\x00'
msg += b'\x0c\x89\x0c\x89\x0c\x88\x00\x0f\x00\x0f\x00\x0f\x00\x0e\x02\x00'
msg += b'\x00\x00\x00\x0f\x00\x00\x02\x05\x02\x01'
msg += correct_checksum(msg)
msg += b'\x15'