2016-07-10 17:36:54 +00:00
|
|
|
"""
|
|
|
|
Support for KNX components.
|
|
|
|
|
|
|
|
For more details about this component, please refer to the documentation at
|
|
|
|
https://home-assistant.io/components/knx/
|
|
|
|
"""
|
|
|
|
import logging
|
2017-07-20 05:01:05 +00:00
|
|
|
import os
|
2016-07-10 17:36:54 +00:00
|
|
|
|
2016-09-14 06:03:30 +00:00
|
|
|
import voluptuous as vol
|
|
|
|
|
2017-04-30 05:04:49 +00:00
|
|
|
import homeassistant.helpers.config_validation as cv
|
2016-09-14 06:03:30 +00:00
|
|
|
from homeassistant.const import (
|
|
|
|
EVENT_HOMEASSISTANT_STOP, CONF_HOST, CONF_PORT)
|
2016-07-10 17:36:54 +00:00
|
|
|
from homeassistant.helpers.entity import Entity
|
2017-07-20 05:01:05 +00:00
|
|
|
from homeassistant.config import load_yaml_config_file
|
2016-07-10 17:36:54 +00:00
|
|
|
|
2017-07-16 11:34:13 +00:00
|
|
|
REQUIREMENTS = ['knxip==0.5']
|
2016-07-10 17:36:54 +00:00
|
|
|
|
2016-09-14 06:03:30 +00:00
|
|
|
_LOGGER = logging.getLogger(__name__)
|
2016-07-10 17:36:54 +00:00
|
|
|
|
2016-09-14 06:03:30 +00:00
|
|
|
DEFAULT_HOST = '0.0.0.0'
|
2017-05-05 23:19:24 +00:00
|
|
|
DEFAULT_PORT = 3671
|
2016-09-14 06:03:30 +00:00
|
|
|
DOMAIN = 'knx'
|
2016-07-10 17:36:54 +00:00
|
|
|
|
2016-09-14 06:03:30 +00:00
|
|
|
EVENT_KNX_FRAME_RECEIVED = 'knx_frame_received'
|
2017-07-12 10:21:15 +00:00
|
|
|
EVENT_KNX_FRAME_SEND = 'knx_frame_send'
|
2016-07-10 17:36:54 +00:00
|
|
|
|
|
|
|
KNXTUNNEL = None
|
2017-07-20 05:01:05 +00:00
|
|
|
KNX_ADDRESS = "address"
|
|
|
|
KNX_DATA = "data"
|
|
|
|
KNX_GROUP_WRITE = "group_write"
|
2017-07-07 05:21:40 +00:00
|
|
|
CONF_LISTEN = "listen"
|
2016-07-10 17:36:54 +00:00
|
|
|
|
2016-09-14 06:03:30 +00:00
|
|
|
CONFIG_SCHEMA = vol.Schema({
|
|
|
|
DOMAIN: vol.Schema({
|
|
|
|
vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
|
|
|
|
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
|
2017-07-07 05:21:40 +00:00
|
|
|
vol.Optional(CONF_LISTEN, default=[]):
|
|
|
|
vol.All(cv.ensure_list, [cv.string]),
|
2016-09-14 06:03:30 +00:00
|
|
|
}),
|
|
|
|
}, extra=vol.ALLOW_EXTRA)
|
2016-07-10 17:36:54 +00:00
|
|
|
|
2017-07-20 05:01:05 +00:00
|
|
|
KNX_WRITE_SCHEMA = vol.Schema({
|
|
|
|
vol.Required(KNX_ADDRESS): vol.All(cv.ensure_list, [cv.string]),
|
|
|
|
vol.Required(KNX_DATA): vol.All(cv.ensure_list, [cv.byte])
|
|
|
|
})
|
|
|
|
|
2016-07-10 17:36:54 +00:00
|
|
|
|
|
|
|
def setup(hass, config):
|
2017-04-30 05:04:49 +00:00
|
|
|
"""Set up the connection to the KNX IP interface."""
|
2016-07-10 17:36:54 +00:00
|
|
|
global KNXTUNNEL
|
|
|
|
|
|
|
|
from knxip.ip import KNXIPTunnel
|
2017-07-07 05:21:40 +00:00
|
|
|
from knxip.core import KNXException, parse_group_address
|
2016-07-10 17:36:54 +00:00
|
|
|
|
2016-09-14 06:03:30 +00:00
|
|
|
host = config[DOMAIN].get(CONF_HOST)
|
|
|
|
port = config[DOMAIN].get(CONF_PORT)
|
2016-07-10 17:36:54 +00:00
|
|
|
|
2017-07-06 03:02:16 +00:00
|
|
|
if host == '0.0.0.0':
|
2016-07-10 17:36:54 +00:00
|
|
|
_LOGGER.debug("Will try to auto-detect KNX/IP gateway")
|
|
|
|
|
|
|
|
KNXTUNNEL = KNXIPTunnel(host, port)
|
|
|
|
try:
|
2016-07-23 20:54:20 +00:00
|
|
|
res = KNXTUNNEL.connect()
|
|
|
|
_LOGGER.debug("Res = %s", res)
|
|
|
|
if not res:
|
2017-07-01 20:30:39 +00:00
|
|
|
_LOGGER.error("Could not connect to KNX/IP interface %s", host)
|
2016-07-23 20:54:20 +00:00
|
|
|
return False
|
|
|
|
|
2016-07-10 17:36:54 +00:00
|
|
|
except KNXException as ex:
|
|
|
|
_LOGGER.exception("Can't connect to KNX/IP interface: %s", ex)
|
|
|
|
KNXTUNNEL = None
|
|
|
|
return False
|
|
|
|
|
|
|
|
_LOGGER.info("KNX IP tunnel to %s:%i established", host, port)
|
|
|
|
|
2017-07-20 05:01:05 +00:00
|
|
|
descriptions = load_yaml_config_file(
|
|
|
|
os.path.join(os.path.dirname(__file__), 'services.yaml'))
|
|
|
|
|
2017-07-07 05:21:40 +00:00
|
|
|
def received_knx_event(address, data):
|
|
|
|
"""Process received KNX message."""
|
|
|
|
if len(data) == 1:
|
|
|
|
data = data[0]
|
|
|
|
hass.bus.fire('knx_event', {
|
|
|
|
'address': address,
|
|
|
|
'data': data
|
|
|
|
})
|
|
|
|
|
|
|
|
for listen in config[DOMAIN].get(CONF_LISTEN):
|
|
|
|
_LOGGER.debug("Registering listener for %s", listen)
|
|
|
|
try:
|
|
|
|
KNXTUNNEL.register_listener(parse_group_address(listen),
|
|
|
|
received_knx_event)
|
|
|
|
except KNXException as knxexception:
|
|
|
|
_LOGGER.error("Can't register KNX listener for address %s (%s)",
|
|
|
|
listen, knxexception)
|
|
|
|
|
2016-07-10 17:36:54 +00:00
|
|
|
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, close_tunnel)
|
2017-07-12 10:21:15 +00:00
|
|
|
|
|
|
|
# Listen to KNX events and send them to the bus
|
2017-07-20 05:01:05 +00:00
|
|
|
def handle_group_write(call):
|
2017-07-12 10:21:15 +00:00
|
|
|
"""Bridge knx_frame_send events to the KNX bus."""
|
2017-07-20 05:01:05 +00:00
|
|
|
# parameters are pre-validated using KNX_WRITE_SCHEMA
|
|
|
|
addrlist = call.data.get("address")
|
|
|
|
knxdata = call.data.get("data")
|
2017-07-12 10:21:15 +00:00
|
|
|
|
2017-07-20 05:01:05 +00:00
|
|
|
knxaddrlist = []
|
|
|
|
for addr in addrlist:
|
2017-07-12 10:21:15 +00:00
|
|
|
try:
|
2017-07-20 05:01:05 +00:00
|
|
|
_LOGGER.debug("Found %s", addr)
|
|
|
|
knxaddr = int(addr)
|
2017-07-12 10:21:15 +00:00
|
|
|
except ValueError:
|
2017-07-20 05:01:05 +00:00
|
|
|
knxaddr = None
|
|
|
|
|
|
|
|
if knxaddr is None:
|
|
|
|
try:
|
|
|
|
knxaddr = parse_group_address(addr)
|
|
|
|
except KNXException:
|
|
|
|
_LOGGER.error("KNX address format incorrect: %s", addr)
|
|
|
|
|
|
|
|
knxaddrlist.append(knxaddr)
|
2017-07-12 10:21:15 +00:00
|
|
|
|
2017-07-20 05:01:05 +00:00
|
|
|
for addr in knxaddrlist:
|
|
|
|
KNXTUNNEL.group_write(addr, knxdata)
|
2017-07-12 10:21:15 +00:00
|
|
|
|
|
|
|
# Listen for when knx_frame_send event is fired
|
2017-07-20 05:01:05 +00:00
|
|
|
hass.services.register(DOMAIN,
|
|
|
|
KNX_GROUP_WRITE,
|
|
|
|
handle_group_write,
|
|
|
|
descriptions[DOMAIN][KNX_GROUP_WRITE],
|
|
|
|
schema=KNX_WRITE_SCHEMA)
|
2017-07-12 10:21:15 +00:00
|
|
|
|
2016-07-10 17:36:54 +00:00
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
def close_tunnel(_data):
|
|
|
|
"""Close the NKX tunnel connection on shutdown."""
|
|
|
|
global KNXTUNNEL
|
|
|
|
|
|
|
|
KNXTUNNEL.disconnect()
|
|
|
|
KNXTUNNEL = None
|
|
|
|
|
|
|
|
|
|
|
|
class KNXConfig(object):
|
|
|
|
"""Handle the fetching of configuration from the config file."""
|
|
|
|
|
|
|
|
def __init__(self, config):
|
|
|
|
"""Initialize the configuration."""
|
|
|
|
from knxip.core import parse_group_address
|
|
|
|
|
|
|
|
self.config = config
|
2016-09-14 06:03:30 +00:00
|
|
|
self.should_poll = config.get('poll', True)
|
|
|
|
if config.get('address'):
|
|
|
|
self._address = parse_group_address(config.get('address'))
|
2016-07-23 20:54:20 +00:00
|
|
|
else:
|
|
|
|
self._address = None
|
2016-09-14 06:03:30 +00:00
|
|
|
if self.config.get('state_address'):
|
2016-07-10 17:36:54 +00:00
|
|
|
self._state_address = parse_group_address(
|
2016-09-14 06:03:30 +00:00
|
|
|
self.config.get('state_address'))
|
2016-07-10 17:36:54 +00:00
|
|
|
else:
|
|
|
|
self._state_address = None
|
|
|
|
|
|
|
|
@property
|
|
|
|
def name(self):
|
2017-04-30 05:04:49 +00:00
|
|
|
"""Return the name given to the entity."""
|
2016-09-14 06:03:30 +00:00
|
|
|
return self.config['name']
|
2016-07-10 17:36:54 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def address(self):
|
2017-04-30 05:04:49 +00:00
|
|
|
"""Return the address of the device as an integer value.
|
2016-07-10 17:36:54 +00:00
|
|
|
|
|
|
|
3 types of addresses are supported:
|
|
|
|
integer - 0-65535
|
|
|
|
2 level - a/b
|
|
|
|
3 level - a/b/c
|
|
|
|
"""
|
|
|
|
return self._address
|
|
|
|
|
|
|
|
@property
|
|
|
|
def state_address(self):
|
2017-04-30 05:04:49 +00:00
|
|
|
"""Return the group address the device sends its current state to.
|
2016-07-10 17:36:54 +00:00
|
|
|
|
|
|
|
Some KNX devices can send the current state to a seperate
|
|
|
|
group address. This makes send e.g. when an actuator can
|
|
|
|
be switched but also have a timer functionality.
|
|
|
|
"""
|
|
|
|
return self._state_address
|
|
|
|
|
|
|
|
|
|
|
|
class KNXGroupAddress(Entity):
|
|
|
|
"""Representation of devices connected to a KNX group address."""
|
|
|
|
|
|
|
|
def __init__(self, hass, config):
|
|
|
|
"""Initialize the device."""
|
|
|
|
self._config = config
|
|
|
|
self._state = False
|
|
|
|
self._data = None
|
2017-07-01 20:30:39 +00:00
|
|
|
_LOGGER.debug(
|
|
|
|
"Initalizing KNX group address for %s (%s)",
|
|
|
|
self.name, self.address
|
|
|
|
)
|
2016-07-10 17:36:54 +00:00
|
|
|
|
|
|
|
def handle_knx_message(addr, data):
|
|
|
|
"""Handle an incoming KNX frame.
|
|
|
|
|
|
|
|
Handle an incoming frame and update our status if it contains
|
|
|
|
information relating to this device.
|
|
|
|
"""
|
|
|
|
if (addr == self.state_address) or (addr == self.address):
|
2017-02-13 21:48:48 +00:00
|
|
|
self._state = data[0]
|
|
|
|
self.schedule_update_ha_state()
|
2016-07-10 17:36:54 +00:00
|
|
|
|
|
|
|
KNXTUNNEL.register_listener(self.address, handle_knx_message)
|
|
|
|
if self.state_address:
|
|
|
|
KNXTUNNEL.register_listener(self.state_address, handle_knx_message)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def name(self):
|
2017-04-30 05:04:49 +00:00
|
|
|
"""Return the entity's display name."""
|
2016-07-10 17:36:54 +00:00
|
|
|
return self._config.name
|
|
|
|
|
|
|
|
@property
|
|
|
|
def config(self):
|
2017-04-30 05:04:49 +00:00
|
|
|
"""Return the entity's configuration."""
|
2016-07-10 17:36:54 +00:00
|
|
|
return self._config
|
|
|
|
|
|
|
|
@property
|
|
|
|
def should_poll(self):
|
|
|
|
"""Return the state of the polling, if needed."""
|
|
|
|
return self._config.should_poll
|
|
|
|
|
|
|
|
@property
|
|
|
|
def is_on(self):
|
|
|
|
"""Return True if the value is not 0 is on, else False."""
|
|
|
|
return self._state != 0
|
|
|
|
|
|
|
|
@property
|
|
|
|
def address(self):
|
|
|
|
"""Return the KNX group address."""
|
|
|
|
return self._config.address
|
|
|
|
|
|
|
|
@property
|
|
|
|
def state_address(self):
|
|
|
|
"""Return the KNX group address."""
|
|
|
|
return self._config.state_address
|
|
|
|
|
|
|
|
@property
|
|
|
|
def cache(self):
|
2017-04-30 05:04:49 +00:00
|
|
|
"""Return the name given to the entity."""
|
2016-09-14 06:03:30 +00:00
|
|
|
return self._config.config.get('cache', True)
|
2016-07-10 17:36:54 +00:00
|
|
|
|
|
|
|
def group_write(self, value):
|
|
|
|
"""Write to the group address."""
|
|
|
|
KNXTUNNEL.group_write(self.address, [value])
|
|
|
|
|
|
|
|
def update(self):
|
|
|
|
"""Get the state from KNX bus or cache."""
|
|
|
|
from knxip.core import KNXException
|
|
|
|
|
|
|
|
try:
|
|
|
|
if self.state_address:
|
2016-09-14 06:03:30 +00:00
|
|
|
res = KNXTUNNEL.group_read(
|
|
|
|
self.state_address, use_cache=self.cache)
|
2016-07-10 17:36:54 +00:00
|
|
|
else:
|
2016-09-14 06:03:30 +00:00
|
|
|
res = KNXTUNNEL.group_read(self.address, use_cache=self.cache)
|
2016-07-10 17:36:54 +00:00
|
|
|
|
|
|
|
if res:
|
|
|
|
self._state = res[0]
|
|
|
|
self._data = res
|
|
|
|
else:
|
2016-09-14 06:03:30 +00:00
|
|
|
_LOGGER.debug(
|
2017-07-01 20:30:39 +00:00
|
|
|
"%s: unable to read from KNX address: %s (None)",
|
|
|
|
self.name, self.address
|
|
|
|
)
|
2016-07-10 17:36:54 +00:00
|
|
|
|
|
|
|
except KNXException:
|
2016-09-14 06:03:30 +00:00
|
|
|
_LOGGER.exception(
|
2017-07-01 20:30:39 +00:00
|
|
|
"%s: unable to read from KNX address: %s",
|
|
|
|
self.name, self.address
|
|
|
|
)
|
2016-07-10 17:36:54 +00:00
|
|
|
return False
|
|
|
|
|
|
|
|
|
2016-07-23 20:54:20 +00:00
|
|
|
class KNXMultiAddressDevice(Entity):
|
2016-07-10 17:36:54 +00:00
|
|
|
"""Representation of devices connected to a multiple KNX group address.
|
|
|
|
|
|
|
|
This is needed for devices like dimmers or shutter actuators as they have
|
|
|
|
to be controlled by multiple group addresses.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, hass, config, required, optional=None):
|
|
|
|
"""Initialize the device.
|
|
|
|
|
|
|
|
The namelist argument lists the required addresses. E.g. for a dimming
|
|
|
|
actuators, the namelist might look like:
|
|
|
|
onoff_address: 0/0/1
|
|
|
|
brightness_address: 0/0/2
|
|
|
|
"""
|
|
|
|
from knxip.core import parse_group_address, KNXException
|
|
|
|
|
2017-06-19 05:30:39 +00:00
|
|
|
self.names = {}
|
|
|
|
self.values = {}
|
|
|
|
|
2016-07-23 20:54:20 +00:00
|
|
|
self._config = config
|
|
|
|
self._state = False
|
|
|
|
self._data = None
|
2017-07-01 20:30:39 +00:00
|
|
|
_LOGGER.debug(
|
|
|
|
"%s: initalizing KNX multi address device",
|
|
|
|
self.name
|
|
|
|
)
|
2016-07-10 17:36:54 +00:00
|
|
|
|
2017-06-19 05:30:39 +00:00
|
|
|
settings = self._config.config
|
2017-07-01 20:30:39 +00:00
|
|
|
if config.address:
|
|
|
|
_LOGGER.debug(
|
|
|
|
"%s: base address: address=%s",
|
|
|
|
self.name, settings.get('address')
|
|
|
|
)
|
|
|
|
self.names[config.address] = 'base'
|
|
|
|
if config.state_address:
|
|
|
|
_LOGGER.debug(
|
|
|
|
"%s, state address: state_address=%s",
|
|
|
|
self.name, settings.get('state_address')
|
|
|
|
)
|
|
|
|
self.names[config.state_address] = 'state'
|
|
|
|
|
2016-07-10 17:36:54 +00:00
|
|
|
# parse required addresses
|
|
|
|
for name in required:
|
2016-09-14 06:03:30 +00:00
|
|
|
paramname = '{}{}'.format(name, '_address')
|
2017-06-19 05:30:39 +00:00
|
|
|
addr = settings.get(paramname)
|
2016-07-10 17:36:54 +00:00
|
|
|
if addr is None:
|
2017-07-01 20:30:39 +00:00
|
|
|
_LOGGER.error(
|
|
|
|
"%s: Required KNX group address %s missing",
|
|
|
|
self.name, paramname
|
|
|
|
)
|
2016-09-14 06:03:30 +00:00
|
|
|
raise KNXException(
|
2017-07-01 20:30:39 +00:00
|
|
|
"%s: Group address for {} missing in "
|
|
|
|
"configuration for {}".format(
|
|
|
|
self.name, paramname
|
|
|
|
)
|
|
|
|
)
|
|
|
|
_LOGGER.debug(
|
|
|
|
"%s: (required parameter) %s=%s",
|
|
|
|
self.name, paramname, addr
|
|
|
|
)
|
2016-07-10 17:36:54 +00:00
|
|
|
addr = parse_group_address(addr)
|
|
|
|
self.names[addr] = name
|
|
|
|
|
|
|
|
# parse optional addresses
|
|
|
|
for name in optional:
|
2016-09-14 06:03:30 +00:00
|
|
|
paramname = '{}{}'.format(name, '_address')
|
2017-06-19 05:30:39 +00:00
|
|
|
addr = settings.get(paramname)
|
2017-07-01 20:30:39 +00:00
|
|
|
_LOGGER.debug(
|
|
|
|
"%s: (optional parameter) %s=%s",
|
|
|
|
self.name, paramname, addr
|
|
|
|
)
|
2016-07-10 17:36:54 +00:00
|
|
|
if addr:
|
|
|
|
try:
|
|
|
|
addr = parse_group_address(addr)
|
|
|
|
except KNXException:
|
2017-07-01 20:30:39 +00:00
|
|
|
_LOGGER.exception(
|
|
|
|
"%s: cannot parse group address %s",
|
|
|
|
self.name, addr
|
|
|
|
)
|
2016-07-10 17:36:54 +00:00
|
|
|
self.names[addr] = name
|
|
|
|
|
2016-07-23 20:54:20 +00:00
|
|
|
@property
|
|
|
|
def name(self):
|
2017-04-30 05:04:49 +00:00
|
|
|
"""Return the entity's display name."""
|
2016-07-23 20:54:20 +00:00
|
|
|
return self._config.name
|
2016-07-10 17:36:54 +00:00
|
|
|
|
2016-07-23 20:54:20 +00:00
|
|
|
@property
|
|
|
|
def config(self):
|
2017-04-30 05:04:49 +00:00
|
|
|
"""Return the entity's configuration."""
|
2016-07-23 20:54:20 +00:00
|
|
|
return self._config
|
2016-07-10 17:36:54 +00:00
|
|
|
|
2016-07-23 20:54:20 +00:00
|
|
|
@property
|
|
|
|
def should_poll(self):
|
|
|
|
"""Return the state of the polling, if needed."""
|
|
|
|
return self._config.should_poll
|
2016-07-10 17:36:54 +00:00
|
|
|
|
2016-07-23 20:54:20 +00:00
|
|
|
@property
|
|
|
|
def cache(self):
|
2017-04-30 05:04:49 +00:00
|
|
|
"""Return the name given to the entity."""
|
2016-09-14 06:03:30 +00:00
|
|
|
return self._config.config.get('cache', True)
|
2016-07-10 17:36:54 +00:00
|
|
|
|
|
|
|
def has_attribute(self, name):
|
|
|
|
"""Check if the attribute with the given name is defined.
|
|
|
|
|
|
|
|
This is mostly important for optional addresses.
|
|
|
|
"""
|
2017-07-01 20:30:39 +00:00
|
|
|
for attributename in self.names.values():
|
2016-07-10 17:36:54 +00:00
|
|
|
if attributename == name:
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2017-06-19 05:30:39 +00:00
|
|
|
def set_percentage(self, name, percentage):
|
|
|
|
"""Set a percentage in knx for a given attribute.
|
|
|
|
|
|
|
|
DPT_Scaling / DPT 5.001 is a single byte scaled percentage
|
|
|
|
"""
|
|
|
|
percentage = abs(percentage) # only accept positive values
|
|
|
|
scaled_value = percentage * 255 / 100
|
|
|
|
value = min(255, scaled_value)
|
2017-07-01 20:30:39 +00:00
|
|
|
return self.set_int_value(name, value)
|
2017-06-19 05:30:39 +00:00
|
|
|
|
|
|
|
def get_percentage(self, name):
|
|
|
|
"""Get a percentage from knx for a given attribute.
|
|
|
|
|
|
|
|
DPT_Scaling / DPT 5.001 is a single byte scaled percentage
|
|
|
|
"""
|
|
|
|
value = self.get_int_value(name)
|
|
|
|
percentage = round(value * 100 / 255)
|
|
|
|
return percentage
|
|
|
|
|
|
|
|
def set_int_value(self, name, value, num_bytes=1):
|
|
|
|
"""Set an integer value for a given attribute."""
|
|
|
|
# KNX packets are big endian
|
|
|
|
value = round(value) # only accept integers
|
|
|
|
b_value = value.to_bytes(num_bytes, byteorder='big')
|
2017-07-01 20:30:39 +00:00
|
|
|
return self.set_value(name, list(b_value))
|
2017-06-19 05:30:39 +00:00
|
|
|
|
|
|
|
def get_int_value(self, name):
|
|
|
|
"""Get an integer value for a given attribute."""
|
|
|
|
# KNX packets are big endian
|
|
|
|
summed_value = 0
|
|
|
|
raw_value = self.value(name)
|
|
|
|
try:
|
|
|
|
# convert raw value in bytes
|
|
|
|
for val in raw_value:
|
|
|
|
summed_value *= 256
|
|
|
|
summed_value += val
|
|
|
|
except TypeError:
|
|
|
|
# pknx returns a non-iterable type for unsuccessful reads
|
|
|
|
pass
|
|
|
|
|
|
|
|
return summed_value
|
|
|
|
|
2016-07-10 17:36:54 +00:00
|
|
|
def value(self, name):
|
|
|
|
"""Return the value to a given named attribute."""
|
|
|
|
from knxip.core import KNXException
|
|
|
|
|
|
|
|
addr = None
|
2016-07-23 20:54:20 +00:00
|
|
|
for attributeaddress, attributename in self.names.items():
|
2016-07-10 17:36:54 +00:00
|
|
|
if attributename == name:
|
|
|
|
addr = attributeaddress
|
|
|
|
|
|
|
|
if addr is None:
|
2017-07-01 20:30:39 +00:00
|
|
|
_LOGGER.error("%s: attribute '%s' undefined",
|
|
|
|
self.name, name)
|
|
|
|
_LOGGER.debug(
|
|
|
|
"%s: defined attributes: %s",
|
|
|
|
self.name, str(self.names)
|
|
|
|
)
|
2016-07-10 17:36:54 +00:00
|
|
|
return False
|
|
|
|
|
|
|
|
try:
|
|
|
|
res = KNXTUNNEL.group_read(addr, use_cache=self.cache)
|
|
|
|
except KNXException:
|
2017-07-01 20:30:39 +00:00
|
|
|
_LOGGER.exception(
|
|
|
|
"%s: unable to read from KNX address: %s",
|
|
|
|
self.name, addr
|
|
|
|
)
|
2016-07-10 17:36:54 +00:00
|
|
|
return False
|
|
|
|
|
|
|
|
return res
|
2016-07-23 20:54:20 +00:00
|
|
|
|
|
|
|
def set_value(self, name, value):
|
|
|
|
"""Set the value of a given named attribute."""
|
|
|
|
from knxip.core import KNXException
|
|
|
|
|
|
|
|
addr = None
|
|
|
|
for attributeaddress, attributename in self.names.items():
|
|
|
|
if attributename == name:
|
|
|
|
addr = attributeaddress
|
|
|
|
|
|
|
|
if addr is None:
|
2017-07-01 20:30:39 +00:00
|
|
|
_LOGGER.error("%s: attribute '%s' undefined",
|
|
|
|
self.name, name)
|
|
|
|
_LOGGER.debug(
|
|
|
|
"%s: defined attributes: %s",
|
|
|
|
self.name, str(self.names)
|
|
|
|
)
|
2016-07-23 20:54:20 +00:00
|
|
|
return False
|
|
|
|
|
|
|
|
try:
|
|
|
|
KNXTUNNEL.group_write(addr, value)
|
|
|
|
except KNXException:
|
2017-07-01 20:30:39 +00:00
|
|
|
_LOGGER.exception(
|
|
|
|
"%s: unable to write to KNX address: %s",
|
|
|
|
self.name, addr
|
|
|
|
)
|
2016-07-23 20:54:20 +00:00
|
|
|
return False
|
|
|
|
|
|
|
|
return True
|