Skip to content

Commit a026ba1

Browse files
author
marq24
committed
support for evcc-io/evcc#32490
1 parent 02d71a7 commit a026ba1

11 files changed

Lines changed: 256 additions & 139 deletions

File tree

custom_components/evcc_intg/__init__.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,6 @@ async def read_evcc_config_on_startup(self, hass: HomeAssistant):
542542
else:
543543
_LOGGER.debug(f"read_evcc_config_on_startup(): NO 'circuits' found [{JSONKEY_CIRCUITS}] in the evcc data: {initdata}")
544544

545-
546545
# init our vehicles data... [this is a JSON DICT]
547546
if JSONKEY_VEHICLES in initdata:
548547
for a_evcc_veh_name in initdata[JSONKEY_VEHICLES]:
@@ -588,11 +587,18 @@ async def read_evcc_config_on_startup(self, hass: HomeAssistant):
588587
if "chargerFeatureIntegratedDevice" in a_loadpoint_object:
589588
is_integrated = a_loadpoint_object["chargerFeatureIntegratedDevice"]
590589

591-
# sind 0.310.0 no MIN+PV mode + no MAX_CURRENT for 'SwitchDevice'
590+
# since 0.310.0 no MIN+PV mode + no MAX_CURRENT for 'SwitchDevice'
592591
is_switch_device = False
593592
if "chargerFeatureSwitchDevice" in a_loadpoint_object:
594593
is_switch_device = a_loadpoint_object["chargerFeatureSwitchDevice"]
595594

595+
# since https://github.com/evcc-io/evcc/pull/32490
596+
# looks like we have different names for the modes - and also a new
597+
# 'feature'...
598+
is_always_charge_present = False
599+
if "alwaysCharge" in a_loadpoint_object:
600+
is_always_charge_present = True
601+
596602
self._loadpoint[f"{api_index}"] = {
597603
EVCC_JSON_KEY_NAME: f"{api_index}",
598604
EVCC_JSON_ORIGIN_OBJECT: a_loadpoint_object,
@@ -603,14 +609,14 @@ async def read_evcc_config_on_startup(self, hass: HomeAssistant):
603609
"is_heating": is_heating,
604610
"is_integrated": is_integrated,
605611
"is_switch_device": is_switch_device,
612+
"is_always_charge_present": is_always_charge_present,
606613
"vehicle_key": a_loadpoint_object["vehicleName"]
607614
}
608615

609616
api_index += 1
610617
else:
611618
_LOGGER.warning(f"read_evcc_config_on_startup(): NO 'loadpoints' found [{JSONKEY_LOADPOINTS}] in the evcc data: {initdata}")
612619

613-
614620
if "smartCostType" in initdata:
615621
self._cost_type = initdata["smartCostType"]
616622
else:

custom_components/evcc_intg/binary_sensor.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, add_
3636
lp_has_phase_auto_option = load_point_config["has_phase_auto_option"]
3737
lp_is_heating = load_point_config["is_heating"]
3838
lp_is_integrated = load_point_config["is_integrated"]
39+
lp_is_switch_device = load_point_config["is_switch_device"]
40+
lp_is_always_charge_present = load_point_config["is_always_charge_present"]
41+
lp_is_single_phase_only = load_point_config["only_single_phase"]
3942

4043
for a_stub in BINARY_ENTITIES_PER_LOADPOINT:
4144
if not lp_is_integrated or a_stub.integrated_supported:

custom_components/evcc_intg/button.py

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -44,30 +44,35 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, add_
4444
lp_has_phase_auto_option = load_point_config["has_phase_auto_option"]
4545
lp_is_heating = load_point_config["is_heating"]
4646
lp_is_integrated = load_point_config["is_integrated"]
47+
lp_is_switch_device = load_point_config["is_switch_device"]
48+
lp_is_always_charge_present = load_point_config["is_always_charge_present"]
49+
lp_is_single_phase_only = load_point_config["only_single_phase"]
4750

