Skip to content

Commit

Permalink
Merge pull request #50 from PatrikTrestik/feature/newEVCtype_42
Browse files Browse the repository at this point in the history
Feature/new ev ctype 42
  • Loading branch information
PatrikTrestik authored Dec 19, 2024
2 parents e3fb26b + 9f08347 commit ed13cf1
Show file tree
Hide file tree
Showing 16 changed files with 1,585 additions and 1,150 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ Please write only issues specific to Http API here.
All other stuff is common and should be discussed in https://github.com/wills106/homeassistant-solax-modbus

# Supported devices
Only SolaX EV Charger X3 is supported.
G1 SolaX EV Charger X3 is supported.
G2 SolaX Smart EV Charger support is under development and testing.
Let me know if you have other device and you are interested in integrating it. Physical device is required as there is no documentation available.

# Installation
Expand Down
20 changes: 9 additions & 11 deletions custom_components/solax_http/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@

import logging

from . import plugin_solax_ev_charger
from .coordinator import SolaxHttpUpdateCoordinator
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_SCAN_INTERVAL
from homeassistant.core import HomeAssistant

from .const import (
DOMAIN,
)
from .const import CONF_SN, DOMAIN
from .coordinator import SolaxHttpUpdateCoordinator
from .plugin_factory import PluginFactory

PLATFORMS = ["button", "time", "number", "select", "sensor"]
PLATFORMS = ["button", "number", "select", "sensor", "time"]


_LOGGER = logging.getLogger(__name__)
Expand All @@ -27,13 +25,13 @@ async def async_setup(hass, config):

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Set up a SolaX Http."""
_LOGGER.debug(f"setup entries - data: {entry.data}, options: {entry.options}")
_LOGGER.debug("setup entries - data: %s, options: %s", entry.data, entry.options)
config = entry.options
name = config[CONF_NAME]

_LOGGER.debug(f"Setup {DOMAIN}.{name}")
_LOGGER.debug("Setup %s.%s", DOMAIN, name)

plugin = plugin_solax_ev_charger.get_plugin_instance()
plugin = await PluginFactory.get_plugin_instance(config[CONF_HOST], config[CONF_SN])
coordinator = SolaxHttpUpdateCoordinator(hass, entry, plugin)
await coordinator.async_config_entry_first_refresh()

Expand Down Expand Up @@ -61,4 +59,4 @@ async def async_unload_entry(hass: HomeAssistant, entry):

async def config_entry_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Update listener, called when the config entry options are changed."""
await hass.config_entries.async_reload(entry.entry_id)
await hass.config_entries.async_reload(entry.entry_id)
45 changes: 23 additions & 22 deletions custom_components/solax_http/button.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
from homeassistant.helpers.update_coordinator import CoordinatorEntity
import logging
from typing import Optional

from homeassistant.components.button import ButtonEntity
from homeassistant.const import CONF_NAME
from homeassistant.core import HomeAssistant, callback
from .const import ATTR_MANUFACTURER, DOMAIN
from .const import BaseHttpButtonEntityDescription
from .const import plugin_base
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import ATTR_MANUFACTURER, DOMAIN, BaseHttpButtonEntityDescription
from .coordinator import SolaxHttpUpdateCoordinator
from typing import Any, Dict, Optional
import logging
from .plugin_base import plugin_base

_LOGGER = logging.getLogger(__name__)

async def async_setup_entry(hass:HomeAssistant, entry, async_add_entities) -> None:

async def async_setup_entry(hass: HomeAssistant, entry, async_add_entities) -> None:
name = entry.options[CONF_NAME]
coordinator: SolaxHttpUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
plugin:plugin_base=coordinator.plugin
plugin: plugin_base = coordinator.plugin

