diff --git a/custom_components/solarman/__init__.py b/custom_components/solarman/__init__.py index 0b185ac..1cab285 100644 --- a/custom_components/solarman/__init__.py +++ b/custom_components/solarman/__init__.py @@ -3,6 +3,7 @@ import socket import logging +from functools import partial from ipaddress import IPv4Address, AddressValueError from homeassistant.const import CONF_NAME @@ -56,10 +57,7 @@ async def async_setup_entry(hass: HomeAssistant, config: ConfigEntry) -> bool: raise vol.Invalid(f"Host {inverter_host} has serial number {s} but is configured with {serial}.") inverter = Inverter(inverter_host, serial, inverter_port, mb_slave_id) - - await inverter.load(name, inverter_mac, lookup_path, lookup_file) - - coordinator = InverterCoordinator(hass, inverter) + coordinator = InverterCoordinator(hass, inverter, partial(inverter.load, name, inverter_mac, lookup_path, lookup_file)) hass.data.setdefault(DOMAIN, {})[config.entry_id] = coordinator diff --git a/custom_components/solarman/api.py b/custom_components/solarman/api.py index 00e9342..49afbdd 100644 --- a/custom_components/solarman/api.py +++ b/custom_components/solarman/api.py @@ -119,7 +119,24 @@ def __init__(self, address, serial, port, mb_slave_id): async def load(self, name, mac, path, file): self.name = name - if (n := process_profile(file if file else "deye_hybrid.yaml")) and (p := await yaml_open(path + n)): + if file == DEFAULT_LOOKUP_FILE: + try: + if (r := await self.get(requests = [{ REQUEST_CODE: 0x03, REQUEST_START: 0x00, REQUEST_END: 0x16 }])) and (d := get_addr_value(r, 0x03, 0)): + match d: + case 0x0002 | 0x0200: + file = "deye_string.yaml" + case 0x0003 | 0x0300: + file = "deye_hybrid.yaml" + case 0x0004 | 0x0400: + file = "deye_4mppt.yaml" if (get_addr_value(r, 0x03, 18) & 0x0F00) // 256 > 2 else "deye_2mppt.yaml" + case 0x0005 | 0x0500: + file = "deye_sg04lp3.yaml" + case 0x0006 | 0x0007 | 0x0600 | 0x0008 | 0x0601: + file = "deye_sg01hp3.yaml" + except BaseException as e: + _LOGGER.error(f"Device autodetection failed. [{format_exception(e)}]") + + if (n := process_profile(file if file and file != DEFAULT_LOOKUP_FILE else "deye_hybrid.yaml")) and (p := await yaml_open(path + n)): self.device_info = build_device_info(self.serial, mac, name, p["info"] if "info" in p else None, n) self.profile = ParameterParser(p) @@ -185,8 +202,8 @@ async def get_failed(self): return self.state == -1 - async def get(self, runtime = 0): - requests = self.profile.schedule_requests(runtime) + async def get(self, runtime = 0, requests = None): + requests = self.profile.schedule_requests(runtime) if not requests else requests requests_count = len(requests) if requests else 0 responses = {} result = {} @@ -227,7 +244,7 @@ async def get(self, runtime = 0): await asyncio.sleep((ACTION_ATTEMPTS - attempts_left) * TIMINGS_WAIT_SLEEP) - result = self.profile.process(responses) + result = self.profile.process(responses) if self.profile else responses if (rc := len(result) if result else 0) > 0 and (now := datetime.now()): _LOGGER.debug(f"[{self.serial}] Returning {rc} new values to the Coordinator. [Previous State: {self.get_connection_state()} ({self.state})]") diff --git a/custom_components/solarman/config_flow.py b/custom_components/solarman/config_flow.py index 3a984b8..d1e2b78 100644 --- a/custom_components/solarman/config_flow.py +++ b/custom_components/solarman/config_flow.py @@ -32,7 +32,7 @@ def step_user_data_prefill(ip, serial): return { CONF_NAME: DEFAULT_NAME, CONF_INVERTER_HOST: ip, CONF_SERIAL: serial, CONF_INVERTER_PORT: DEFAULT_INVERTER_PORT, CONF_MB_SLAVE_ID: DEFAULT_MB_SLAVE_ID, CONF_LOOKUP_FILE: DEFAULT_LOOKUP_FILE, CONF_BATTERY_NOMINAL_VOLTAGE: DEFAULT_BATTERY_NOMINAL_VOLTAGE, CONF_BATTERY_LIFE_CYCLE_RATING: DEFAULT_BATTERY_LIFE_CYCLE_RATING } async def step_user_data_schema(hass: HomeAssistant, data: dict[str, Any] = { CONF_NAME: DEFAULT_NAME, CONF_INVERTER_PORT: DEFAULT_INVERTER_PORT, CONF_MB_SLAVE_ID: DEFAULT_MB_SLAVE_ID, CONF_LOOKUP_FILE: DEFAULT_LOOKUP_FILE, CONF_BATTERY_NOMINAL_VOLTAGE: DEFAULT_BATTERY_NOMINAL_VOLTAGE, CONF_BATTERY_LIFE_CYCLE_RATING: DEFAULT_BATTERY_LIFE_CYCLE_RATING }) -> vol.Schema: - lookup_files = await async_listdir(hass.config.path(LOOKUP_DIRECTORY_PATH)) + await async_listdir(hass.config.path(LOOKUP_CUSTOM_DIRECTORY_PATH), "custom/") + lookup_files = [DEFAULT_LOOKUP_FILE] + await async_listdir(hass.config.path(LOOKUP_DIRECTORY_PATH)) + await async_listdir(hass.config.path(LOOKUP_CUSTOM_DIRECTORY_PATH), "custom/") _LOGGER.debug(f"step_user_data_schema: data: {data}, {LOOKUP_DIRECTORY_PATH}: {lookup_files}") #data_schema = vol.Schema({ vol.Required(CONF_NAME, default = data.get(CONF_NAME)): str }, extra = vol.PREVENT_EXTRA) if wname else vol.Schema({}, extra = vol.PREVENT_EXTRA) #data_schema = data_schema.extend( diff --git a/custom_components/solarman/const.py b/custom_components/solarman/const.py index c870cf4..c05453c 100644 --- a/custom_components/solarman/const.py +++ b/custom_components/solarman/const.py @@ -33,7 +33,7 @@ DEFAULT_NAME = "Inverter" DEFAULT_INVERTER_PORT = 8899 DEFAULT_MB_SLAVE_ID = 1 -DEFAULT_LOOKUP_FILE = "deye_hybrid.yaml" +DEFAULT_LOOKUP_FILE = "Auto" DEFAULT_BATTERY_NOMINAL_VOLTAGE = 48 DEFAULT_BATTERY_LIFE_CYCLE_RATING = 6000 diff --git a/custom_components/solarman/coordinator.py b/custom_components/solarman/coordinator.py index 21b3cee..2fe27bb 100644 --- a/custom_components/solarman/coordinator.py +++ b/custom_components/solarman/coordinator.py @@ -3,17 +3,19 @@ import logging from typing import Any +from collections.abc import Awaitable, Callable from homeassistant.core import HomeAssistant from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from .const import * +from .api import Inverter _LOGGER = logging.getLogger(__name__) class InverterCoordinator(DataUpdateCoordinator[dict[str, Any]]): - def __init__(self, hass: HomeAssistant, inverter): - super().__init__(hass, _LOGGER, name = inverter.name, update_interval = TIMINGS_UPDATE_INTERVAL, always_update = False) + def __init__(self, hass: HomeAssistant, inverter: Inverter, setup_method: Callable[[], Awaitable[None]] | None = None): + super().__init__(hass, _LOGGER, name = inverter.name, setup_method = setup_method, update_interval = TIMINGS_UPDATE_INTERVAL, always_update = False) self.inverter = inverter self._counter = 0