Skip to content

Commit 66fdc91

Browse files
committed
separate update-interval's for extended Vehicle & Meter data
1 parent d528da5 commit 66fdc91

7 files changed

Lines changed: 93 additions & 34 deletions

File tree

custom_components/evcc_intg/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@
6767
CONF_INCLUDE_EVCC,
6868
CONF_USE_WS,
6969
CONF_EXTENDED_VEHICLE_DATA,
70+
CONF_EXTENDED_VEHICLE_DATA_INTERVAL,
7071
CONF_EXTENDED_METER_DATA,
72+
CONF_EXTENDED_METER_DATA_INTERVAL,
7173
CONF_PURGE_ALL,
7274
CONFIG_VERSION,
7375
CONFIG_MINOR_VERSION,
@@ -323,7 +325,9 @@ def __init__(self, hass: HomeAssistant, http_session: aiohttp.ClientSession, con
323325
# the interval from the coordinator in the bridge (for the config updates)...
324326
self._update_interval_in_seconds_from_config_entry = config_entry.data.get(CONF_SCAN_INTERVAL, 30)
325327
self._request_ext_vehicle_data = config_entry.data.get(CONF_EXTENDED_VEHICLE_DATA, False)
328+
self._request_ext_vehicle_data_interval = config_entry.data.get(CONF_EXTENDED_VEHICLE_DATA_INTERVAL, 3600)
326329
self._request_ext_meter_data = config_entry.data.get(CONF_EXTENDED_METER_DATA, False)
330+
self._request_ext_meter_data_interval = config_entry.data.get(CONF_EXTENDED_METER_DATA_INTERVAL, 3600)
327331

328332
self.bridge = EvccApiBridge(host=config_entry.data.get(CONF_HOST, "NOT-CONFIGURED"),
329333
web_session=http_session,

custom_components/evcc_intg/config_flow.py

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import logging
2-
from typing import Any
2+
from typing import Any, Final
33

44
import aiohttp
55
import voluptuous as vol
@@ -16,19 +16,24 @@
1616
CONF_USE_WS,
1717
CONF_PURGE_ALL,
1818
CONF_EXTENDED_VEHICLE_DATA,
19+
CONF_EXTENDED_VEHICLE_DATA_INTERVAL,
1920
CONF_EXTENDED_METER_DATA,
20-
CONFIG_VERSION, CONFIG_MINOR_VERSION
21+
CONF_EXTENDED_METER_DATA_INTERVAL,
22+
CONFIG_VERSION,
23+
CONFIG_MINOR_VERSION
2124
)
2225

2326
_LOGGER: logging.Logger = logging.getLogger(__package__)
2427

25-
DEFAULT_NAME = "evcc"
26-
DEFAULT_HOST = "http://your-evcc-ip:7070"
27-
DEFAULT_SCAN_INTERVAL = 60
28-
DEFAULT_USE_WS = True
29-
DEFAULT_INCLUDE_EVCC = False
30-
DEFAULT_EXTENDED_VEHICLE_DATA = False
31-
DEFAULT_EXTENDED_METER_DATA = False
28+
DEFAULT_NAME: Final = "evcc"
29+
DEFAULT_HOST: Final = "http://your-evcc-ip:7070"
30+
DEFAULT_SCAN_INTERVAL: Final = 60
31+
DEFAULT_USE_WS: Final = True
32+
DEFAULT_INCLUDE_EVCC: Final = False
33+
DEFAULT_EXTENDED_VEHICLE_DATA: Final = False
34+
DEFAULT_EXTENDED_VEHICLE_DATA_INTERVAL: Final = 3600
35+
DEFAULT_EXTENDED_METER_DATA: Final = False
36+
DEFAULT_EXTENDED_METER_DATA_INTERVAL: Final = 3600
3237

3338
class EvccFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
3439
"""Config flow for evcc_intg."""
@@ -47,7 +52,9 @@ def __init__(self):
4752
self._default_use_ws = DEFAULT_USE_WS
4853
self._default_include_evcc = DEFAULT_INCLUDE_EVCC
4954
self._default_extended_vehicle_data = DEFAULT_EXTENDED_VEHICLE_DATA
55+
self._default_extended_vehicle_data_interval = DEFAULT_EXTENDED_VEHICLE_DATA_INTERVAL
5056
self._default_extended_meter_data = DEFAULT_EXTENDED_METER_DATA
57+
self._default_extended_meter_data_interval = DEFAULT_EXTENDED_METER_DATA_INTERVAL
5158
self._need_purge_all_list = None
5259

5360
async def async_step_reconfigure(self, user_input: dict[str, Any] | None = None) -> ConfigFlowResult:
@@ -59,7 +66,9 @@ async def async_step_reconfigure(self, user_input: dict[str, Any] | None = None)
5966
self._default_use_ws = entry_data.get(CONF_USE_WS, DEFAULT_USE_WS)
6067
self._default_include_evcc = entry_data.get(CONF_INCLUDE_EVCC, DEFAULT_INCLUDE_EVCC)
6168
self._default_extended_vehicle_data = entry_data.get(CONF_EXTENDED_VEHICLE_DATA, DEFAULT_EXTENDED_VEHICLE_DATA)
69+
self._default_extended_vehicle_data_interval = entry_data.get(CONF_EXTENDED_VEHICLE_DATA_INTERVAL, DEFAULT_EXTENDED_VEHICLE_DATA_INTERVAL)
6270
self._default_extended_meter_data = entry_data.get(CONF_EXTENDED_METER_DATA, DEFAULT_EXTENDED_METER_DATA)
71+
self._default_extended_meter_data_interval = entry_data.get(CONF_EXTENDED_METER_DATA_INTERVAL, DEFAULT_EXTENDED_METER_DATA_INTERVAL)
6372
self._need_purge_all_list = [self._default_extended_vehicle_data, self._default_extended_meter_data]
6473
return await self.async_step_user()
6574

@@ -122,7 +131,9 @@ async def async_step_user(self, user_input=None):
122131
user_input[CONF_USE_WS] = self._default_use_ws
123132
user_input[CONF_INCLUDE_EVCC] = self._default_include_evcc
124133
user_input[CONF_EXTENDED_VEHICLE_DATA] = self._default_extended_vehicle_data
134+
user_input[CONF_EXTENDED_VEHICLE_DATA_INTERVAL] = self._default_extended_vehicle_data_interval
125135
user_input[CONF_EXTENDED_METER_DATA] = self._default_extended_meter_data
136+
user_input[CONF_EXTENDED_METER_DATA_INTERVAL] = self._default_extended_meter_data_interval
126137
user_input[CONF_PURGE_ALL] = False
127138

128139
return self.async_show_form(
@@ -133,8 +144,10 @@ async def async_step_user(self, user_input=None):
133144
vol.Required(CONF_USE_WS, default=user_input.get(CONF_USE_WS)): bool,
134145
vol.Required(CONF_SCAN_INTERVAL, default=user_input.get(CONF_SCAN_INTERVAL)): int,
135146
vol.Optional(CONF_PASSWORD, default=user_input.get(CONF_PASSWORD, "")): str,
136-
vol.Optional(CONF_EXTENDED_VEHICLE_DATA, default=user_input.get(CONF_EXTENDED_VEHICLE_DATA, DEFAULT_EXTENDED_VEHICLE_DATA)): bool,
137-
vol.Optional(CONF_EXTENDED_METER_DATA, default=user_input.get(CONF_EXTENDED_METER_DATA, DEFAULT_EXTENDED_METER_DATA)): bool,
147+
vol.Optional(CONF_EXTENDED_VEHICLE_DATA, default=user_input.get(CONF_EXTENDED_VEHICLE_DATA)): bool,
148+
vol.Optional(CONF_EXTENDED_VEHICLE_DATA_INTERVAL, default=user_input.get(CONF_EXTENDED_VEHICLE_DATA_INTERVAL)): int,
149+
vol.Optional(CONF_EXTENDED_METER_DATA, default=user_input.get(CONF_EXTENDED_METER_DATA)): bool,
150+
vol.Optional(CONF_EXTENDED_METER_DATA_INTERVAL, default=user_input.get(CONF_EXTENDED_METER_DATA_INTERVAL)): int,
138151
vol.Required(CONF_INCLUDE_EVCC, default=user_input.get(CONF_INCLUDE_EVCC)): bool,
139152
vol.Optional(CONF_PURGE_ALL, default=user_input.get(CONF_PURGE_ALL)): bool,
140153
}),

