Skip to content

Commit

Permalink
Updated for HA 2025.1, added support for Kosovo, added Dutch translat…
Browse files Browse the repository at this point in the history
…ion, fixed IDs for calendar entities not being unique
  • Loading branch information
signalkraft committed Jan 4, 2025
1 parent 5093427 commit 6f14749
Show file tree
Hide file tree
Showing 10 changed files with 171 additions and 8 deletions.
20 changes: 20 additions & 0 deletions custom_components/mypyllant/calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,10 @@ def time_program(self) -> ZoneTimeProgram:
def name(self) -> str:
return self.name_prefix

@property
def unique_id(self) -> str:
return f"{DOMAIN}_{self.id_infix}_heating_calendar"

def _get_calendar_id_prefix(self):
return f"zone_heating_{self.zone.index}"

Expand Down Expand Up @@ -326,6 +330,10 @@ def time_program(self) -> ZoneTimeProgram:
def name(self) -> str:
return self.name_prefix

@property
def unique_id(self) -> str:
return f"{DOMAIN}_{self.id_infix}_coolingg_calendar"

def _get_calendar_id_prefix(self):
return f"zone_cooling_{self.zone.index}"

Expand Down Expand Up @@ -364,6 +372,10 @@ def time_program(self) -> DHWTimeProgram:
def name(self) -> str:
return self.name_prefix

@property
def unique_id(self) -> str:
return f"{DOMAIN}_{self.id_infix}_heating_calendar"

def _get_calendar_id_prefix(self):
return f"dhw_{self.domestic_hot_water.index}"

Expand Down Expand Up @@ -404,6 +416,10 @@ def time_program(self) -> DHWTimeProgram:
def name(self) -> str:
return f"Circulating Water in {self.name_prefix}"

@property
def unique_id(self) -> str:
return f"{DOMAIN}_{self.id_infix}_circulation_calendar"

def _get_calendar_id_prefix(self):
return f"dhw_circulation_{self.domestic_hot_water.index}"

Expand Down Expand Up @@ -443,6 +459,10 @@ def time_program(self) -> RoomTimeProgram:
def name(self) -> str:
return f"{self.name_prefix} Schedule"

@property
def unique_id(self) -> str:
return f"{DOMAIN}_{self.id_infix}_heating_calendar"

def _get_calendar_id_prefix(self):
return f"room_{self.room.room_index}"

