Skip to content

Commit 8b05107

Browse files
authored
Merge pull request BenPru#476 from BenPru/add_TDI_selector
Add entities to set day for Thermal Desinfection, and Start/End date for Away mode
2 parents 577c661 + dfc7e10 commit 8b05107

File tree

11 files changed

+486
-2
lines changed

11 files changed

+486
-2
lines changed

custom_components/luxtronik/base.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,6 @@ def __init__(
6969
)
7070
else:
7171
self._attr_extra_state_attributes[field] = value
72-
if description.translation_key is None:
73-
description.translation_key = description.key.value
7472
if description.entity_registry_enabled_default:
7573
description.entity_registry_enabled_default = coordinator.entity_visible(
7674
description

custom_components/luxtronik/const.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
Platform.UPDATE,
3333
Platform.WATER_HEATER,
3434
Platform.CLIMATE,
35+
Platform.SELECT,
36+
Platform.DATE,
3537
]
3638
UPDATE_INTERVAL_FAST: Final = timedelta(seconds=10)
3739
UPDATE_INTERVAL_NORMAL: Final = timedelta(minutes=1)
@@ -272,6 +274,40 @@ class LuxSwitchoffReason(Enum):
272274
LUX_MODELS_OTHER = ["CB", "CI", "CN", "CS"]
273275
# endregion Lux Definitions
274276

277+
278+
class LuxDaySelectorParameter(StrEnum):
279+
"""Luxtronik parameters for day selector (TDI activation per weekday)."""
280+
281+
MONDAY: Final = "parameters.ID_Einst_BwTDI_akt_MO"
282+
TUESDAY: Final = "parameters.ID_Einst_BwTDI_akt_DI"
283+
WEDNESDAY: Final = "parameters.ID_Einst_BwTDI_akt_MI"
284+
THURSDAY: Final = "parameters.ID_Einst_BwTDI_akt_DO"
285+
FRIDAY: Final = "parameters.ID_Einst_BwTDI_akt_FR"
286+
SATURDAY: Final = "parameters.ID_Einst_BwTDI_akt_SA"
287+
SUNDAY: Final = "parameters.ID_Einst_BwTDI_akt_SO"
288+
289+
290+
DAY_NAME_TO_PARAM: Final[dict[str, LuxDaySelectorParameter]] = {
291+
"Monday": LuxDaySelectorParameter.MONDAY,
292+
"Tuesday": LuxDaySelectorParameter.TUESDAY,
293+
"Wednesday": LuxDaySelectorParameter.WEDNESDAY,
294+
"Thursday": LuxDaySelectorParameter.THURSDAY,
295+
"Friday": LuxDaySelectorParameter.FRIDAY,
296+
"Saturday": LuxDaySelectorParameter.SATURDAY,
297+
"Sunday": LuxDaySelectorParameter.SUNDAY,
298+
}
299+
300+
DAY_SELECTOR_OPTIONS: Final[list[str]] = [
301+
"None",
302+
"Monday",
303+
"Tuesday",
304+
"Wednesday",
305+
"Thursday",
306+
"Friday",
307+
"Saturday",
308+
"Sunday",
309+
]
310+
275311
# region Lux parameters
276312

277313

@@ -426,6 +462,11 @@ class LuxParameter(StrEnum):
426462
P1158_POWER_LIMIT_SWITCH: Final = "parameters.Unknown_Parameter_1158"
427463
P1159_POWER_LIMIT_VALUE: Final = "parameters.Unknown_Parameter_1159"
428464

465+
P0731_AWAY_HEATING_STARTDATE: Final = "parameters.ID_SU_FstdHz"
466+
P0006_AWAY_HEATING_ENDDATE: Final = "parameters.ID_SU_FrkdHz"
467+
P0732_AWAY_DHW_STARTDATE: Final = "parameters.ID_SU_FstdBw"
468+
P0007_AWAY_DHW_ENDDATE: Final = "parameters.ID_SU_FrkdBw"
469+
429470

430471
# endregion Lux parameters
431472

@@ -805,6 +846,12 @@ class SensorKey(StrEnum):
805846
PUMP_VENT_HUP = "pump_vent_hup"
806847
PUMP_VENT_TIMER_H = "pump_vent_timer_h"
807848
PUMP_VENT_ACTIVE = "pump_vent_active"
849+
THERMAL_DESINFECTION_DAY = "thermal_desinfection_day"
850+
851+
AWAY_HEATING_STARTDATE = "away_heating_startdate"
852+
AWAY_HEATING_ENDDATE = "away_heating_enddate"
853+
AWAY_DHW_STARTDATE = "away_dhw_startdate"
854+
AWAY_DHW_ENDDATE = "away_dhw_enddate"
808855

809856

