Skip to content

Commit

Permalink
feat: Autodetection for devices compatible w/ Deye
Browse files Browse the repository at this point in the history
  • Loading branch information
davidrapan committed Dec 1, 2024
1 parent f386822 commit 24f7c81
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 12 deletions.
6 changes: 2 additions & 4 deletions custom_components/solarman/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import socket
import logging

from functools import partial
from ipaddress import IPv4Address, AddressValueError

from homeassistant.const import CONF_NAME
Expand Down Expand Up @@ -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

Expand Down
25 changes: 21 additions & 4 deletions custom_components/solarman/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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 = {}
Expand Down Expand Up @@ -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})]")
Expand Down
2 changes: 1 addition & 1 deletion custom_components/solarman/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion custom_components/solarman/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 4 additions & 2 deletions custom_components/solarman/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down

0 comments on commit 24f7c81

Please sign in to comment.