Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@
LuxtronikBinarySensorEntityDescription(
key=SensorKey.EVU_UNLOCKED,
luxtronik_key=LC.C0031_EVU_UNLOCKED,
icon="mdi:lock",
device_class=BinarySensorDeviceClass.LOCK,
icon="mdi:transmission-tower",
visibility=LV.V0121_EVU_LOCKED,
),
LuxtronikBinarySensorEntityDescription(
key=SensorKey.EVU2,
luxtronik_key=LC.C0185_EVU2,
icon="mdi:transmission-tower",
),
LuxtronikBinarySensorEntityDescription(
key=SensorKey.COMPRESSOR,
luxtronik_key=LC.C0044_COMPRESSOR,
Expand Down
21 changes: 21 additions & 0 deletions custom_components/luxtronik/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,17 @@ class LuxMode(StrEnum):
holidays: Final = "Holidays"


class LuxSmartGridStatus(StrEnum):
"""SmartGrid status based on EVU and EVU2 inputs."""

locked: Final = "evu_locked" # EVU=1, EVU2=0 - Status 1 - EVU lock
reduced: Final = "reduced_operation" # EVU=0, EVU2=0 - Status 2 - Reduced operation
normal: Final = "normal_operation" # EVU=0, EVU2=1 - Status 3 - Normal operation
increased: Final = (
"increased_operation" # EVU=1, EVU2=1 - Status 4 - Increased operation
)


class LuxStatus1Option(StrEnum):
"""LuxStatus1 option defrost etc."""

Expand Down Expand Up @@ -271,6 +282,13 @@ class LuxSwitchoffReason(Enum):
LuxOperationMode.cooling.value: "mdi:air-conditioner",
}

LUX_SMART_GRID_ICON_MAP: Final[dict[StateType | date | datetime | Decimal, str]] = {
LuxSmartGridStatus.locked.value: "mdi:cancel", # EVU lock - Blocked
LuxSmartGridStatus.reduced.value: "mdi:arrow-down-circle", # Reduced operation
LuxSmartGridStatus.normal.value: "mdi:check-circle", # Normal operation
LuxSmartGridStatus.increased.value: "mdi:arrow-up-circle", # Increased operation
}

LUX_MODELS_ALPHA_INNOTEC = ["LWP", "LWV", "MSW", "SWC", "SWP"]
LUX_MODELS_NOVELAN = ["BW", "LA", "LD", "LI", "SI", "ZLW"]
LUX_MODELS_OTHER = ["CB", "CI", "CN", "CS"]
Expand Down Expand Up @@ -591,6 +609,7 @@ class LuxCalculation(StrEnum):
C0180_HIGH_PRESSURE: Final = "calculations.ID_WEB_LIN_HD"
C0181_LOW_PRESSURE: Final = "calculations.ID_WEB_LIN_ND"
C0182_COMPRESSOR_HEATER: Final = "calculations.ID_WEB_LIN_VDH_out"
C0185_EVU2: Final = "calculations.ID_WEB_HZIO_EVU2"
# C0187_CURRENT_OUTPUT: Final = "calculations.ID_WEB_SEC_Qh_Soll"
# C0188_CURRENT_OUTPUT: Final = "calculations.ID_WEB_SEC_Qh_Ist"
C0204_HEAT_SOURCE_INPUT_TEMPERATURE: Final = "calculations.ID_WEB_Temperatur_TWE"
Expand Down Expand Up @@ -825,6 +844,8 @@ class SensorKey(StrEnum):
)
SOLAR_PUMP_MAX_TEMPERATURE_COLLECTOR = "solar_pump_max_temperature_collector"
EVU_UNLOCKED = "evu_unlocked"
EVU2 = "evu2"
SMART_GRID_STATUS = "smart_grid_status"
COMPRESSOR = "compressor"
COMPRESSOR2 = "compressor2"
PUMP_FLOW = "pump_flow"
Expand Down
73 changes: 71 additions & 2 deletions custom_components/luxtronik/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@
DeviceKey,
LuxCalculation as LC,
LuxOperationMode,
LuxSmartGridStatus,
LuxStatus1Option,
LuxStatus3Option,
SensorAttrKey as SA,
SensorKey,
)
from .coordinator import LuxtronikCoordinator, LuxtronikCoordinatorData
from .evu_helper import LuxtronikEVUTracker
Expand Down Expand Up @@ -84,7 +86,10 @@ async def async_setup_entry(
for description in SENSORS_STATUS
if (
coordinator.entity_active(description)
and key_exists(coordinator.data, description.luxtronik_key)
and (
description.luxtronik_key == LC.UNSET
or key_exists(coordinator.data, description.luxtronik_key)
)
)
],
True,
Expand Down Expand Up @@ -208,7 +213,71 @@ def _handle_coordinator_update(
self, data: LuxtronikCoordinatorData | None = None
) -> None:
"""Handle updated data from the coordinator."""
super()._handle_coordinator_update(data)
# Calculate SmartGrid Status from EVU and EVU2 inputs
if self.entity_description.key == SensorKey.SMART_GRID_STATUS:
# Check if SmartGrid is enabled (P1030)
from .const import LuxParameter as LP

