Skip to content

Commit 28c4e5f

Browse files
committed
* Optimize solar detection
* Add diagnostics with all current sensor values * Trim recorder data
1 parent 87dc883 commit 28c4e5f

File tree

7 files changed

+154
-11
lines changed

7 files changed

+154
-11
lines changed

custom_components/luxtronik/binary_sensor.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
CONF_VISIBILITIES, DEFAULT_DEVICE_CLASS, DEVICE_CLASSES,
2727
DOMAIN, LOGGER,
2828
LUX_BINARY_SENSOR_ADDITIONAL_CIRCULATION_PUMP,
29-
LUX_BINARY_SENSOR_DOMESTIC_WATER_RECIRCULATION_PUMP,
3029
LUX_BINARY_SENSOR_CIRCULATION_PUMP_HEATING,
30+
LUX_BINARY_SENSOR_DOMESTIC_WATER_RECIRCULATION_PUMP,
3131
LUX_BINARY_SENSOR_EVU_UNLOCKED,
3232
LUX_BINARY_SENSOR_SOLAR_PUMP)
3333
from .helpers.helper import get_sensor_text
@@ -253,7 +253,6 @@ async def async_setup_entry(
253253

254254
deviceInfoDomesticWater = hass.data[f"{DOMAIN}_DeviceInfo_Domestic_Water"]
255255
if deviceInfoDomesticWater is not None:
256-
text_solar_pump = get_sensor_text(lang, "solar_pump")
257256
if luxtronik.has_domestic_water_circulation_pump:
258257
circulation_pump_unique_id = 'domestic_water_circulation_pump'
259258
text_domestic_water_circulation_pump = text_circulation_pump
@@ -282,6 +281,7 @@ async def async_setup_entry(
282281
]
283282
solar_present = luxtronik.detect_solar_present()
284283
if solar_present:
284+
text_solar_pump = get_sensor_text(lang, "solar_pump")
285285
entities += [
286286
LuxtronikBinarySensor(
287287
luxtronik=luxtronik,

custom_components/luxtronik/const.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ class LuxMode(Enum):
250250
]
251251
# endregion Luxtronik Sensor ids
252252

253-
LUX_DETECT_SOLAR_SENSOR: Final = "parameters.ID_BSTD_Solar"
253+
LUX_DETECT_SOLAR_SENSOR: Final = "visibilities.ID_Visi_Solar"
254254
LUX_MK_SENSORS = ['parameters.ID_Einst_MK1Typ_akt',
255255
'parameters.ID_Einst_MK2Typ_akt',
256256
'parameters.ID_Einst_MK3Typ_akt']
+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
"""Diagnostics support for Luxtronik."""
2+
from __future__ import annotations
3+
4+
from functools import partial
5+
from ipaddress import IPv6Address, ip_address
6+
7+
from async_timeout import timeout
8+
from getmac import get_mac_address
9+
from homeassistant.components.diagnostics import async_redact_data
10+
from homeassistant.config_entries import ConfigEntry
11+
from homeassistant.const import (CONF_HOST, CONF_PASSWORD, CONF_PORT,
12+
CONF_USERNAME)
13+
from homeassistant.core import HomeAssistant
14+
from homeassistant.helpers import device_registry
15+
from luxtronik import Luxtronik as Lux
16+
17+
from .const import CONF_COORDINATOR, DOMAIN
18+
19+
TO_REDACT = {CONF_USERNAME, CONF_PASSWORD}
20+
21+
22+
async def async_get_config_entry_diagnostics(
23+
hass: HomeAssistant, entry: ConfigEntry
24+
) -> dict:
25+
"""Return diagnostics for a config entry."""
26+
data: dict = entry.data
27+
client = Lux(data[CONF_HOST], data[CONF_PORT], True)
28+
client.read()
29+
30+
mac = ""
31+
async with timeout(10):
32+
mac = await _async_get_mac_address(hass, data[CONF_HOST])
33+
mac = mac[:9] + '*'
34+
35+
entry_data = async_redact_data(entry.as_dict(), TO_REDACT)
36+
if "data" not in entry_data:
37+
entry_data["data"] = {}
38+
entry_data["data"]["mac"] = mac
39+
diag_data = {
40+
"entry": entry_data,
41+
"parameters": _dump_items(client.parameters.parameters),
42+
"calculations": _dump_items(client.calculations.calculations),
43+
"visibilities": _dump_items(client.visibilities.visibilities),
44+
}
45+
return diag_data
46+
47+
48+
def _dump_items(items: dict) -> dict:
49+
dump = dict()
50+
for index, item in items.items():
51+
dump[f"{index:<4d} {item.name:<60}"] = f"{items.get(index)}"
52+
return dump
53+
54+
55+
async def _async_get_mac_address(hass: HomeAssistant, host: str) -> str | None:
56+
"""Get mac address from host name, IPv4 address, or IPv6 address."""
57+
# Help mypy, which has trouble with the async_add_executor_job + partial call
58+
mac_address: str | None
59+
# getmac has trouble using IPv6 addresses as the "hostname" parameter so
60+
# assume host is an IP address, then handle the case it's not.
61+
try:
62+
ip_addr = ip_address(host)
63+
except ValueError:
64+
mac_address = await hass.async_add_executor_job(
65+
partial(get_mac_address, hostname=host)
66+
)
67+
else:
68+
if ip_addr.version == 4:
69+
mac_address = await hass.async_add_executor_job(
70+
partial(get_mac_address, ip=host)
71+
)
72+
else:
73+
# Drop scope_id from IPv6 address by converting via int
74+
ip_addr = IPv6Address(int(ip_addr))
75+
mac_address = await hass.async_add_executor_job(
76+
partial(get_mac_address, ip6=str(ip_addr))
77+
)
78+
79+
if not mac_address:
80+
return None
81+
82+
return device_registry.format_mac(mac_address)
83+
84+

custom_components/luxtronik/luxtronik_device.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,10 @@ def detect_cooling_Mk(self):
107107

108108
def detect_solar_present(self):
109109
sensor_value = self.get_value(LUX_DETECT_SOLAR_SENSOR)
110-
SolarPresent = (sensor_value > 0.01)
110+
solar_koll = self.get_value("visibilities.ID_Visi_Temp_Solarkoll")
111+
solar_buffer = self.get_value("visibilities.ID_Visi_Temp_Solarsp")
112+
working_hours = self.get_value("parameters.ID_BSTD_Solar")
113+
SolarPresent = (sensor_value > 0 or working_hours > 0.01 or solar_koll > 0 or solar_buffer > 0)
111114
LOGGER.info(f"SolarPresent = {SolarPresent}")
112115
return SolarPresent
113116

custom_components/luxtronik/manifest.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"domain": "luxtronik2",
33
"name": "Luxtronik",
4-
"version": "2022.12.30",
4+
"version": "2023.01.05",
55
"integration_type": "hub",
66
"config_flow": true,
77
"iot_class": "local_polling",
@@ -10,7 +10,7 @@
1010
"dependencies": [],
1111
"after_dependencies": ["http"],
1212
"codeowners": ["@bouni", "@benpru"],
13-
"requirements": ["luxtronik>=0.3.14"],
13+
"requirements": ["luxtronik>=0.3.14", "getmac>=0.8.2"],
1414
"homeassistant": "2022.9.0",
1515
"dhcp": [
1616
{
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""Integration platform for recorder."""
2+
from __future__ import annotations
3+
4+
from homeassistant.core import HomeAssistant, callback
5+
6+
from .const import (ATTR_EXTRA_STATE_ATTRIBUTE_LAST_THERMAL_DESINFECTION,
7+
ATTR_STATUS_TEXT, DOMAIN)
8+
9+
10+
@callback
11+
def exclude_attributes(hass: HomeAssistant) -> set[str]:
12+
"""Exclude attributes from being recorded in the database."""
13+
return {
14+
ATTR_STATUS_TEXT,
15+
ATTR_EXTRA_STATE_ATTRIBUTE_LAST_THERMAL_DESINFECTION,
16+
'WP Seit (ID_WEB_Time_WPein_akt)',
17+
'ZWE1 seit (ID_WEB_Time_ZWE1_akt)',
18+
'ZWE2 seit (ID_WEB_Time_ZWE2_akt)',
19+
'Netzeinschaltv. (ID_WEB_Timer_EinschVerz)',
20+
'Schaltspielsperre SSP-Aus-Zeit (ID_WEB_Time_SSPAUS_akt)',
21+
'Schaltspielsperre SSP-Ein-Zeit (ID_WEB_Time_SSPEIN_akt)',
22+
'VD-Stand (ID_WEB_Time_VDStd_akt)',
23+
'Heizungsregler Mehr-Zeit HRM-Zeit (ID_WEB_Time_HRM_akt)',
24+
'Heizungsregler Weniger-Zeit HRW-Stand (ID_WEB_Time_HRW_akt)',
25+
'ID_WEB_Time_LGS_akt',
26+
'Sperre WW? ID_WEB_Time_SBW_akt',
27+
'Abtauen in ID_WEB_Time_AbtIn',
28+
'ID_WEB_Time_Heissgas',
29+
'switch_gap',
30+
f"sensor.{DOMAIN}_status_time",
31+
'status raw',
32+
'EVU first start time',
33+
'EVU first end time',
34+
'EVU second start time',
35+
'EVU second end time',
36+
'EVU minutes until next event',
37+
}

custom_components/luxtronik/sensor.py

+24-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Luxtronik heatpump sensor."""
22
# region Imports
3-
from typing import Any
43
from datetime import datetime, time
4+
from typing import Any
55

66
from homeassistant.components.sensor import (ENTITY_ID_FORMAT,
77
STATE_CLASS_MEASUREMENT,
@@ -588,7 +588,6 @@ async def async_setup_entry(
588588
text_operation_hours_domestic_water = get_sensor_text(
589589
lang, "operation_hours_domestic_water"
590590
)
591-
text_operation_hours_solar = get_sensor_text(lang, "operation_hours_solar")
592591
text_heat_amount_domestic_water = get_sensor_text(
593592
lang, "heat_amount_domestic_water"
594593
)
@@ -630,8 +629,22 @@ async def async_setup_entry(
630629
),
631630
]
632631

633-
solar_present = luxtronik.detect_solar_present()
634-
if solar_present:
632+
if luxtronik.get_value("visibilities.ID_Visi_Temp_Solarkoll") > 0:
633+
entities += [
634+
LuxtronikSensor(
635+
luxtronik,
636+
device_info_domestic_water,
637+
"calculations.ID_WEB_Temperatur_TSK",
638+
"solar_collector_temperature",
639+
f"Solar {text_collector}",
640+
"mdi:solar-panel-large",
641+
entity_category=None,
642+
),
643+
]
644+
# Temp. disabled:
645+
# solar_present = luxtronik.detect_solar_present()
646+
# if solar_present:
647+
if luxtronik.get_value("visibilities.ID_Visi_Temp_Solarkoll") > 0:
635648
entities += [
636649
LuxtronikSensor(
637650
luxtronik,
@@ -642,6 +655,9 @@ async def async_setup_entry(
642655
"mdi:solar-panel-large",
643656
entity_category=None,
644657
),
658+
]
659+
if luxtronik.get_value("visibilities.ID_Visi_Temp_Solarsp") > 0:
660+
entities += [
645661
LuxtronikSensor(
646662
luxtronik,
647663
device_info_domestic_water,
@@ -651,6 +667,10 @@ async def async_setup_entry(
651667
"mdi:propane-tank-outline",
652668
entity_category=None,
653669
),
670+
]
671+
if luxtronik.get_value("parameters.ID_BSTD_Solar") > 0:
672+
text_operation_hours_solar = get_sensor_text(lang, "operation_hours_solar")
673+
entities += [
654674
LuxtronikSensor(
655675
luxtronik,
656676
device_info_domestic_water,
@@ -664,7 +684,6 @@ async def async_setup_entry(
664684
entity_category=EntityCategory.DIAGNOSTIC,
665685
factor=SECOUND_TO_HOUR_FACTOR,
666686
),
667-
668687
]
669688

670689
deviceInfoCooling = hass.data[f"{DOMAIN}_DeviceInfo_Cooling"]

0 commit comments

Comments
 (0)