Skip to content

Commit

Permalink
Updated underyling library, added more extra state attributes, added …
Browse files Browse the repository at this point in the history
…climate entity for ventilation
  • Loading branch information
signalkraft committed Nov 15, 2023
1 parent 86ba7df commit 1188028
Show file tree
Hide file tree
Showing 17 changed files with 475 additions and 303 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ repos:
- aioresponses
- pytest
- types-requests
- myPyllant==0.5.13b1
- myPyllant==0.6.0
- polyfactory
- repo: local
hooks:
Expand Down
76 changes: 38 additions & 38 deletions custom_components/mypyllant/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,38 +71,6 @@ def device_info(self) -> DeviceInfo | None:
}


class CircuitEntity(CoordinatorEntity, BinarySensorEntity):
def __init__(
self,
system_index: int,
circuit_index: int,
coordinator: SystemCoordinator,
):
super().__init__(coordinator)
self.system_index = system_index
self.circuit_index = circuit_index

@property
def system(self) -> System:
return self.coordinator.data[self.system_index]

@property
def circuit_name(self) -> str:
return f"Circuit {self.circuit_index}"

@property
def circuit(self) -> Circuit:
return self.coordinator.data[self.system_index].circuits[self.circuit_index]

@property
def device_info(self) -> DeviceInfo | None:
return DeviceInfo(
identifiers={(DOMAIN, f"circuit{self.circuit.index}")},
name=self.circuit_name,
manufacturer=self.system.brand_name,
)


