Use centralized KnxEntity for all KNX platforms (#40381)
This commit is contained in:
parent
c13fbf795d
commit
3d6434be75
12 changed files with 206 additions and 338 deletions
|
@ -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,
|
||||
)
|
||||
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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())
|
||||
|
|
51
homeassistant/components/knx/knx_entity.py
Normal file
51
homeassistant/components/knx/knx_entity.py
Normal 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)
|
|
@ -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()
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue