Skip to content

Commit

Permalink
Add basic support for AtlanticPassAPCHeatingAndCoolingZone (#380)
Browse files Browse the repository at this point in the history
  • Loading branch information
iMicknl authored Jul 14, 2021
1 parent 4a77161 commit 1bbacf1
Show file tree
Hide file tree
Showing 4 changed files with 273 additions and 0 deletions.
6 changes: 6 additions & 0 deletions custom_components/tahoma/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
from .climate_devices.atlantic_electrical_towel_dryer import (
AtlanticElectricalTowelDryer,
)
from .climate_devices.atlantic_pass_apc_heating_and_cooling_zone import (
AtlanticPassAPCHeatingAndCoolingZone,
)
from .climate_devices.atlantic_pass_apc_zone_control import AtlanticPassAPCZoneControl
from .climate_devices.atlantic_pass_apcdhw import AtlanticPassAPCDHW
from .climate_devices.dimmer_exterior_heating import DimmerExteriorHeating
from .climate_devices.evo_home_controller import EvoHomeController
Expand All @@ -25,6 +29,8 @@
"AtlanticElectricalHeaterWithAdjustableTemperatureSetpoint": AtlanticElectricalHeaterWithAdjustableTemperatureSetpoint,
"AtlanticElectricalTowelDryer": AtlanticElectricalTowelDryer,
"AtlanticPassAPCDHW": AtlanticPassAPCDHW,
"AtlanticPassAPCHeatingAndCoolingZone": AtlanticPassAPCHeatingAndCoolingZone,
"AtlanticPassAPCZoneControl": AtlanticPassAPCZoneControl,
"DimmerExteriorHeating": DimmerExteriorHeating,
"EvoHomeController": EvoHomeController,
"HeatingSetPoint": HeatingSetPoint,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
"""Support for Atlantic Pass APC Heating And Cooling Zone."""
import logging
from typing import List, Optional

from homeassistant.components.climate import SUPPORT_TARGET_TEMPERATURE, ClimateEntity
from homeassistant.components.climate.const import (
HVAC_MODE_AUTO,
HVAC_MODE_HEAT,
HVAC_MODE_OFF,
)
from homeassistant.const import (
ATTR_TEMPERATURE,
EVENT_HOMEASSISTANT_START,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
TEMP_CELSIUS,
)
from homeassistant.core import callback
from homeassistant.helpers.event import async_track_state_change

from ..coordinator import TahomaDataUpdateCoordinator
from ..tahoma_entity import TahomaEntity

_LOGGER = logging.getLogger(__name__)

COMMAND_REFRESH_OPERATING_MODE = "refreshOperatingMode"
COMMAND_REFRESH_PASS_APC_HEATING_PROFILE = "refreshPassAPCHeatingProfile"
COMMAND_REFRESH_TARGET_TEMPERATURE = "refreshTargetTemperature"
COMMAND_SET_HEATING_LEVEL = "setHeatingLevel"
COMMAND_SET_HEATING_ON_OFF_STATE = "setHeatingOnOffState"
COMMAND_SET_HEATING_TARGET_TEMPERATURE = "setHeatingTargetTemperature"
COMMAND_SET_OPERATING_MODE = "setOperatingMode"
COMMAND_SET_PASS_APC_HEATING_MODE = "setPassAPCHeatingMode"
COMMAND_SET_TARGET_TEMPERATURE = "setTargetTemperature"

CORE_HEATING_ON_OFF_STATE = "core:HeatingOnOffState"
CORE_HEATING_TARGET_TEMPERATURE_STATE = "core:HeatingTargetTemperatureState"
CORE_MINIMUM_HEATING_TARGET_TEMPERATURE_STATE = (
"core:MinimumHeatingTargetTemperatureState"
)
CORE_MAXIMUM_HEATING_TARGET_TEMPERATURE_STATE = (
"core:MaximumHeatingTargetTemperatureState"
)
CORE_ON_OFF_STATE = "core:OnOffState"
CORE_OPERATING_MODE_STATE = "core:OperatingModeState"
CORE_TARGET_TEMPERATURE_STATE = "core:TargetTemperatureState"

IO_PASS_APC_HEATING_MODE_STATE = "io:PassAPCHeatingModeState"
IO_TARGET_HEATING_LEVEL_STATE = "io:TargetHeatingLevelState"

# Map TaHoma HVAC modes to Home Assistant HVAC modes
TAHOMA_TO_HVAC_MODE = {
"stop": HVAC_MODE_OFF, # fallback
"off": HVAC_MODE_OFF,
"manu": HVAC_MODE_HEAT,
"auto": HVAC_MODE_AUTO, # fallback
"internalScheduling": HVAC_MODE_AUTO, # prog
}

HVAC_MODE_TO_TAHOMA = {v: k for k, v in TAHOMA_TO_HVAC_MODE.items()}


class AtlanticPassAPCHeatingAndCoolingZone(TahomaEntity, ClimateEntity):
"""Representation of Atlantic Pass APC Heating and Cooling Zone."""

def __init__(self, device_url: str, coordinator: TahomaDataUpdateCoordinator):
"""Init method."""
super().__init__(device_url, coordinator)

self._temp_sensor_entity_id = None
self._current_temperature = None

async def async_added_to_hass(self):
"""Register temperature sensor after added to hass."""
await super().async_added_to_hass()

base_url = self.get_base_device_url()
entity_registry = await self.hass.helpers.entity_registry.async_get_registry()

# The linked temperature sensor uses subsystem_id + 1
new_subsystem_id = int(self.device_url.split("#", 1)[1]) + 1

self._temp_sensor_entity_id = next(
(
entity_id
for entity_id, entry in entity_registry.entities.items()
if entry.unique_id == f"{base_url}#{str(new_subsystem_id)}"
),
None,
)

if self._temp_sensor_entity_id:
async_track_state_change(
self.hass, self._temp_sensor_entity_id, self._async_temp_sensor_changed
)

else:
_LOGGER.warning(
"Temperature sensor could not be found for entity %s", self.name
)

@callback
def _async_startup(event):
"""Init on startup."""
if self._temp_sensor_entity_id:
temp_sensor_state = self.hass.states.get(self._temp_sensor_entity_id)
if temp_sensor_state and temp_sensor_state.state != STATE_UNKNOWN:
self.update_temp(temp_sensor_state)

self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, _async_startup)