4851
for a_stub in BUTTONS_ENTITIES_PER_LOADPOINT:
49-
if not lp_is_integrated or a_stub.integrated_supported:
50-
force_enable_by_default = configuration_data_available and a_stub.tag.type == EP_TYPE.EVCCCONF
51-
the_key = a_stub.tag.entity_key if a_stub.tag.entity_key is not None else a_stub.tag.json_key
52-
description = ExtButtonEntityDescription(
53-
tag=a_stub.tag,
54-
lp_idx=lp_api_index,
55-
key=f"{lp_id_addon}_{the_key}",
56-
translation_key=the_key,
57-
name_addon=lp_name_addon if multi_loadpoint_config else None,
58-
icon=a_stub.icon,
59-
device_class=a_stub.device_class,
60-
unit_of_measurement=a_stub.unit_of_measurement,
61-
entity_category=a_stub.entity_category,
62-
entity_registry_enabled_default=True if force_enable_by_default else a_stub.entity_registry_enabled_default,
63-
is_lp_integrated_device=lp_is_integrated,
64-
65-
# the entity type specific values...
66-
payload=a_stub.payload
67-
)
68-
69-
entity = EvccButton(coordinator, description)
70-
entities.append(entity)
52+
# get rid of all stub's that are not supported by integrated devices
53+
if lp_is_integrated and not a_stub.integrated_supported:
54+
continue
55+
force_enable_by_default = configuration_data_available and a_stub.tag.type == EP_TYPE.EVCCCONF
56+
the_key = a_stub.tag.entity_key if a_stub.tag.entity_key is not None else a_stub.tag.json_key
57+
description = ExtButtonEntityDescription(
58+
tag=a_stub.tag,
59+
lp_idx=lp_api_index,
60+
key=f"{lp_id_addon}_{the_key}",
61+
translation_key=the_key,
62+
name_addon=lp_name_addon if multi_loadpoint_config else None,
63+
icon=a_stub.icon,
64+
device_class=a_stub.device_class,
65+
unit_of_measurement=a_stub.unit_of_measurement,
66+
entity_category=a_stub.entity_category,
67+
entity_registry_enabled_default=True if force_enable_by_default else a_stub.entity_registry_enabled_default,
68+
is_lp_integrated_device=lp_is_integrated,
69+
70+
# the entity type specific values...
71+
payload=a_stub.payload
72+
)
73+
74+
entity = EvccButton(coordinator, description)
75+
entities.append(entity)
7176

7277
add_entity_cb(entities)
7378

