Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Overkiz AtlanticPassAPCHeatingAndCoolingZone #78659

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
daaea46
Add Overkiz AtlanticPassAPCHeatingAndCoolingZone
nyroDev Sep 17, 2022
2b42f7b
Fix commands instanciations to be simpler
nyroDev Sep 17, 2022
4ea72b8
Update AtlanticPassAPCHeatingAndCoolingZone to show temperature and f…
nyroDev Sep 21, 2022
c483c44
Simplify async_execute_commands in order to receive simpler list
nyroDev Sep 21, 2022
5dc7948
Fix get and set temperature in derogation or auto mode
nyroDev Sep 21, 2022
3aea914
Remove hvac_action from AtlanticPassAPCHeatingAndCoolingZone
nyroDev Sep 22, 2022
d51205f
Remove unused lines
nyroDev Sep 23, 2022
b5c4f9f
Merge branch 'home-assistant:dev' into overkiz/atlantic_pass_apc_heat…
nyroDev Sep 26, 2022
2b6f4a4
Merge branch 'overkiz/atlantic_pass_apc_heating_and_cooling_zone' of …
nyroDev Sep 26, 2022
6cfce13
Merge branch 'home-assistant:dev' into overkiz/atlantic_pass_apc_heat…
nyroDev Sep 28, 2022
dc20991
Merge branch 'overkiz/atlantic_pass_apc_heating_and_cooling_zone' of …
nyroDev Sep 28, 2022
bd2548b
Merge branch 'home-assistant:dev' into overkiz/atlantic_pass_apc_heat…
nyroDev Oct 1, 2022
8a807ea
Merge branch 'overkiz/atlantic_pass_apc_heating_and_cooling_zone' of …
nyroDev Oct 1, 2022
afb7367
Merge branch 'home-assistant:dev' into overkiz/atlantic_pass_apc_heat…
nyroDev Oct 3, 2022
bc6287e
Merge branch 'overkiz/atlantic_pass_apc_heating_and_cooling_zone' of …
nyroDev Oct 3, 2022
4e3c9f8
Update async_execute_commands to work like async_execute_command
nyroDev Oct 3, 2022
bc1a51f
Improve to use preset_home for internal scheduling
nyroDev Oct 3, 2022
2e454b2
Remove async_execute_commands
nyroDev Oct 4, 2022
972a2df
Improvement for AtlanticPassAPCHeatingAndCoolingZone
nyroDev Oct 5, 2022
533b1b8
Merge branch 'home-assistant:dev' into overkiz/atlantic_pass_apc_heat…
nyroDev Oct 17, 2022
82abacb
Merge branch 'home-assistant:dev' into overkiz/atlantic_pass_apc_heat…
nyroDev Oct 28, 2022
c817ba9
Update homeassistant/components/overkiz/climate_entities/__init__.py
nyroDev Nov 3, 2022
e36c56f
Update homeassistant/components/overkiz/climate_entities/atlantic_pas…
nyroDev Nov 3, 2022
ed0c7eb
Update homeassistant/components/overkiz/climate_entities/atlantic_pas…
nyroDev Nov 3, 2022
4c1f098
Update homeassistant/components/overkiz/const.py
nyroDev Nov 3, 2022
0d92ab0
Merge branch 'home-assistant:dev' into overkiz/atlantic_pass_apc_heat…
nyroDev Nov 3, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
from .atlantic_electrical_heater import AtlanticElectricalHeater
from .atlantic_electrical_towel_dryer import AtlanticElectricalTowelDryer
from .atlantic_heat_recovery_ventilation import AtlanticHeatRecoveryVentilation
from .atlantic_pass_apc_heating_and_cooling_zone import (
AtlanticPassAPCHeatingAndCoolingZone,
)
from .atlantic_pass_apc_zone_control import AtlanticPassAPCZoneControl
from .somfy_thermostat import SomfyThermostat