class ControlError(SystemControlEntity):
def __init__(
self,
Expand Down Expand Up @@ -173,11 +141,11 @@ def __init__(

@property
def is_on(self) -> bool | None:
return self.system.claim.firmware.get("update_required", None)
return self.system.home.firmware.get("update_required", None)

@property
def name(self) -> str:
return f"Firmware Update Required {self.system.claim.name}"
return f"Firmware Update Required {self.system.home.name}"

@property
def unique_id(self) -> str:
Expand All @@ -189,7 +157,7 @@ def device_class(self) -> BinarySensorDeviceClass | None:

@property
def device_info(self) -> DeviceInfo | None:
return {"identifiers": {(DOMAIN, f"claim{self.system.id}")}}
return {"identifiers": {(DOMAIN, f"home{self.system.id}")}}


class FirmwareUpdateEnabled(SystemControlEntity):
Expand All @@ -203,19 +171,51 @@ def __init__(

@property
def is_on(self) -> bool | None:
return self.system.claim.firmware.get("update_enabled", None)
return self.system.home.firmware.get("update_enabled", None)

@property
def name(self) -> str:
return f"Firmware Update Enabled {self.system.claim.name}"
return f"Firmware Update Enabled {self.system.home.name}"

@property
def unique_id(self) -> str:
return f"{DOMAIN}_firmware_update_enabled_{self.system_index}"

@property
def device_info(self) -> DeviceInfo | None:
return {"identifiers": {(DOMAIN, f"claim{self.system.id}")}}
return {"identifiers": {(DOMAIN, f"home{self.system.id}")}}


class CircuitEntity(CoordinatorEntity, BinarySensorEntity):
def __init__(
self,
system_index: int,
circuit_index: int,
coordinator: SystemCoordinator,
):
super().__init__(coordinator)
self.system_index = system_index
self.circuit_index = circuit_index

@property
def system(self) -> System:
return self.coordinator.data[self.system_index]

@property
def circuit_name(self) -> str:
return f"Circuit {self.circuit_index}"

@property
def circuit(self) -> Circuit:
return self.coordinator.data[self.system_index].circuits[self.circuit_index]

@property
def device_info(self) -> DeviceInfo | None:
return DeviceInfo(
identifiers={(DOMAIN, f"circuit{self.circuit.index}")},
name=self.circuit_name,
manufacturer=self.system.brand_name,
)


class CircuitIsCoolingAllowed(CircuitEntity):
Expand Down
154 changes: 153 additions & 1 deletion custom_components/mypyllant/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
HVACMode,
)
from homeassistant.components.climate.const import (
FAN_AUTO,
FAN_LOW,
FAN_OFF,
FAN_ON,
PRESET_AWAY,
PRESET_BOOST,
PRESET_NONE,
Expand All @@ -32,6 +36,9 @@
)
from myPyllant.models import (
System,
Ventilation,
VentilationFanStageType,
VentilationOperationMode,
Zone,
ZoneCurrentSpecialFunction,
ZoneHeatingOperatingMode,
Expand All @@ -49,6 +56,7 @@
SERVICE_SET_HOLIDAY,
SERVICE_SET_MANUAL_MODE_SETPOINT,
SERVICE_SET_QUICK_VETO,
SERVICE_SET_VENTILATION_FAN_STAGE,
SERVICE_SET_ZONE_TIME_PROGRAM,
)

Expand All @@ -72,6 +80,23 @@
PRESET_SLEEP: ZoneCurrentSpecialFunction.SYSTEM_OFF,
}

VENTILATION_HVAC_MODE_MAP = {
HVACMode.FAN_ONLY: VentilationOperationMode.NORMAL,
HVACMode.AUTO: VentilationOperationMode.TIME_CONTROLLED,
}

VENTILATION_FAN_MODE_MAP = {
FAN_OFF: VentilationOperationMode.OFF,
FAN_ON: VentilationOperationMode.NORMAL,
FAN_LOW: VentilationOperationMode.REDUCED,
FAN_AUTO: VentilationOperationMode.TIME_CONTROLLED,
}

_FAN_STAGE_TYPE_OPTIONS = [
selector.SelectOptionDict(value=v.value, label=v.value.title())
for v in VentilationFanStageType
]


async def async_setup_entry(
hass: HomeAssistant, config: ConfigEntry, async_add_entities: AddEntitiesCallback
Expand All @@ -91,6 +116,7 @@ async def async_setup_entry(
return

zone_entities: list[ClimateEntity] = []
ventilation_entities: list[ClimateEntity] = []

for index, system in enumerate(coordinator.data):
for zone_index, _ in enumerate(system.zones):
Expand All @@ -103,8 +129,17 @@ async def async_setup_entry(
time_program_overwrite,
)
)
for ventilation_index, _ in enumerate(system.ventilation):
ventilation_entities.append(
VentilationClimate(
index,
ventilation_index,
coordinator,
)
)

async_add_entities(zone_entities)
async_add_entities(ventilation_entities)

if len(zone_entities) > 0:
platform = entity_platform.async_get_current_platform()
Expand Down Expand Up @@ -181,6 +216,27 @@ async def async_setup_entry(
"set_zone_time_program",
)

if len(ventilation_entities) > 0:
platform = entity_platform.async_get_current_platform()
_LOGGER.debug("Setting up ventilation climate entity services for %s", platform)
# noinspection PyTypeChecker
# Wrapping the schema in vol.Schema() breaks entity_id passing
platform.async_register_entity_service(
SERVICE_SET_VENTILATION_FAN_STAGE,
{
vol.Required("maximum_fan_stage"): vol.All(
vol.Coerce(int), vol.Clamp(min=1, max=6)
),
vol.Required("fan_stage_type"): selector.SelectSelector(
selector.SelectSelectorConfig(
options=_FAN_STAGE_TYPE_OPTIONS,
mode=selector.SelectSelectorMode.LIST,
),
),
},
"set_ventilation_fan_stage",
)


class ZoneClimate(CoordinatorEntity, ClimateEntity):
"""Climate for a zone."""
Expand Down Expand Up @@ -236,7 +292,7 @@ def extra_state_attributes(self) -> Mapping[str, Any] | None:
"quick_veto_start_date_time": self.zone.quick_veto_start_date_time,
"quick_veto_end_date_time": self.zone.quick_veto_end_date_time,
}
return attr
return attr | self.zone.extra_fields

async def set_holiday(self, **kwargs):
_LOGGER.debug(
Expand Down Expand Up @@ -420,3 +476,99 @@ async def async_set_preset_mode(self, preset_mode):
await self.async_set_hvac_mode(HVACMode.OFF)

await self.coordinator.async_request_refresh_delayed()


class VentilationClimate(CoordinatorEntity, ClimateEntity):
coordinator: SystemCoordinator
_attr_fan_modes = [str(k) for k in VENTILATION_FAN_MODE_MAP.keys()]
_attr_hvac_modes = [str(k) for k in VENTILATION_HVAC_MODE_MAP.keys()]
_attr_temperature_unit = UnitOfTemperature.CELSIUS

def __init__(
self,
system_index: int,
ventilation_index: int,
coordinator: SystemCoordinator,
) -> None:
super().__init__(coordinator)
self.system_index = system_index
self.ventilation_index = ventilation_index
self.entity_id = f"{DOMAIN}.ventilation_{ventilation_index}"

@property
def system(self) -> System:
return self.coordinator.data[self.system_index]

@property
def ventilation(self) -> Ventilation:
return self.system.ventilation[self.ventilation_index]

@property
def device_info(self) -> DeviceInfo:
return DeviceInfo(
identifiers={(DOMAIN, f"ventilation{self.ventilation.index}")},
name=self.name,
manufacturer=self.system.brand_name,
)

@property
def unique_id(self) -> str:
return f"{DOMAIN}_climate_ventilation_{self.ventilation_index}"

@property
def name(self) -> str:
return [d for d in self.system.devices if d.type == "ventilation"][
0
].name_display

@property
def extra_state_attributes(self) -> Mapping[str, Any] | None:
attr = {
"time_program_ventilation": self.ventilation.time_program_ventilation,
}
return attr

@property
def supported_features(self) -> ClimateEntityFeature:
"""Return the list of supported features."""
return ClimateEntityFeature.FAN_MODE

@property
def hvac_mode(self) -> HVACMode:
return [
k
for k, v in VENTILATION_HVAC_MODE_MAP.items()
if v == self.ventilation.operation_mode_ventilation
][0]

async def async_set_hvac_mode(self, hvac_mode):
await self.coordinator.api.set_ventilation_operation_mode(
self.ventilation,
VENTILATION_HVAC_MODE_MAP[hvac_mode],
)
await self.coordinator.async_request_refresh_delayed()

@property
def fan_mode(self) -> HVACMode:
return [
k
for k, v in VENTILATION_FAN_MODE_MAP.items()
if v == self.ventilation.operation_mode_ventilation
][0]

async def async_set_fan_mode(self, fan_mode: str) -> None:
await self.coordinator.api.set_ventilation_operation_mode(
self.ventilation,
VENTILATION_FAN_MODE_MAP[fan_mode],
)
await self.coordinator.async_request_refresh_delayed()

async def set_ventilation_fan_stage(
self, maximum_fan_stage: int | str, **kwargs: Any
) -> None:
await self.coordinator.api.set_ventilation_fan_stage(
self.ventilation,
int(maximum_fan_stage),
VentilationFanStageType(kwargs.get("fan_stage_type")),
)
await self.coordinator.async_request_refresh_delayed()
Loading

0 comments on commit 1188028

Please sign in to comment.