update knx module to xknx 0.11.3 ()

- add expose attribute function and default value
- default climate havc_mode to "heat" if modes are not supported
- support `update_entity` service call
pull/37072/head
Matthias Alphart 2020-05-13 15:19:00 +02:00 committed by GitHub
parent c41fb2a21f
commit 7147c5306d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 85 additions and 19 deletions

View File

@ -4,15 +4,20 @@ import logging
import voluptuous as vol
from xknx import XKNX
from xknx.devices import ActionCallback, DateTime, DateTimeBroadcastType, ExposeSensor
from xknx.dpt import DPTArray, DPTBinary
from xknx.exceptions import XKNXException
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 (
CONF_ENTITY_ID,
CONF_HOST,
CONF_PORT,
EVENT_HOMEASSISTANT_STOP,
STATE_OFF,
STATE_ON,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.core import callback
from homeassistant.helpers import discovery
@ -35,6 +40,8 @@ CONF_KNX_STATE_UPDATER = "state_updater"
CONF_KNX_RATE_LIMIT = "rate_limit"
CONF_KNX_EXPOSE = "expose"
CONF_KNX_EXPOSE_TYPE = "type"
CONF_KNX_EXPOSE_ATTRIBUTE = "attribute"
CONF_KNX_EXPOSE_DEFAULT = "default"
CONF_KNX_EXPOSE_ADDRESS = "address"
SERVICE_KNX_SEND = "send"
@ -57,6 +64,8 @@ EXPOSE_SCHEMA = vol.Schema(
{
vol.Required(CONF_KNX_EXPOSE_TYPE): cv.string,
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,
}
)
@ -244,6 +253,8 @@ class KNXModule:
for to_expose in self.config[DOMAIN][CONF_KNX_EXPOSE]:
expose_type = to_expose.get(CONF_KNX_EXPOSE_TYPE)
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)
if expose_type in ["time", "date", "datetime"]:
exposure = KNXExposeTime(self.xknx, expose_type, address)
@ -251,7 +262,13 @@ class KNXModule:
self.exposures.append(exposure)
else:
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()
self.exposures.append(exposure)
@ -325,23 +342,26 @@ class KNXExposeTime:
class KNXExposeSensor:
"""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."""
self.hass = hass
self.xknx = xknx
self.type = expose_type
self.entity_id = entity_id
self.expose_attribute = attribute
self.expose_default = default
self.address = address
self.device = None
@callback
def async_register(self):
"""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.xknx,
name=self.entity_id,
group_address=self.address,
value_type=self.type,
self.xknx, name=_name, group_address=self.address, value_type=self.type,
)
self.xknx.devices.add(self.device)
async_track_state_change(self.hass, self.entity_id, self._async_entity_changed)
@ -350,13 +370,31 @@ class KNXExposeSensor:
"""Handle entity change."""
if new_state is None:
return
if new_state.state == "unknown":
if new_state.state in (STATE_UNKNOWN, STATE_UNAVAILABLE):
return
if self.type == "binary":
if new_state.state == "on":
await self.device.set(True)
elif new_state.state == "off":
await self.device.set(False)
if self.expose_attribute is not None:
new_attribute = new_state.attributes.get(self.expose_attribute)
if old_state is not None:
old_attribute = old_state.attributes.get(self.expose_attribute)
if old_attribute == new_attribute:
# don't send same value sequentially
return
await self._async_set_knx_value(new_attribute)
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)

View File

@ -124,6 +124,10 @@ class KNXBinarySensor(BinarySensorEntity):
"""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."""

View File

@ -3,7 +3,7 @@ from typing import List, Optional
import voluptuous as vol
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.const import (
@ -215,6 +215,11 @@ class KNXClimate(ClimateEntity):
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."""
@ -279,7 +284,8 @@ class KNXClimate(ClimateEntity):
return OPERATION_MODES.get(
self.device.mode.operation_mode.value, HVAC_MODE_HEAT
)
return None
# default to "heat"
return HVAC_MODE_HEAT
@property
def hvac_modes(self) -> Optional[List[str]]:
@ -293,7 +299,9 @@ class KNXClimate(ClimateEntity):
_operations.append(HVAC_MODE_HEAT)
_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:
"""Set operation mode."""

View File

@ -116,6 +116,10 @@ class KNXCover(CoverEntity):
"""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."""

View File

@ -162,6 +162,10 @@ class KNXLight(LightEntity):
"""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."""

View File

@ -2,6 +2,6 @@
"domain": "knx",
"name": "KNX",
"documentation": "https://www.home-assistant.io/integrations/knx",
"requirements": ["xknx==0.11.2"],
"requirements": ["xknx==0.11.3"],
"codeowners": ["@Julius2342"]
}

View File

@ -77,6 +77,10 @@ class KNXSensor(Entity):
"""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."""

View File

@ -73,6 +73,10 @@ class KNXSwitch(SwitchEntity):
"""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."""

View File

@ -2207,7 +2207,7 @@ xboxapi==0.1.1
xfinity-gateway==0.0.4
# homeassistant.components.knx
xknx==0.11.2
xknx==0.11.3
# homeassistant.components.bluesound
# homeassistant.components.rest