Skip to content

Commit 250fdae

Browse files
committed
refactored meter-'sensor' enabale/disable detection
1 parent bae7746 commit 250fdae

2 files changed

Lines changed: 69 additions & 46 deletions

File tree

custom_components/evcc_intg/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@
1111
"iot_class": "local_push",
1212
"issue_tracker": "https://github.com/marq24/ha-evcc/issues",
1313
"requirements": ["packaging>=21.0"],
14-
"version": "2026.5.5"
14+
"version": "2026.5.6"
1515
}

custom_components/evcc_intg/sensor.py

Lines changed: 68 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import asyncio
12
import logging
23
from dataclasses import replace
34
from datetime import datetime, timezone
@@ -9,7 +10,6 @@
910
from homeassistant.core import HomeAssistant
1011
from homeassistant.helpers import entity_registry as er
1112
from homeassistant.helpers.entity_platform import AddEntitiesCallback
12-
from homeassistant.helpers.event import async_call_later
1313
from homeassistant.helpers.restore_state import RestoreEntity
1414

1515
from custom_components.evcc_intg.pyevcc_ha.const import (
@@ -23,7 +23,8 @@
2323
FORECAST_CONTENT,
2424
SESSIONS_KEY_TOTAL,
2525
EVCCCONF_KEY_CONFIG,
26-
EVCCCONF_DEVICE_TYPES
26+
EVCCCONF_DEVICE_TYPES,
27+
EVCCCONF_KEY_DATA
2728
)
2829
from custom_components.evcc_intg.pyevcc_ha.keys import Tag, camel_to_snake
2930
from . import EvccDataUpdateCoordinator, EvccBaseEntity
@@ -54,6 +55,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, add_
5455
configuration_data_available = True
5556

5657
entities = []
58+
entries_to_check = {}
5759
the_sensors_list = SENSOR_ENTITIES
5860

5961
# we need to check if the grid data (power & currents) is available as a separate object...
@@ -289,12 +291,73 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, add_
289291
lookup=a_stub.lookup,
290292
ignore_zero=a_stub.ignore_zero
291293
)
292-
293-
entity = EvccSensor(coordinator, description, do_check=True)
294+
entity = EvccSensor(coordinator, description)
294295
entities.append(entity)
296+
entries_to_check[entity.entity_id] = {
297+
"tag": description.tag,
298+
"evcc_config_id": description.evcc_config_id,
299+
"description_key": description.key
300+
}
295301

296302
add_entity_cb(entities)
297303

304+
async def _check_for_entities_to_enabled():
305+
_LOGGER.debug("_check_for_entities_to_enabled(): SENSORS launched (will sleep now)")
306+
try:
307+
await asyncio.sleep(30)
308+
need_to_wait = True
309+
check_run_counter = 0
310+
311+
while check_run_counter < 11 and need_to_wait:
312+
check_run_counter += 1
313+
meter_devices_data = coordinator.data.get(ADDITIONAL_ENDPOINTS_DATA_EVCCCONF, {}).get(EVCCCONF_KEY_DATA, {}).get(EVCCCONF_DEVICE_TYPES.METER.value)
314+
if meter_devices_data is not None:
315+
avail_data_count = 0
316+
for a_meter_device_id, a_meter_object in meter_devices_data.items():
317+
a_meter_device_has_error = False
318+
for a_key, a_value in a_meter_object.items():
319+
if "error" in a_value and a_value["error"] is not None and len(a_value["error"]) > 0:
320+
a_meter_device_has_error = True
321+
break
322+
if not a_meter_device_has_error:
323+
avail_data_count +=1
324+
325+
if avail_data_count == len(meter_devices_data):
326+
need_to_wait = False
327+
328+
if need_to_wait:
329+
_LOGGER.debug(f"_check_for_entities_to_enabled(): No meters data found (or they are marked with 'errors'), waiting another 30 seconds... {meter_devices_data}")
330+
await asyncio.sleep(30)
331+
332+
_LOGGER.debug("_check_for_entities_to_enabled(): SENSORS will start now...")
333+
registry = er.async_get(hass)
334+
if registry is not None:
335+
for a_entity_id in entries_to_check:
336+
a_entity_data = entries_to_check[a_entity_id]
337+
value = coordinator.read_tag_configuration(a_entity_data["tag"], a_entity_data["evcc_config_id"])
338+
_LOGGER.debug(f"_check_for_entities_to_enabled(): {a_entity_data["description_key"]}: {value}")
339+
entry = registry.async_get(a_entity_id)
340+
if entry is not None:
341+
if value is None:
342+
if entry.disabled_by is None:
343+
_LOGGER.debug(f"_check_for_entities_to_enabled(): Disabling entity {a_entity_id} due to missing data from evcc")
344+
registry.async_update_entity(
345+
a_entity_id,
346+
disabled_by=er.RegistryEntryDisabler.INTEGRATION
347+
)
348+
else:
349+
if entry.disabled_by == er.RegistryEntryDisabler.INTEGRATION:
350+
_LOGGER.debug(f"_check_for_entities_to_enabled(): Enable entity {a_entity_id} due since data is available")
351+
registry.async_update_entity(
352+
a_entity_id,
353+
disabled_by=None
354+
)
355+
_LOGGER.debug("_check_for_entities_to_enabled(): SENSOR init is COMPLETED")
356+
except BaseException as err:
357+
_LOGGER.warning(f"_check_for_entities_to_enabled(): SENSOR Error: {type(err).__name__} {err}")
358+
359+
asyncio.create_task(_check_for_entities_to_enabled())
360+
298361
def compress_data(data):
299362
return compress_general(data, "start", "value")
300363