smartgrid_enabled = self._get_value(LP.P1030_SMART_GRID_SWITCH)

# If SmartGrid is disabled, set sensor to unavailable
if not smartgrid_enabled or smartgrid_enabled in [
False,
0,
"false",
"False",
]:
self._attr_available = False
self._attr_native_value = None
else:
self._attr_available = True

evu = self._get_value(LC.C0031_EVU_UNLOCKED)
evu2 = self._get_value(LC.C0185_EVU2)

# Convert to boolean (handle True/False/1/0/"true"/"false")
evu_on = evu in [True, 1, "true", "True"]
evu2_on = evu2 in [True, 1, "true", "True"]

# Determine SmartGrid status based on EVU and EVU2 inputs
# EVU=0, EVU2=0 → Status 2 (reduced operation)
# EVU=1, EVU2=0 → Status 1 (EVU locked)
# EVU=0, EVU2=1 → Status 3 (normal operation)
# EVU=1, EVU2=1 → Status 4 (increased operation)
if evu_on and not evu2_on:
self._attr_native_value = (
LuxSmartGridStatus.locked.value
) # Status 1
elif not evu_on and not evu2_on:
self._attr_native_value = (
LuxSmartGridStatus.reduced.value
) # Status 2
elif not evu_on and evu2_on:
self._attr_native_value = (
LuxSmartGridStatus.normal.value
) # Status 3
else: # evu_on and evu2_on
self._attr_native_value = (
LuxSmartGridStatus.increased.value
) # Status 4

# Set icon based on current state
descr = self.entity_description
if (
descr.icon_by_state
and self._attr_native_value in descr.icon_by_state
):
self._attr_icon = descr.icon_by_state.get(self._attr_native_value)
elif descr.icon:
self._attr_icon = descr.icon

# Don't call super() to avoid setting value to None (luxtronik_key=UNSET)
self._enrich_extra_attributes()
self.async_write_ha_state()
else:
# For normal status sensors, use the parent's update logic
super()._handle_coordinator_update(data)

self._evu_tracker.update(self._attr_native_value)

if self._attr_native_value is None or self._last_state is None:
Expand Down
10 changes: 10 additions & 0 deletions custom_components/luxtronik/sensor_entities_predefined.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from homeassistant.helpers.entity import EntityCategory

