Use centralized KnxEntity for all KNX platforms (#40381)

This commit is contained in:
Marvin Wichmann 2020-09-21 18:08:35 +02:00 committed by GitHub
parent c13fbf795d
commit 3d6434be75
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 206 additions and 338 deletions

View file

@ -29,7 +29,7 @@ from homeassistant.helpers import discovery
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.event import async_track_state_change_event
from .const import DATA_KNX, DOMAIN, SupportedPlatforms
from .const import DOMAIN, SupportedPlatforms
from .factory import create_knx_device
from .schema import (
BinarySensorSchema,
@ -139,9 +139,9 @@ SERVICE_KNX_SEND_SCHEMA = vol.Schema(
async def async_setup(hass, config):
"""Set up the KNX component."""
try:
hass.data[DATA_KNX] = KNXModule(hass, config)
hass.data[DATA_KNX].async_create_exposures()
await hass.data[DATA_KNX].start()
hass.data[DOMAIN] = KNXModule(hass, config)
hass.data[DOMAIN].async_create_exposures()
await hass.data[DOMAIN].start()
except XKNXException as ex:
_LOGGER.warning("Could not connect to KNX interface: %s", ex)
hass.components.persistent_notification.async_create(
@ -151,7 +151,7 @@ async def async_setup(hass, config):
for platform in SupportedPlatforms:
if platform.value in config[DOMAIN]:
for device_config in config[DOMAIN][platform.value]:
create_knx_device(platform, hass.data[DATA_KNX].xknx, device_config)
create_knx_device(platform, hass.data[DOMAIN].xknx, device_config)
# We need to wait until all entities are loaded into the device list since they could also be created from other platforms
for platform in SupportedPlatforms:
@ -159,7 +159,7 @@ async def async_setup(hass, config):
discovery.async_load_platform(hass, platform.value, DOMAIN, {}, config)
)
if not hass.data[DATA_KNX].xknx.devices:
if not hass.data[DOMAIN].xknx.devices:
_LOGGER.warning(
"No KNX devices are configured. Please read "
"https://www.home-assistant.io/blog/2020/09/17/release-115/#breaking-changes"
@ -168,7 +168,7 @@ async def async_setup(hass, config):
hass.services.async_register(
DOMAIN,
SERVICE_KNX_SEND,
hass.data[DATA_KNX].service_send_to_knx_bus,
hass.data[DOMAIN].service_send_to_knx_bus,
schema=SERVICE_KNX_SEND_SCHEMA,
)

View file

@ -3,72 +3,41 @@ from typing import Any, Dict, Optional
from xknx.devices import BinarySensor as XknxBinarySensor
from homeassistant.components.binary_sensor import BinarySensorEntity
from homeassistant.core import callback
from homeassistant.components.binary_sensor import DEVICE_CLASSES, BinarySensorEntity
from .const import ATTR_COUNTER, DATA_KNX
from .const import ATTR_COUNTER, DOMAIN
from .knx_entity import KnxEntity
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up binary sensor(s) for KNX platform."""
entities = []
for device in hass.data[DATA_KNX].xknx.devices:
for device in hass.data[DOMAIN].xknx.devices:
if isinstance(device, XknxBinarySensor):
entities.append(KNXBinarySensor(device))
async_add_entities(entities)
class KNXBinarySensor(BinarySensorEntity):
class KNXBinarySensor(KnxEntity, BinarySensorEntity):
"""Representation of a KNX binary sensor."""
def __init__(self, device: XknxBinarySensor):
"""Initialize of KNX binary sensor."""
self.device = device
@callback
def async_register_callbacks(self):
"""Register callbacks to update hass after device was changed."""
async def after_update_callback(device: XknxBinarySensor):
"""Call after device was updated."""
self.async_write_ha_state()
self.device.register_device_updated_cb(after_update_callback)
async def async_added_to_hass(self):
"""Store register state change callback."""
self.async_register_callbacks()
async def async_update(self):
"""Request a state update from KNX bus."""
await self.device.sync()
@property
def name(self):
"""Return the name of the KNX device."""
return self.device.name
@property
def available(self):
"""Return True if entity is available."""
return self.hass.data[DATA_KNX].connected
@property
def should_poll(self):
"""No polling needed within KNX."""
return False
super().__init__(device)
@property
def device_class(self):
"""Return the class of this sensor."""
return self.device.device_class
if self._device.device_class in DEVICE_CLASSES:
return self._device.device_class
return None
@property
def is_on(self):
"""Return true if the binary sensor is on."""
return self.device.is_on()
return self._device.is_on()
@property
def device_state_attributes(self) -> Optional[Dict[str, Any]]:
"""Return device specific state attributes."""
return {ATTR_COUNTER: self.device.counter}
return {ATTR_COUNTER: self._device.counter}

View file

@ -14,8 +14,8 @@ from homeassistant.components.climate.const import (
)
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS
from . import DATA_KNX
from .const import OPERATION_MODES, PRESET_MODES
from .const import DOMAIN, OPERATION_MODES, PRESET_MODES
from .knx_entity import KnxEntity
OPERATION_MODES_INV = dict(reversed(item) for item in OPERATION_MODES.items())
PRESET_MODES_INV = dict(reversed(item) for item in PRESET_MODES.items())
@ -24,18 +24,19 @@ PRESET_MODES_INV = dict(reversed(item) for item in PRESET_MODES.items())
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up climate(s) for KNX platform."""
entities = []
for device in hass.data[DATA_KNX].xknx.devices:
for device in hass.data[DOMAIN].xknx.devices:
if isinstance(device, XknxClimate):
entities.append(KNXClimate(device))
async_add_entities(entities)
class KNXClimate(ClimateEntity):
class KNXClimate(KnxEntity, ClimateEntity):
"""Representation of a KNX climate device."""
def __init__(self, device: XknxClimate):
"""Initialize of a KNX climate device."""
self.device = device
super().__init__(device)
self._unit_of_measurement = TEMP_CELSIUS
@property
@ -43,35 +44,10 @@ class KNXClimate(ClimateEntity):
"""Return the list of supported features."""
return SUPPORT_TARGET_TEMPERATURE | SUPPORT_PRESET_MODE
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."""
self.async_write_ha_state()
self.device.register_device_updated_cb(after_update_callback)
self.device.mode.register_device_updated_cb(after_update_callback)
async def async_update(self):
"""Request a state update from KNX bus."""
await self.device.sync()
await self.device.mode.sync()
@property
def name(self) -> str:
"""Return the name of the KNX device."""
return self.device.name
@property
def available(self) -> bool:
"""Return True if entity is available."""
return self.hass.data[DATA_KNX].connected
@property
def should_poll(self) -> bool:
"""No polling needed within KNX."""
return False
await self._device.sync()
await self._device.mode.sync()
@property
def temperature_unit(self):
@ -81,44 +57,44 @@ class KNXClimate(ClimateEntity):
@property
def current_temperature(self):
"""Return the current temperature."""
return self.device.temperature.value
return self._device.temperature.value
@property
def target_temperature_step(self):
"""Return the supported step of target temperature."""
return self.device.temperature_step
return self._device.temperature_step
@property
def target_temperature(self):
"""Return the temperature we try to reach."""
return self.device.target_temperature.value
return self._device.target_temperature.value
@property
def min_temp(self):
"""Return the minimum temperature."""
return self.device.target_temperature_min
return self._device.target_temperature_min
@property
def max_temp(self):
"""Return the maximum temperature."""
return self.device.target_temperature_max
return self._device.target_temperature_max
async def async_set_temperature(self, **kwargs) -> None:
"""Set new target temperature."""
temperature = kwargs.get(ATTR_TEMPERATURE)
if temperature is None:
return
await self.device.set_target_temperature(temperature)
await self._device.set_target_temperature(temperature)
self.async_write_ha_state()
@property
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:
if self._device.supports_on_off and not self._device.is_on:
return HVAC_MODE_OFF
if self.device.mode.supports_operation_mode:
if self._device.mode.supports_operation_mode:
return OPERATION_MODES.get(
self.device.mode.operation_mode.value, HVAC_MODE_HEAT
self._device.mode.operation_mode.value, HVAC_MODE_HEAT
)
# default to "heat"
return HVAC_MODE_HEAT
@ -128,10 +104,10 @@ class KNXClimate(ClimateEntity):
"""Return the list of available operation modes."""
_operations = [
OPERATION_MODES.get(operation_mode.value)
for operation_mode in self.device.mode.operation_modes
for operation_mode in self._device.mode.operation_modes
]
if self.device.supports_on_off:
if self._device.supports_on_off:
if not _operations:
_operations.append(HVAC_MODE_HEAT)
_operations.append(HVAC_MODE_OFF)
@ -142,16 +118,16 @@ class KNXClimate(ClimateEntity):
async def async_set_hvac_mode(self, hvac_mode: str) -> None:
"""Set operation mode."""
if self.device.supports_on_off and hvac_mode == HVAC_MODE_OFF:
await self.device.turn_off()
if self._device.supports_on_off and hvac_mode == HVAC_MODE_OFF:
await self._device.turn_off()
else:
if self.device.supports_on_off and not self.device.is_on:
await self.device.turn_on()
if self.device.mode.supports_operation_mode:
if self._device.supports_on_off and not self._device.is_on:
await self._device.turn_on()
if self._device.mode.supports_operation_mode:
knx_operation_mode = HVACOperationMode(
OPERATION_MODES_INV.get(hvac_mode)
)
await self.device.mode.set_operation_mode(knx_operation_mode)
await self._device.mode.set_operation_mode(knx_operation_mode)
self.async_write_ha_state()
@property
@ -160,8 +136,8 @@ class KNXClimate(ClimateEntity):
Requires SUPPORT_PRESET_MODE.
"""
if self.device.mode.supports_operation_mode:
return PRESET_MODES.get(self.device.mode.operation_mode.value, PRESET_AWAY)
if self._device.mode.supports_operation_mode:
return PRESET_MODES.get(self._device.mode.operation_mode.value, PRESET_AWAY)
return None
@property
@ -172,14 +148,14 @@ class KNXClimate(ClimateEntity):
"""
_presets = [
PRESET_MODES.get(operation_mode.value)
for operation_mode in self.device.mode.operation_modes
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."""
if self.device.mode.supports_operation_mode:
if self._device.mode.supports_operation_mode:
knx_operation_mode = HVACOperationMode(PRESET_MODES_INV.get(preset_mode))
await self.device.mode.set_operation_mode(knx_operation_mode)
await self._device.mode.set_operation_mode(knx_operation_mode)
self.async_write_ha_state()

View file

@ -15,7 +15,6 @@ from homeassistant.components.climate.const import (
)
DOMAIN = "knx"
DATA_KNX = "data_knx"
CONF_STATE_ADDRESS = "state_address"
CONF_SYNC_STATE = "sync_state"

View file

@ -15,65 +15,39 @@ from homeassistant.components.cover import (
from homeassistant.core import callback
from homeassistant.helpers.event import async_track_utc_time_change
from . import DATA_KNX
from .const import DOMAIN
from .knx_entity import KnxEntity
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up cover(s) for KNX platform."""
entities = []
for device in hass.data[DATA_KNX].xknx.devices:
for device in hass.data[DOMAIN].xknx.devices:
if isinstance(device, XknxCover):
entities.append(KNXCover(device))
async_add_entities(entities)
class KNXCover(CoverEntity):
class KNXCover(KnxEntity, CoverEntity):
"""Representation of a KNX cover."""
def __init__(self, device: XknxCover):
"""Initialize the cover."""
self.device = device
super().__init__(device)
self._unsubscribe_auto_updater = None
@callback
def async_register_callbacks(self):
"""Register callbacks to update hass after device was changed."""
async def after_update_callback(device):
"""Call after device was updated."""
self.async_write_ha_state()
if self.device.is_traveling():
self.start_auto_updater()
self.device.register_device_updated_cb(after_update_callback)
async def async_added_to_hass(self):
"""Store register state change callback."""
self.async_register_callbacks()
async def async_update(self):
"""Request a state update from KNX bus."""
await self.device.sync()
@property
def name(self):
"""Return the name of the KNX device."""
return self.device.name
@property
def available(self):
"""Return True if entity is available."""
return self.hass.data[DATA_KNX].connected
@property
def should_poll(self):
"""No polling needed within KNX."""
return False
async def after_update_callback(self, device):
"""Call after device was updated."""
self.async_write_ha_state()
if self._device.is_traveling():
self.start_auto_updater()
@property
def device_class(self):
"""Return the class of this device, from component DEVICE_CLASSES."""
if self.device.supports_angle:
if self._device.supports_angle:
return DEVICE_CLASS_BLIND
return None
@ -81,9 +55,9 @@ class KNXCover(CoverEntity):
def supported_features(self):
"""Flag supported features."""
supported_features = SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_SET_POSITION
if self.device.supports_stop:
if self._device.supports_stop:
supported_features |= SUPPORT_STOP
if self.device.supports_angle:
if self._device.supports_angle:
supported_features |= SUPPORT_SET_TILT_POSITION
return supported_features
@ -95,57 +69,57 @@ class KNXCover(CoverEntity):
"""
# In KNX 0 is open, 100 is closed.
try:
return 100 - self.device.current_position()
return 100 - self._device.current_position()
except TypeError:
return None
@property
def is_closed(self):
"""Return if the cover is closed."""
return self.device.is_closed()
return self._device.is_closed()
@property
def is_opening(self):
"""Return if the cover is opening or not."""
return self.device.is_opening()
return self._device.is_opening()
@property
def is_closing(self):
"""Return if the cover is closing or not."""
return self.device.is_closing()
return self._device.is_closing()
async def async_close_cover(self, **kwargs):
"""Close the cover."""
await self.device.set_down()
await self._device.set_down()
async def async_open_cover(self, **kwargs):
"""Open the cover."""
await self.device.set_up()
await self._device.set_up()
async def async_set_cover_position(self, **kwargs):
"""Move the cover to a specific position."""
knx_position = 100 - kwargs[ATTR_POSITION]
await self.device.set_position(knx_position)
await self._device.set_position(knx_position)
async def async_stop_cover(self, **kwargs):
"""Stop the cover."""
await self.device.stop()
await self._device.stop()
self.stop_auto_updater()
@property
def current_cover_tilt_position(self):
"""Return current tilt position of cover."""
if not self.device.supports_angle:
if not self._device.supports_angle:
return None
try:
return 100 - self.device.current_angle()
return 100 - self._device.current_angle()
except TypeError:
return None
async def async_set_cover_tilt_position(self, **kwargs):
"""Move the cover tilt to a specific position."""
knx_tilt_position = 100 - kwargs[ATTR_TILT_POSITION]
await self.device.set_angle(knx_tilt_position)
await self._device.set_angle(knx_tilt_position)
def start_auto_updater(self):
"""Start the autoupdater to update Home Assistant while cover is moving."""
@ -164,7 +138,7 @@ class KNXCover(CoverEntity):
def auto_updater_hook(self, now):
"""Call for the autoupdater."""
self.async_write_ha_state()
if self.device.position_reached():
if self._device.position_reached():
self.stop_auto_updater()
self.hass.add_job(self.device.auto_stop_if_necessary())
self.hass.add_job(self._device.auto_stop_if_necessary())

View file

@ -0,0 +1,51 @@
"""Base class for KNX devices."""
from xknx.devices import Climate as XknxClimate, Device as XknxDevice
from homeassistant.helpers.entity import Entity
from .const import DOMAIN
class KnxEntity(Entity):
"""Representation of a KNX entity."""
def __init__(self, device: XknxDevice):
"""Set up device."""
self._device = device
@property
def name(self):
"""Return the name of the KNX device."""
return self._device.name
@property
def available(self):
"""Return True if entity is available."""
return self.hass.data[DOMAIN].connected
@property
def should_poll(self):
"""No polling needed within KNX."""
return False
async def async_update(self):
"""Request a state update from KNX bus."""
await self._device.sync()
async def after_update_callback(self, device: XknxDevice):
"""Call after device was updated."""
self.async_write_ha_state()
async def async_added_to_hass(self) -> None:
"""Store register state change callback."""
self._device.register_device_updated_cb(self.after_update_callback)
if isinstance(self._device, XknxClimate):
self._device.mode.register_device_updated_cb(self.after_update_callback)
async def async_will_remove_from_hass(self) -> None:
"""Disconnect device object when removed."""
self._device.unregister_device_updated_cb(self.after_update_callback)
if isinstance(self._device, XknxClimate):
self._device.mode.unregister_device_updated_cb(self.after_update_callback)

View file

@ -12,10 +12,10 @@ from homeassistant.components.light import (
SUPPORT_WHITE_VALUE,
LightEntity,
)
from homeassistant.core import callback
import homeassistant.util.color as color_util
from . import DATA_KNX
from .const import DOMAIN
from .knx_entity import KnxEntity
DEFAULT_COLOR = (0.0, 0.0)
DEFAULT_BRIGHTNESS = 255
@ -25,18 +25,18 @@ DEFAULT_WHITE_VALUE = 255
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up lights for KNX platform."""
entities = []
for device in hass.data[DATA_KNX].xknx.devices:
for device in hass.data[DOMAIN].xknx.devices:
if isinstance(device, XknxLight):
entities.append(KNXLight(device))
async_add_entities(entities)
class KNXLight(LightEntity):
class KNXLight(KnxEntity, LightEntity):
"""Representation of a KNX light."""
def __init__(self, device: XknxLight):
"""Initialize of KNX light."""
self.device = device
super().__init__(device)
self._min_kelvin = device.min_kelvin
self._max_kelvin = device.max_kelvin
@ -47,46 +47,13 @@ class KNXLight(LightEntity):
self._min_kelvin
)
@callback
def async_register_callbacks(self):
"""Register callbacks to update hass after device was changed."""
async def after_update_callback(device):
"""Call after device was updated."""
self.async_write_ha_state()
self.device.register_device_updated_cb(after_update_callback)
async def async_added_to_hass(self):
"""Store register state change callback."""
self.async_register_callbacks()
async def async_update(self):
"""Request a state update from KNX bus."""
await self.device.sync()
@property
def name(self):
"""Return the name of the KNX device."""
return self.device.name
@property
def available(self):
"""Return True if entity is available."""
return self.hass.data[DATA_KNX].connected
@property
def should_poll(self):
"""No polling needed within KNX."""
return False
@property
def brightness(self):
"""Return the brightness of this light between 0..255."""
if self.device.supports_brightness:
return self.device.current_brightness
if self._device.supports_brightness:
return self._device.current_brightness
hsv_color = self._hsv_color
if self.device.supports_color and hsv_color:
if self._device.supports_color and hsv_color:
return round(hsv_color[-1] / 100 * 255)
return None
@ -94,35 +61,35 @@ class KNXLight(LightEntity):
def hs_color(self):
"""Return the HS color value."""
rgb = None
if self.device.supports_rgbw or self.device.supports_color:
rgb, _ = self.device.current_color
if self._device.supports_rgbw or self._device.supports_color:
rgb, _ = self._device.current_color
return color_util.color_RGB_to_hs(*rgb) if rgb else None
@property
def _hsv_color(self):
"""Return the HSV color value."""
rgb = None
if self.device.supports_rgbw or self.device.supports_color:
rgb, _ = self.device.current_color
if self._device.supports_rgbw or self._device.supports_color:
rgb, _ = self._device.current_color
return color_util.color_RGB_to_hsv(*rgb) if rgb else None
@property
def white_value(self):
"""Return the white value."""
white = None
if self.device.supports_rgbw:
_, white = self.device.current_color
if self._device.supports_rgbw:
_, white = self._device.current_color
return white
@property
def color_temp(self):
"""Return the color temperature in mireds."""
if self.device.supports_color_temperature:
kelvin = self.device.current_color_temperature
if self._device.supports_color_temperature:
kelvin = self._device.current_color_temperature
if kelvin is not None:
return color_util.color_temperature_kelvin_to_mired(kelvin)
if self.device.supports_tunable_white:
relative_ct = self.device.current_tunable_white
if self._device.supports_tunable_white:
relative_ct = self._device.current_tunable_white
if relative_ct is not None:
# as KNX devices typically use Kelvin we use it as base for
# calculating ct from percent
@ -155,19 +122,22 @@ class KNXLight(LightEntity):
@property
def is_on(self):
"""Return true if light is on."""
return self.device.state
return self._device.state
@property
def supported_features(self):
"""Flag supported features."""
flags = 0
if self.device.supports_brightness:
if self._device.supports_brightness:
flags |= SUPPORT_BRIGHTNESS
if self.device.supports_color:
if self._device.supports_color:
flags |= SUPPORT_COLOR | SUPPORT_BRIGHTNESS
if self.device.supports_rgbw:
if self._device.supports_rgbw:
flags |= SUPPORT_COLOR | SUPPORT_WHITE_VALUE
if self.device.supports_color_temperature or self.device.supports_tunable_white:
if (
self._device.supports_color_temperature
or self._device.supports_tunable_white
):
flags |= SUPPORT_COLOR_TEMP
return flags
@ -191,14 +161,16 @@ class KNXLight(LightEntity):
or update_white_value
or update_color_temp
):
await self.device.set_on()
await self._device.set_on()
if self.device.supports_brightness and (update_brightness and not update_color):
if self._device.supports_brightness and (
update_brightness and not update_color
):
# if we don't need to update the color, try updating brightness
# directly if supported; don't do it if color also has to be
# changed, as RGB color implicitly sets the brightness as well
await self.device.set_brightness(brightness)
elif (self.device.supports_rgbw or self.device.supports_color) and (
await self._device.set_brightness(brightness)
elif (self._device.supports_rgbw or self._device.supports_color) and (
update_brightness or update_color or update_white_value
):
# change RGB color, white value (if supported), and brightness
@ -208,25 +180,25 @@ class KNXLight(LightEntity):
brightness = DEFAULT_BRIGHTNESS
if hs_color is None:
hs_color = DEFAULT_COLOR
if white_value is None and self.device.supports_rgbw:
if white_value is None and self._device.supports_rgbw:
white_value = DEFAULT_WHITE_VALUE
rgb = color_util.color_hsv_to_RGB(*hs_color, brightness * 100 / 255)
await self.device.set_color(rgb, white_value)
await self._device.set_color(rgb, white_value)
if update_color_temp:
kelvin = int(color_util.color_temperature_mired_to_kelvin(mireds))
kelvin = min(self._max_kelvin, max(self._min_kelvin, kelvin))
if self.device.supports_color_temperature:
await self.device.set_color_temperature(kelvin)
elif self.device.supports_tunable_white:
if self._device.supports_color_temperature:
await self._device.set_color_temperature(kelvin)
elif self._device.supports_tunable_white:
relative_ct = int(
255
* (kelvin - self._min_kelvin)
/ (self._max_kelvin - self._min_kelvin)
)
await self.device.set_tunable_white(relative_ct)
await self._device.set_tunable_white(relative_ct)
async def async_turn_off(self, **kwargs):
"""Turn the light off."""
await self.device.set_off()
await self._device.set_off()

View file

@ -5,13 +5,13 @@ from xknx.devices import Notification as XknxNotification
from homeassistant.components.notify import BaseNotificationService
from . import DATA_KNX
from .const import DOMAIN
async def async_get_service(hass, config, discovery_info=None):
"""Get the KNX notification service."""
notification_devices = []
for device in hass.data[DATA_KNX].xknx.devices:
for device in hass.data[DOMAIN].xknx.devices:
if isinstance(device, XknxNotification):
notification_devices.append(device)
return (

View file

@ -5,30 +5,26 @@ from xknx.devices import Scene as XknxScene
from homeassistant.components.scene import Scene
from . import DATA_KNX
from .const import DOMAIN
from .knx_entity import KnxEntity
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the scenes for KNX platform."""
entities = []
for device in hass.data[DATA_KNX].xknx.devices:
for device in hass.data[DOMAIN].xknx.devices:
if isinstance(device, XknxScene):
entities.append(KNXScene(device))
async_add_entities(entities)
class KNXScene(Scene):
class KNXScene(KnxEntity, Scene):
"""Representation of a KNX scene."""
def __init__(self, scene: XknxScene):
def __init__(self, device: XknxScene):
"""Init KNX scene."""
self.scene = scene
@property
def name(self):
"""Return the name of the scene."""
return self.scene.name
super().__init__(device)
async def async_activate(self, **kwargs: Any) -> None:
"""Activate the scene."""
await self.scene.run()
await self._device.run()

View file

@ -1,77 +1,43 @@
"""Support for KNX/IP sensors."""
from xknx.devices import Sensor as XknxSensor
from homeassistant.core import callback
from homeassistant.components.sensor import DEVICE_CLASSES
from homeassistant.helpers.entity import Entity
from . import DATA_KNX
from .const import DOMAIN
from .knx_entity import KnxEntity
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up sensor(s) for KNX platform."""
entities = []
for device in hass.data[DATA_KNX].xknx.devices:
for device in hass.data[DOMAIN].xknx.devices:
if isinstance(device, XknxSensor):
entities.append(KNXSensor(device))
async_add_entities(entities)
class KNXSensor(Entity):
class KNXSensor(KnxEntity, Entity):
"""Representation of a KNX sensor."""
def __init__(self, device: XknxSensor):
"""Initialize of a KNX sensor."""
self.device = device
@callback
def async_register_callbacks(self):
"""Register callbacks to update hass after device was changed."""
async def after_update_callback(device):
"""Call after device was updated."""
self.async_write_ha_state()
self.device.register_device_updated_cb(after_update_callback)
async def async_added_to_hass(self):
"""Store register state change callback."""
self.async_register_callbacks()
async def async_update(self):
"""Update the state from KNX."""
await self.device.sync()
@property
def name(self):
"""Return the name of the KNX device."""
return self.device.name
@property
def available(self):
"""Return True if entity is available."""
return self.hass.data[DATA_KNX].connected
@property
def should_poll(self):
"""No polling needed within KNX."""
return False
super().__init__(device)
@property
def state(self):
"""Return the state of the sensor."""
return self.device.resolve_state()
return self._device.resolve_state()
@property
def unit_of_measurement(self):
"""Return the unit this state is expressed in."""
return self.device.unit_of_measurement()
return self._device.unit_of_measurement()
@property
def device_class(self):
"""Return the device class of the sensor."""
return self.device.ha_device_class()
@property
def device_state_attributes(self):
"""Return the state attributes."""
device_class = self._device.ha_device_class()
if device_class in DEVICE_CLASSES:
return device_class
return None

View file

@ -2,69 +2,36 @@
from xknx.devices import Switch as XknxSwitch
from homeassistant.components.switch import SwitchEntity
from homeassistant.core import callback
from . import DATA_KNX
from . import DOMAIN
from .knx_entity import KnxEntity
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up switch(es) for KNX platform."""
entities = []
for device in hass.data[DATA_KNX].xknx.devices:
for device in hass.data[DOMAIN].xknx.devices:
if isinstance(device, XknxSwitch):
entities.append(KNXSwitch(device))
async_add_entities(entities)
class KNXSwitch(SwitchEntity):
class KNXSwitch(KnxEntity, SwitchEntity):
"""Representation of a KNX switch."""
def __init__(self, device: XknxSwitch):
"""Initialize of KNX switch."""
self.device = device
@callback
def async_register_callbacks(self):
"""Register callbacks to update hass after device was changed."""
async def after_update_callback(device):
"""Call after device was updated."""
self.async_write_ha_state()
self.device.register_device_updated_cb(after_update_callback)
async def async_added_to_hass(self):
"""Store register state change callback."""
self.async_register_callbacks()
async def async_update(self):
"""Request a state update from KNX bus."""
await self.device.sync()
@property
def name(self):
"""Return the name of the KNX device."""
return self.device.name
@property
def available(self):
"""Return true if entity is available."""
return self.hass.data[DATA_KNX].connected
@property
def should_poll(self):
"""Return the polling state. Not needed within KNX."""
return False
super().__init__(device)
@property
def is_on(self):
"""Return true if device is on."""
return self.device.state
return self._device.state
async def async_turn_on(self, **kwargs):
"""Turn the device on."""
await self.device.set_on()
await self._device.set_on()
async def async_turn_off(self, **kwargs):
"""Turn the device off."""
await self.device.set_off()
await self._device.set_off()

View file

@ -5,34 +5,30 @@ from xknx.devices import Weather as XknxWeather
from homeassistant.components.weather import WeatherEntity
from homeassistant.const import TEMP_CELSIUS
from .const import DATA_KNX
from .const import DOMAIN
from .knx_entity import KnxEntity
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the scenes for KNX platform."""
entities = []
for device in hass.data[DATA_KNX].xknx.devices:
for device in hass.data[DOMAIN].xknx.devices:
if isinstance(device, XknxWeather):
entities.append(KNXWeather(device))
async_add_entities(entities)
class KNXWeather(WeatherEntity):
class KNXWeather(KnxEntity, WeatherEntity):
"""Representation of a KNX weather device."""
def __init__(self, device: XknxWeather):
"""Initialize of a KNX sensor."""
self.device = device
@property
def name(self):
"""Return the name of the weather device."""
return self.device.name
super().__init__(device)
@property
def temperature(self):
"""Return current temperature."""
return self.device.temperature
return self._device.temperature
@property
def temperature_unit(self):
@ -44,25 +40,27 @@ class KNXWeather(WeatherEntity):
"""Return current air pressure."""
# KNX returns pA - HA requires hPa
return (
self.device.air_pressure / 100
if self.device.air_pressure is not None
self._device.air_pressure / 100
if self._device.air_pressure is not None
else None
)
@property
def condition(self):
"""Return current weather condition."""
return self.device.ha_current_state().value
return self._device.ha_current_state().value
@property
def humidity(self):
"""Return current humidity."""
return self.device.humidity if self.device.humidity is not None else None
return self._device.humidity if self._device.humidity is not None else None
@property
def wind_speed(self):
"""Return current wind speed in km/h."""
# KNX only supports wind speed in m/s
return (
self.device.wind_speed * 3.6 if self.device.wind_speed is not None else None
self._device.wind_speed * 3.6
if self._device.wind_speed is not None
else None
)