diff --git a/homeassistant/components/knx/__init__.py b/homeassistant/components/knx/__init__.py index f9f78f195bb..501ff856333 100644 --- a/homeassistant/components/knx/__init__.py +++ b/homeassistant/components/knx/__init__.py @@ -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, ) diff --git a/homeassistant/components/knx/binary_sensor.py b/homeassistant/components/knx/binary_sensor.py index 24fbe472ae3..a62e95f1def 100644 --- a/homeassistant/components/knx/binary_sensor.py +++ b/homeassistant/components/knx/binary_sensor.py @@ -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} diff --git a/homeassistant/components/knx/climate.py b/homeassistant/components/knx/climate.py index b5aaeb67907..1960627a8d6 100644 --- a/homeassistant/components/knx/climate.py +++ b/homeassistant/components/knx/climate.py @@ -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() diff --git a/homeassistant/components/knx/const.py b/homeassistant/components/knx/const.py index 427bdb0bd0b..8b0dd90393b 100644 --- a/homeassistant/components/knx/const.py +++ b/homeassistant/components/knx/const.py @@ -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" diff --git a/homeassistant/components/knx/cover.py b/homeassistant/components/knx/cover.py index 8c50bb2afe9..c677b12c0ee 100644 --- a/homeassistant/components/knx/cover.py +++ b/homeassistant/components/knx/cover.py @@ -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()) diff --git a/homeassistant/components/knx/knx_entity.py b/homeassistant/components/knx/knx_entity.py new file mode 100644 index 00000000000..296bcb2f540 --- /dev/null +++ b/homeassistant/components/knx/knx_entity.py @@ -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) diff --git a/homeassistant/components/knx/light.py b/homeassistant/components/knx/light.py index 6d8438df0f9..d9f0f9c0d3a 100644 --- a/homeassistant/components/knx/light.py +++ b/homeassistant/components/knx/light.py @@ -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() diff --git a/homeassistant/components/knx/notify.py b/homeassistant/components/knx/notify.py index e47cfca2794..7210795bd71 100644 --- a/homeassistant/components/knx/notify.py +++ b/homeassistant/components/knx/notify.py @@ -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 ( diff --git a/homeassistant/components/knx/scene.py b/homeassistant/components/knx/scene.py index b4df94a0fd4..6c76fdbd199 100644 --- a/homeassistant/components/knx/scene.py +++ b/homeassistant/components/knx/scene.py @@ -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() diff --git a/homeassistant/components/knx/sensor.py b/homeassistant/components/knx/sensor.py index d87119239cf..fc2cbced8bb 100644 --- a/homeassistant/components/knx/sensor.py +++ b/homeassistant/components/knx/sensor.py @@ -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 diff --git a/homeassistant/components/knx/switch.py b/homeassistant/components/knx/switch.py index c378d1b0ca4..ae3048e2d23 100644 --- a/homeassistant/components/knx/switch.py +++ b/homeassistant/components/knx/switch.py @@ -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() diff --git a/homeassistant/components/knx/weather.py b/homeassistant/components/knx/weather.py index 09dc1a305c6..097fa661f4a 100644 --- a/homeassistant/components/knx/weather.py +++ b/homeassistant/components/knx/weather.py @@ -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 )