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

KNX: Migrate climate component to new climate platform #24931

Merged
merged 4 commits into from
Jul 3, 2019
Merged
Changes from 3 commits
Commits
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
108 changes: 73 additions & 35 deletions homeassistant/components/knx/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
from homeassistant.components.climate import PLATFORM_SCHEMA, ClimateDevice
from homeassistant.components.climate.const import (
HVAC_MODE_DRY, HVAC_MODE_FAN_ONLY, HVAC_MODE_HEAT, HVAC_MODE_OFF,
STATE_ECO, STATE_MANUAL, SUPPORT_ON_OFF, SUPPORT_TARGET_TEMPERATURE)
PRESET_ECO, PRESET_SLEEP, PRESET_AWAY, PRESET_COMFORT,
SUPPORT_PRESET_MODE, SUPPORT_TARGET_TEMPERATURE)
from homeassistant.const import ATTR_TEMPERATURE, CONF_NAME, TEMP_CELSIUS
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
from typing import Optional, List

from . import ATTR_DISCOVER_DEVICES, DATA_KNX

Expand Down Expand Up @@ -41,11 +43,6 @@
DEFAULT_SETPOINT_SHIFT_MIN = -6
# Map KNX operation modes to HA modes. This list might not be full.
OPERATION_MODES = {
# Map DPT 201.100 HVAC operating modes
"Frost Protection": STATE_MANUAL,
"Night": HVAC_MODE_OFF,
"Standby": STATE_ECO,
"Comfort": HVAC_MODE_HEAT,
# Map DPT 201.104 HVAC control modes
"Fan only": HVAC_MODE_FAN_ONLY,
"Dehumidification": HVAC_MODE_DRY
Expand All @@ -54,6 +51,17 @@
OPERATION_MODES_INV = dict((
reversed(item) for item in OPERATION_MODES.items()))

PRESET_MODES = {
# Map DPT 201.100 HVAC operating modes to HA presets
"Frost Protection": PRESET_ECO,
"Night": PRESET_SLEEP,
"Standby": PRESET_AWAY,
"Comfort": PRESET_COMFORT,
}

PRESET_MODES_INV = dict((
reversed(item) for item in PRESET_MODES.items()))

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Required(CONF_TEMPERATURE_ADDRESS): cv.string,
Expand Down Expand Up @@ -167,32 +175,29 @@ def __init__(self, device):
self._unit_of_measurement = TEMP_CELSIUS

@property
def supported_features(self):
def supported_features(self) -> int:
"""Return the list of supported features."""
support = SUPPORT_TARGET_TEMPERATURE
if self.device.supports_on_off:
support |= SUPPORT_ON_OFF
return support
return SUPPORT_TARGET_TEMPERATURE | SUPPORT_PRESET_MODE

async def async_added_to_hass(self):
async def async_added_to_hass(self) -> None:
"""Register callbacks to update hass after device was changed."""
async def after_update_callback(device):
"""Call after device was updated."""
await self.async_update_ha_state()
self.device.register_device_updated_cb(after_update_callback)

@property
def name(self):
def name(self) -> str:
"""Return the name of the KNX device."""
return self.device.name

@property
def available(self):
def available(self) -> bool:
"""Return True if entity is available."""
return self.hass.data[DATA_KNX].connected

@property
def should_poll(self):
def should_poll(self) -> bool:
"""No polling needed within KNX."""
return False

Expand All @@ -209,7 +214,7 @@ def current_temperature(self):
@property
def target_temperature_step(self):
"""Return the supported step of target temperature."""
return self.device.setpoint_shift_step
return self.device.temperature_step

@property
def target_temperature(self):
Expand All @@ -226,7 +231,7 @@ def max_temp(self):
"""Return the maximum temperature."""
return self.device.target_temperature_max

async def async_set_temperature(self, **kwargs):
async def async_set_temperature(self, **kwargs) -> None:
"""Set new target temperature."""
temperature = kwargs.get(ATTR_TEMPERATURE)
if temperature is None:
Expand All @@ -235,39 +240,72 @@ async def async_set_temperature(self, **kwargs):
await self.async_update_ha_state()

@property
def hvac_mode(self):
def hvac_mode(self) -> Optional[str]:
"""Return current operation ie. heat, cool, idle."""
if self.device.supports_on_off and not self.device.is_on:
return HVAC_MODE_OFF
elif self.device.supports_on_off and self.device.is_on:
return HVAC_MODE_HEAT
if self.device.mode.supports_operation_mode:
return OPERATION_MODES.get(self.device.mode.operation_mode.value)
return OPERATION_MODES.get(self.device.mode.operation_mode.value, HVAC_MODE_HEAT)
return None

@property
def hvac_modes(self):
def hvac_modes(self) -> Optional[List[str]]:
"""Return the list of available operation modes."""
return [OPERATION_MODES.get(operation_mode.value) for
operation_mode in
self.device.mode.operation_modes]
_operations = [OPERATION_MODES.get(operation_mode.value) for
operation_mode in
self.device.mode.operation_modes]

if self.device.supports_on_off:
_operations[HVAC_MODE_HEAT] = HVAC_MODE_HEAT
Copy link
Member

Choose a reason for hiding this comment

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

_operations is a list, right? What do we want to do here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

_operations[HVAC_MODE_OFF] = HVAC_MODE_OFF

return list(filter(None, _operations))
Copy link
Member

Choose a reason for hiding this comment

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

We prefer list comprehension without using filter.

return [op for op in _operations if op is not None]

Copy link
Contributor Author

Choose a reason for hiding this comment

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


async def async_set_hvac_mode(self, hvac_mode):
async def async_set_hvac_mode(self, hvac_mode: str) -> None:
"""Set operation mode."""
if self.device.mode.supports_operation_mode:
if self.device.supports_on_off and hvac_mode == HVAC_MODE_OFF:
await self.device.turn_off()
elif self.device.supports_on_off and hvac_mode == HVAC_MODE_HEAT:
await self.device.turn_on()
elif self.device.mode.supports_operation_mode:
from xknx.knx import HVACOperationMode
knx_operation_mode = HVACOperationMode(
OPERATION_MODES_INV.get(hvac_mode))
await self.device.mode.set_operation_mode(knx_operation_mode)
await self.async_update_ha_state()

@property
def is_on(self):
"""Return true if the device is on."""
if self.device.supports_on_off:
return self.device.is_on
def preset_mode(self) -> Optional[str]:
"""Return the current preset mode, e.g., home, away, temp.

Requires SUPPORT_PRESET_MODE.
"""
if self.device.mode.supports_operation_mode:
return PRESET_MODES.get(self.device.mode.operation_mode.value, PRESET_AWAY)
return None

async def async_turn_on(self):
"""Turn on."""
await self.device.turn_on()
@property
def preset_modes(self) -> Optional[List[str]]:
"""Return a list of available preset modes.

Requires SUPPORT_PRESET_MODE.
"""
_presets = [PRESET_MODES.get(operation_mode.value) for
operation_mode in
self.device.mode.operation_modes]

return list(filter(None, _presets))

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

async def async_turn_off(self):
"""Turn off."""
await self.device.turn_off()
This method must be run in the event loop and returns a coroutine.
"""
if self.device.mode.supports_operation_mode:
from xknx.knx import HVACOperationMode
knx_operation_mode = HVACOperationMode(
PRESET_MODES_INV.get(preset_mode))
await self.device.mode.set_operation_mode(knx_operation_mode)
await self.async_update_ha_state()