@@ -347,53 +410,13 @@ def compress_general(data, time_key:str, value_key:str):
347410

348411

349412
class EvccSensor(EvccBaseEntity, SensorEntity, RestoreEntity):
350-
def __init__(self, coordinator: EvccDataUpdateCoordinator, description: ExtSensorEntityDescription, do_check:bool=False):
413+
def __init__(self, coordinator: EvccDataUpdateCoordinator, description: ExtSensorEntityDescription):
351414
super().__init__(entity_type=Platform.SENSOR, coordinator=coordinator, description=description)
352-
self._do_check = do_check
353415
self._previous_float_value: float | None = None
354-
self._cancel_delayed_check = None
355416
if self.tag.type == EP_TYPE.TARIFF or self.tag in [Tag.FORECAST_GRID, Tag.FORECAST_SOLAR, Tag.FORECAST_FEEDIN, Tag.FORECAST_PLANNER]:
356417
self._last_calculated_key = None
357418
self._last_calculated_value = None
358419

359-
async def async_added_to_hass(self):
360-
"""Run when entity about to be added to hass."""
361-
await super().async_added_to_hass()
362-
# Schedule the check code for 120 seconds (2 minutes)
363-
# Note: The callback is a function, not a coroutine
364-
if self._do_check:
365-
self._cancel_delayed_check = async_call_later(self.hass,120, self._perform_delayed_check)
366-
367-
async def async_will_remove_from_hass(self):
368-
"""Run when the entity is removed from hass."""
369-
if self._do_check:
370-
if self._cancel_delayed_check is not None:
371-
self._cancel_delayed_check()
372-
self._cancel_delayed_check = None
373-
await super().async_will_remove_from_hass()
374-
375-
async def _perform_delayed_check(self, _now):
376-
value = self.coordinator.read_tag_configuration(self.tag, self.entity_description.evcc_config_id)
377-
_LOGGER.debug(f"_perform_delayed_check(): {self.entity_description.key}: {value}")
378-
registry = er.async_get(self.hass)
379-
if registry is not None:
380-
entry = registry.async_get(self.entity_id)
381-
if entry is not None:
382-
if value is None:
383-
if entry.disabled_by is None:
384-
_LOGGER.debug(f"_perform_delayed_check(): Disabling entity {self.entity_id} due to missing data from evcc")
385-
registry.async_update_entity(
386-
self.entity_id,
387-
disabled_by=er.RegistryEntryDisabler.INTEGRATION
388-
)
389-
else:
390-
if entry.disabled_by == er.RegistryEntryDisabler.INTEGRATION:
391-
_LOGGER.debug(f"_perform_delayed_check(): Enable entity {self.entity_id} due since data is available")
392-
registry.async_update_entity(
393-
self.entity_id,
394-
disabled_by=None
395-
)
396-
397420
@property
398421
def extra_state_attributes(self):
399422
"""Return sensor attributes"""

0 commit comments

Comments
 (0)