custom_components/evcc_intg/const.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@
5757
CONF_PURGE_ALL: Final = "purge_all_devices"
5858
CONF_USE_WS= "use_websocket"
5959
CONF_EXTENDED_VEHICLE_DATA: Final = "extended_vehicle_data"
60+
CONF_EXTENDED_VEHICLE_DATA_INTERVAL: Final = "extended_vehicle_data_interval"
6061
CONF_EXTENDED_METER_DATA: Final = "extended_meter_data"
62+
CONF_EXTENDED_METER_DATA_INTERVAL: Final = "extended_meter_data_interval"
6163

6264
EVCC_JSON_KEY_NAME: Final = "evccName"
6365
EVCC_JSON_ORIGIN_OBJECT = "originObject"

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.11"
14+
"version": "2026.5.12"
1515
}

custom_components/evcc_intg/pyevcc_ha/__init__.py

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -229,11 +229,17 @@ def __init__(self, host: str, web_session, coordinator: DataUpdateCoordinator =
229229

230230
self._TARIFF_LAST_UPDATE_QUARTER_HOUR = -1
231231
self._SESSIONS_LAST_UPDATE_HOUR = -1
232-
self._CONFIG_LAST_UPDATE = -1
233-
if self.coordinator is not None and hasattr(self.coordinator, '_update_interval_in_seconds_from_config_entry'):
234-
self._CONFIG_UPDATE_INTERVAL_IN_SECONDS = self.coordinator._update_interval_in_seconds_from_config_entry
232+
self._CONFIG_VEHICLE_LAST_UPDATE = -1
233+
self._CONFIG_METER_LAST_UPDATE = -1
234+
if self.coordinator is not None and hasattr(self.coordinator, '_request_ext_vehicle_data_interval'):
235+
self._CONFIG_VEHICLE_UPDATE_INTERVAL_IN_SECONDS = self.coordinator._request_ext_vehicle_data_interval
235236
else:
236-
self._CONFIG_UPDATE_INTERVAL_IN_SECONDS = 60 * 15 # the default update will be every 15 minutes
237+
self._CONFIG_VEHICLE_UPDATE_INTERVAL_IN_SECONDS = 60 * 60
238+
239+
if self.coordinator is not None and hasattr(self.coordinator, '_request_ext_meter_data_interval'):
240+
self._CONFIG_METER_UPDATE_INTERVAL_IN_SECONDS = self.coordinator._request_ext_meter_data_interval
241+
else:
242+
self._CONFIG_METER_UPDATE_INTERVAL_IN_SECONDS = 60 * 60
237243

238244
self._data = {}
239245

@@ -270,7 +276,8 @@ def available_fields(self) -> int:
270276
def clear_data(self, clear_evcc_data: bool = True):
271277
self._TARIFF_LAST_UPDATE_QUARTER_HOUR = -1
272278
self._SESSIONS_LAST_UPDATE_HOUR = -1
273-
self._CONFIG_LAST_UPDATE = -1
279+
self._CONFIG_VEHICLE_LAST_UPDATE = -1
280+
self._CONFIG_METER_LAST_UPDATE = -1
274281
self._ws_LAST_UPDATE = -1
275282
self._ws_LAST_NEW_DATA_NOTIFY = -1
276283
if clear_evcc_data:
@@ -298,7 +305,8 @@ async def connect_ws(self):
298305
if self._data is None or len(self._data) == 0:
299306
self._TARIFF_LAST_UPDATE_QUARTER_HOUR = -1
300307
self._SESSIONS_LAST_UPDATE_HOUR = -1
301-
self._CONFIG_LAST_UPDATE = -1
308+
self._CONFIG_VEHICLE_LAST_UPDATE = -1
309+
self._CONFIG_METER_LAST_UPDATE = -1
302310
await self.read_all_data()
303311
except:
304312
_LOGGER.info(f"could not read initial data from evcc@{self.host} - ignoring")
@@ -489,12 +497,20 @@ async def read_all_data(self, request_all:bool=True,
489497
# additional configuration endpoint data
490498
if request_all or request_config:
491499
now_time = time()
492-
if self._CONFIG_LAST_UPDATE + self._CONFIG_UPDATE_INTERVAL_IN_SECONDS <= time():
493-
_LOGGER.debug(f"going to request 'configuration' data from evcc@{self.host}")
494-
json_resp, data_was_fetched = await self.read_config_data(json_resp, log_requests=log_config_requests)
500+
request_vehicle_data = self._request_ext_vehicle_data and self._CONFIG_VEHICLE_LAST_UPDATE + self._CONFIG_VEHICLE_UPDATE_INTERVAL_IN_SECONDS <= time()
501+
request_meter_data = self._request_ext_meter_data and self._CONFIG_METER_LAST_UPDATE + self._CONFIG_METER_UPDATE_INTERVAL_IN_SECONDS <= time()
502+
if request_vehicle_data or request_meter_data:
503+
_LOGGER.debug(f"going to request 'configuration' data vehicle: {request_vehicle_data}, meter: {request_meter_data} from evcc@{self.host}")
504+
json_resp, data_was_fetched = await self.read_config_data(json_resp,
505+
request_vehicle_data=request_vehicle_data,
506+
request_meter_data=request_meter_data,
507+
log_requests=log_config_requests)
495508
if data_was_fetched:
496509
self._data_coordinator_update_needed = True
497-
self._CONFIG_LAST_UPDATE = now_time
510+
if request_vehicle_data:
511+
self._CONFIG_VEHICLE_LAST_UPDATE = now_time
512+
if request_meter_data:
513+
self._CONFIG_METER_LAST_UPDATE = now_time
498514
else:
499515
# we must copy the previous existing data to the new json_resp!
500516
if self._data is not None and ADDITIONAL_ENDPOINTS_DATA_EVCCCONF in self._data:
@@ -558,7 +574,7 @@ async def read_sessions_data(self, json_resp: dict) -> dict:
558574

559575
return json_resp, session_data_was_fetched
560576

561-
async def read_config_data(self, json_resp: dict, log_requests:bool=False):
577+
async def read_config_data(self, json_resp: dict, request_vehicle_data:bool=False, request_meter_data:bool=False, log_requests:bool=False):
562578
config_data_was_fetched = False
563579
if await self.ensure_session_is_authorized():
564580
if ADDITIONAL_ENDPOINTS_DATA_EVCCCONF not in json_resp:
@@ -571,12 +587,27 @@ async def read_config_data(self, json_resp: dict, log_requests:bool=False):
571587
a_config = json_resp[ADDITIONAL_ENDPOINTS_DATA_EVCCCONF][EVCCCONF_KEY_CONFIG]
572588

573589
for a_device_type, value_list in a_config.items():
574-
if a_device_type == EVCCCONF_DEVICE_TYPES.VEHICLE.value and not self._request_ext_vehicle_data:
575-
_LOGGER.debug(f"skipping vehicle data since 'request_ext_vehicle_data' is set to False")
576-
continue
590+
# first we check if we must fetch vehicle or meter data at all... and if not (either cause it's completely
591+
# disabled by the user/config, or if the update interval is just not reached yet
592+
just_copy_old_data_for_device_type = False
593+
if a_device_type == EVCCCONF_DEVICE_TYPES.VEHICLE.value and (not request_vehicle_data or not self._request_ext_vehicle_data):
594+
_LOGGER.debug(f"skipping vehicle data since 'request_ext_vehicle_data' is set to False (or update-interval-not-reached-yet)")
595+
just_copy_old_data_for_device_type = True
596+
597+
if a_device_type == EVCCCONF_DEVICE_TYPES.METER.value and (not request_meter_data or not self._request_ext_meter_data):
598+
_LOGGER.debug(f"skipping meter data since 'request_ext_meter_data' is set to False (or update-interval-not-reached-yet)")
599+
just_copy_old_data_for_device_type = True
600+
601+
if just_copy_old_data_for_device_type:
602+
# make sure that the data is initialized (this is requird, when self._data is not set (yet))
603+
if a_device_type not in json_resp[ADDITIONAL_ENDPOINTS_DATA_EVCCCONF][EVCCCONF_KEY_DATA]:
604+
json_resp[ADDITIONAL_ENDPOINTS_DATA_EVCCCONF][EVCCCONF_KEY_DATA][a_device_type] = {}
577605

578-
if a_device_type == EVCCCONF_DEVICE_TYPES.METER.value and not self._request_ext_meter_data:
579-
_LOGGER.debug(f"skipping meter data since 'request_ext_meter_data' is set to False")
606+
if self._data is not None and ADDITIONAL_ENDPOINTS_DATA_EVCCCONF in self._data:
607+
if EVCCCONF_KEY_DATA in self._data[ADDITIONAL_ENDPOINTS_DATA_EVCCCONF]:
608+
if a_device_type in self._data[ADDITIONAL_ENDPOINTS_DATA_EVCCCONF][EVCCCONF_KEY_DATA]:
609+
object_to_copy = self._data[ADDITIONAL_ENDPOINTS_DATA_EVCCCONF][EVCCCONF_KEY_DATA][a_device_type]
610+
json_resp[ADDITIONAL_ENDPOINTS_DATA_EVCCCONF][EVCCCONF_KEY_DATA][a_device_type] = object_to_copy
580611
continue
581612

582613
for a_device_id in value_list:
@@ -937,7 +968,8 @@ async def _read_config_setup(self, log_requests:bool=False):
937968

938969
async def force_config_update(self):
939970
_LOGGER.debug(f"force_config_update(): forcing config update")
940-
self._CONFIG_LAST_UPDATE = -1
971+
self._CONFIG_VEHICLE_LAST_UPDATE = -1
972+
self._CONFIG_METER_LAST_UPDATE = -1
941973
await self.read_all_data(request_all=False, request_config=True)
942974
if self.coordinator is not None:
943975
self.coordinator.async_set_updated_data(self._data)

custom_components/evcc_intg/translations/de.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,20 @@
1313
"host": "Deine lokale evcc-Server Addresse (einschließlich des Ports)",
1414
"password": "Admin Passwort für den evcc-Server (optional)",
1515
"use_websocket": "WebSocket Verbindung verwenden",
16-
"scan_interval": "Aktualisierungsintervall in Sekunden [min: 5sek]",
16+
"scan_interval": "Haupt-Aktualisierungsintervall in Sekunden [min: 5sek]",
1717
"include_evcc": "Allen Namen der Sensoren den Präfix '[evcc]' voranstellen",
1818
"extended_vehicle_data": "Erweiterte Fahrzeugdaten von der evcc Konfiguration abrufen",
1919
"extended_meter_data": "Zählerdaten von der evcc Konfiguration abrufen",
2020
"purge_all_devices": "Alle Geräte (Devices) Löschen und neu Erstellen"
2121
},
2222
"data_description": {
23-
"scan_interval": "Wenn Du die WebSocket Verbindung aktiviert hast, ist das Aktualisierungsintervall nur für die Zugriffe auf die _optionale_ evcc Konfigurations-API relevant.",
23+
"use_websocket": "Die WebSocket Verbindung stellt sicher, dass die Sensoren mehr oder weniger sofort aktualisiert werden, sobald sich in Daten in evcc ändern. Wenn diese Option aktiviert ist, wird das Aktualisierungsintervall ignoriert.",
24+
"scan_interval": "Wenn Du die WebSocket Verbindung aktiviert hast, ist das Haupt-Aktualisierungsintervall irrelevant.",
2425
"password": "Die Angabe Deines evcc Admin-Passwort ist __optional__ und nur für erweiterte Features der Integration notwendig. Wenn Du Dir unsicher bist, lass dieses Feld bitte leer.\nDu kannst das Passwort auch jederzeit nachträglich hinzufügen, wenn Du feststellst, dass Du die erweiterten Funktionen der Integration verwendne möchtest (z.B. Neustart des evcc-Servers via HA).\n\nWenn Du Dein aktuelles Passwort __entfernen__ möchtest, dann mußt Du ein __LEERZEICHEN__ eingeben (sonst erkennt HA keine Änderung)!",
25-
"extended_vehicle_data": "Erfordert Zugriff auf die evcc Konfigurations-API (Admin Passwort notwendig). Wenn aktiviert, sammelt die Integration zusätzliche Fahrzeugdaten wie Ladestatus, Reichweite, Kilometerstand oder Ladestand für jedes konfigurierte Fahrzeug.\n**Warnung**: Der Abruf der erweiterten Fahrzeugdaten _kann_ dazu führen, das mit jedem Aktualisierungsinterval, evcc die Daten direkt von Deinem Fahrzeug abruft. Je nach API-Design Deines Fahrzeug-Herstellers kann dies eine Entladung der 12V Batterie zur Folge haben.",
26+
"extended_vehicle_data": "Erfordert Zugriff auf die evcc Konfigurations-API (Admin Passwort notwendig). Wenn aktiviert, sammelt die Integration zusätzliche Fahrzeugdaten wie Ladestatus, Reichweite, Kilometerstand oder Ladestand für jedes konfigurierte Fahrzeug.\nNormalerweise (ohne diese aktivierte Option) sind diese Informationen nur dann in der Integration verfügbar, wenn ein Fahrzeug an einen evcc Ladepunkt angeschlossen ist.\n\n**Warnung**: Der Abruf der erweiterten Fahrzeugdaten _kann_ dazu führen, das mit jedem Aktualisierungsinterval, evcc die Daten direkt von Deinem Fahrzeug abruft. Je nach Fahrzeug-Herstellers-API-Design [wie bei der _Hyundai Bluelink (EU)_] kann dies eine Entladung der 12V Batterie zur Folge haben. Ebenso kann ein mögliches API-Request-Limit schneller aufgebraucht sein (z.B. bei Testa). Also AugenAuf!",
27+
"extended_vehicle_data_interval": "Wenn die Option '_Erweiterte Fahrzeugdaten von der evcc Konfiguration abrufen_' aktiviert ist, werden diese _zusätzlichen_ Fahrzeugdaten in dem eingestellten Interval aktualisiert. Bitte beachte auch die zusätzliche **Warnung** oben (Default-Wert: 3600 Sekunden = jede Stunde).",
2628
"extended_meter_data": "Erfordert Zugriff auf die evcc Konfigurations-API (Admin Passwort notwendig). Wenn aktiviert, sammelt die Integration zusätzliche Zählerdaten wie Leistung, Energie, Phasenströme, Phasenspannungen oder Ladestand/Temperaturen für jeden konfigurierten Zähler.",
29+
"extended_meter_data_interval": "Wenn die Option '_Zählerdaten von der evcc Konfiguration abrufen_' aktiviert ist, werden die erweiterte Zählerdaten in dem eingestellten Interval aktualisiert (Default-Wert: 3600 Sekunden = jede Stunde).",
2730
"purge_all_devices": "Dies kann notwendig werden, wenn Du verwaiste Geräte (Einträge) bei Dir in HA hast. Diese Einstellung wird automatisch zurückgesetzt."
2831
}
2932
}

0 commit comments

Comments
 (0)