Skip to content

Commit

Permalink
* Fix double devices
Browse files Browse the repository at this point in the history
* Correct unique ids and names for integration and devices
* Add new senors heat_energy_input (parameters.Unknown_Parameter_1136) and domestic_water_energy_input (parameters.Unknown_Parameter_1137)
  • Loading branch information
BenPru committed Jan 7, 2023
1 parent cefad40 commit 9f0815b
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 35 deletions.
106 changes: 74 additions & 32 deletions custom_components/luxtronik/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_PORT, EVENT_HOMEASSISTANT_STOP
from homeassistant.core import Event, HomeAssistant
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.entity import DeviceInfo, EntityDescription
from homeassistant.helpers.typing import ConfigType

from luxtronik import LOGGER as LuxLogger

from .const import (
Expand Down Expand Up @@ -131,39 +131,32 @@ def setup_internal(hass, data, conf):
hass.data[f"{DOMAIN}_conf"] = conf

# Create DeviceInfos:
serial_number_date = luxtronik.get_value("parameters.ID_WP_SerienNummer_DATUM")
serial_number_hex = hex(
int(luxtronik.get_value("parameters.ID_WP_SerienNummer_HEX"))
)
serial_number = f"{serial_number_date}-{serial_number_hex}".replace("x", "")
model = luxtronik.get_value("calculations.ID_WEB_Code_WP_akt")

hass.data[f"{DOMAIN}_DeviceInfo"] = build_device_info(
luxtronik, serial_number, text_heatpump, data[CONF_HOST]
luxtronik, text_heatpump, data[CONF_HOST]
)
hass.data[f"{DOMAIN}_DeviceInfo_Domestic_Water"] = DeviceInfo(
identifiers={(DOMAIN, "Domestic_Water", serial_number)},
identifiers={(DOMAIN, f"{luxtronik.unique_id}_domestic_water")},
configuration_url="https://www.heatpump24.com/",
default_name=text_domestic_water,
name=text_domestic_water,
manufacturer=get_manufacturer_by_model(model),
model=model,
manufacturer=luxtronik.manufacturer,
model=luxtronik.model,
)
hass.data[f"{DOMAIN}_DeviceInfo_Heating"] = DeviceInfo(
identifiers={(DOMAIN, "Heating", serial_number)},
configuration_url=get_manufacturer_firmware_url_by_model(model),
identifiers={(DOMAIN, f"{luxtronik.unique_id}_heating")},
configuration_url=get_manufacturer_firmware_url_by_model(luxtronik.model),
default_name=text_heating,
name=text_heating,
manufacturer=get_manufacturer_by_model(model),
model=model,
manufacturer=luxtronik.manufacturer,
model=luxtronik.model,
)
hass.data[f"{DOMAIN}_DeviceInfo_Cooling"] = (
DeviceInfo(
identifiers={(DOMAIN, "Cooling", serial_number)},
identifiers={(DOMAIN, f"{luxtronik.unique_id}_cooling")},
default_name=text_cooling,
name=text_cooling,
manufacturer=get_manufacturer_by_model(model),
model=model,
manufacturer=luxtronik.manufacturer,
model=luxtronik.model,
)
if luxtronik.detect_cooling_present()
else None
Expand All @@ -185,39 +178,88 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
if luxtronik is None:
return True

unload_ok = False
try:
await hass.async_add_executor_job(luxtronik.disconnect)

await hass.services.async_remove(DOMAIN, SERVICE_WRITE)

unload_ok = await hass.config_entries.async_unload_platforms(
config_entry, PLATFORMS
)
if unload_ok:
hass.data[DOMAIN] = None
hass.data.pop(DOMAIN)

except Exception as e:
LOGGER.critical("Remove service!", e, exc_info=True)

unload_ok = await hass.config_entries.async_unload_platforms(
config_entry, PLATFORMS
)
if unload_ok:
hass.data[DOMAIN] = None
hass.data.pop(DOMAIN)

return unload_ok


def build_device_info(
luxtronik: LuxtronikDevice, sn: str, name: str, ip_host: str
luxtronik: LuxtronikDevice, name: str, ip_host: str
) -> DeviceInfo:
"""Build luxtronik device info."""
model = luxtronik.get_value("calculations.ID_WEB_Code_WP_akt")
device_info = DeviceInfo(
identifiers={(DOMAIN, "Heatpump", sn)}, # type: ignore
identifiers={
(
DOMAIN,
f"{luxtronik.unique_id}_heatpump",
)
},
configuration_url=f"http://{ip_host}/",
name=f"{name} S/N {sn}",
name=f"{name} {luxtronik.serial_number}",
default_name=name,
default_manufacturer="Alpha Innotec",
manufacturer=get_manufacturer_by_model(model),
manufacturer=luxtronik.manufacturer,
default_model="",
model=model,
model=luxtronik.model,
suggested_area="Utility room",
sw_version=luxtronik.get_value("calculations.ID_WEB_SoftStand"),
)
LOGGER.debug("build_device_info '%s'", device_info)
return device_info


async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
"""Migrate old entry."""
LOGGER.debug("Migrating from version %s", config_entry.version)

if config_entry.version == 1:
new = {**config_entry.data}
luxtronik = LuxtronikDevice.connect(new[CONF_HOST], new[CONF_PORT])

_delete_legacy_devices(hass, config_entry, luxtronik.unique_id)
config_entry.unique_id = luxtronik.unique_id
config_entry.title = f"{luxtronik.manufacturer} {luxtronik.model} {luxtronik.serial_number}"
config_entry.version = 2
hass.config_entries.async_update_entry(config_entry, data=new)

LOGGER.info("Migration to version %s successful", config_entry.version)

return True


def _identifiers_exists(
identifiers_list: list[set[tuple[str, str]]], identifiers: set[tuple[str, str]]
) -> bool:
for ident in identifiers_list:
if ident == identifiers:
return True
return False


def _delete_legacy_devices(hass: HomeAssistant, config_entry: ConfigEntry, unique_id: str):
dr_instance = dr.async_get(hass)
devices: list[dr.DeviceEntry] = dr.async_entries_for_config_entry(
dr_instance, config_entry.entry_id
)
identifiers_list = list()
identifiers_list.append({(DOMAIN, f"{unique_id}_heatpump")})
identifiers_list.append({(DOMAIN, f"{unique_id}_domestic_water")})
identifiers_list.append({(DOMAIN, f"{unique_id}_heating")})
identifiers_list.append({(DOMAIN, f"{unique_id}_cooling")})
for device in devices:
if not _identifiers_exists(identifiers_list, device.identifiers):
dr_instance.async_remove_device(device.id)
13 changes: 10 additions & 3 deletions custom_components/luxtronik/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,15 @@
LOGGER,
)
from .helpers.lux_helper import discover
from .luxtronik_device import LuxtronikDevice

# endregion Imports


class LuxtronikFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a Luxtronik heatpump controller config flow."""

VERSION = 1
VERSION = 2
_hassio_discovery = None
_discovery_host = None
_discovery_port = None
Expand Down Expand Up @@ -61,7 +62,9 @@ async def async_step_dhcp(self, discovery_info) -> FlowResult:
broadcast_discover_ip, broadcast_discover_port = discover()
if broadcast_discover_ip != discovery_info.ip:
return None
await self.async_set_unique_id(discovery_info.hostname)

luxtronik = LuxtronikDevice.connect(broadcast_discover_ip, broadcast_discover_port)
await self.async_set_unique_id(luxtronik.unique_id)
self._abort_if_unique_id_configured()

self._discovery_host = discovery_info.ip
Expand Down Expand Up @@ -103,7 +106,11 @@ async def async_step_user(
}
self._async_abort_entries_match(data)

return self.async_create_entry(title=user_input[CONF_HOST], data=data)
luxtronik = LuxtronikDevice.connect(user_input[CONF_HOST], user_input[CONF_PORT])

await self.async_set_unique_id(luxtronik.unique_id)
self._abort_if_unique_id_configured()
return self.async_create_entry(title=f"{luxtronik.manufacturer} {luxtronik.model} {luxtronik.serial_number}", data=data)

@staticmethod
@callback
Expand Down
30 changes: 30 additions & 0 deletions custom_components/luxtronik/luxtronik_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
MIN_TIME_BETWEEN_UPDATES,
)
from .helpers.debounce import debounce
from .helpers.lux_helper import get_manufacturer_by_model

# endregion Imports

Expand All @@ -38,6 +39,11 @@ def __init__(self, host: str, port: int, safe: bool, lock_timeout_sec: int) -> N
self._luxtronik = Lux(host, port, safe)
self.update()

@staticmethod
def connect(host: str, port: int):
"""Connect to heatpump."""
return LuxtronikDevice(host, port, False, 30)

async def async_will_remove_from_hass(self):
"""Disconnect from Luxtronik by stopping monitor."""
self.disconnect()
Expand Down Expand Up @@ -73,6 +79,30 @@ def get_sensor(self, group, sensor_id):
sensor = self._luxtronik.visibilities.get(sensor_id)
return sensor

@property
def serial_number(self) -> str:
"""Return the serial number."""
serial_number_date = self.get_value("parameters.ID_WP_SerienNummer_DATUM")
serial_number_hex = hex(
int(self.get_value("parameters.ID_WP_SerienNummer_HEX"))
)
return f"{serial_number_date}-{serial_number_hex}".replace("x", "")

@property
def unique_id(self) -> str:
"""Return the unique id."""
return self.serial_number.lower().replace("-", "_")

@property
def model(self) -> str:
"""Return the heatpump model."""
return self.get_value("calculations.ID_WEB_Code_WP_akt")

@property
def manufacturer(self) -> str:
"""Return the heatpump manufacturer."""
return get_manufacturer_by_model(self.model)

@property
def has_second_heat_generator(self) -> bool:
"""Is second heat generator activated 1=electrical heater"""
Expand Down
26 changes: 26 additions & 0 deletions custom_components/luxtronik/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,19 @@ async def async_setup_entry(
unit_of_measurement=ENERGY_KILO_WATT_HOUR,
entity_category=EntityCategory.DIAGNOSTIC,
),
LuxtronikSensor(
luxtronik,
device_info_heating,
sensor_key="parameters.Unknown_Parameter_1136",
unique_id="heat_energy_input",
name="Heat energy input",
icon="mdi:circle-slice-3",
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
unit_of_measurement=ENERGY_KILO_WATT_HOUR,
entity_category=EntityCategory.DIAGNOSTIC,
factor=0.01,
),
]

if luxtronik.get_value("calculations.ID_WEB_Temperatur_TRL_ext") != 5.0:
Expand Down Expand Up @@ -627,6 +640,19 @@ async def async_setup_entry(
unit_of_measurement=ENERGY_KILO_WATT_HOUR,
entity_category=EntityCategory.DIAGNOSTIC,
),
LuxtronikSensor(
luxtronik,
device_info_domestic_water,
sensor_key="parameters.Unknown_Parameter_1137",
unique_id="domestic_water_energy_input",
name="Domestic water energy input",
icon="mdi:circle-slice-3",
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
unit_of_measurement=ENERGY_KILO_WATT_HOUR,
entity_category=EntityCategory.DIAGNOSTIC,
factor=0.01,
),
]

# Temp. disabled:
Expand Down

0 comments on commit 9f0815b

Please sign in to comment.