Skip to content

Commit

Permalink
Added Manual Cooling Duration on all controllers, Added option for de…
Browse files Browse the repository at this point in the history
…fault Manual Cooling Duration in Days, Refactored some constants
  • Loading branch information
signalkraft committed Jul 5, 2024
1 parent fffd9ff commit f8150e9
Show file tree
Hide file tree
Showing 17 changed files with 142 additions and 55 deletions.
6 changes: 3 additions & 3 deletions custom_components/mypyllant/calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
RoomTimeProgram,
System,
)
from myPyllant.enums import ZoneTimeProgramType
from myPyllant.enums import ZoneOperatingType

from . import SystemCoordinator
from .const import DOMAIN, WEEKDAYS_TO_RFC5545, RFC5545_TO_WEEKDAYS
Expand Down Expand Up @@ -309,7 +309,7 @@ def build_event(

async def update_time_program(self):
await self.coordinator.api.set_zone_time_program(
self.zone, str(ZoneTimeProgramType.HEATING), self.time_program
self.zone, str(ZoneOperatingType.HEATING), self.time_program
)
await self.coordinator.async_request_refresh_delayed()

Expand Down Expand Up @@ -348,7 +348,7 @@ def build_event(

async def update_time_program(self):
await self.coordinator.api.set_zone_time_program(
self.zone, str(ZoneTimeProgramType.COOLING), self.time_program
self.zone, str(ZoneOperatingType.COOLING), self.time_program
)
await self.coordinator.async_request_refresh_delayed()

Expand Down
85 changes: 48 additions & 37 deletions custom_components/mypyllant/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,7 @@
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.template import as_datetime
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from myPyllant.const import (
DEFAULT_MANUAL_SETPOINT_TYPE,
DEFAULT_QUICK_VETO_DURATION,
ZONE_MANUAL_SETPOINT_TYPES,
)
from myPyllant.const import DEFAULT_QUICK_VETO_DURATION
from myPyllant.models import (
System,
Zone,
Expand All @@ -47,6 +43,7 @@
ZoneCurrentSpecialFunction,
CircuitState,
AmbisenseRoomOperationMode,
ZoneOperatingType,
)

from custom_components.mypyllant.utils import (
Expand Down Expand Up @@ -78,14 +75,15 @@
SERVICE_SET_TIME_CONTROLLED_COOLING_SETPOINT,
SERVICE_SET_VENTILATION_BOOST,
SERVICE_CANCEL_VENTILATION_BOOST,
DEFAULT_MANUAL_SETPOINT_TYPE,
)
from .ventilation_climate import _FAN_STAGE_TYPE_OPTIONS, VentilationClimate

_LOGGER = logging.getLogger(__name__)

_ZONE_MANUAL_SETPOINT_TYPES_OPTIONS = [
selector.SelectOptionDict(value=k, label=v)
for k, v in ZONE_MANUAL_SETPOINT_TYPES.items()
selector.SelectOptionDict(value=v.value, label=v.title())
for v in list(ZoneOperatingType)
]

_ZONE_OPERATING_MODE_OPTIONS = [
Expand All @@ -95,22 +93,6 @@
)
]

ZONE_PRESET_MAP = {
PRESET_BOOST: ZoneCurrentSpecialFunction.QUICK_VETO,
PRESET_ECO: ZoneCurrentSpecialFunction.NONE,
PRESET_NONE: ZoneCurrentSpecialFunction.NONE,
PRESET_AWAY: ZoneCurrentSpecialFunction.HOLIDAY,
"system_off": ZoneCurrentSpecialFunction.SYSTEM_OFF,
"ventilation_boost": ZoneCurrentSpecialFunction.VENTILATION_BOOST,
}

ZONE_PRESET_MAP_VRC700 = {
ZoneOperatingModeVRC700.OFF: PRESET_NONE,
ZoneOperatingModeVRC700.DAY: PRESET_COMFORT,
ZoneOperatingModeVRC700.AUTO: PRESET_NONE,
ZoneOperatingModeVRC700.SET_BACK: PRESET_ECO,
}

ZONE_HVAC_ACTION_MAP = {
CircuitState.STANDBY: HVACAction.IDLE,
CircuitState.HEATING: HVACAction.HEATING,
Expand Down Expand Up @@ -420,6 +402,27 @@ def hvac_mode_map(self):
mode_map[HVAC_MODE_COOLING_FOR_DAYS] = HVACMode.COOL
return mode_map

@property
def preset_mode_map(self):
if self.zone.control_identifier.is_vrc700:
return {
ZoneOperatingModeVRC700.OFF: PRESET_NONE,
ZoneOperatingModeVRC700.DAY: PRESET_COMFORT,
ZoneOperatingModeVRC700.AUTO: PRESET_NONE,
ZoneOperatingModeVRC700.SET_BACK: PRESET_ECO,
}
else:
preset_modes = {
PRESET_BOOST: ZoneCurrentSpecialFunction.QUICK_VETO,
PRESET_NONE: ZoneCurrentSpecialFunction.NONE,
PRESET_AWAY: ZoneCurrentSpecialFunction.HOLIDAY,
"system_off": ZoneCurrentSpecialFunction.SYSTEM_OFF,
"ventilation_boost": ZoneCurrentSpecialFunction.VENTILATION_BOOST,
}
if self.zone.is_eco_mode:
preset_modes[PRESET_ECO] = ZoneCurrentSpecialFunction.NONE
return preset_modes

@property
def default_quick_veto_duration(self):
return self.config.options.get(
Expand Down Expand Up @@ -634,8 +637,6 @@ def supported_features(self) -> ClimateEntityFeature:

@property
def target_temperature(self) -> float | None:
if self.zone.is_eco_mode:
return self.zone.heating.set_back_temperature
return self.zone.desired_room_temperature_setpoint

@property
Expand Down Expand Up @@ -664,17 +665,21 @@ def current_humidity(self) -> float | None:
def hvac_mode(self) -> HVACMode:
if self.system.manual_cooling_ongoing:
return self.hvac_mode_map.get(HVAC_MODE_COOLING_FOR_DAYS)
return self.hvac_mode_map.get(self.zone.heating.operation_mode_heating)
return self.hvac_mode_map.get(self.zone.active_operation_mode)

async def async_set_hvac_mode(self, hvac_mode: HVACMode):
if hvac_mode == HVACMode.COOL:
if not self.system.manual_cooling_ongoing:
await self.set_cooling_for_days()
else:
refresh_delay = 10
if self.system.manual_cooling_ongoing:
await self.coordinator.api.cancel_cooling_for_days(self.system)
refresh_delay = 20
mode = [k for k, v in self.hvac_mode_map.items() if v == hvac_mode][0]
await self.set_zone_operating_mode(mode, refresh_delay=20)
await self.set_zone_operating_mode(
mode, self.zone.active_operating_type, refresh_delay=refresh_delay
)

async def set_zone_operating_mode(
self,
Expand All @@ -701,6 +706,12 @@ async def set_zone_operating_mode(
raise ValueError(
f"Invalid mode, use one of {', '.join(ZoneOperatingMode)}"
)
_LOGGER.debug(
"Setting %s on %s to %s",
operating_type,
self.zone.name,
mode,
)
await self.coordinator.api.set_zone_operating_mode(
self.zone,
mode,
Expand Down Expand Up @@ -781,20 +792,20 @@ async def async_set_temperature(self, **kwargs: Any) -> None:
@property
def preset_modes(self) -> list[str]:
if self.zone.control_identifier.is_vrc700:
return list({v for v in ZONE_PRESET_MAP_VRC700.values()})
return list({v for v in self.preset_mode_map.values()})
else:
return [k for k in ZONE_PRESET_MAP.keys()]
return list(self.preset_mode_map.keys())

@property
def preset_mode(self) -> str:
if self.zone.control_identifier.is_vrc700:
return ZONE_PRESET_MAP_VRC700[self.zone.heating.operation_mode_heating] # type: ignore
return self.preset_mode_map[self.zone.active_operation_mode] # type: ignore
else:
if self.zone.is_eco_mode:
return PRESET_ECO
return [
k
for k, v in ZONE_PRESET_MAP.items()
for k, v in self.preset_mode_map.items()
if v == self.zone.current_special_function
][0]

Expand All @@ -810,21 +821,21 @@ async def async_set_preset_mode(self, preset_mode):
if preset_mode == PRESET_NONE:
# None can map to off or auto mode, if it's selected by the user we want auto
requested_mode = ZoneOperatingModeVRC700.AUTO
elif preset_mode in ZONE_PRESET_MAP_VRC700.values():
elif preset_mode in self.preset_mode_map.values():
requested_mode = [
k for k, v in ZONE_PRESET_MAP_VRC700.items() if v == preset_mode
k for k, v in self.preset_mode_map.items() if v == preset_mode
][0]
else:
raise ValueError(
f'Invalid preset mode, use one of {", ".join(set(ZONE_PRESET_MAP_VRC700.values()))}'
f'Invalid preset mode, use one of {", ".join(set(self.preset_mode_map.values()))}'
)
await self.set_zone_operating_mode(requested_mode)
else:
if preset_mode not in ZONE_PRESET_MAP:
if preset_mode not in self.preset_mode_map:
raise ValueError(
f'Invalid preset mode, use one of {", ".join(ZONE_PRESET_MAP.keys())}'
f'Invalid preset mode {preset_mode}, use one of {", ".join(self.preset_mode_map.keys())}'
)
requested_mode = ZONE_PRESET_MAP[preset_mode]
requested_mode = self.preset_mode_map[preset_mode]
if requested_mode != self.zone.current_special_function:
# Cancel previous special function
if (
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 @@ -51,6 +51,8 @@
DEFAULT_FETCH_ENERGY_MANAGEMENT,
OPTION_FETCH_EEBUS,
DEFAULT_FETCH_EEBUS,
OPTION_DEFAULT_MANUAL_COOLING_DURATION,
DEFAULT_MANUAL_COOLING_DURATION,
)

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -162,6 +164,13 @@ async def async_step_init(
DEFAULT_HOLIDAY_SETPOINT,
),
): vol.All(vol.Coerce(float), vol.Clamp(min=0, max=30)),
vol.Required(
OPTION_DEFAULT_MANUAL_COOLING_DURATION,
default=self.config_entry.options.get(
OPTION_DEFAULT_MANUAL_COOLING_DURATION,
DEFAULT_MANUAL_COOLING_DURATION,
),
): positive_int,
vol.Required(
OPTION_TIME_PROGRAM_OVERWRITE,
default=self.config_entry.options.get(
Expand Down
5 changes: 5 additions & 0 deletions custom_components/mypyllant/const.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from myPyllant.enums import ZoneOperatingType

DOMAIN = "mypyllant"
OPTION_UPDATE_INTERVAL = "update_interval"
OPTION_UPDATE_INTERVAL_DAILY = "update_interval_daily"
OPTION_REFRESH_DELAY = "refresh_delay"
OPTION_DEFAULT_QUICK_VETO_DURATION = "quick_veto_duration"
OPTION_DEFAULT_HOLIDAY_DURATION = "holiday_duration"
OPTION_DEFAULT_MANUAL_COOLING_DURATION = "manual_cooling_duration"
OPTION_COUNTRY = "country"
OPTION_BRAND = "brand"
OPTION_TIME_PROGRAM_OVERWRITE = "time_program_overwrite"
Expand All @@ -16,6 +19,7 @@
DEFAULT_UPDATE_INTERVAL = 60 # in seconds
DEFAULT_UPDATE_INTERVAL_DAILY = 3600 # in seconds
DEFAULT_REFRESH_DELAY = 5 # in seconds
DEFAULT_MANUAL_COOLING_DURATION = 30 # in days
DEFAULT_COUNTRY = "germany"
DEFAULT_TIME_PROGRAM_OVERWRITE = False
DEFAULT_HOLIDAY_SETPOINT = 10.0 # in °C
Expand All @@ -24,6 +28,7 @@
DEFAULT_FETCH_AMBISENSE_ROOMS = True
DEFAULT_FETCH_ENERGY_MANAGEMENT = True
DEFAULT_FETCH_EEBUS = True
DEFAULT_MANUAL_SETPOINT_TYPE = ZoneOperatingType.HEATING
QUOTA_PAUSE_INTERVAL = 3 * 3600 # in seconds
API_DOWN_PAUSE_INTERVAL = 15 * 60 # in seconds
HVAC_MODE_COOLING_FOR_DAYS = "COOLING_FOR_DAYS"
Expand Down
4 changes: 2 additions & 2 deletions custom_components/mypyllant/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"iot_class": "cloud_polling",
"issue_tracker": "https://github.com/signalkraft/mypyllant-component/issues",
"requirements": [
"myPyllant==0.8.21"
"myPyllant==0.8.23"
],
"version": "v0.8.9"
"version": "v0.8.11"
}
33 changes: 33 additions & 0 deletions custom_components/mypyllant/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ async def async_setup_entry(
sensors: EntityList[NumberEntity] = EntityList()
for index, system in enumerate(coordinator.data):
sensors.append(lambda: SystemHolidayDurationNumber(index, coordinator))
if system.is_cooling_allowed:
sensors.append(lambda: SystemManualCoolingDays(index, coordinator))

for zone_index, zone in enumerate(system.zones):
sensors.append(
Expand Down Expand Up @@ -116,6 +118,37 @@ def unique_id(self) -> str:
return f"{DOMAIN}_{self.id_infix}_holiday_duration_remaining"


class SystemManualCoolingDays(SystemCoordinatorEntity, NumberEntity):
_attr_native_unit_of_measurement = UnitOfTime.DAYS
_attr_icon = "mdi:snowflake"
_attr_step = 1 # type: ignore

@property
def name(self):
return f"{self.name_prefix} Manual Cooling Duration"

@property
def native_value(self):
return self.system.manual_cooling_days or 0

async def async_set_native_value(self, value: float) -> None:
if value == 0:
await self.coordinator.api.cancel_cooling_for_days(self.system)
else:
await self.coordinator.api.set_cooling_for_days(
self.system, duration_days=int(value)
)
await self.coordinator.async_request_refresh_delayed(20)

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

@property
def available(self) -> bool:
return self.system.is_cooling_allowed


class ZoneQuickVetoDurationNumber(ZoneCoordinatorEntity, NumberEntity):
_attr_native_unit_of_measurement = UnitOfTime.HOURS
_attr_icon = "mdi:rocket-launch"
Expand Down
8 changes: 2 additions & 6 deletions custom_components/mypyllant/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,9 @@ def is_on(self):
return self.system.manual_cooling_planned

async def async_turn_on(self, **kwargs):
_, end = get_default_holiday_dates(
self.manual_cooling_start,
self.manual_cooling_end,
self.system.timezone,
self.default_manual_cooling_duration,
await self.coordinator.api.set_cooling_for_days(
self.system, duration_days=self.default_manual_cooling_duration
)
await self.coordinator.api.set_cooling_for_days(self.system, end=end)
await self.coordinator.async_request_refresh_delayed(20)

async def async_turn_off(self, **kwargs):
Expand Down
1 change: 1 addition & 0 deletions custom_components/mypyllant/translations/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"holiday_duration": "Nastavená doba trvání dovolené ve dnech",
"time_program_overwrite": "Regulátory teploty přepisují časový program namísto nastavení rychlého veta",
"default_holiday_setpoint": "Výchozí teplota pro dovolenou",
"manual_cooling_duration": "Doba trvání manuálního chlazení v dnech",
"country": "Země",
"brand": "Značka",
"fetch_rts": "Načíst statistiky v reálném čase (není podporováno na každém systému)",
Expand Down
5 changes: 3 additions & 2 deletions custom_components/mypyllant/translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
"preset_mode": {
"state": {
"system_off": "System aus",
"ventilation_boost": "Lüftungs-Boost",
"ventilation_boost": "Stoßlüften",
"boost": "Quick Veto",
"away": "Abwesenheit"
"away": "Abwesenheitsmodus"
}
}
}
Expand All @@ -46,6 +46,7 @@
"holiday_duration": "Standard Dauer in Tagen für Abwesenheitsmodus",
"time_program_overwrite": "Temperaturänderungen in der Zeitsteuerung speichern, statt Quick Veto",
"default_holiday_setpoint": "Standardtemperatur während Abwesenheitsmodus",
"manual_cooling_duration": "Standard Dauer in Tagen für manuelle Kühlung",
"country": "Land",
"brand": "Hersteller",
"fetch_rts": "Echtzeit-Statistiken abrufen (nicht auf jedem System unterstützt)",
Expand Down
1 change: 1 addition & 0 deletions custom_components/mypyllant/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"holiday_duration": "Default duration in days for away mode",
"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",
"country": "Country",
"brand": "Brand",
"fetch_rts": "Fetch real-time statistics (not supported on every system)",
Expand Down
1 change: 1 addition & 0 deletions custom_components/mypyllant/translations/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"holiday_duration": "Durata di default in giorni per le vacanze",
"time_program_overwrite": "Controlla la temperatura sovrascrivendo la programmazione oraria invece di attivare acqua calda rapida",
"default_holiday_setpoint": "Temperatura di default per le vacanze",
"manual_cooling_duration": "Durata di default in giorni per il raffreddamento manuale",
"country": "Stato",
"brand": "Marca",
"fetch_rts": "Recupera le statistiche in tempo reale (non supportato su tutti i sistemi)",
Expand Down
1 change: 1 addition & 0 deletions custom_components/mypyllant/translations/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"holiday_duration": "Domyślny czas trwania wakacji w dniach",
"time_program_overwrite": "Kontrola temperatury nadpisuje program czasowy zamiast ustawiać szybkie weto",
"default_holiday_setpoint": "Domyślna temperatura wakacyjna",
"manual_cooling_duration": "Domyślny czas trwania ręcznego chłodzenia w dniach",
"country": "Kraj",
"brand": "Marka",
"fetch_rts": "Pobierz statystyki w czasie rzeczywistym (nie jest obsługiwane na każdym systemie)",
Expand Down
Loading

0 comments on commit f8150e9

Please sign in to comment.