diff --git a/custom_components/solarman/__init__.py b/custom_components/solarman/__init__.py index 0cca8cf..0001232 100644 --- a/custom_components/solarman/__init__.py +++ b/custom_components/solarman/__init__.py @@ -85,6 +85,9 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bulk_delete(new_data, CONF_OLD_SERIAL) bulk_delete(new_options, CONF_OLD_SERIAL, "inverter_host", "inverter_port", CONF_BATTERY_NOMINAL_VOLTAGE, CONF_BATTERY_LIFE_CYCLE_RATING) + if not new_options.get(CONF_ADDITIONAL_OPTIONS): + del new_options[CONF_ADDITIONAL_OPTIONS] + hass.config_entries.async_update_entry(config_entry, options = new_options, minor_version = ConfigFlowHandler.MINOR_VERSION, version = ConfigFlowHandler.VERSION) _LOGGER.debug("Migration to configuration version %s.%s successful", config_entry.version, config_entry.minor_version) diff --git a/custom_components/solarman/common.py b/custom_components/solarman/common.py index d536f04..ccfa373 100644 --- a/custom_components/solarman/common.py +++ b/custom_components/solarman/common.py @@ -125,14 +125,20 @@ def is_ethernet_frame(frame): def format_exception(e): return re.sub(r"\s+", " ", f"{type(e).__name__}{f': {e}' if f'{e}' else ''}") -def process_descriptions(item, group, table, code): +def process_descriptions(item, group, table, code, mod): bulk_inherit(item, group, *(REQUEST_UPDATE_INTERVAL, REQUEST_CODE) if "registers" in item else REQUEST_UPDATE_INTERVAL) if not REQUEST_CODE in item and (r := item.get("registers")) is not None and (addr := min(r)) is not None: item[REQUEST_CODE] = table.get(addr, code) + if (c := item.get("scale")) is not None and isinstance(c, list): + item["scale"] = c[mod] if (sensors := item.get("sensors")) is not None: for s in sensors: + if (c := s.get("scale")) is not None and isinstance(c, list): + s["scale"] = c[mod] bulk_inherit(s, item, REQUEST_CODE, "scale") if (m := item.get("multiply")) is not None: + if (c := m.get("scale")) is not None and isinstance(c, list): + m["scale"] = c[mod] bulk_inherit(m, s, REQUEST_CODE, "scale") return item diff --git a/custom_components/solarman/config_flow.py b/custom_components/solarman/config_flow.py index b0846d1..2e27418 100644 --- a/custom_components/solarman/config_flow.py +++ b/custom_components/solarman/config_flow.py @@ -31,6 +31,7 @@ vol.Required(CONF_ADDITIONAL_OPTIONS): section( vol.Schema( { + vol.Optional(CONF_MOD, default = DEFAULT_TABLE[CONF_MOD], description = {"suggested_value": DEFAULT_TABLE[CONF_MOD]}): bool, vol.Optional(CONF_MPPT, default = DEFAULT_TABLE[CONF_MPPT], description = {"suggested_value": DEFAULT_TABLE[CONF_MPPT]}): vol.All(vol.Coerce(int), vol.Range(min = 1, max = 12)), vol.Optional(CONF_PHASE, default = DEFAULT_TABLE[CONF_PHASE], description = {"suggested_value": DEFAULT_TABLE[CONF_PHASE]}): vol.All(vol.Coerce(int), vol.Range(min = 1, max = 3)), vol.Optional(CONF_BATTERY_NOMINAL_VOLTAGE, default = DEFAULT_TABLE[CONF_BATTERY_NOMINAL_VOLTAGE], description = {"suggested_value": DEFAULT_TABLE[CONF_BATTERY_NOMINAL_VOLTAGE]}): cv.positive_int, @@ -87,7 +88,7 @@ def remove_defaults(user_input: dict[str, Any]): return user_input class ConfigFlowHandler(ConfigFlow, domain = DOMAIN): - MINOR_VERSION = 3 + MINOR_VERSION = 4 VERSION = 1 async def _async_set_and_abort_if_unique_id_configured(self, suffix: str): diff --git a/custom_components/solarman/const.py b/custom_components/solarman/const.py index 995cec1..46e8be1 100644 --- a/custom_components/solarman/const.py +++ b/custom_components/solarman/const.py @@ -28,6 +28,7 @@ CONF_ADDITIONAL_OPTIONS = "additional_options" CONF_MPPT = "mppt" CONF_PHASE = "phase" +CONF_MOD = "mod" CONF_BATTERY_NOMINAL_VOLTAGE = "battery_nominal_voltage" CONF_BATTERY_LIFE_CYCLE_RATING = "battery_life_cycle_rating" CONF_MB_SLAVE_ID = "mb_slave_id" @@ -49,6 +50,7 @@ CONF_LOOKUP_FILE: "Auto", CONF_MPPT: 4, CONF_PHASE: 3, + CONF_MOD: False, CONF_BATTERY_NOMINAL_VOLTAGE: 48, CONF_BATTERY_LIFE_CYCLE_RATING: 6000, UPDATE_INTERVAL: 60, diff --git a/custom_components/solarman/inverter_definitions/deye_hybrid.yaml b/custom_components/solarman/inverter_definitions/deye_hybrid.yaml index 957633d..885ce4f 100644 --- a/custom_components/solarman/inverter_definitions/deye_hybrid.yaml +++ b/custom_components/solarman/inverter_definitions/deye_hybrid.yaml @@ -658,7 +658,7 @@ parameters: state_class: "measurement" uom: "W" rule: 2 - scale: 10 # out of date docs + scale: [1, 10] # out of date docs registers: [0x00A7] icon: "mdi:transmission-tower" @@ -668,7 +668,7 @@ parameters: state_class: "measurement" uom: "W" rule: 2 - scale: 10 # out of date docs + scale: [1, 10] # out of date docs registers: [0x00A8] icon: "mdi:transmission-tower" @@ -678,7 +678,7 @@ parameters: state_class: "measurement" uom: "W" rule: 2 - scale: 10 # out of date docs + scale: [1, 10] # out of date docs registers: [0x00A9] icon: "mdi:transmission-tower" attributes: [inverse] @@ -709,7 +709,7 @@ parameters: state_class: "measurement" uom: "W" rule: 2 - scale: 10 # out of date docs + scale: [1, 10] # out of date docs registers: [0x00AA] icon: "mdi:transmission-tower" @@ -719,7 +719,7 @@ parameters: state_class: "measurement" uom: "W" rule: 2 - scale: 10 # out of date docs + scale: [1, 10] # out of date docs registers: [0x00AB] icon: "mdi:transmission-tower" @@ -729,7 +729,7 @@ parameters: state_class: "measurement" uom: "W" rule: 2 - scale: 10 # out of date docs + scale: [1, 10] # out of date docs registers: [0x00AC] icon: "mdi:transmission-tower" @@ -759,7 +759,7 @@ parameters: state_class: "measurement" uom: "W" rule: 2 - scale: 10 # out of date docs + scale: [1, 10] # out of date docs registers: [0x00B0] - name: "Load L2 Power" @@ -768,7 +768,7 @@ parameters: state_class: "measurement" uom: "W" rule: 2 - scale: 10 # out of date docs + scale: [1, 10] # out of date docs registers: [0x00B1] - name: "Load Power" @@ -777,7 +777,7 @@ parameters: state_class: "measurement" uom: "W" rule: 2 - scale: 10 # out of date docs + scale: [1, 10] # out of date docs registers: [0x00B2] - name: "Load L1 Current" diff --git a/custom_components/solarman/manifest.json b/custom_components/solarman/manifest.json index 6358dca..3f952a1 100644 --- a/custom_components/solarman/manifest.json +++ b/custom_components/solarman/manifest.json @@ -9,5 +9,5 @@ "iot_class": "local_polling", "issue_tracker": "https://github.com/davidrapan/ha-solarman/issues", "requirements": ["ipaddress", "pyyaml", "umodbus", "pysolarmanv5>=3.0.4"], - "version": "24.12.06_3" + "version": "24.12.06_4" } diff --git a/custom_components/solarman/parser.py b/custom_components/solarman/parser.py index 1db50b7..43d30d3 100644 --- a/custom_components/solarman/parser.py +++ b/custom_components/solarman/parser.py @@ -43,7 +43,7 @@ def __init__(self, profile, attr): table = {r: get_request_code(pr) for pr in profile["requests"] for r in range(pr[REQUEST_START], pr[REQUEST_END] + 1)} if "requests" in profile and not "requests_fine_control" in profile else {} - self._items = sorted([process_descriptions(item, group, table, self._code) for group in profile["parameters"] for item in group["items"] if len((a := item.keys() & attr.keys())) == 0 or ((k := next(iter(a))) and item[k] <= attr[k])], key = lambda x: (get_code(x, "read", self._code), max(x["registers"])) if "registers" in x else (-1, -1)) + self._items = sorted([process_descriptions(item, group, table, self._code, attr["mod"]) for group in profile["parameters"] for item in group["items"] if len((a := item.keys() & attr.keys())) == 0 or ((k := next(iter(a))) and item[k] <= attr[k])], key = lambda x: (get_code(x, "read", self._code), max(x["registers"])) if "registers" in x else (-1, -1)) if (items_codes := [get_code(i, "read", self._code) for i in self._items if "registers" in i]) and (is_single_code := all_same(items_codes)): self._is_single_code = is_single_code diff --git a/custom_components/solarman/provider.py b/custom_components/solarman/provider.py index 150f19e..ea48286 100644 --- a/custom_components/solarman/provider.py +++ b/custom_components/solarman/provider.py @@ -109,7 +109,11 @@ def auto(self) -> bool: @cached_property def attributes(self) -> str: #return {k: v for k, v in self.additional if k in XXX} - return {ATTR_MPPT: self._additional_options.get(CONF_MPPT, DEFAULT_TABLE[CONF_MPPT]), ATTR_PHASE: self._additional_options.get(CONF_PHASE, DEFAULT_TABLE[CONF_PHASE])} + return { + CONF_MOD: int(self._additional_options.get(CONF_MOD, DEFAULT_TABLE[CONF_MOD])), + ATTR_MPPT: self._additional_options.get(CONF_MPPT, DEFAULT_TABLE[CONF_MPPT]), + ATTR_PHASE: self._additional_options.get(CONF_PHASE, DEFAULT_TABLE[CONF_PHASE]) + } async def resolve(self, request: Callable[[], Awaitable[None]] | None = None): _LOGGER.debug(f"Device autodetection is {"enabled" if self.auto and request else f"disabled. Selected profile: {self.filename}"}")