Expand Down
9 changes: 9 additions & 0 deletions custom_components/mypyllant/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
DEFAULT_FETCH_EEBUS,
OPTION_DEFAULT_MANUAL_COOLING_DURATION,
DEFAULT_MANUAL_COOLING_DURATION,
OPTION_DEFAULT_DHW_LEGIONELLA_PROTECTION_TEMPERATURE,
DEFAULT_DHW_LEGIONELLA_PROTECTION_TEMPERATURE,
)

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -178,6 +180,13 @@ async def async_step_init(
DEFAULT_TIME_PROGRAM_OVERWRITE,
),
): bool,
vol.Required(
OPTION_DEFAULT_DHW_LEGIONELLA_PROTECTION_TEMPERATURE,
default=self.config_entry.options.get(
OPTION_DEFAULT_DHW_LEGIONELLA_PROTECTION_TEMPERATURE,
DEFAULT_DHW_LEGIONELLA_PROTECTION_TEMPERATURE,
),
): float,
vol.Required(
OPTION_BRAND,
default=self.config_entry.options.get(
Expand Down
7 changes: 6 additions & 1 deletion custom_components/mypyllant/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
OPTION_DEFAULT_QUICK_VETO_DURATION = "quick_veto_duration"
OPTION_DEFAULT_HOLIDAY_DURATION = "holiday_duration"
OPTION_DEFAULT_MANUAL_COOLING_DURATION = "manual_cooling_duration"
OPTION_DEFAULT_DHW_LEGIONELLA_PROTECTION_TEMPERATURE = (
"dhw_legionella_protection_temperature"
)
OPTION_COUNTRY = "country"
OPTION_BRAND = "brand"
OPTION_TIME_PROGRAM_OVERWRITE = "time_program_overwrite"
Expand All @@ -17,7 +20,7 @@
OPTION_FETCH_ENERGY_MANAGEMENT = "fetch_energy_management"
OPTION_FETCH_EEBUS = "fetch_eebus"
DEFAULT_UPDATE_INTERVAL = 60 # in seconds
DEFAULT_UPDATE_INTERVAL_DAILY = 3600 # in seconds
DEFAULT_UPDATE_INTERVAL_DAILY = 7200 # in seconds
DEFAULT_REFRESH_DELAY = 5 # in seconds
DEFAULT_MANUAL_COOLING_DURATION = 30 # in days
DEFAULT_COUNTRY = "germany"
Expand All @@ -29,9 +32,11 @@
DEFAULT_FETCH_ENERGY_MANAGEMENT = True
DEFAULT_FETCH_EEBUS = True
DEFAULT_MANUAL_SETPOINT_TYPE = ZoneOperatingType.HEATING
DEFAULT_DHW_LEGIONELLA_PROTECTION_TEMPERATURE = 70.0
QUOTA_PAUSE_INTERVAL = 3 * 3600 # in seconds
API_DOWN_PAUSE_INTERVAL = 15 * 60 # in seconds
HVAC_MODE_COOLING_FOR_DAYS = "COOLING_FOR_DAYS"
DHW_LEGIONELLA_PROTECTION_DATETIME = "dhw_legionella_protection_datetime"

SERVICE_SET_QUICK_VETO = "set_quick_veto"
SERVICE_SET_MANUAL_MODE_SETPOINT = "set_manual_mode_setpoint"
Expand Down
66 changes: 65 additions & 1 deletion custom_components/mypyllant/datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,17 @@
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from custom_components.mypyllant.const import DOMAIN, DEFAULT_HOLIDAY_SETPOINT
from custom_components.mypyllant.const import (
DOMAIN,
DEFAULT_HOLIDAY_SETPOINT,
DEFAULT_DHW_LEGIONELLA_PROTECTION_TEMPERATURE,
)
from custom_components.mypyllant.coordinator import SystemCoordinator
from custom_components.mypyllant.utils import (
HolidayEntity,
EntityList,
ManualCoolingEntity,
DomesticHotWaterCoordinatorEntity,
)
from myPyllant.utils import get_default_holiday_dates

Expand All @@ -39,6 +44,19 @@ async def async_setup_entry(
sensors.append(
lambda: SystemHolidayEndDateTimeEntity(index, coordinator, config)
)
for dhw_index, dhw in enumerate(system.domestic_hot_water):
if dhw.current_dhw_temperature is not None:
key = f"{DOMAIN}_{system.id}_{dhw_index}_legionella_protection_datetime"
if key not in hass.data[DOMAIN][config.entry_id]:
hass.data[DOMAIN][config.entry_id][key] = None
sensors.append(
lambda: DomesticHotWaterLegionellaProtectionDateTime(
index,
dhw_index,
coordinator,
hass.data[DOMAIN][config.entry_id][key],
)
)
if not system.control_identifier.is_vrc700 and system.is_cooling_allowed:
sensors.append(
lambda: SystemManualCoolingStartDateTimeEntity(
Expand Down Expand Up @@ -178,3 +196,49 @@ async def async_set_value(self, value: datetime) -> None:
@property
def unique_id(self) -> str:
return f"{DOMAIN}_{self.id_infix}_manual_cooling_end_date_time"


class DomesticHotWaterLegionellaProtectionDateTime(
DomesticHotWaterCoordinatorEntity, DateTimeEntity
):
_attr_icon = "mdi:temperature-water"
data: datetime | None = None

def __init__(
self, system_index: int, dhw_index: int, coordinator: SystemCoordinator, data
):
super().__init__(system_index, dhw_index, coordinator)
self.data = data

async def async_update(self) -> None:
"""
Save last active HVAC mode after update, so it can be restored in turn_on
"""
await super().async_update()

if self.enabled and self.legionella_protection_active:
self.data = datetime.now(tz=self.system.timezone)

@property
def name(self):
return f"{self.name_prefix} Legionella Protection Temperature Reached"

@property
def legionella_protection_active(self):
return (
self.domestic_hot_water.current_dhw_temperature
> DEFAULT_DHW_LEGIONELLA_PROTECTION_TEMPERATURE
)

@property
def native_value(self):
if self.legionella_protection_active:
self.data = datetime.now(tz=self.system.timezone)
return self.data

def set_value(self, value: datetime) -> None:
self.data = value

@property
def unique_id(self) -> str:
return f"{DOMAIN}_{self.id_infix}_legionella_protection_datetime"
5 changes: 3 additions & 2 deletions custom_components/mypyllant/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
],
"config_flow": true,
"documentation": "https://github.com/signalkraft/mypyllant-component#readme",
"homeassistant": "2025.1.0b0",
"integration_type": "hub",
"iot_class": "cloud_polling",
"issue_tracker": "https://github.com/signalkraft/mypyllant-component/issues",
"requirements": [
"myPyllant==0.8.36"
"myPyllant==0.9.0b0"
],
"version": "v0.8.22"
"version": "v0.9.0b0"
}
1 change: 1 addition & 0 deletions custom_components/mypyllant/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"time_program_overwrite": "Temperature controls overwrite time program instead of setting quick veto",
"default_holiday_setpoint": "Default temperature setpoint for away mode",
"manual_cooling_duration": "Default duration for manual cooling in days",
"dhw_legionella_protection_temperature": "Temperature above which legionella protection is considered active",
"country": "Country",
"brand": "Brand",
"fetch_rts": "Fetch real-time statistics (not supported on every system)",
Expand Down
61 changes: 61 additions & 0 deletions custom_components/mypyllant/translations/nl.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"config": {
"step": {
"user": {
"data": {
"password": "Wachtwoord",
"username": "Gebruikersnaam",
"country": "Land",
"brand": "Merk"
},
"title": "Inloggegevens",
"description": "Hetzelfde als de myVAILLANT-app"
}
},
"error": {
"login_endpoint_invalid": "Er is geen inlogmethode gevonden voor deze combinatie van merk en land",
"realm_invalid": "Voor dit merk moet een land worden geselecteerd",
"authentication_failed": "Authenticatie mislukt, controleer uw gebruikersnaam en wachtwoord en zorg ervoor dat u het juiste land en apparaatmerk heeft geselecteerd",
"unknown": "Onverwachte fout"
}
},
"entity": {
"climate": {
"mypyllant_zone": {
"state_attributes": {
"preset_mode": {
"state": {
"system_off": "Systeem uit",
"ventilation_boost": "Ventilatieboost",
"boost": "Quick Veto",
"away": "Vakantie"
}
}
}
}
}
},
"options": {
"step": {
"init": {
"data": {
"update_interval": "Seconden tussen updates (verkleint het risico op 'quota overschreden'-fouten en tijdelijke verbanningen)",
"update_interval_daily": "Seconden tussen updates van energiegegevens (verlagen van het risico op 'quota overschreden'-fouten en tijdelijke verbanningen)",
"refresh_delay": "Vertraging in seconden voordat gegevens worden vernieuwd na updates",
"quick_veto_duration": "Standaardduur in uren voor quick veto",
"holiday_duration": "Standaardduur in dagen voor de afwezigheidsmodus",
"time_program_overwrite": "Temperatuurregelaars overschrijven het tijdprogramma in plaats van de instelling quick veto",
"default_holiday_setpoint": "Standaard temperatuurinstelpunt voor afwezigheidsmodus",
"manual_cooling_duration": "Standaardduur voor handmatige koeling in dagen",
"country": "Land",
"brand": "Merk",
"fetch_rts": "Haal realtime statistieken op (niet op elk systeem ondersteund)",
"fetch_mpc": "Realtime energieverbruik ophalen (niet op elk systeem ondersteund)",
"fetch_ambisense_rooms": "Haal Ambisense kamerthermostaten op",
"fetch_energy_management": "Energiebeheergegevens ophalen",
"fetch_eebus": "Haal EEBUS-gegevens op"
}
}
}
}
}
4 changes: 2 additions & 2 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ PyYAML~=6.0.1
types-PyYAML~=6.0.12.20240311

# Need specific versions
pytest-homeassistant-custom-component==0.13.142
myPyllant==0.8.36
pytest-homeassistant-custom-component==0.13.200
myPyllant==0.9.0b0

# Versions handled by pytest-homeassistant-custom-component
freezegun
Expand Down
2 changes: 2 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ def __init__(
title="Mock Title",
state=None,
options={},
discovery_keys={},
pref_disable_new_entities=None,
pref_disable_polling=None,
unique_id=None,
Expand All @@ -137,6 +138,7 @@ def __init__(
"pref_disable_new_entities": pref_disable_new_entities,
"pref_disable_polling": pref_disable_polling,
"options": options,
"discovery_keys": discovery_keys,
"version": version,
"title": title,
"unique_id": unique_id,
Expand Down
4 changes: 2 additions & 2 deletions tests/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,15 @@ async def test_user_flow_minimum_fields(hass: HomeAssistant):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["type"] == data_entry_flow.FlowResultType.FORM
assert result["step_id"] == "user"

result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input=test_user_input,
)

assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["type"] == data_entry_flow.FlowResultType.FORM


@pytest.mark.parametrize("test_data", list_test_data())
Expand Down

0 comments on commit 6f14749

Please sign in to comment.