implent register mapping
This commit is contained in:
@@ -1,16 +1,92 @@
|
||||
|
||||
import struct
|
||||
import json
|
||||
import logging
|
||||
from typing import Generator
|
||||
|
||||
if __name__ == "app.src.gen3.infos_g3":
|
||||
from app.src.infos import Infos
|
||||
from app.src.infos import Infos, Register
|
||||
else: # pragma: no cover
|
||||
from infos import Infos
|
||||
from infos import Infos, Register
|
||||
|
||||
|
||||
class RegisterMap:
|
||||
map = {
|
||||
0x00092ba8: Register.COLLECTOR_FW_VERSION,
|
||||
0x000927c0: Register.CHIP_TYPE,
|
||||
0x00092f90: Register.CHIP_MODEL,
|
||||
0x00095a88: Register.TRACE_URL,
|
||||
0x00095aec: Register.LOGGER_URL,
|
||||
0x0000000a: Register.PRODUCT_NAME,
|
||||
0x00000014: Register.MANUFACTURER,
|
||||
0x0000001e: Register.VERSION,
|
||||
0x00000028: Register.SERIAL_NUMBER,
|
||||
0x00000032: Register.EQUIPMENT_MODEL,
|
||||
0x00013880: Register.NO_INPUTS,
|
||||
0xffffff00: Register.INVERTER_CNT,
|
||||
0xffffff01: Register.UNKNOWN_SNR,
|
||||
0xffffff02: Register.UNKNOWN_MSG,
|
||||
0xffffff03: Register.INVALID_DATA_TYPE,
|
||||
0xffffff04: Register.INTERNAL_ERROR,
|
||||
0xffffff05: Register.UNKNOWN_CTRL,
|
||||
0xffffff06: Register.OTA_START_MSG,
|
||||
0xffffff07: Register.SW_EXCEPTION,
|
||||
0xfffffffe: Register.TEST_REG1,
|
||||
0xffffffff: Register.TEST_REG2,
|
||||
0x00000640: Register.OUTPUT_POWER,
|
||||
0x000005dc: Register.RATED_POWER,
|
||||
0x00000514: Register.INVERTER_TEMP,
|
||||
0x000006a4: Register.PV1_VOLTAGE,
|
||||
0x00000708: Register.PV1_CURRENT,
|
||||
0x0000076c: Register.PV1_POWER,
|
||||
0x000007d0: Register.PV2_VOLTAGE,
|
||||
0x00000834: Register.PV2_CURRENT,
|
||||
0x00000898: Register.PV2_POWER,
|
||||
0x000008fc: Register.PV3_VOLTAGE,
|
||||
0x00000960: Register.PV3_CURRENT,
|
||||
0x000009c4: Register.PV3_POWER,
|
||||
0x00000a28: Register.PV4_VOLTAGE,
|
||||
0x00000a8c: Register.PV4_CURRENT,
|
||||
0x00000af0: Register.PV4_POWER,
|
||||
0x00000c1c: Register.PV1_DAILY_GENERATION,
|
||||
0x00000c80: Register.PV1_TOTAL_GENERATION,
|
||||
0x00000ce4: Register.PV2_DAILY_GENERATION,
|
||||
0x00000d48: Register.PV2_TOTAL_GENERATION,
|
||||
0x00000dac: Register.PV3_DAILY_GENERATION,
|
||||
0x00000e10: Register.PV3_TOTAL_GENERATION,
|
||||
0x00000e74: Register.PV4_DAILY_GENERATION,
|
||||
0x00000ed8: Register.PV4_TOTAL_GENERATION,
|
||||
0x00000b54: Register.DAILY_GENERATION,
|
||||
0x00000bb8: Register.TOTAL_GENERATION,
|
||||
0x000003e8: Register.GRID_VOLTAGE,
|
||||
0x0000044c: Register.GRID_CURRENT,
|
||||
0x000004b0: Register.GRID_FREQUENCY,
|
||||
0x000cfc38: Register.CONNECT_COUNT,
|
||||
0x000c3500: Register.SIGNAL_STRENGTH,
|
||||
0x000c96a8: Register.POWER_ON_TIME,
|
||||
0x000d0020: Register.COLLECT_INTERVAL,
|
||||
0x000cf850: Register.DATA_UP_INTERVAL,
|
||||
0x000c7f38: Register.COMMUNICATION_TYPE,
|
||||
0x00000191: Register.EVENT_401,
|
||||
0x00000192: Register.EVENT_402,
|
||||
0x00000193: Register.EVENT_403,
|
||||
0x00000194: Register.EVENT_404,
|
||||
0x00000195: Register.EVENT_405,
|
||||
0x00000196: Register.EVENT_406,
|
||||
0x00000197: Register.EVENT_407,
|
||||
0x00000198: Register.EVENT_408,
|
||||
0x00000199: Register.EVENT_409,
|
||||
0x0000019a: Register.EVENT_410,
|
||||
0x0000019b: Register.EVENT_411,
|
||||
0x0000019c: Register.EVENT_412,
|
||||
0x0000019d: Register.EVENT_413,
|
||||
0x0000019e: Register.EVENT_414,
|
||||
0x0000019f: Register.EVENT_415,
|
||||
0x000001a0: Register.EVENT_416,
|
||||
}
|
||||
|
||||
|
||||
class InfosG3(Infos):
|
||||
|
||||
def ha_confs(self, ha_prfx, node_id, snr, singleton: bool, sug_area='') \
|
||||
-> Generator[tuple[dict, str], None, None]:
|
||||
'''Generator function yields a json register struct for home-assistant
|
||||
@@ -21,119 +97,14 @@ class InfosG3(Infos):
|
||||
snr:str ==> serial number of the inverter, used to build unique
|
||||
entity strings
|
||||
sug_area:str ==> suggested area string from the config file'''
|
||||
tab = self.info_defs
|
||||
for key in tab:
|
||||
row = tab[key]
|
||||
if 'singleton' in row:
|
||||
if singleton != row['singleton']:
|
||||
continue
|
||||
elif singleton:
|
||||
# iterate over RegisterMap.map and get the register values
|
||||
for key, reg in RegisterMap.map.items():
|
||||
if reg not in self.info_defs:
|
||||
continue
|
||||
prfx = ha_prfx + node_id
|
||||
|
||||
# check if we have details for home assistant
|
||||
if 'ha' in row:
|
||||
ha = row['ha']
|
||||
if 'comp' in ha:
|
||||
component = ha['comp']
|
||||
else:
|
||||
component = 'sensor'
|
||||
attr = {}
|
||||
if 'name' in ha:
|
||||
attr['name'] = ha['name']
|
||||
else:
|
||||
attr['name'] = row['name'][-1]
|
||||
|
||||
attr['stat_t'] = prfx + row['name'][0]
|
||||
attr['dev_cla'] = ha['dev_cla']
|
||||
attr['stat_cla'] = ha['stat_cla']
|
||||
attr['uniq_id'] = ha['id']+snr
|
||||
if 'val_tpl' in ha:
|
||||
attr['val_tpl'] = ha['val_tpl']
|
||||
elif 'fmt' in ha:
|
||||
attr['val_tpl'] = '{{value_json' + f"['{row['name'][-1]}'] {ha['fmt']}" + '}}' # eg. 'val_tpl': "{{ value_json['Output_Power']|float }} # noqa: E501
|
||||
else:
|
||||
self.inc_counter('Internal_Error')
|
||||
logging.error(f"Infos._info_defs: the row for {key} do"
|
||||
" not have a 'val_tpl' nor a 'fmt' value")
|
||||
|
||||
# add unit_of_meas only, if status_class isn't none. If
|
||||
# status_cla is None we want a number format and not line
|
||||
# graph in home assistant. A unit will change the number
|
||||
# format to a line graph
|
||||
if 'unit' in row and attr['stat_cla'] is not None:
|
||||
attr['unit_of_meas'] = row['unit'] # 'unit_of_meas'
|
||||
if 'icon' in ha:
|
||||
attr['ic'] = ha['icon'] # icon for the entity
|
||||
if 'nat_prc' in ha:
|
||||
attr['sug_dsp_prc'] = ha['nat_prc'] # precison of floats
|
||||
if 'ent_cat' in ha:
|
||||
attr['ent_cat'] = ha['ent_cat'] # diagnostic, config
|
||||
|
||||
# enabled_by_default is deactivated, since it avoid the via
|
||||
# setup of the devices. It seems, that there is a bug in home
|
||||
# assistant. tested with 'Home Assistant 2023.10.4'
|
||||
# if 'en' in ha: # enabled_by_default
|
||||
# attr['en'] = ha['en']
|
||||
|
||||
if 'dev' in ha:
|
||||
device = self.info_devs[ha['dev']]
|
||||
|
||||
if 'dep' in device and self.ignore_this_device(device['dep']): # noqa: E501
|
||||
continue
|
||||
|
||||
dev = {}
|
||||
|
||||
# the same name for 'name' and 'suggested area', so we get
|
||||
# dedicated devices in home assistant with short value
|
||||
# name and headline
|
||||
if (sug_area == '' or
|
||||
('singleton' in device and device['singleton'])):
|
||||
dev['name'] = device['name']
|
||||
dev['sa'] = device['name']
|
||||
else:
|
||||
dev['name'] = device['name']+' - '+sug_area
|
||||
dev['sa'] = device['name']+' - '+sug_area
|
||||
|
||||
if 'via' in device: # add the link to the parent device
|
||||
via = device['via']
|
||||
if via in self.info_devs:
|
||||
via_dev = self.info_devs[via]
|
||||
if 'singleton' in via_dev and via_dev['singleton']:
|
||||
dev['via_device'] = via
|
||||
else:
|
||||
dev['via_device'] = f"{via}_{snr}"
|
||||
else:
|
||||
self.inc_counter('Internal_Error')
|
||||
logging.error(f"Infos._info_defs: the row for "
|
||||
f"{key} has an invalid via value: "
|
||||
f"{via}")
|
||||
|
||||
for key in ('mdl', 'mf', 'sw', 'hw'): # add optional
|
||||
# values fpr 'modell', 'manufacturer', 'sw version' and
|
||||
# 'hw version'
|
||||
if key in device:
|
||||
data = self.dev_value(device[key])
|
||||
if data is not None:
|
||||
dev[key] = data
|
||||
|
||||
if 'singleton' in device and device['singleton']:
|
||||
dev['ids'] = [f"{ha['dev']}"]
|
||||
else:
|
||||
dev['ids'] = [f"{ha['dev']}_{snr}"]
|
||||
|
||||
attr['dev'] = dev
|
||||
|
||||
origin = {}
|
||||
origin['name'] = self.app_name
|
||||
origin['sw'] = self.version
|
||||
attr['o'] = origin
|
||||
else:
|
||||
self.inc_counter('Internal_Error')
|
||||
logging.error(f"Infos._info_defs: the row for {key} "
|
||||
"missing 'dev' value for ha register")
|
||||
|
||||
yield json.dumps(attr), component, node_id, attr['uniq_id']
|
||||
row = self.info_defs[reg]
|
||||
res = self.ha_conf(row, reg, ha_prfx, node_id, snr, singleton, sug_area) # noqa: E501
|
||||
if res:
|
||||
yield res
|
||||
|
||||
def parse(self, buf, ind=0) -> Generator[tuple[str, bool], None, None]:
|
||||
'''parse a data sequence received from the inverter and
|
||||
@@ -146,7 +117,11 @@ class InfosG3(Infos):
|
||||
ind += 4
|
||||
while i < elms:
|
||||
result = struct.unpack_from('!lB', buf, ind)
|
||||
info_id = result[0]
|
||||
addr = result[0]
|
||||
if addr not in RegisterMap.map:
|
||||
info_id = -1
|
||||
else:
|
||||
info_id = RegisterMap.map[addr]
|
||||
data_type = result[1]
|
||||
ind += 5
|
||||
keys, level, unit, must_incr, new_val = self._key_obj(info_id)
|
||||
|
||||
Reference in New Issue
Block a user