From 6c87acb7ade7f6f9915acca2422df3c7461a481a Mon Sep 17 00:00:00 2001 From: marq24 Date: Fri, 10 Jan 2025 20:29:34 +0100 Subject: [PATCH] handling new grid object (supporting PR https://github.com/evcc-io/evcc/pull/18063) --- custom_components/evcc_intg/__init__.py | 16 ++- custom_components/evcc_intg/const.py | 116 ++++++++++++------ .../evcc_intg/pyevcc_ha/const.py | 11 +- custom_components/evcc_intg/pyevcc_ha/keys.py | 7 +- custom_components/evcc_intg/sensor.py | 16 ++- 5 files changed, 121 insertions(+), 45 deletions(-) diff --git a/custom_components/evcc_intg/__init__.py b/custom_components/evcc_intg/__init__.py index 6ee5a9b..8087148 100644 --- a/custom_components/evcc_intg/__init__.py +++ b/custom_components/evcc_intg/__init__.py @@ -143,6 +143,7 @@ def __init__(self, hass: HomeAssistant, config_entry): self._loadpoint = {} self._vehicle = {} self._version = None + self._grid_data_as_object = False super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=SCAN_INTERVAL) @@ -220,8 +221,12 @@ async def read_evcc_config_on_startup(self): if self._currency == "EUR": self._currency = "€" + if "grid" in initdata: + if "power" in initdata["grid"] and "currents" in initdata["grid"]: + self._grid_data_as_object = True + _LOGGER.debug( - f"read_evcc_config: LPs: {len(self._loadpoint)} VEHs: {len(self._vehicle)} CT: '{self._cost_type}' CUR: {self._currency}") + f"read_evcc_config: LPs: {len(self._loadpoint)} VEHs: {len(self._vehicle)} CT: '{self._cost_type}' CUR: {self._currency} GAO: {self._grid_data_as_object}") async def _async_update_data(self) -> dict: """Update data via library.""" @@ -397,17 +402,20 @@ def _convert_time(value: str): return None @property - def system_id(self): + def system_id(self) -> str: return self._system_id @property - def currency(self): + def currency(self) -> str: return self._currency @property - def device_info_dict(self): + def device_info_dict(self) -> dict: return self._device_info_dict + @property + def grid_data_as_object(self) -> bool: + return self._grid_data_as_object class EvccBaseEntity(Entity): _attr_should_poll = False diff --git a/custom_components/evcc_intg/const.py b/custom_components/evcc_intg/const.py index 7a9d45f..9a07a4b 100644 --- a/custom_components/evcc_intg/const.py +++ b/custom_components/evcc_intg/const.py @@ -396,49 +396,63 @@ class ExtSwitchEntityDescription(SwitchEntityDescription): ) ] -SENSOR_SENSORS = [ +SENSOR_SENSORS_GRID_PREFIXS = [ ExtSensorEntityDescription( - tag=Tag.AUXPOWER, - key=Tag.AUXPOWER.key, - icon="mdi:home-circle-outline", + tag=Tag.GRIDPOWER, + key=Tag.GRIDPOWER.key, + icon="mdi:transmission-tower", state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=UnitOfPower.WATT, - device_class=SensorDeviceClass.POWER, - entity_registry_enabled_default=False + suggested_display_precision=2, + device_class=SensorDeviceClass.POWER ), ExtSensorEntityDescription( - tag=Tag.BATTERYMODE, - key=f"{Tag.BATTERYMODE.key}_value", - icon="mdi:state-machine", - lookup=True + tag=Tag.GRIDCURRENTS, + key=f"{Tag.GRIDCURRENTS.key}_0", + array_idx=0, + icon="mdi:current-ac", + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + device_class=SensorDeviceClass.CURRENT, + entity_registry_enabled_default=False ), ExtSensorEntityDescription( - tag=Tag.BATTERYMODE, - key=f"{Tag.BATTERYMODE.key}", - icon="mdi:state-machine", + tag=Tag.GRIDCURRENTS, + key=f"{Tag.GRIDCURRENTS.key}_1", + array_idx=1, + icon="mdi:current-ac", + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + device_class=SensorDeviceClass.CURRENT, entity_registry_enabled_default=False ), ExtSensorEntityDescription( - tag=Tag.BATTERYPOWER, - key=Tag.BATTERYPOWER.key, - icon="mdi:battery-charging", + tag=Tag.GRIDCURRENTS, + key=f"{Tag.GRIDCURRENTS.key}_2", + array_idx=2, + icon="mdi:current-ac", state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=UnitOfPower.WATT, - device_class=SensorDeviceClass.POWER - ), + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + device_class=SensorDeviceClass.CURRENT, + entity_registry_enabled_default=False + ) +] + +SENSOR_SENSORS_GRID_AS_OBJECT = [ ExtSensorEntityDescription( - tag=Tag.BATTERYSOC, - key=Tag.BATTERYSOC.key, - icon="mdi:home-battery-outline", + tag=Tag.GRID, + key=Tag.GRIDPOWER.key, + idx = "power", + icon="mdi:transmission-tower", state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=PERCENTAGE, - device_class=None, - suggested_display_precision=0 + native_unit_of_measurement=UnitOfPower.WATT, + suggested_display_precision=2, + device_class=SensorDeviceClass.POWER ), ExtSensorEntityDescription( - tag=Tag.GRIDCURRENTS, + tag=Tag.GRID, key=f"{Tag.GRIDCURRENTS.key}_0", - array_idx=0, + tuple_idx = ["currents", 0], icon="mdi:current-ac", state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, @@ -446,9 +460,9 @@ class ExtSwitchEntityDescription(SwitchEntityDescription): entity_registry_enabled_default=False ), ExtSensorEntityDescription( - tag=Tag.GRIDCURRENTS, + tag=Tag.GRID, key=f"{Tag.GRIDCURRENTS.key}_1", - array_idx=1, + tuple_idx = ["currents", 1], icon="mdi:current-ac", state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, @@ -456,24 +470,56 @@ class ExtSwitchEntityDescription(SwitchEntityDescription): entity_registry_enabled_default=False ), ExtSensorEntityDescription( - tag=Tag.GRIDCURRENTS, + tag=Tag.GRID, key=f"{Tag.GRIDCURRENTS.key}_2", - array_idx=2, + tuple_idx = ["currents", 2], icon="mdi:current-ac", state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, entity_registry_enabled_default=False ), +] + +SENSOR_SENSORS = [ ExtSensorEntityDescription( - tag=Tag.GRIDPOWER, - key=Tag.GRIDPOWER.key, - icon="mdi:transmission-tower", + tag=Tag.AUXPOWER, + key=Tag.AUXPOWER.key, + icon="mdi:home-circle-outline", + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, + entity_registry_enabled_default=False + ), + ExtSensorEntityDescription( + tag=Tag.BATTERYMODE, + key=f"{Tag.BATTERYMODE.key}_value", + icon="mdi:state-machine", + lookup=True + ), + ExtSensorEntityDescription( + tag=Tag.BATTERYMODE, + key=f"{Tag.BATTERYMODE.key}", + icon="mdi:state-machine", + entity_registry_enabled_default=False + ), + ExtSensorEntityDescription( + tag=Tag.BATTERYPOWER, + key=Tag.BATTERYPOWER.key, + icon="mdi:battery-charging", state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=UnitOfPower.WATT, - suggested_display_precision=2, device_class=SensorDeviceClass.POWER ), + ExtSensorEntityDescription( + tag=Tag.BATTERYSOC, + key=Tag.BATTERYSOC.key, + icon="mdi:home-battery-outline", + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=PERCENTAGE, + device_class=None, + suggested_display_precision=0 + ), ExtSensorEntityDescription( tag=Tag.HOMEPOWER, key=Tag.HOMEPOWER.key, diff --git a/custom_components/evcc_intg/pyevcc_ha/const.py b/custom_components/evcc_intg/pyevcc_ha/const.py index 1fd366c..ecb2500 100644 --- a/custom_components/evcc_intg/pyevcc_ha/const.py +++ b/custom_components/evcc_intg/pyevcc_ha/const.py @@ -10,8 +10,6 @@ JSONKEY_BATTERYMODE: Final = "batteryMode" JSONKEY_BATTERYPOWER: Final = "batteryPower" JSONKEY_BATTERYSOC: Final = "batterySoc" -JSONKEY_GRIDCURRENTS: Final = "gridCurrents" -JSONKEY_GRIDPOWER: Final = "gridPower" JSONKEY_HOMEPOWER: Final = "homePower" JSONKEY_PVENERGY: Final = "pvEnergy" JSONKEY_PVPOWER: Final = "pvPower" @@ -22,13 +20,18 @@ JSONKEY_STATISTICS_365D: Final = "365d" JSONKEY_STATISTICS_30D: Final = "30d" +JSONKEY_GRIDCURRENTS: Final = "gridCurrents" +JSONKEY_GRIDPOWER: Final = "gridPower" +JSONKEY_GRID: Final = "grid" + STATES: Final = [JSONKEY_LOADPOINTS, JSONKEY_AUXPOWER, JSONKEY_BATTERYMODE, JSONKEY_BATTERYPOWER, JSONKEY_BATTERYSOC, - JSONKEY_GRIDCURRENTS, JSONKEY_GRIDPOWER, JSONKEY_HOMEPOWER, JSONKEY_PVENERGY, JSONKEY_PVPOWER, JSONKEY_PV, JSONKEY_VEHICLES, JSONKEY_STATISTICS] + JSONKEY_GRID, JSONKEY_GRIDCURRENTS, JSONKEY_GRIDPOWER, JSONKEY_HOMEPOWER, JSONKEY_PVENERGY, + JSONKEY_PVPOWER, JSONKEY_PV, JSONKEY_VEHICLES, JSONKEY_STATISTICS] # FILTER_LOADPOINTS: Final = f"?jq=.{JSONKEY_LOADPOINTS}" STATE_QUERY = ( - f"?jq={{{JSONKEY_LOADPOINTS}:.{JSONKEY_LOADPOINTS},{JSONKEY_AUXPOWER}:.{JSONKEY_AUXPOWER},{JSONKEY_BATTERYMODE}:.{JSONKEY_BATTERYMODE},{JSONKEY_BATTERYPOWER}:.{JSONKEY_BATTERYPOWER},{JSONKEY_BATTERYSOC}:.{JSONKEY_BATTERYSOC},{JSONKEY_GRIDCURRENTS}:.{JSONKEY_GRIDCURRENTS},{JSONKEY_GRIDPOWER}:.{JSONKEY_GRIDPOWER},{JSONKEY_HOMEPOWER}:.{JSONKEY_HOMEPOWER},{JSONKEY_HOMEPOWER}:.{JSONKEY_HOMEPOWER},{JSONKEY_PVENERGY}:.{JSONKEY_PVENERGY},{JSONKEY_PVPOWER}:.{JSONKEY_PVPOWER},{JSONKEY_PV}:.{JSONKEY_PV},{JSONKEY_VEHICLES}:.{JSONKEY_VEHICLES},{JSONKEY_STATISTICS}:.{JSONKEY_STATISTICS}}}" + f"?jq={{{JSONKEY_LOADPOINTS}:.{JSONKEY_LOADPOINTS},{JSONKEY_AUXPOWER}:.{JSONKEY_AUXPOWER},{JSONKEY_BATTERYMODE}:.{JSONKEY_BATTERYMODE},{JSONKEY_BATTERYPOWER}:.{JSONKEY_BATTERYPOWER},{JSONKEY_BATTERYSOC}:.{JSONKEY_BATTERYSOC},{JSONKEY_GRID}:.{JSONKEY_GRID},{JSONKEY_GRIDCURRENTS}:.{JSONKEY_GRIDCURRENTS},{JSONKEY_GRIDPOWER}:.{JSONKEY_GRIDPOWER},{JSONKEY_HOMEPOWER}:.{JSONKEY_HOMEPOWER},{JSONKEY_HOMEPOWER}:.{JSONKEY_HOMEPOWER},{JSONKEY_PVENERGY}:.{JSONKEY_PVENERGY},{JSONKEY_PVPOWER}:.{JSONKEY_PVPOWER},{JSONKEY_PV}:.{JSONKEY_PV},{JSONKEY_VEHICLES}:.{JSONKEY_VEHICLES},{JSONKEY_STATISTICS}:.{JSONKEY_STATISTICS}}}" ) MIN_CURRENT_LIST: Final = ["0.125", "0.25", "0.5", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", diff --git a/custom_components/evcc_intg/pyevcc_ha/keys.py b/custom_components/evcc_intg/pyevcc_ha/keys.py index 794b5ea..1bcbcb3 100644 --- a/custom_components/evcc_intg/pyevcc_ha/keys.py +++ b/custom_components/evcc_intg/pyevcc_ha/keys.py @@ -51,7 +51,7 @@ class ApiKey(NamedTuple): @property def snake_case(self) -> str: - return _camel_to_snake(self.key) + return camel_to_snake(self.key) # see https://docs.evcc.io/docs/reference/api for details class Tag(ApiKey, Enum): @@ -84,6 +84,11 @@ def __str__(self): # "gridPower": -6280.24, GRIDPOWER = ApiKey(key="gridPower", type=EP_TYPE.SITE) + # "grid": { "currents": [17.95, 7.71, 1.99], + # "power": -6280.24, + # ...} + GRID = ApiKey(key="grid", type=EP_TYPE.SITE) + # "homePower": 2594.19, HOMEPOWER = ApiKey(key="homePower", type=EP_TYPE.SITE) diff --git a/custom_components/evcc_intg/sensor.py b/custom_components/evcc_intg/sensor.py index 73901aa..653ce5d 100644 --- a/custom_components/evcc_intg/sensor.py +++ b/custom_components/evcc_intg/sensor.py @@ -7,7 +7,8 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity from . import EvccDataUpdateCoordinator, EvccBaseEntity -from .const import DOMAIN, SENSOR_SENSORS, SENSOR_SENSORS_PER_LOADPOINT, ExtSensorEntityDescription +from .const import DOMAIN, SENSOR_SENSORS, SENSOR_SENSORS_GRID_PREFIXS, SENSOR_SENSORS_GRID_AS_OBJECT, \ + SENSOR_SENSORS_PER_LOADPOINT, ExtSensorEntityDescription _LOGGER = logging.getLogger(__name__) @@ -20,6 +21,19 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, add_ entity = EvccSensor(coordinator, description) entities.append(entity) + # we need to check if the grid data (power & currents) is available as separate object... + # or if it's still part of the main/site object (as gridPower, gridCurrents) + if coordinator.grid_data_as_object(): + _LOGGER.debug("evcc grid data is available as separate object") + for description in SENSOR_SENSORS_GRID_AS_OBJECT: + entity = EvccSensor(coordinator, description) + entities.append(entity) + else: + _LOGGER.debug("evcc grid data is prefixed") + for description in SENSOR_SENSORS_GRID_PREFIXS: + entity = EvccSensor(coordinator, description) + entities.append(entity) + multi_loadpoint_config = len(coordinator._loadpoint) > 1 for a_lp_key in coordinator._loadpoint: load_point_config = coordinator._loadpoint[a_lp_key]