Skip to content

Commit b1c80e7

Browse files
authored
Merge pull request #26 from rhammen/main
Enhanced manufacturer detection and changed cooling present detection
2 parents f33cfb6 + dc88317 commit b1c80e7

File tree

7 files changed

+155
-59
lines changed

7 files changed

+155
-59
lines changed

custom_components/luxtronik/__init__.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
DOMAIN,
2020
LANG_DEFAULT,
2121
LOGGER,
22-
LUX_SENSOR_DETECT_COOLING,
2322
PLATFORMS,
2423
SERVICE_WRITE,
2524
SERVICE_WRITE_SCHEMA,
@@ -123,28 +122,37 @@ def setup_internal(hass, data, conf):
123122

124123
hass.data[DOMAIN] = luxtronik
125124
hass.data[f"{DOMAIN}_conf"] = conf
125+
126126
# Create DeviceInfos:
127127
serial_number = luxtronik.get_value("parameters.ID_WP_SerienNummer_DATUM")
128+
model = luxtronik.get_value("calculations.ID_WEB_Code_WP_akt")
129+
128130
hass.data[f"{DOMAIN}_DeviceInfo"] = build_device_info(
129131
luxtronik, serial_number, text_heatpump
130132
)
131133
hass.data[f"{DOMAIN}_DeviceInfo_Domestic_Water"] = DeviceInfo(
132134
identifiers={(DOMAIN, "Domestic_Water", serial_number)},
133135
default_name=text_domestic_water,
134136
name=text_domestic_water,
137+
manufacturer=get_manufacturer_by_model(model),
138+
model=model,
135139
)
136140
hass.data[f"{DOMAIN}_DeviceInfo_Heating"] = DeviceInfo(
137141
identifiers={(DOMAIN, "Heating", serial_number)},
138142
default_name=text_heating,
139143
name=text_heating,
144+
manufacturer=get_manufacturer_by_model(model),
145+
model=model,
140146
)
141147
hass.data[f"{DOMAIN}_DeviceInfo_Cooling"] = (
142148
DeviceInfo(
143149
identifiers={(DOMAIN, "Cooling", serial_number)},
144150
default_name=text_cooling,
145151
name=text_cooling,
152+
manufacturer=get_manufacturer_by_model(model),
153+
model=model,
146154
)
147-
if luxtronik.get_value(LUX_SENSOR_DETECT_COOLING)
155+
if luxtronik.detect_cooling_present()
148156
else None
149157
)
150158
return True

custom_components/luxtronik/binary_sensor.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,8 @@ async def async_setup_entry(
233233
]
234234