WIDGET_TO_CLIMATE_ENTITY = {
UIWidget.ATLANTIC_ELECTRICAL_HEATER: AtlanticElectricalHeater,
UIWidget.ATLANTIC_ELECTRICAL_TOWEL_DRYER: AtlanticElectricalTowelDryer,
UIWidget.ATLANTIC_HEAT_RECOVERY_VENTILATION: AtlanticHeatRecoveryVentilation,
UIWidget.ATLANTIC_PASS_APC_HEATING_AND_COOLING_ZONE: AtlanticPassAPCHeatingAndCoolingZone,
UIWidget.ATLANTIC_PASS_APC_ZONE_CONTROL: AtlanticPassAPCZoneControl,
UIWidget.SOMFY_THERMOSTAT: SomfyThermostat,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
"""Support for Atlantic Pass APC Heating And Cooling Zone Control."""
from __future__ import annotations

from typing import Any, cast

from pyoverkiz.enums import OverkizCommand, OverkizCommandParam, OverkizState

from homeassistant.components.climate import (
PRESET_AWAY,
PRESET_COMFORT,
PRESET_ECO,
PRESET_HOME,
PRESET_SLEEP,
ClimateEntity,
ClimateEntityFeature,
HVACMode,
)
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS

from ..coordinator import OverkizDataUpdateCoordinator
from ..entity import OverkizEntity

OVERKIZ_TO_HVAC_MODE: dict[str, str] = {
OverkizCommandParam.AUTO: HVACMode.AUTO,
OverkizCommandParam.ECO: HVACMode.AUTO,
OverkizCommandParam.MANU: HVACMode.HEAT,
OverkizCommandParam.HEATING: HVACMode.HEAT,
OverkizCommandParam.STOP: HVACMode.OFF,
OverkizCommandParam.INTERNAL_SCHEDULING: HVACMode.AUTO,
OverkizCommandParam.COMFORT: HVACMode.HEAT,
}

HVAC_MODE_TO_OVERKIZ = {v: k for k, v in OVERKIZ_TO_HVAC_MODE.items()}

OVERKIZ_TO_PRESET_MODES: dict[str, str] = {
OverkizCommandParam.OFF: PRESET_ECO,
OverkizCommandParam.STOP: PRESET_ECO,
OverkizCommandParam.MANU: PRESET_COMFORT,
OverkizCommandParam.COMFORT: PRESET_COMFORT,
OverkizCommandParam.ABSENCE: PRESET_AWAY,
OverkizCommandParam.ECO: PRESET_ECO,
OverkizCommandParam.INTERNAL_SCHEDULING: PRESET_HOME,
}

PRESET_MODES_TO_OVERKIZ = {v: k for k, v in OVERKIZ_TO_PRESET_MODES.items()}
nyroDev marked this conversation as resolved.
Show resolved Hide resolved

OVERKIZ_TO_PROFILE_MODES: dict[str, str] = {
OverkizCommandParam.OFF: PRESET_SLEEP,
OverkizCommandParam.STOP: PRESET_SLEEP,
OverkizCommandParam.ECO: PRESET_ECO,
OverkizCommandParam.ABSENCE: PRESET_AWAY,
OverkizCommandParam.MANU: PRESET_COMFORT,
OverkizCommandParam.DEROGATION: PRESET_COMFORT,
OverkizCommandParam.COMFORT: PRESET_COMFORT,
}

OVERKIZ_TEMPERATURE_STATE_BY_PROFILE: dict[str, str] = {
OverkizCommandParam.ECO: OverkizState.CORE_ECO_HEATING_TARGET_TEMPERATURE,
OverkizCommandParam.COMFORT: OverkizState.CORE_COMFORT_HEATING_TARGET_TEMPERATURE,
OverkizCommandParam.DEROGATION: OverkizState.CORE_DEROGATED_TARGET_TEMPERATURE,
}


class AtlanticPassAPCHeatingAndCoolingZone(OverkizEntity, ClimateEntity):
"""Representation of Atlantic Pass APC Heating And Cooling Zone Control."""

_attr_hvac_modes = [*HVAC_MODE_TO_OVERKIZ]
_attr_preset_modes = [*PRESET_MODES_TO_OVERKIZ]
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE
)
_attr_temperature_unit = TEMP_CELSIUS

def __init__(
self, device_url: str, coordinator: OverkizDataUpdateCoordinator
) -> None:
"""Init method."""
super().__init__(device_url, coordinator)

# Temperature sensor use the same base_device_url and use the n+1 index
self.temperature_device = self.executor.linked_device(
int(self.index_device_url) + 1
)

@property
def current_temperature(self) -> float | None:
"""Return the current temperature."""
if temperature := self.temperature_device.states[OverkizState.CORE_TEMPERATURE]:
return cast(float, temperature.value)

return None

@property
def hvac_mode(self) -> str:
"""Return hvac operation ie. heat, cool mode."""
return OVERKIZ_TO_HVAC_MODE[
cast(str, self.executor.select_state(OverkizState.IO_PASS_APC_HEATING_MODE))
]

@property
def current_heating_profile(self) -> str:
"""Return current heating profile."""
return cast(
str,
self.executor.select_state(OverkizState.IO_PASS_APC_HEATING_PROFILE),
)

async def async_set_heating_mode(self, mode: str) -> None:
"""Set new heating mode and refresh states."""
await self.executor.async_execute_command(
OverkizCommand.SET_PASS_APC_HEATING_MODE, mode
)

if self.current_heating_profile == OverkizCommandParam.DEROGATION:
# If current mode is in derogation, disable it
await self.executor.async_execute_command(
OverkizCommand.SET_DEROGATION_ON_OFF_STATE, OverkizCommandParam.OFF
)

# We also needs to execute these 2 commands to make it work correctly
await self.executor.async_execute_command(
OverkizCommand.REFRESH_PASS_APC_HEATING_MODE
)
await self.executor.async_execute_command(
OverkizCommand.REFRESH_PASS_APC_HEATING_PROFILE
)

async def async_set_hvac_mode(self, hvac_mode: str) -> None:
"""Set new target hvac mode."""
await self.async_set_heating_mode(HVAC_MODE_TO_OVERKIZ[hvac_mode])