810857
# endregion Keys
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
from __future__ import annotations
2+
3+
from datetime import date, datetime
4+
5+
from homeassistant.components.date import DateEntity, ENTITY_ID_FORMAT
6+
from homeassistant.config_entries import ConfigEntry
7+
from homeassistant.core import HomeAssistant, callback
8+
from homeassistant.exceptions import ConfigEntryNotReady
9+
from homeassistant.helpers.entity_platform import AddEntitiesCallback
10+
11+
from .base import LuxtronikEntity
12+
from .common import get_sensor_data, key_exists
13+
from .const import (
14+
CONF_COORDINATOR,
15+
CONF_HA_SENSOR_PREFIX,
16+
DOMAIN,
17+
LOGGER,
18+
DeviceKey,
19+
)
20+
from .coordinator import LuxtronikCoordinator, LuxtronikCoordinatorData
21+
from .date_entities_predefined import CALENDAR_ENTITIES
22+
from .model import LuxtronikDateEntityDescription
23+
24+
25+
async def async_setup_entry(
26+
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
27+
) -> None:
28+
data = hass.data.get(DOMAIN, {}).get(entry.entry_id)
29+
if not data or CONF_COORDINATOR not in data:
30+
raise ConfigEntryNotReady
31+
32+
coordinator: LuxtronikCoordinator = data[CONF_COORDINATOR]
33+
34+
if not coordinator.last_update_success:
35+
raise ConfigEntryNotReady
36+
37+
unavailable_keys = [
38+
i.luxtronik_key
39+
for i in CALENDAR_ENTITIES
40+
if not key_exists(coordinator.data, i.luxtronik_key)
41+
]
42+
if unavailable_keys:
43+
LOGGER.warning("Not present in Luxtronik data, skipping: %s", unavailable_keys)
44+
45+
async_add_entities(
46+
[
47+
LuxtronikDateEntity(
48+
hass, entry, coordinator, description, description.device_key
49+
)
50+
for description in CALENDAR_ENTITIES
51+
if (
52+
coordinator.entity_active(description)
53+
and key_exists(coordinator.data, description.luxtronik_key)
54+
)
55+
],
56+
True,
57+
)
58+
59+
60+
class LuxtronikDateEntity(LuxtronikEntity, DateEntity):
61+
"""Luxtronik Date Entity that supports user-editable dates."""
62+
63+
entity_description: LuxtronikDateEntityDescription
64+
65+
def __init__(
66+
self,
67+
hass: HomeAssistant,
68+
entry: ConfigEntry,
69+
coordinator: LuxtronikCoordinator,
70+
description: LuxtronikDateEntityDescription,
71+
device_info_ident: DeviceKey,
72+
) -> None:
73+
super().__init__(
74+
coordinator=coordinator,
75+
description=description,
76+
device_info_ident=device_info_ident,
77+
)
78+
79+
prefix = entry.data[CONF_HA_SENSOR_PREFIX]
80+
self.entity_id = ENTITY_ID_FORMAT.format(f"{prefix}_{description.key}")
81+
self._attr_unique_id = self.entity_id
82+
83+
@callback
84+
def _handle_coordinator_update(
85+
self, data: LuxtronikCoordinatorData | None = None
86+
) -> None:
87+
data = self.coordinator.data if data is None else data
88+
if data is None:
89+
return
90+
91+
value = get_sensor_data(data, self.entity_description.luxtronik_key.value)
92+
93+
if isinstance(value, (int, float)):
94+
try:
95+
dt_value = datetime.fromtimestamp(value)
96+
self._attr_native_value = dt_value.date()
97+
except (ValueError, OSError):
98+
self._attr_native_value = None
99+
elif isinstance(value, date):
100+
self._attr_native_value = value
101+
else:
102+
self._attr_native_value = None
103+
104+
self.async_write_ha_state()
105+
super()._handle_coordinator_update()
106+
107+
async def async_set_value(self, value: date) -> None:
108+
"""Handle user-set date from the UI."""
109+
self._attr_native_value = value
110+
timestamp = int(datetime.combine(value, datetime.min.time()).timestamp())
111+
await self.coordinator.async_write(
112+
self.entity_description.luxtronik_key.value.split(".")[1], timestamp
113+
)
114+
self.async_write_ha_state()
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from homeassistant.helpers.entity import EntityCategory
2+
3+
from .const import (
4+
DeviceKey,
5+
LuxParameter as LP,
6+
SensorKey as SK,
7+
)
8+
from .model import (
9+
LuxtronikDateEntityDescription,
10+
)
11+
12+
13+
CALENDAR_ENTITIES: list[LuxtronikDateEntityDescription] = [
14+
LuxtronikDateEntityDescription(
15+
key=SK.AWAY_DHW_STARTDATE,
16+
luxtronik_key=LP.P0732_AWAY_DHW_STARTDATE,
17+
device_key=DeviceKey.domestic_water,
18+
entity_category=EntityCategory.CONFIG,
19+
),
20+
LuxtronikDateEntityDescription(
21+
key=SK.AWAY_DHW_ENDDATE,
22+
luxtronik_key=LP.P0007_AWAY_DHW_ENDDATE,
23+
device_key=DeviceKey.domestic_water,
24+
entity_category=EntityCategory.CONFIG,
25+
),
26+
LuxtronikDateEntityDescription(
27+
key=SK.AWAY_HEATING_STARTDATE,
28+
luxtronik_key=LP.P0731_AWAY_HEATING_STARTDATE,
29+
device_key=DeviceKey.heating,
30+
entity_category=EntityCategory.CONFIG,
31+
),
32+
LuxtronikDateEntityDescription(
33+
key=SK.AWAY_HEATING_ENDDATE,
34+
luxtronik_key=LP.P0006_AWAY_HEATING_ENDDATE,
35+
device_key=DeviceKey.heating,
36+
entity_category=EntityCategory.CONFIG,
37+
),
38+
]

custom_components/luxtronik/model.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
ClimateEntityFeature,
1818
HVACMode,
1919
)
20+
from homeassistant.components.date import DateEntityDescription
2021
from homeassistant.components.number import NumberEntityDescription, NumberMode
2122
from homeassistant.components.sensor import SensorEntityDescription
2223
from homeassistant.components.switch import SwitchEntityDescription
@@ -220,3 +221,13 @@ class LuxtronikUpdateEntityDescription(
220221

221222
device_class = UpdateDeviceClass.FIRMWARE
222223
platform = Platform.UPDATE
224+
225+
226+
@dataclass
227+
class LuxtronikDateEntityDescription(
228+
LuxtronikEntityDescription,
229+
DateEntityDescription,
230+
):
231+
"""Class describing Luxtronik date entities."""
232+
233+
platform = Platform.DATE

0 commit comments

Comments
 (0)