self.schedule_update_ha_state(True)

async def _async_temp_sensor_changed(self, entity_id, old_state, new_state) -> None:
"""Handle temperature changes."""
if new_state is None or old_state == new_state:
return

self.update_temp(new_state)
self.schedule_update_ha_state()

@callback
def update_temp(self, state):
"""Update thermostat with latest state from sensor."""
if state is None or state.state in [STATE_UNKNOWN, STATE_UNAVAILABLE]:
return

try:
self._current_temperature = float(state.state)
except ValueError as ex:
_LOGGER.error("Unable to update from sensor: %s", ex)

@property
def min_temp(self) -> float:
"""Return the minimum temperature."""
return self.select_state(CORE_MINIMUM_HEATING_TARGET_TEMPERATURE_STATE)

@property
def max_temp(self) -> float:
"""Return the maximum temperature."""
return self.select_state(CORE_MAXIMUM_HEATING_TARGET_TEMPERATURE_STATE)

@property
def current_temperature(self) -> Optional[float]:
"""Return the current temperature."""
return self._current_temperature

@property
def temperature_unit(self) -> str:
"""Return the unit of measurement used by the platform."""
return TEMP_CELSIUS

@property
def supported_features(self) -> int:
"""Return the list of supported features."""
supported_features = 0
supported_features |= SUPPORT_TARGET_TEMPERATURE

return supported_features

@property
def hvac_modes(self) -> List[str]:
"""Return the list of available hvac operation modes."""
return [*HVAC_MODE_TO_TAHOMA]

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

if self.select_state(CORE_HEATING_ON_OFF_STATE) == "off":
return HVAC_MODE_OFF

return TAHOMA_TO_HVAC_MODE[self.select_state(IO_PASS_APC_HEATING_MODE_STATE)]

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

