Skip to content

Commit

Permalink
Add Overkiz support for AtlanticPassAPCHeatingAndCoolingZone widget
Browse files Browse the repository at this point in the history
  • Loading branch information
Tronix117 committed Feb 13, 2024
1 parent 8fb04d7 commit 5bec218
Show file tree
Hide file tree
Showing 8 changed files with 248 additions and 9 deletions.
4 changes: 2 additions & 2 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -969,8 +969,8 @@ build.json @home-assistant/supervisor
/tests/components/otbr/ @home-assistant/core
/homeassistant/components/ourgroceries/ @OnFreund
/tests/components/ourgroceries/ @OnFreund
/homeassistant/components/overkiz/ @imicknl @vlebourl @tetienne @nyroDev
/tests/components/overkiz/ @imicknl @vlebourl @tetienne @nyroDev
/homeassistant/components/overkiz/ @imicknl @vlebourl @tetienne @nyroDev @tronix117
/tests/components/overkiz/ @imicknl @vlebourl @tetienne @nyroDev @tronix117
/homeassistant/components/ovo_energy/ @timmo001
/tests/components/ovo_energy/ @timmo001
/homeassistant/components/p1_monitor/ @klaasnicolaas
Expand Down
6 changes: 4 additions & 2 deletions homeassistant/components/overkiz/climate_entities/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
)
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_heating_zone import AtlanticPassAPCHeatingZone
from .atlantic_pass_apc_zone_control import AtlanticPassAPCZoneControl
from .hitachi_air_to_air_heat_pump_hlrrwifi import HitachiAirToAirHeatPumpHLRRWIFI
Expand All @@ -20,8 +23,7 @@
UIWidget.ATLANTIC_ELECTRICAL_HEATER_WITH_ADJUSTABLE_TEMPERATURE_SETPOINT: AtlanticElectricalHeaterWithAdjustableTemperatureSetpoint,
UIWidget.ATLANTIC_ELECTRICAL_TOWEL_DRYER: AtlanticElectricalTowelDryer,
UIWidget.ATLANTIC_HEAT_RECOVERY_VENTILATION: AtlanticHeatRecoveryVentilation,
# ATLANTIC_PASS_APC_HEATING_AND_COOLING_ZONE works exactly the same as ATLANTIC_PASS_APC_HEATING_ZONE
UIWidget.ATLANTIC_PASS_APC_HEATING_AND_COOLING_ZONE: AtlanticPassAPCHeatingZone,
UIWidget.ATLANTIC_PASS_APC_HEATING_AND_COOLING_ZONE: AtlanticPassAPCHeatingAndCoolingZone,
UIWidget.ATLANTIC_PASS_APC_HEATING_ZONE: AtlanticPassAPCHeatingZone,
UIWidget.ATLANTIC_PASS_APC_ZONE_CONTROL: AtlanticPassAPCZoneControl,
UIWidget.SOMFY_HEATING_TEMPERATURE_INTERFACE: SomfyHeatingTemperatureInterface,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
"""Support for Atlantic Pass APC Heating Control."""
from __future__ import annotations

from typing import Any, cast

from pyoverkiz.enums import OverkizCommand, OverkizCommandParam, OverkizState

from homeassistant.components.climate import ClimateEntityFeature, HVACMode
from homeassistant.components.climate.const import PRESET_NONE
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature

from ..const import DOMAIN
from ..coordinator import OverkizDataUpdateCoordinator
from .atlantic_pass_apc_heating_zone import AtlanticPassAPCHeatingZone
from .atlantic_pass_apc_zone_control import OVERKIZ_TO_HVAC_MODE

PRESET_SCHEDULE = "schedule"
PRESET_MANUAL = "manual"

OVERKIZ_MODE_TO_PRESET_MODES: dict[str, str] = {
OverkizCommandParam.MANU: PRESET_MANUAL,
OverkizCommandParam.INTERNAL_SCHEDULING: PRESET_SCHEDULE,
}

PRESET_MODES_TO_OVERKIZ = {v: k for k, v in OVERKIZ_MODE_TO_PRESET_MODES.items()}