device_info = {
"identifiers": {(DOMAIN, name)},
Expand All @@ -24,29 +26,26 @@ async def async_setup_entry(hass:HomeAssistant, entry, async_add_entities) -> No

entities = []
for button_info in plugin.BUTTON_TYPES:
if plugin.matchWithMask(button_info.allowedtypes,button_info.blacklist):
button = SolaXHttpButton(
coordinator,
name,
device_info,
button_info
)
if plugin.matchWithMask(button_info.allowedtypes, button_info.blacklist):
button = SolaXHttpButton(coordinator, name, device_info, button_info)

entities.append(button)

async_add_entities(entities)
return True


class SolaXHttpButton(CoordinatorEntity, ButtonEntity):
"""Representation of an SolaX Http button."""

coordinator: SolaxHttpUpdateCoordinator

def __init__(
self,
coordinator:SolaxHttpUpdateCoordinator,
platform_name,
device_info,
description: BaseHttpButtonEntityDescription,
self,
coordinator: SolaxHttpUpdateCoordinator,
platform_name,
device_info,
description: BaseHttpButtonEntityDescription,
) -> None:
"""Initialize the selector."""
super().__init__(coordinator, context=description)
Expand Down Expand Up @@ -78,5 +77,7 @@ def unique_id(self) -> Optional[str]:

async def async_press(self) -> None:
"""Write the button value."""
success=await self.coordinator.write_register(self.entity_description.register, 1)
# await self.coordinator.async_request_refresh()
success = await self.coordinator.write_register(
self.entity_description.register, 1
)
# await self.coordinator.async_request_refresh()
104 changes: 41 additions & 63 deletions custom_components/solax_http/const.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
"""Module contains constants and base classes for the SolaX HTTP integration."""

from dataclasses import dataclass

from homeassistant.components.button import ButtonEntityDescription
from homeassistant.components.switch import SwitchEntityDescription
from homeassistant.components.number import NumberEntityDescription
from homeassistant.components.select import SelectEntityDescription
from homeassistant.components.sensor import SensorEntityDescription
from homeassistant.components.switch import SwitchEntityDescription
from homeassistant.components.time import TimeEntityDescription
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.components.number import (
NumberDeviceClass,
NumberEntityDescription,
)
from dataclasses import dataclass

DOMAIN = "solax_http"

Expand All @@ -30,73 +26,54 @@
S32 = "_int32"


# ==================================== plugin base class ====================================================================

@dataclass
class plugin_base:
plugin_name: str
TIME_TYPES: list[TimeEntityDescription]
SENSOR_TYPES: list[SensorEntityDescription]
BUTTON_TYPES: list[ButtonEntityDescription]
NUMBER_TYPES: list[NumberEntityDescription]
SELECT_TYPES: list[SelectEntityDescription]

invertertype = None

async def initialize(self)->None:
pass

def map_data(self, descr, data)->any:
return None

def map_payload(self, address, payload):
return None

def matchWithMask(self, entitymask, blacklist = None):
return False

# =================================== base class for sensor entity descriptions =========================================

@dataclass
class BaseHttpSensorEntityDescription(SensorEntityDescription):
""" base class for modbus sensor declarations """
allowedtypes: int = 0 # overload with ALLDEFAULT from plugin
scale: float = 1 # can be float, dictionary or callable function(initval, descr, datadict)
read_scale_exceptions: list = None # additional scaling when reading from modbus
"""Base class for modbus sensor declarations."""

allowedtypes: int = 0 # overload with ALLDEFAULT from plugin
scale: float = (
1 # can be float, dictionary or callable function(initval, descr, datadict)
)
read_scale_exceptions: list = None # additional scaling when reading from modbus
blacklist: list = None
unit: int = None # e.g. U16
register: int = -1 # initialize with invalid register
unit: int = None # e.g. U16
register: int = -1 # initialize with invalid register
rounding: int = 1
value_function: callable = None # value = function(initval, descr, datadict)
value_function: callable = None # value = function(initval, descr, datadict)


@dataclass
class BaseHttpButtonEntityDescription(ButtonEntityDescription):
allowedtypes: int = 0 # overload with ALLDEFAULT from plugin
allowedtypes: int = 0 # overload with ALLDEFAULT from plugin
register: int = None
command: int = None
blacklist: list = None # none or list of serial number prefixes
value_function: callable = None # value = function(initval, descr, datadict)
blacklist: list = None # none or list of serial number prefixes
value_function: callable = None # value = function(initval, descr, datadict)


@dataclass
class BaseHttpSwitchEntityDescription(SwitchEntityDescription):
allowedtypes: int = 0 # overload with ALLDEFAULT from plugin
allowedtypes: int = 0 # overload with ALLDEFAULT from plugin
register: int = None
blacklist: list = None # none or list of serial number prefixes
blacklist: list = None # none or list of serial number prefixes


@dataclass
class BaseHttpSelectEntityDescription(SelectEntityDescription):
allowedtypes: int = 0 # overload with ALLDEFAULT from plugin
allowedtypes: int = 0 # overload with ALLDEFAULT from plugin
register: int = None
scale: dict = None
unit: int = None
rounding: int = 1
reverse_option_dict: dict = None # autocomputed
blacklist: list = None # none or list of serial number prefixes
initvalue: int = None # initial default value for WRITE_DATA_LOCAL entities
reverse_option_dict: dict = None # autocomputed
blacklist: list = None # none or list of serial number prefixes
initvalue: int = None # initial default value for WRITE_DATA_LOCAL entities


@dataclass
class BaseHttpNumberEntityDescription(NumberEntityDescription):
allowedtypes: int = 0 # overload with ALLDEFAULT from plugin
allowedtypes: int = 0 # overload with ALLDEFAULT from plugin
register: int = None
read_scale_exceptions: list = None
read_scale: float = 1
Expand All @@ -105,16 +82,17 @@ class BaseHttpNumberEntityDescription(NumberEntityDescription):
scale: float = 1
rounding: int = 1
state: str = None
max_exceptions: list = None # None or list with structue [ ('U50EC' , 40,) ]
min_exceptions_minus: list = None # same structure as max_exceptions, values are applied with a minus
blacklist: list = None # None or list of serial number prefixes like
initvalue: int = None # initial default value for WRITE_DATA_LOCAL entities
prevent_update: bool = False # if set to True, value will not be re-read/updated with each polling cycle; only when read value changes
max_exceptions: list = None # None or list with structue [ ('U50EC' , 40,) ]
min_exceptions_minus: list = (
None # same structure as max_exceptions, values are applied with a minus
)
blacklist: list = None # None or list of serial number prefixes like
initvalue: int = None # initial default value for WRITE_DATA_LOCAL entities
prevent_update: bool = False # if set to True, value will not be re-read/updated with each polling cycle; only when read value changes


@dataclass
class BaseHttpTimeEntityDescription(TimeEntityDescription):
allowedtypes: int = 0 # overload with ALLDEFAULT from plugin
allowedtypes: int = 0 # overload with ALLDEFAULT from plugin
register: int = None
blacklist: list = None # None or list of serial number prefixes like


blacklist: list = None # None or list of serial number prefixes like
Loading

0 comments on commit ed13cf1

Please sign in to comment.