mirror of
https://github.com/home-assistant/core.git
synced 2025-07-25 14:17:45 +00:00
update knx module to xknx 0.11.3 (#35154)
- add expose attribute function and default value - default climate havc_mode to "heat" if modes are not supported - support `update_entity` service call
This commit is contained in:
parent
c41fb2a21f
commit
7147c5306d
@ -4,15 +4,20 @@ import logging
|
|||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from xknx import XKNX
|
from xknx import XKNX
|
||||||
from xknx.devices import ActionCallback, DateTime, DateTimeBroadcastType, ExposeSensor
|
from xknx.devices import ActionCallback, DateTime, DateTimeBroadcastType, ExposeSensor
|
||||||
|
from xknx.dpt import DPTArray, DPTBinary
|
||||||
from xknx.exceptions import XKNXException
|
from xknx.exceptions import XKNXException
|
||||||
from xknx.io import DEFAULT_MCAST_PORT, ConnectionConfig, ConnectionType
|
from xknx.io import DEFAULT_MCAST_PORT, ConnectionConfig, ConnectionType
|
||||||
from xknx.knx import AddressFilter, DPTArray, DPTBinary, GroupAddress, Telegram
|
from xknx.telegram import AddressFilter, GroupAddress, Telegram
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_ENTITY_ID,
|
CONF_ENTITY_ID,
|
||||||
CONF_HOST,
|
CONF_HOST,
|
||||||
CONF_PORT,
|
CONF_PORT,
|
||||||
EVENT_HOMEASSISTANT_STOP,
|
EVENT_HOMEASSISTANT_STOP,
|
||||||
|
STATE_OFF,
|
||||||
|
STATE_ON,
|
||||||
|
STATE_UNAVAILABLE,
|
||||||
|
STATE_UNKNOWN,
|
||||||
)
|
)
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers import discovery
|
from homeassistant.helpers import discovery
|
||||||
@ -35,6 +40,8 @@ CONF_KNX_STATE_UPDATER = "state_updater"
|
|||||||
CONF_KNX_RATE_LIMIT = "rate_limit"
|
CONF_KNX_RATE_LIMIT = "rate_limit"
|
||||||
CONF_KNX_EXPOSE = "expose"
|
CONF_KNX_EXPOSE = "expose"
|
||||||
CONF_KNX_EXPOSE_TYPE = "type"
|
CONF_KNX_EXPOSE_TYPE = "type"
|
||||||
|
CONF_KNX_EXPOSE_ATTRIBUTE = "attribute"
|
||||||
|
CONF_KNX_EXPOSE_DEFAULT = "default"
|
||||||
CONF_KNX_EXPOSE_ADDRESS = "address"
|
CONF_KNX_EXPOSE_ADDRESS = "address"
|
||||||
|
|
||||||
SERVICE_KNX_SEND = "send"
|
SERVICE_KNX_SEND = "send"
|
||||||
@ -57,6 +64,8 @@ EXPOSE_SCHEMA = vol.Schema(
|
|||||||
{
|
{
|
||||||
vol.Required(CONF_KNX_EXPOSE_TYPE): cv.string,
|
vol.Required(CONF_KNX_EXPOSE_TYPE): cv.string,
|
||||||
vol.Optional(CONF_ENTITY_ID): cv.entity_id,
|
vol.Optional(CONF_ENTITY_ID): cv.entity_id,
|
||||||
|
vol.Optional(CONF_KNX_EXPOSE_ATTRIBUTE): cv.string,
|
||||||
|
vol.Optional(CONF_KNX_EXPOSE_DEFAULT): cv.match_all,
|
||||||
vol.Required(CONF_KNX_EXPOSE_ADDRESS): cv.string,
|
vol.Required(CONF_KNX_EXPOSE_ADDRESS): cv.string,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -244,6 +253,8 @@ class KNXModule:
|
|||||||
for to_expose in self.config[DOMAIN][CONF_KNX_EXPOSE]:
|
for to_expose in self.config[DOMAIN][CONF_KNX_EXPOSE]:
|
||||||
expose_type = to_expose.get(CONF_KNX_EXPOSE_TYPE)
|
expose_type = to_expose.get(CONF_KNX_EXPOSE_TYPE)
|
||||||
entity_id = to_expose.get(CONF_ENTITY_ID)
|
entity_id = to_expose.get(CONF_ENTITY_ID)
|
||||||
|
attribute = to_expose.get(CONF_KNX_EXPOSE_ATTRIBUTE)
|
||||||
|
default = to_expose.get(CONF_KNX_EXPOSE_DEFAULT)
|
||||||
address = to_expose.get(CONF_KNX_EXPOSE_ADDRESS)
|
address = to_expose.get(CONF_KNX_EXPOSE_ADDRESS)
|
||||||
if expose_type in ["time", "date", "datetime"]:
|
if expose_type in ["time", "date", "datetime"]:
|
||||||
exposure = KNXExposeTime(self.xknx, expose_type, address)
|
exposure = KNXExposeTime(self.xknx, expose_type, address)
|
||||||
@ -251,7 +262,13 @@ class KNXModule:
|
|||||||
self.exposures.append(exposure)
|
self.exposures.append(exposure)
|
||||||
else:
|
else:
|
||||||
exposure = KNXExposeSensor(
|
exposure = KNXExposeSensor(
|
||||||
self.hass, self.xknx, expose_type, entity_id, address
|
self.hass,
|
||||||
|
self.xknx,
|
||||||
|
expose_type,
|
||||||
|
entity_id,
|
||||||
|
attribute,
|
||||||
|
default,
|
||||||
|
address,
|
||||||
)
|
)
|
||||||
exposure.async_register()
|
exposure.async_register()
|
||||||
self.exposures.append(exposure)
|
self.exposures.append(exposure)
|
||||||
@ -325,23 +342,26 @@ class KNXExposeTime:
|
|||||||
class KNXExposeSensor:
|
class KNXExposeSensor:
|
||||||
"""Object to Expose Home Assistant entity to KNX bus."""
|
"""Object to Expose Home Assistant entity to KNX bus."""
|
||||||
|
|
||||||
def __init__(self, hass, xknx, expose_type, entity_id, address):
|
def __init__(self, hass, xknx, expose_type, entity_id, attribute, default, address):
|
||||||
"""Initialize of Expose class."""
|
"""Initialize of Expose class."""
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self.xknx = xknx
|
self.xknx = xknx
|
||||||
self.type = expose_type
|
self.type = expose_type
|
||||||
self.entity_id = entity_id
|
self.entity_id = entity_id
|
||||||
|
self.expose_attribute = attribute
|
||||||
|
self.expose_default = default
|
||||||
self.address = address
|
self.address = address
|
||||||
self.device = None
|
self.device = None
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_register(self):
|
def async_register(self):
|
||||||
"""Register listener."""
|
"""Register listener."""
|
||||||
|
if self.expose_attribute is not None:
|
||||||
|
_name = self.entity_id + "__" + self.expose_attribute
|
||||||
|
else:
|
||||||
|
_name = self.entity_id
|
||||||
self.device = ExposeSensor(
|
self.device = ExposeSensor(
|
||||||
self.xknx,
|
self.xknx, name=_name, group_address=self.address, value_type=self.type,
|
||||||
name=self.entity_id,
|
|
||||||
group_address=self.address,
|
|
||||||
value_type=self.type,
|
|
||||||
)
|
)
|
||||||
self.xknx.devices.add(self.device)
|
self.xknx.devices.add(self.device)
|
||||||
async_track_state_change(self.hass, self.entity_id, self._async_entity_changed)
|
async_track_state_change(self.hass, self.entity_id, self._async_entity_changed)
|
||||||
@ -350,13 +370,31 @@ class KNXExposeSensor:
|
|||||||
"""Handle entity change."""
|
"""Handle entity change."""
|
||||||
if new_state is None:
|
if new_state is None:
|
||||||
return
|
return
|
||||||
if new_state.state == "unknown":
|
if new_state.state in (STATE_UNKNOWN, STATE_UNAVAILABLE):
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.type == "binary":
|
if self.expose_attribute is not None:
|
||||||
if new_state.state == "on":
|
new_attribute = new_state.attributes.get(self.expose_attribute)
|
||||||
await self.device.set(True)
|
if old_state is not None:
|
||||||
elif new_state.state == "off":
|
old_attribute = old_state.attributes.get(self.expose_attribute)
|
||||||
await self.device.set(False)
|
if old_attribute == new_attribute:
|
||||||
|
# don't send same value sequentially
|
||||||
|
return
|
||||||
|
await self._async_set_knx_value(new_attribute)
|
||||||
else:
|
else:
|
||||||
await self.device.set(new_state.state)
|
await self._async_set_knx_value(new_state.state)
|
||||||
|
|
||||||
|
async def _async_set_knx_value(self, value):
|
||||||
|
"""Set new value on xknx ExposeSensor."""
|
||||||
|
if value is None:
|
||||||
|
if self.expose_default is None:
|
||||||
|
return
|
||||||
|
value = self.expose_default
|
||||||
|
|
||||||
|
if self.type == "binary":
|
||||||
|
if value == STATE_ON:
|
||||||
|
value = True
|
||||||
|
elif value == STATE_OFF:
|
||||||
|
value = False
|
||||||
|
|
||||||
|
await self.device.set(value)
|
||||||
|
@ -124,6 +124,10 @@ class KNXBinarySensor(BinarySensorEntity):
|
|||||||
"""Store register state change callback."""
|
"""Store register state change callback."""
|
||||||
self.async_register_callbacks()
|
self.async_register_callbacks()
|
||||||
|
|
||||||
|
async def async_update(self):
|
||||||
|
"""Request a state update from KNX bus."""
|
||||||
|
await self.device.sync()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""Return the name of the KNX device."""
|
"""Return the name of the KNX device."""
|
||||||
|
@ -3,7 +3,7 @@ from typing import List, Optional
|
|||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from xknx.devices import Climate as XknxClimate, ClimateMode as XknxClimateMode
|
from xknx.devices import Climate as XknxClimate, ClimateMode as XknxClimateMode
|
||||||
from xknx.knx import HVACOperationMode
|
from xknx.dpt import HVACOperationMode
|
||||||
|
|
||||||
from homeassistant.components.climate import PLATFORM_SCHEMA, ClimateEntity
|
from homeassistant.components.climate import PLATFORM_SCHEMA, ClimateEntity
|
||||||
from homeassistant.components.climate.const import (
|
from homeassistant.components.climate.const import (
|
||||||
@ -215,6 +215,11 @@ class KNXClimate(ClimateEntity):
|
|||||||
self.device.register_device_updated_cb(after_update_callback)
|
self.device.register_device_updated_cb(after_update_callback)
|
||||||
self.device.mode.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
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
"""Return the name of the KNX device."""
|
"""Return the name of the KNX device."""
|
||||||
@ -279,7 +284,8 @@ class KNXClimate(ClimateEntity):
|
|||||||
return OPERATION_MODES.get(
|
return OPERATION_MODES.get(
|
||||||
self.device.mode.operation_mode.value, HVAC_MODE_HEAT
|
self.device.mode.operation_mode.value, HVAC_MODE_HEAT
|
||||||
)
|
)
|
||||||
return None
|
# default to "heat"
|
||||||
|
return HVAC_MODE_HEAT
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hvac_modes(self) -> Optional[List[str]]:
|
def hvac_modes(self) -> Optional[List[str]]:
|
||||||
@ -293,7 +299,9 @@ class KNXClimate(ClimateEntity):
|
|||||||
_operations.append(HVAC_MODE_HEAT)
|
_operations.append(HVAC_MODE_HEAT)
|
||||||
_operations.append(HVAC_MODE_OFF)
|
_operations.append(HVAC_MODE_OFF)
|
||||||
|
|
||||||
return [op for op in _operations if op is not None]
|
_modes = list(filter(None, _operations))
|
||||||
|
# default to ["heat"]
|
||||||
|
return _modes if _modes else [HVAC_MODE_HEAT]
|
||||||
|
|
||||||
async def async_set_hvac_mode(self, hvac_mode: str) -> None:
|
async def async_set_hvac_mode(self, hvac_mode: str) -> None:
|
||||||
"""Set operation mode."""
|
"""Set operation mode."""
|
||||||
|
@ -116,6 +116,10 @@ class KNXCover(CoverEntity):
|
|||||||
"""Store register state change callback."""
|
"""Store register state change callback."""
|
||||||
self.async_register_callbacks()
|
self.async_register_callbacks()
|
||||||
|
|
||||||
|
async def async_update(self):
|
||||||
|
"""Request a state update from KNX bus."""
|
||||||
|
await self.device.sync()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""Return the name of the KNX device."""
|
"""Return the name of the KNX device."""
|
||||||
|
@ -162,6 +162,10 @@ class KNXLight(LightEntity):
|
|||||||
"""Store register state change callback."""
|
"""Store register state change callback."""
|
||||||
self.async_register_callbacks()
|
self.async_register_callbacks()
|
||||||
|
|
||||||
|
async def async_update(self):
|
||||||
|
"""Request a state update from KNX bus."""
|
||||||
|
await self.device.sync()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""Return the name of the KNX device."""
|
"""Return the name of the KNX device."""
|
||||||
|
@ -2,6 +2,6 @@
|
|||||||
"domain": "knx",
|
"domain": "knx",
|
||||||
"name": "KNX",
|
"name": "KNX",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/knx",
|
"documentation": "https://www.home-assistant.io/integrations/knx",
|
||||||
"requirements": ["xknx==0.11.2"],
|
"requirements": ["xknx==0.11.3"],
|
||||||
"codeowners": ["@Julius2342"]
|
"codeowners": ["@Julius2342"]
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,10 @@ class KNXSensor(Entity):
|
|||||||
"""Store register state change callback."""
|
"""Store register state change callback."""
|
||||||
self.async_register_callbacks()
|
self.async_register_callbacks()
|
||||||
|
|
||||||
|
async def async_update(self):
|
||||||
|
"""Update the state from KNX."""
|
||||||
|
await self.device.sync()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""Return the name of the KNX device."""
|
"""Return the name of the KNX device."""
|
||||||
|
@ -73,6 +73,10 @@ class KNXSwitch(SwitchEntity):
|
|||||||
"""Store register state change callback."""
|
"""Store register state change callback."""
|
||||||
self.async_register_callbacks()
|
self.async_register_callbacks()
|
||||||
|
|
||||||
|
async def async_update(self):
|
||||||
|
"""Request a state update from KNX bus."""
|
||||||
|
await self.device.sync()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""Return the name of the KNX device."""
|
"""Return the name of the KNX device."""
|
||||||
|
@ -2207,7 +2207,7 @@ xboxapi==0.1.1
|
|||||||
xfinity-gateway==0.0.4
|
xfinity-gateway==0.0.4
|
||||||
|
|
||||||
# homeassistant.components.knx
|
# homeassistant.components.knx
|
||||||
xknx==0.11.2
|
xknx==0.11.3
|
||||||
|
|
||||||
# homeassistant.components.bluesound
|
# homeassistant.components.bluesound
|
||||||
# homeassistant.components.rest
|
# homeassistant.components.rest
|
||||||
|
Loading…
x
Reference in New Issue
Block a user