if hvac_mode == HVAC_MODE_OFF:
await self.async_execute_command(COMMAND_SET_HEATING_ON_OFF_STATE, "off")
else:
if self.hvac_mode == HVAC_MODE_OFF:
await self.async_execute_command(COMMAND_SET_HEATING_ON_OFF_STATE, "on")

await self.async_execute_command(
COMMAND_SET_PASS_APC_HEATING_MODE, HVAC_MODE_TO_TAHOMA[hvac_mode]
)

await self.async_execute_command(COMMAND_REFRESH_PASS_APC_HEATING_PROFILE)
await self.async_execute_command(COMMAND_REFRESH_OPERATING_MODE)

@property
def target_temperature(self) -> None:
"""Return the temperature."""
return self.select_state(CORE_HEATING_TARGET_TEMPERATURE_STATE)

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

await self.async_execute_command(
COMMAND_SET_HEATING_TARGET_TEMPERATURE, temperature
)
await self.async_execute_command(COMMAND_REFRESH_TARGET_TEMPERATURE)
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""Support for Atlantic Pass APC Zone Control."""
import logging
from typing import List

from homeassistant.components.climate import ClimateEntity
from homeassistant.components.climate.const import (
HVAC_MODE_COOL,
HVAC_MODE_DRY,
HVAC_MODE_HEAT,
HVAC_MODE_OFF,
)
from homeassistant.const import TEMP_CELSIUS

from ..tahoma_entity import TahomaEntity

_LOGGER = logging.getLogger(__name__)

COMMAND_SET_PASS_APC_OPERATING_MODE = "setPassAPCOperatingMode"

IO_PASS_APC_OPERATING_MODE_STATE = "io:PassAPCOperatingModeState"

# Map TaHoma HVAC modes to Home Assistant HVAC modes
TAHOMA_TO_HVAC_MODE = {
"heating": HVAC_MODE_HEAT,
"drying": HVAC_MODE_DRY,
"cooling": HVAC_MODE_COOL,
"stop": HVAC_MODE_OFF,
}

HVAC_MODE_TO_TAHOMA = {v: k for k, v in TAHOMA_TO_HVAC_MODE.items()}


class AtlanticPassAPCZoneControl(TahomaEntity, ClimateEntity):
"""Representation of Atlantic Pass APC Zone Control."""

@property
def temperature_unit(self) -> str:
"""Return the unit of measurement used by the platform."""
return TEMP_CELSIUS

@property
def supported_features(self) -> int:
"""Return the list of supported features."""
supported_features = 0

return supported_features

@property
def hvac_modes(self) -> List[str]:
"""Return the list of available hvac operation modes."""
return [*HVAC_MODE_TO_TAHOMA]

@property
def hvac_mode(self) -> str:
"""Return hvac operation ie. heat, cool mode."""
return TAHOMA_TO_HVAC_MODE[self.select_state(IO_PASS_APC_OPERATING_MODE_STATE)]

async def async_set_hvac_mode(self, hvac_mode: str) -> None:
"""Set new target hvac mode."""
await self.async_execute_command(
COMMAND_SET_PASS_APC_OPERATING_MODE, HVAC_MODE_TO_TAHOMA[hvac_mode]
)
2 changes: 2 additions & 0 deletions custom_components/tahoma/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@
"AtlanticElectricalHeaterWithAdjustableTemperatureSetpoint": CLIMATE, # widgetName, uiClass is HeatingSystem (not supported)
"AtlanticElectricalTowelDryer": CLIMATE, # widgetName, uiClass is HeatingSystem (not supported)
"AtlanticPassAPCDHW": CLIMATE, # widgetName, uiClass is WaterHeatingSystem (not supported)
"AtlanticPassAPCHeatingAndCoolingZone": CLIMATE, # widgetName, uiClass is HeatingSystem (not supported)
"AtlanticPassAPCZoneControl": CLIMATE, # widgetName, uiClass is HeatingSystem (not supported)
"Awning": COVER,
"CarButtonSensor": BINARY_SENSOR,
"ConsumptionSensor": SENSOR,
Expand Down

0 comments on commit 1bbacf1

Please sign in to comment.