# Those device depends on a main probe that choose the operating mode (heating, cooling, ...)
class AtlanticPassAPCHeatingAndCoolingZone(AtlanticPassAPCHeatingZone):
"""Representation of Atlantic Pass APC Heating And Cooling Zone Control."""

# Modes are not configurable, they will follow current HVAC Mode of Zone Control.
_attr_hvac_modes = []

# Those are available and tested presets on Shogun.
_attr_preset_modes = [*PRESET_MODES_TO_OVERKIZ]

# Thermostat internalScheduling stop manu eco

_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.PRESET_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_attr_translation_key = DOMAIN
_enable_turn_on_off_backwards_compatibility = False

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

# Those APC Heating and Cooling probes depends on the zone control device (main probe).
# Only the base device (#1) can be used to get/set some states.
# Like to retrieve and set the current operating mode (heating, cooling, drying, off).
self.zone_control_device = self.executor.linked_device(1)

@property
def zone_control_hvac_mode(self) -> HVACMode:
"""Return hvac operation ie. heat, cool, dry, off mode."""

return OVERKIZ_TO_HVAC_MODE[
self.zone_control_device.states[
OverkizState.IO_PASS_APC_OPERATING_MODE
].value_as_str
]

@property
def hvac_mode(self) -> HVACMode:
"""Return hvac operation ie. heat, cool, dry, off mode."""

zone_control_hvac_mode = self.zone_control_hvac_mode

# Should be same, because either thermostat or this integration change both.
on_off_state = cast(
str,
self.executor.select_state(
OverkizState.CORE_COOLING_ON_OFF
if zone_control_hvac_mode == HVACMode.COOL
else OverkizState.CORE_HEATING_ON_OFF
),
)

# Device is Stopped, it means the air flux is flowing but its venting door is closed.
if on_off_state == OverkizCommandParam.OFF:
hvac_mode = HVACMode.OFF
else:
hvac_mode = zone_control_hvac_mode

# It helps keep it consistent with the Zone Control, within the interface.
if self._attr_hvac_modes != [zone_control_hvac_mode, HVACMode.OFF]:
self._attr_hvac_modes = [zone_control_hvac_mode, HVACMode.OFF]
self.async_write_ha_state()

return hvac_mode

async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set new target hvac mode."""

# They are mainly managed by the Zone Control device
# However, it make sense to map the OFF Mode to the Overkiz STOP Preset

if hvac_mode == HVACMode.OFF:
await self.executor.async_execute_command(
OverkizCommand.SET_COOLING_ON_OFF,
OverkizCommandParam.OFF,
)
await self.executor.async_execute_command(
OverkizCommand.SET_HEATING_ON_OFF,
OverkizCommandParam.OFF,
)
else:
await self.executor.async_execute_command(
OverkizCommand.SET_COOLING_ON_OFF,
OverkizCommandParam.ON,
)
await self.executor.async_execute_command(
OverkizCommand.SET_HEATING_ON_OFF,
OverkizCommandParam.ON,
)

await self.async_refresh_modes()

@property
def preset_mode(self) -> str:
"""Return the current preset mode, e.g., schedule, manual."""

mode = OVERKIZ_MODE_TO_PRESET_MODES[
cast(
str,
self.executor.select_state(
OverkizState.IO_PASS_APC_HEATING_MODE
if self.zone_control_hvac_mode == HVACMode.COOL
else OverkizState.IO_PASS_APC_COOLING_MODE
),
)
]

return mode if mode is not None else PRESET_NONE

async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set new preset mode."""

mode = PRESET_MODES_TO_OVERKIZ[preset_mode]

# For consistency, it is better both are synced like on the Thermostat.
await self.executor.async_execute_command(
OverkizCommand.SET_PASS_APC_HEATING_MODE, mode
)
await self.executor.async_execute_command(
OverkizCommand.SET_PASS_APC_COOLING_MODE, mode
)

await self.async_refresh_modes()

@property
def target_temperature(self) -> float:
"""Return hvac target temperature."""

if self.zone_control_hvac_mode == HVACMode.COOL:
return cast(
float,
self.executor.select_state(
OverkizState.CORE_COOLING_TARGET_TEMPERATURE
),
)

if self.zone_control_hvac_mode == HVACMode.HEAT:
return cast(
float,
self.executor.select_state(
OverkizState.CORE_HEATING_TARGET_TEMPERATURE
),
)