from .const import (
LUX_SMART_GRID_ICON_MAP,
LUX_STATE_ICON_MAP,
SECOND_TO_HOUR_FACTOR,
UPDATE_INTERVAL_NORMAL,
Expand All @@ -23,6 +24,7 @@
LuxCalculation as LC,
LuxOperationMode,
LuxParameter as LP,
LuxSmartGridStatus,
LuxStatus1Option,
LuxStatus3Option,
LuxSwitchoffReason,
Expand Down Expand Up @@ -56,6 +58,14 @@
options=[e.value for e in LuxOperationMode],
update_interval=UPDATE_INTERVAL_NORMAL,
),
descr(
key=SensorKey.SMART_GRID_STATUS,
luxtronik_key=LC.UNSET, # Calculated from EVU and EVU2 inputs
icon_by_state=LUX_SMART_GRID_ICON_MAP,
device_class=SensorDeviceClass.ENUM,
options=[e.value for e in LuxSmartGridStatus],
update_interval=UPDATE_INTERVAL_NORMAL,
),
]

SENSORS_INDEX: list[descr] = [
Expand Down
12 changes: 12 additions & 0 deletions custom_components/luxtronik/translations/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"evu_unlocked": {
"name": "HDO"
},
"evu2": {
"name": "EVU2"
},
"compressor": {
"name": "Kompresor"
},
Expand Down Expand Up @@ -270,6 +273,15 @@
}
}
},
"smart_grid_status": {
"name": "Stav SmartGrid",
"state": {
"evu_locked": "Blokováno EVU",
"reduced_operation": "Snížený provoz",
"normal_operation": "Normální provoz",
"increased_operation": "Zvýšený provoz"
}
},
"error_reason": {
"name": "Posledn\u00ed chyba",
"state": {
Expand Down
12 changes: 12 additions & 0 deletions custom_components/luxtronik/translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"evu_unlocked": {
"name": "Sperrzeit Freigabe"
},
"evu2": {
"name": "EVU2"
},
"compressor": {
"name": "Verdichter"
},
Expand Down Expand Up @@ -270,6 +273,15 @@
}
}
},
"smart_grid_status": {
"name": "SmartGrid Status",
"state": {
"evu_locked": "EVU-Sperre",
"reduced_operation": "abgesenkte Betriebsweise",
"normal_operation": "Normalbetrieb",
"increased_operation": "erhöhte Betriebsweise"
}
},
"error_reason": {
"name": "Letzter Fehler",
"state": {
Expand Down
12 changes: 12 additions & 0 deletions custom_components/luxtronik/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"evu_unlocked": {
"name": "Locktime"
},
"evu2": {
"name": "EVU2"
},
"compressor": {
"name": "Compressor"
},
Expand Down Expand Up @@ -273,6 +276,15 @@
}
}
},
"smart_grid_status": {
"name": "SmartGrid Status",
"state": {
"evu_locked": "EVU locked",
"reduced_operation": "Reduced operation",
"normal_operation": "Normal operation",
"increased_operation": "Increased operation"
}
},
"error_reason": {
"name": "Last error",
"state": {
Expand Down
12 changes: 12 additions & 0 deletions custom_components/luxtronik/translations/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"evu_unlocked": {
"name": "Tijdvrijgave blokkeren"
},
"evu2": {
"name": "EVU2"
},
"compressor": {
"name": "Compressor"
},
Expand Down Expand Up @@ -267,6 +270,15 @@
}
}
},
"smart_grid_status": {
"name": "SmartGrid Status",
"state": {
"evu_locked": "EVU geblokkeerd",
"reduced_operation": "Verminderde werking",
"normal_operation": "Normale werking",
"increased_operation": "Verhoogde werking"
}
},
"error_reason": {
"name": "Laatste fout",
"state": {
Expand Down
12 changes: 12 additions & 0 deletions custom_components/luxtronik/translations/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"evu_unlocked": {
"name": "Czas blokady"
},
"evu2": {
"name": "EVU2"
},
"compressor": {
"name": "Kompresor"
},
Expand Down Expand Up @@ -261,6 +264,15 @@
}
}
},
"smart_grid_status": {
"name": "Status SmartGrid",
"state": {
"evu_locked": "Zablokowane EVU",
"reduced_operation": "Praca obniżona",
"normal_operation": "Praca normalna",
"increased_operation": "Praca zwiększona"
}
},
"error_reason": {
"name": "Ostatni b\u0142\u0105d",
"state": {
Expand Down
Loading