235235
deviceInfoDomesticWater = hass.data[f"{DOMAIN}_DeviceInfo_Domestic_Water"]
236-
if deviceInfoDomesticWater is not None:
236+
solar_present = luxtronik.detect_solar_present()
237+
if (deviceInfoDomesticWater is not None) & (solar_present):
237238
text_solar_pump = get_sensor_text(lang, "solar_pump")
238239
entities += [
239240
LuxtronikBinarySensor(

custom_components/luxtronik/const.py

+17-3
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ class LuxMode(Enum):
125125
holidays: Final = "Holidays"
126126

127127

128+
128129
# endregion Lux Modes
129130

130131

@@ -190,7 +191,6 @@ class LuxMode(Enum):
190191

191192

192193
# region Luxtronik Sensor ids
193-
LUX_SENSOR_DETECT_COOLING: Final = "calculations.ID_WEB_FreigabKuehl"
194194
LUX_SENSOR_STATUS: Final = "calculations.ID_WEB_WP_BZ_akt"
195195
LUX_SENSOR_STATUS1: Final = "calculations.ID_WEB_HauptMenuStatus_Zeile1"
196196
LUX_SENSOR_STATUS3: Final = "calculations.ID_WEB_HauptMenuStatus_Zeile3"
@@ -218,8 +218,6 @@ class LuxMode(Enum):
218218
LUX_SENSOR_COOLING_STOP_DELAY: Final = "parameters.ID_Einst_Kuhl_Zeit_Aus_akt"
219219
LUX_SENSOR_COOLING_TARGET: Final = "parameters.ID_Sollwert_KuCft2_akt"
220220
LUX_SENSOR_MODE_COOLING: Final = "parameters.ID_Einst_BA_Kuehl_akt"
221-
# Future use:
222-
# LUX_SENSOR_MODE_COOLING: Final = 'parameters.ID_Einst_BA_Kuehl_akt'
223221

224222
LUX_SENSOR_DOMESTIC_WATER_CURRENT_TEMPERATURE: Final = (
225223
"calculations.ID_WEB_Temperatur_TBW"
@@ -242,6 +240,21 @@ class LuxMode(Enum):
242240
]
243241
# endregion Luxtronik Sensor ids
244242

243+
LUX_DETECT_SOLAR_SENSOR: Final = "parameters.ID_BSTD_Solar"
244+
LUX_MK_SENSORS = ['parameters.ID_Einst_MK1Typ_akt',
245+
'parameters.ID_Einst_MK2Typ_akt',
246+
'parameters.ID_Einst_MK3Typ_akt']
247+
248+
class LuxMkTypes(Enum):
249+
off: Final = 0
250+
discharge: Final = 1
251+
load: Final = 2
252+
cooling: Final = 3
253+
heating_cooling: Final = 4
254+
255+
LUX_MODELS_AlphaInnotec = ['LWP','LWV','MSW','SWC','SWP']
256+
LUX_MODELS_Novelan = ['BW','LA','LD','LI','SI','ZLW']
257+
245258
GLOBAL_STATUS_SENSOR_TYPES: tuple[LuxtronikSensorEntityDescription, ...] = (
246259
LuxtronikSensorEntityDescription(
247260
key="status",
@@ -535,3 +548,4 @@ class LuxMode(Enum):
535548
CONF_FLOW: "l/h",
536549
}
537550
# endregion Legacy consts
551+

custom_components/luxtronik/helpers/lux_helper.py

+10-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
"""Helper for luxtronik heatpump module."""
22
import socket
33

4-
from ..const import LOGGER
5-
6-
4+
from ..const import (
5+
LOGGER,
6+
LUX_MODELS_AlphaInnotec,
7+
LUX_MODELS_Novelan
8+
)
9+
710
def discover():
811
"""Broadcast discovery for luxtronik heatpumps."""
912

@@ -52,8 +55,11 @@ def discover():
5255

5356
def get_manufacturer_by_model(model: str) -> str:
5457
"""Return the manufacturer."""
58+
5559
if model is None:
5660
return None
57-
if model.startswith("LD"):
61+
if model.startswith(tuple(LUX_MODELS_Novelan)):
5862
return "Novelan"
63+
if model.startswith(tuple(LUX_MODELS_AlphaInnotec)):
64+
return "Alpha Innotec"
5965
return None

custom_components/luxtronik/luxtronik_device.py

+45-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
# region Imports
33
import threading
44
import time
5-
5+
import re
6+
67
from luxtronik import Luxtronik as Lux
78
from homeassistant.util import Throttle
89

@@ -12,6 +13,8 @@
1213
CONF_VISIBILITIES,
1314
LOGGER,
1415
MIN_TIME_BETWEEN_UPDATES,
16+
LUX_MK_SENSORS, LuxMkTypes,
17+
LUX_DETECT_SOLAR_SENSOR
1518
)
1619
from .helpers.debounce import debounce
1720

@@ -67,6 +70,47 @@ def get_sensor(self, group, sensor_id):
6770
sensor = self._luxtronik.visibilities.get(sensor_id)
6871
return sensor
6972

73+
74+
def detect_cooling_Mk(self):
75+
""" returns list of parameters that are may show cooling is enabled """
76+
coolingMk = []
77+
for Mk in LUX_MK_SENSORS:
78+
sensor_value = self.get_value(Mk)
79+
#LOGGER.info(f"{Mk} = {sensor_value}")
80+
if sensor_value in [LuxMkTypes.cooling.value,
81+
LuxMkTypes.heating_cooling.value]:
82+
coolingMk = coolingMk + [Mk]
83+
84+
LOGGER.info(f"CoolingMk = {coolingMk}")
85+
return coolingMk
86+
87+
def detect_solar_present(self):
88+
sensor_value = self.get_value(LUX_DETECT_SOLAR_SENSOR)
89+
SolarPresent = (sensor_value > 0.01)
90+
LOGGER.info(f"SolarPresent = {SolarPresent}")
91+
return SolarPresent
92+
93+
94+
def detect_cooling_present(self):
95+
""" returns True if Cooling is present """
96+
CoolingPresent = (len(self.detect_cooling_Mk()) > 0)
97+
LOGGER.info(f"CoolingPresent = {CoolingPresent}")
98+
return CoolingPresent
99+
100+
101+
def detect_cooling_target_temperature_sensor(self):
102+
""" if only 1 MK parameter related to cooling is returned
103+
return the corresponding colloing_target_temperature sensor"""
104+
Mk_param = self.detect_cooling_Mk()
105+
if len(Mk_param) == 1:
106+
Mk = re.findall('[0-9]+', Mk_param[0])[0]
107+
cooling_target_temperature_sensor = f"parameters.ID_Sollwert_KuCft{Mk}_akt"
108+
else:
109+
cooling_target_temperature_sensor = None
110+
LOGGER.info(f"cooling_target_temperature_sensor = '{cooling_target_temperature_sensor}' ")
111+
return cooling_target_temperature_sensor
112+
113+
70114
def write(
71115
self, parameter, value, use_debounce=True, update_immediately_after_write=False
72116
):

custom_components/luxtronik/number.py

+22-14
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
LUX_SENSOR_COOLING_THRESHOLD,
2222
LUX_SENSOR_COOLING_START_DELAY,
2323
LUX_SENSOR_COOLING_STOP_DELAY,
24-
LUX_SENSOR_COOLING_TARGET,
2524
LUX_SENSOR_DOMESTIC_WATER_TARGET_TEMPERATURE,
2625
LUX_SENSOR_HEATING_CIRCUIT_CURVE1_TEMPERATURE,
2726
LUX_SENSOR_HEATING_CIRCUIT_CURVE2_TEMPERATURE,
@@ -82,19 +81,14 @@ async def async_setup_entry(
8281

8382
deviceInfoHeating = hass.data[f"{DOMAIN}_DeviceInfo_Heating"]
8483
if deviceInfoHeating is not None:
85-
text_heating_room_temperature_impact_factor = get_sensor_text(lang, 'heating_room_temperature_impact_factor')
8684
text_heating_threshold = get_sensor_text(lang, 'heating_threshold')
8785
text_correction = get_sensor_text(lang, 'correction')
8886
text_min_flow_out_temperature = get_sensor_text(lang, 'min_flow_out_temperature')
8987
text_heating_circuit_curve1_temperature = get_sensor_text(lang, 'circuit_curve1_temperature')
9088
text_heating_circuit_curve2_temperature = get_sensor_text(lang, 'circuit_curve2_temperature')
9189
text_heating_circuit_curve_night_temperature = get_sensor_text(lang, 'circuit_curve_night_temperature')
90+
has_room_temp = luxtronik.get_value("parameters.ID_Einst_RFVEinb_akt") != 0
9291
entities += [
93-
LuxtronikNumber(
94-
hass, luxtronik, deviceInfoHeating,
95-
number_key=LUX_SENSOR_HEATING_ROOM_TEMPERATURE_IMPACT_FACTOR,
96-
unique_id='heating_room_temperature_impact_factor', name=f"{text_heating_room_temperature_impact_factor}",
97-
icon='mdi:thermometer-chevron-up', unit_of_measurement=PERCENTAGE, min_value=100, max_value=200, step=5, mode=MODE_AUTO, entity_category=EntityCategory.CONFIG),
9892
LuxtronikNumber(
9993
hass, luxtronik, deviceInfoHeating,
10094
number_key=LUX_SENSOR_HEATING_TARGET_CORRECTION,
@@ -127,6 +121,16 @@ async def async_setup_entry(
127121
icon='mdi:chart-bell-curve', unit_of_measurement=TEMP_CELSIUS, min_value=-15.0, max_value=10.0, step=0.5, mode=MODE_BOX, entity_category=EntityCategory.CONFIG)
128122
]
129123

124+
if has_room_temp:
125+
text_heating_room_temperature_impact_factor = get_sensor_text(lang, 'heating_room_temperature_impact_factor')
126+
entities += [
127+
LuxtronikNumber(
128+
hass, luxtronik, deviceInfoHeating,
129+
number_key=LUX_SENSOR_HEATING_ROOM_TEMPERATURE_IMPACT_FACTOR,
130+
unique_id='heating_room_temperature_impact_factor', name=f"{text_heating_room_temperature_impact_factor}",
131+
icon='mdi:thermometer-chevron-up', unit_of_measurement=PERCENTAGE, min_value=100, max_value=200, step=10, mode=MODE_BOX, entity_category=EntityCategory.CONFIG)
132+
]
133+
130134
deviceInfoDomesticWater = hass.data[f"{DOMAIN}_DeviceInfo_Domestic_Water"]
131135
if deviceInfoDomesticWater is not None:
132136
text_target = get_sensor_text(lang, 'target')
@@ -149,6 +153,7 @@ async def async_setup_entry(
149153
lang, 'cooling_stop_delay_hours')
150154
text_cooling_target_temperature = get_sensor_text(
151155
lang, 'cooling_target_temperature')
156+
152157
entities += [
153158
LuxtronikNumber(hass, luxtronik, deviceInfoCooling,
154159
number_key=LUX_SENSOR_COOLING_THRESHOLD,
@@ -157,13 +162,6 @@ async def async_setup_entry(
157162
icon='mdi:sun-thermometer',
158163
unit_of_measurement=TEMP_CELSIUS,
159164
min_value=18.0, max_value=30.0, step=0.5, mode=MODE_BOX),
160-
LuxtronikNumber(hass, luxtronik, deviceInfoCooling,
161-
number_key=LUX_SENSOR_COOLING_TARGET,
162-
unique_id='cooling_target_temperature',
163-
name=f"{text_cooling_target_temperature}",
164-
icon='mdi:snowflake-thermometer',
165-
unit_of_measurement=TEMP_CELSIUS,
166-
min_value=18.0, max_value=25.0, step=1.0, mode=MODE_BOX),
167165
LuxtronikNumber(hass, luxtronik, deviceInfoCooling,
168166
number_key=LUX_SENSOR_COOLING_START_DELAY,
169167
unique_id='cooling_start_delay_hours',
@@ -179,6 +177,16 @@ async def async_setup_entry(
179177
unit_of_measurement=TIME_HOURS,
180178
min_value=0.0, max_value=12.0, step=0.5, mode=MODE_BOX),
181179
]
180+
LUX_SENSOR_COOLING_TARGET = luxtronik.detect_cooling_target_temperature_sensor()
181+
entities += [
182+
LuxtronikNumber(hass, luxtronik, deviceInfoCooling,
183+
number_key=LUX_SENSOR_COOLING_TARGET,
184+
unique_id='cooling_target_temperature',
185+
name=f"{text_cooling_target_temperature}",
186+
icon='mdi:snowflake-thermometer',
187+
unit_of_measurement=TEMP_CELSIUS,
188+
min_value=18.0, max_value=25.0, step=1.0, mode=MODE_BOX)
189+
] if LUX_SENSOR_COOLING_TARGET != None else []
182190

183191
async_add_entities(entities)
184192
# endregion Setup

0 commit comments

Comments
 (0)