async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set new preset mode."""
await self.async_set_heating_mode(PRESET_MODES_TO_OVERKIZ[preset_mode])

@property
def preset_mode(self) -> str:
"""Return the current preset mode, e.g., home, away, temp."""
heating_mode = cast(
str, self.executor.select_state(OverkizState.IO_PASS_APC_HEATING_MODE)
)

if heating_mode == OverkizCommandParam.INTERNAL_SCHEDULING:
# In Internal scheduling, it could be comfort or eco
return OVERKIZ_TO_PROFILE_MODES[
cast(
str,
self.executor.select_state(
OverkizState.IO_PASS_APC_HEATING_PROFILE
),
)
]

return OVERKIZ_TO_PRESET_MODES[heating_mode]

@property
def target_temperature(self) -> float:
"""Return hvac target temperature."""
current_heating_profile = self.current_heating_profile
if current_heating_profile in OVERKIZ_TEMPERATURE_STATE_BY_PROFILE:
return cast(
float,
self.executor.select_state(
OVERKIZ_TEMPERATURE_STATE_BY_PROFILE[current_heating_profile]
),
)
return cast(
nyroDev marked this conversation as resolved.
Show resolved Hide resolved
float, self.executor.select_state(OverkizState.CORE_TARGET_TEMPERATURE)
)

async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new temperature."""
temperature = kwargs[ATTR_TEMPERATURE]

if self.hvac_mode == HVACMode.AUTO:
await self.executor.async_execute_command(
OverkizCommand.SET_COMFORT_HEATING_TARGET_TEMPERATURE,
temperature,
)
await self.executor.async_execute_command(
OverkizCommand.REFRESH_COMFORT_HEATING_TARGET_TEMPERATURE
)
await self.executor.async_execute_command(
OverkizCommand.REFRESH_TARGET_TEMPERATURE
)
else:
await self.executor.async_execute_command(
OverkizCommand.SET_DEROGATED_TARGET_TEMPERATURE,
temperature,
)
await self.executor.async_execute_command(
OverkizCommand.SET_DEROGATION_ON_OFF_STATE,
OverkizCommandParam.ON,
)
await self.executor.async_execute_command(
OverkizCommand.REFRESH_TARGET_TEMPERATURE
)
await self.executor.async_execute_command(
OverkizCommand.REFRESH_PASS_APC_HEATING_MODE
)
await self.executor.async_execute_command(
OverkizCommand.REFRESH_PASS_APC_HEATING_PROFILE
)
1 change: 1 addition & 0 deletions homeassistant/components/overkiz/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
UIWidget.ATLANTIC_ELECTRICAL_HEATER: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported)
UIWidget.ATLANTIC_ELECTRICAL_TOWEL_DRYER: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported)
UIWidget.ATLANTIC_HEAT_RECOVERY_VENTILATION: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported)
UIWidget.ATLANTIC_PASS_APC_HEATING_AND_COOLING_ZONE: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported)
UIWidget.ATLANTIC_PASS_APC_ZONE_CONTROL: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported)
UIWidget.DOMESTIC_HOT_WATER_TANK: Platform.SWITCH, # widgetName, uiClass is WaterHeatingSystem (not supported)
UIWidget.MY_FOX_ALARM_CONTROLLER: Platform.ALARM_CONTROL_PANEL, # widgetName, uiClass is Alarm (not supported)
Expand Down
5 changes: 4 additions & 1 deletion homeassistant/components/overkiz/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ def __init__(
"""Initialize the device."""
super().__init__(coordinator)
self.device_url = device_url
self.base_device_url, *_ = self.device_url.split("#")
split_device_url = self.device_url.split("#")
self.base_device_url = split_device_url[0]
if len(split_device_url) == 2:
self.index_device_url = split_device_url[1]
self.executor = OverkizExecutor(device_url, coordinator)
Comment on lines +31 to 34
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this code do? Tbh, I don't find this code very clean / readable. And it seems that self.index_device_url is not typed / having None as default.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other devices, like somfy_thermostat for instance, we use a constant:

def __init__(
self, device_url: str, coordinator: OverkizDataUpdateCoordinator
) -> None:
"""Init method."""
super().__init__(device_url, coordinator)
self.temperature_device = self.executor.linked_device(
TEMPERATURE_SENSOR_DEVICE_INDEX
)

I think it might be clearer an less prone to error than doing n+1 as you do here

        # Temperature sensor use the same base_device_url and use the n+1 index
        self.temperature_device = self.executor.linked_device(
            int(self.index_device_url) + 1
        )

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This type of device don't work like that.
We have to use the climate device index and search for n+1 index, which will be the temperature sensor.
The n+1 is required

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it resolves the conversation ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When this PR is merged and published, this code will be updated.

Copy link
Member

@Quentame Quentame Nov 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, so, for now we are keeping it like this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's working like this, it could be merged as it is.
Up to @iMicknl to decide I guess.


self._attr_assumed_state = not self.device.states
Expand Down