custom_components/evcc_intg/const.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,12 +494,29 @@ class ExtSwitchEntityDescription(SwitchEntityDescription):
494494
entity_registry_enabled_default=False
495495
),
496496
]
497+
497498
SELECT_ENTITIES_PER_LOADPOINT = [
499+
# THE MODE NOW DEPENDS ON FROM the `is_always_charge_present` @ Loadpoint config
500+
# we then enable the select entity for the loadpoint, if the loadpoint is NOT an
501+
# always-charge-present device we use the 'MODE_BEFORE_RENAMING' else the
502+
# 'MODE'
503+
ExtSelectEntityDescriptionStub(
504+
tag=Tag.MODE_RENAMED_IN_SUMMER_2026,
505+
#entity_category=EntityCategory.CONFIG,
506+
icon="mdi:state-machine"
507+
),
498508
ExtSelectEntityDescriptionStub(
499509
tag=Tag.MODE,
500510
#entity_category=EntityCategory.CONFIG,
501511
icon="mdi:state-machine"
502512
),
513+
ExtSelectEntityDescriptionStub(
514+
tag=Tag.ALWAYSCHARGE,
515+
#entity_category=EntityCategory.CONFIG,
516+
icon="mdi:sync"
517+
),
518+
519+
503520
ExtSelectEntityDescriptionStub(
504521
tag=Tag.PHASES,
505522
entity_category=EntityCategory.CONFIG,

custom_components/evcc_intg/number.py

Lines changed: 55 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -40,57 +40,63 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, add_
4040
lp_has_phase_auto_option = load_point_config["has_phase_auto_option"]
4141
lp_is_heating = load_point_config["is_heating"]
4242
lp_is_integrated = load_point_config["is_integrated"]
43+
lp_is_switch_device = load_point_config["is_switch_device"]
44+
lp_is_always_charge_present = load_point_config["is_always_charge_present"]
45+
lp_is_single_phase_only = load_point_config["only_single_phase"]
4346

4447
for a_stub in NUMBER_ENTITIES_PER_LOADPOINT:
45-
if not lp_is_integrated or a_stub.integrated_supported:
46-
force_celsius = lp_is_heating and a_stub.tag == Tag.LIMITSOC
47-
the_key = a_stub.tag.entity_key if a_stub.tag.entity_key is not None else a_stub.tag.json_key
48-
description = ExtNumberEntityDescription(
49-
tag=a_stub.tag,
50-
lp_idx=lp_api_index,
51-
key=f"{lp_id_addon}_{the_key}",
52-
translation_key=the_key,
53-
name_addon=lp_name_addon if multi_loadpoint_config else None,
54-
icon="mdi:thermometer" if force_celsius else a_stub.icon,
55-
device_class=SensorDeviceClass.TEMPERATURE if force_celsius else a_stub.device_class,
56-
unit_of_measurement=UnitOfTemperature.CELSIUS if force_celsius else a_stub.unit_of_measurement,
57-
entity_category=a_stub.entity_category,
58-
entity_registry_enabled_default=a_stub.entity_registry_enabled_default,
59-
is_lp_integrated_device=lp_is_integrated,
60-
61-
# the entity type specific values...
62-
max_value=a_stub.max_value,
63-
min_value=a_stub.min_value,
64-
mode=a_stub.mode,
65-
native_max_value=a_stub.native_max_value,
66-
native_min_value=a_stub.native_min_value,
67-
native_step=1 if force_celsius else a_stub.native_step,
68-
native_unit_of_measurement=UnitOfTemperature.CELSIUS if force_celsius else a_stub.native_unit_of_measurement,
69-
step=a_stub.step,
70-
)
71-
72-
if a_stub.tag in [Tag.SMARTCOSTLIMIT, Tag.SMARTFEEDINPRIORITYLIMIT, Tag.BATTERYGRIDCHARGELIMIT]:
73-
if coordinator._cost_type == "co2":
74-
description = replace(
75-
description,
76-
translation_key = f"{a_stub.tag.json_key}_co2",
77-
icon = "mdi:molecule-co2",
78-
native_max_value=500,
79-
native_min_value=0,
80-
native_step=5,
81-
native_unit_of_measurement="g/kWh"
82-
)
83-
84-
# for SEK, NOK, DKK we need to patch the maxvalue (1€ ~ 10 Krone)
85-
elif coordinator._currency != "€":
86-
description = replace(
87-
description,
88-
native_max_value = a_stub.native_max_value * 10,
89-
native_min_value = a_stub.native_min_value * 10
90-
)
91-
92-
entity = EvccNumber(coordinator, description)
93-
entities.append(entity)
48+
# get rid of all stub's that are not supported by integrated devices
49+
if lp_is_integrated and not a_stub.integrated_supported:
50+
continue
51+
52+
force_celsius = lp_is_heating and a_stub.tag == Tag.LIMITSOC
53+
the_key = a_stub.tag.entity_key if a_stub.tag.entity_key is not None else a_stub.tag.json_key
54+
description = ExtNumberEntityDescription(
55+
tag=a_stub.tag,
56+
lp_idx=lp_api_index,
57+
key=f"{lp_id_addon}_{the_key}",
58+
translation_key=the_key,
59+
name_addon=lp_name_addon if multi_loadpoint_config else None,
60+
icon="mdi:thermometer" if force_celsius else a_stub.icon,
61+
device_class=SensorDeviceClass.TEMPERATURE if force_celsius else a_stub.device_class,
62+
unit_of_measurement=UnitOfTemperature.CELSIUS if force_celsius else a_stub.unit_of_measurement,
63+
entity_category=a_stub.entity_category,
64+
entity_registry_enabled_default=a_stub.entity_registry_enabled_default,
65+
is_lp_integrated_device=lp_is_integrated,
66+
67+
# the entity type specific values...
68+
max_value=a_stub.max_value,
69+
min_value=a_stub.min_value,
70+
mode=a_stub.mode,
71+
native_max_value=a_stub.native_max_value,
72+
native_min_value=a_stub.native_min_value,
73+
native_step=1 if force_celsius else a_stub.native_step,
74+
native_unit_of_measurement=UnitOfTemperature.CELSIUS if force_celsius else a_stub.native_unit_of_measurement,
75+
step=a_stub.step,
76+
)
77+
78+
if a_stub.tag in [Tag.SMARTCOSTLIMIT, Tag.SMARTFEEDINPRIORITYLIMIT, Tag.BATTERYGRIDCHARGELIMIT]:
79+
if coordinator._cost_type == "co2":
80+
description = replace(
81+
description,
82+
translation_key = f"{a_stub.tag.json_key}_co2",
83+
icon = "mdi:molecule-co2",
84+
native_max_value=500,
85+
native_min_value=0,
86+
native_step=5,
87+
native_unit_of_measurement="g/kWh"
88+
)
89+
90+
# for SEK, NOK, DKK we need to patch the maxvalue (1€ ~ 10 Krone)
91+
elif coordinator._currency != "€":
92+
description = replace(
93+
description,
94+
native_max_value = a_stub.native_max_value * 10,
95+
native_min_value = a_stub.native_min_value * 10
96+
)
97+
98+
entity = EvccNumber(coordinator, description)
99+
entities.append(entity)
94100

95101
add_entity_cb(entities)
96102

custom_components/evcc_intg/pyevcc_ha/keys.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,10 +366,23 @@ def __str__(self):
366366
LP_VEHICLEWELCOMEACTIVE = ApiKey(json_key="vehicleWelcomeActive", type=EP_TYPE.LOADPOINTS)
367367

368368
# "mode": "off", -> (off/pv/minpv/now)
369-
MODE = ApiKey(
369+
MODE_RENAMED_IN_SUMMER_2026 = ApiKey(
370370
json_key="mode", type=EP_TYPE.LOADPOINTS,
371371
writeable=True, write_key="mode", options=["off", "pv", "minpv", "now"]
372372
)
373+
374+
# "mode": "off", -> (off/pv/minpv/now)
375+
MODE = ApiKey(
376+
json_key="mode", type=EP_TYPE.LOADPOINTS,
377+
writeable=True, write_key="mode", options=["off", "smart", "now"]
378+
)
379+
380+
# /api/loadpoints/{id}/alwayscharge/{off|on|once}
381+
ALWAYSCHARGE = ApiKey(
382+
json_key="alwaysCharge", type=EP_TYPE.LOADPOINTS,
383+
writeable=True, write_key="alwayscharge", options=["off", "on", "once"]
384+
)
385+
373386
# "limitSoc": 0, -> write 'limitsoc' in %
374387
LIMITSOC = ApiKey(json_key="limitSoc", type=EP_TYPE.LOADPOINTS, writeable=True, write_key="limitsoc")
375388

0 commit comments

Comments
 (0)