# Not sure this temperature is relevant.
return cast(
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]

# Change both (heating/cooling) temperature is a good way to have consistency
await self.executor.async_execute_command(
OverkizCommand.SET_HEATING_TARGET_TEMPERATURE,
temperature,
)
await self.executor.async_execute_command(
OverkizCommand.SET_COOLING_TARGET_TEMPERATURE,
temperature,
)
await self.executor.async_execute_command(
OverkizCommand.SET_DEROGATION_ON_OFF_STATE,
OverkizCommandParam.OFF,
)

# Target temperature may take up to 1 minute to get refreshed.
await self.executor.async_execute_command(
OverkizCommand.REFRESH_TARGET_TEMPERATURE
)

async def async_refresh_modes(self) -> None:
"""Refresh the device modes to have new states."""

await self.executor.async_execute_command(
OverkizCommand.REFRESH_PASS_APC_HEATING_MODE
)

await self.executor.async_execute_command(
OverkizCommand.REFRESH_PASS_APC_HEATING_PROFILE
)

await self.executor.async_execute_command(
OverkizCommand.REFRESH_PASS_APC_COOLING_MODE
)

await self.executor.async_execute_command(
OverkizCommand.REFRESH_PASS_APC_COOLING_PROFILE
)

await self.executor.async_execute_command(
OverkizCommand.REFRESH_TARGET_TEMPERATURE
)
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,15 @@
OverkizCommandParam.INTERNAL_SCHEDULING: PRESET_HOME,
}

PRESET_MODES_TO_OVERKIZ = {v: k for k, v in OVERKIZ_TO_PRESET_MODES.items()}
PRESET_MODES_TO_OVERKIZ: dict[str, str] = {
PRESET_COMFORT: OverkizCommandParam.COMFORT,
PRESET_AWAY: OverkizCommandParam.ABSENCE,
PRESET_ECO: OverkizCommandParam.ECO,
PRESET_FROST_PROTECTION: OverkizCommandParam.FROSTPROTECTION,
PRESET_EXTERNAL: OverkizCommandParam.EXTERNAL_SCHEDULING,
PRESET_HOME: OverkizCommandParam.INTERNAL_SCHEDULING,
}


OVERKIZ_TO_PROFILE_MODES: dict[str, str] = {
OverkizCommandParam.OFF: PRESET_SLEEP,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def hvac_mode(self) -> HVACMode:

async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set new target hvac mode."""

await self.executor.async_execute_command(
OverkizCommand.SET_PASS_APC_OPERATING_MODE, HVAC_MODE_TO_OVERKIZ[hvac_mode]
)
4 changes: 2 additions & 2 deletions homeassistant/components/overkiz/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"domain": "overkiz",
"name": "Overkiz",
"codeowners": ["@imicknl", "@vlebourl", "@tetienne", "@nyroDev"],
"codeowners": ["@imicknl", "@vlebourl", "@tetienne", "@nyroDev", "@tronix117"],
"config_flow": true,
"dhcp": [
{
Expand All @@ -13,7 +13,7 @@
"integration_type": "hub",
"iot_class": "local_polling",
"loggers": ["boto3", "botocore", "pyhumps", "pyoverkiz", "s3transfer"],
"requirements": ["pyoverkiz==1.13.3"],
"requirements": ["pyoverkiz==1.13.6"],
"zeroconf": [
{
"type": "_kizbox._tcp.local.",
Expand Down
2 changes: 1 addition & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2023,7 +2023,7 @@ pyotgw==2.1.3
pyotp==2.8.0

# homeassistant.components.overkiz
pyoverkiz==1.13.3
pyoverkiz==1.13.6

# homeassistant.components.openweathermap
pyowm==3.2.0
Expand Down
2 changes: 1 addition & 1 deletion requirements_test_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1562,7 +1562,7 @@ pyotgw==2.1.3
pyotp==2.8.0

# homeassistant.components.overkiz
pyoverkiz==1.13.3
pyoverkiz==1.13.6

# homeassistant.components.openweathermap
pyowm==3.2.0
Expand Down

0 comments on commit 5bec218

Please sign in to comment.