Stable and asynchronous KNX library. (#8725)
* First draft of XKNX module for Home-Assistant * XKNX does now take path of xknx.yaml as parameter * small fix, telegram_received_callback has different signature * changed method of registering callbacks of devices * removed non async command lines from xknx * telegram_received_cb not needed within HASS module * updated requirements * Configuration if XKNX should connect via Routing or Tunneling * bumping version to 0.6.1 * small fix within xknx plugin * bumped version * XKNX-Switches are now BinarySensors and Logic from Sensor was moved to BinarySensor * renamed Outlet to Switch * pylint * configuration of KNX lights via HASS config, yay! * changed name of attribute * Added configuration for xknx to switch component * added support for sensors within hass configuration * added support for climate within hass configuration * Thermostat -> Climate * added configuration support for binary_sensors * renamed Shutter to Cover * added configuration support for cover * restructured file structure according to HASS requirements * pylint * pylint * pylint * pylint * pylint * pylint * updated version * pylint * pylint * pylint * added setpoint support for climate devices * devices are now in a different module * more asyncio :-) * pydocstyle * pydocstyle * added actions to binary_sensor * allow more than one automation * readded requirement * Modifications suggested by hound * Modifications suggested by hound * Modifications suggested by hound * Modifications suggested by hound * xknx now imported as local import * hound *sigh* * lint * 'fixed' coverage. * next try for getting gen_requirements_all.py working * removed blank line * XKNX 0.7.1 with logging functionality, replaced some print() calls with _LOGGER * updated requirements_all.txt * Fixes issue https://github.com/XKNX/xknx/issues/51 * https://github.com/XKNX/xknx/issues/52 added raw access to KNX bus from HASS component. * bumped version - 0.7.3 contains some bugfixes * bumped version - 0.7.3 contains some bugfixes * setting setpoint within climate device has to be async * bumped version to 0.7.4 * bumped version * https://github.com/XKNX/xknx/issues/48 Adding HVAC support. * pylint suggestions * Made target temperature and set point required attributes * renamed value_type to type within sensor configuration * Issue https://github.com/XKNX/xknx/issues/52 : added filter functionality for not flooding the event bus. * suggestions by pylint * Added notify support for knx platform. * logging error if discovery_info is None. * review suggestions by @armills * line too long * Using discovery_info to notifiy component which devices should be added. * moved XKNX automation to main level. * renamed xknx component to knx. * reverted change within .coveragerc * changed dependency * updated docstrings. * updated version of xknx within requirements_all.txt * moved requirement to correct position * renamed configuration attribute * added @callback-decorator and async_prefix. * added @callback decorator and async_ prefix to register_callbacks functions * fixed typo * pylint suggestions * added angle position and invert_position and invert_angle to cover.knx * typo * bumped version within requirements_all.txt * bumped version * Added support for HVAC controller status
This commit is contained in:
parent
9a7089bad3
commit
77d0ad1797
10 changed files with 1041 additions and 887 deletions
|
@ -1,184 +1,111 @@
|
|||
"""
|
||||
Sensors of a KNX Device.
|
||||
Support for KNX/IP sensors.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/knx/
|
||||
https://home-assistant.io/components/sensor.knx/
|
||||
"""
|
||||
from enum import Enum
|
||||
|
||||
import logging
|
||||
import asyncio
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import (
|
||||
CONF_NAME, CONF_MAXIMUM, CONF_MINIMUM,
|
||||
CONF_TYPE, TEMP_CELSIUS
|
||||
)
|
||||
from homeassistant.components.knx import (KNXConfig, KNXGroupAddress)
|
||||
from homeassistant.components.knx import DATA_KNX, ATTR_DISCOVER_DEVICES
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
CONF_ADDRESS = 'address'
|
||||
CONF_TYPE = 'type'
|
||||
|
||||
DEFAULT_NAME = 'KNX Sensor'
|
||||
DEPENDENCIES = ['knx']
|
||||
|
||||
DEFAULT_NAME = "KNX sensor"
|
||||
|
||||
CONF_TEMPERATURE = 'temperature'
|
||||
CONF_ADDRESS = 'address'
|
||||
CONF_ILLUMINANCE = 'illuminance'
|
||||
CONF_PERCENTAGE = 'percentage'
|
||||
CONF_SPEED_MS = 'speed_ms'
|
||||
|
||||
|
||||
class KNXAddressType(Enum):
|
||||
"""Enum to indicate conversion type for the KNX address."""
|
||||
|
||||
FLOAT = 1
|
||||
PERCENT = 2
|
||||
|
||||
|
||||
# define the fixed settings required for each sensor type
|
||||
FIXED_SETTINGS_MAP = {
|
||||
# Temperature as defined in KNX Standard 3.10 - 9.001 DPT_Value_Temp
|
||||
CONF_TEMPERATURE: {
|
||||
'unit': TEMP_CELSIUS,
|
||||
'default_minimum': -273,
|
||||
'default_maximum': 670760,
|
||||
'address_type': KNXAddressType.FLOAT
|
||||
},
|
||||
# Speed m/s as defined in KNX Standard 3.10 - 9.005 DPT_Value_Wsp
|
||||
CONF_SPEED_MS: {
|
||||
'unit': 'm/s',
|
||||
'default_minimum': 0,
|
||||
'default_maximum': 670760,
|
||||
'address_type': KNXAddressType.FLOAT
|
||||
},
|
||||
# Luminance(LUX) as defined in KNX Standard 3.10 - 9.004 DPT_Value_Lux
|
||||
CONF_ILLUMINANCE: {
|
||||
'unit': 'lx',
|
||||
'default_minimum': 0,
|
||||
'default_maximum': 670760,
|
||||
'address_type': KNXAddressType.FLOAT
|
||||
},
|
||||
# Percentage(%) as defined in KNX Standard 3.10 - 5.001 DPT_Scaling
|
||||
CONF_PERCENTAGE: {
|
||||
'unit': '%',
|
||||
'default_minimum': 0,
|
||||
'default_maximum': 100,
|
||||
'address_type': KNXAddressType.PERCENT
|
||||
}
|
||||
}
|
||||
|
||||
SENSOR_TYPES = set(FIXED_SETTINGS_MAP.keys())
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_TYPE): vol.In(SENSOR_TYPES),
|
||||
vol.Required(CONF_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_MINIMUM): vol.Coerce(float),
|
||||
vol.Optional(CONF_MAXIMUM): vol.Coerce(float)
|
||||
vol.Optional(CONF_TYPE): cv.string,
|
||||
})
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Set up the KNX Sensor platform."""
|
||||
add_devices([KNXSensor(hass, KNXConfig(config))])
|
||||
@asyncio.coroutine
|
||||
def async_setup_platform(hass, config, add_devices,
|
||||
discovery_info=None):
|
||||
"""Set up sensor(s) for KNX platform."""
|
||||
if DATA_KNX not in hass.data \
|
||||
or not hass.data[DATA_KNX].initialized:
|
||||
return False
|
||||
|
||||
if discovery_info is not None:
|
||||
async_add_devices_discovery(hass, discovery_info, add_devices)
|
||||
else:
|
||||
async_add_devices_config(hass, config, add_devices)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class KNXSensor(KNXGroupAddress):
|
||||
"""Representation of a KNX Sensor device."""
|
||||
@callback
|
||||
def async_add_devices_discovery(hass, discovery_info, add_devices):
|
||||
"""Set up sensors for KNX platform configured via xknx.yaml."""
|
||||
entities = []
|
||||
for device_name in discovery_info[ATTR_DISCOVER_DEVICES]:
|
||||
device = hass.data[DATA_KNX].xknx.devices[device_name]
|
||||
entities.append(KNXSensor(hass, device))
|
||||
add_devices(entities)
|
||||
|
||||
def __init__(self, hass, config):
|
||||
"""Initialize a KNX Float Sensor."""
|
||||
# set up the KNX Group address
|
||||
KNXGroupAddress.__init__(self, hass, config)
|
||||
|
||||
device_type = config.config.get(CONF_TYPE)
|
||||
sensor_config = FIXED_SETTINGS_MAP.get(device_type)
|
||||
@callback
|
||||
def async_add_devices_config(hass, config, add_devices):
|
||||
"""Set up sensor for KNX platform configured within plattform."""
|
||||
import xknx
|
||||
sensor = xknx.devices.Sensor(
|
||||
hass.data[DATA_KNX].xknx,
|
||||
name=config.get(CONF_NAME),
|
||||
group_address=config.get(CONF_ADDRESS),
|
||||
value_type=config.get(CONF_TYPE))
|
||||
hass.data[DATA_KNX].xknx.devices.add(sensor)
|
||||
add_devices([KNXSensor(hass, sensor)])
|
||||
|
||||
if not sensor_config:
|
||||
raise NotImplementedError()
|
||||
|
||||
# set up the conversion function based on the address type
|
||||
address_type = sensor_config.get('address_type')
|
||||
if address_type == KNXAddressType.FLOAT:
|
||||
self.convert = convert_float
|
||||
elif address_type == KNXAddressType.PERCENT:
|
||||
self.convert = convert_percent
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
class KNXSensor(Entity):
|
||||
"""Representation of a KNX sensor."""
|
||||
|
||||
# other settings
|
||||
self._unit_of_measurement = sensor_config.get('unit')
|
||||
default_min = float(sensor_config.get('default_minimum'))
|
||||
default_max = float(sensor_config.get('default_maximum'))
|
||||
self._minimum_value = config.config.get(CONF_MINIMUM, default_min)
|
||||
self._maximum_value = config.config.get(CONF_MAXIMUM, default_max)
|
||||
_LOGGER.debug(
|
||||
"%s: configured additional settings: unit=%s, "
|
||||
"min=%f, max=%f, type=%s",
|
||||
self.name, self._unit_of_measurement,
|
||||
self._minimum_value, self._maximum_value, str(address_type)
|
||||
)
|
||||
def __init__(self, hass, device):
|
||||
"""Initialization of KNXSensor."""
|
||||
self.device = device
|
||||
self.hass = hass
|
||||
self.async_register_callbacks()
|
||||
|
||||
self._value = None
|
||||
@callback
|
||||
def async_register_callbacks(self):
|
||||
"""Register callbacks to update hass after device was changed."""
|
||||
@asyncio.coroutine
|
||||
def after_update_callback(device):
|
||||
"""Callback after device was updated."""
|
||||
# pylint: disable=unused-argument
|
||||
yield from self.async_update_ha_state()
|
||||
self.device.register_device_updated_cb(after_update_callback)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the KNX device."""
|
||||
return self.device.name
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""No polling needed within KNX."""
|
||||
return False
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the Value of the KNX Sensor."""
|
||||
return self._value
|
||||
"""Return the state of the sensor."""
|
||||
return self.device.resolve_state()
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return the defined Unit of Measurement for the KNX Sensor."""
|
||||
return self._unit_of_measurement
|
||||
|
||||
def update(self):
|
||||
"""Update KNX sensor."""
|
||||
super().update()
|
||||
|
||||
self._value = None
|
||||
|
||||
if self._data:
|
||||
if self._data == 0:
|
||||
value = 0
|
||||
else:
|
||||
value = self.convert(self._data)
|
||||
if self._minimum_value <= value <= self._maximum_value:
|
||||
self._value = value
|
||||
"""Return the unit this state is expressed in."""
|
||||
return self.device.unit_of_measurement()
|
||||
|
||||
@property
|
||||
def cache(self):
|
||||
"""We don't want to cache any Sensor Value."""
|
||||
return False
|
||||
|
||||
|
||||
def convert_float(raw_value):
|
||||
"""Conversion for 2 byte floating point values.
|
||||
|
||||
2byte Floating Point KNX Telegram.
|
||||
Defined in KNX 3.7.2 - 3.10
|
||||
"""
|
||||
from knxip.conversion import knx2_to_float
|
||||
from knxip.core import KNXException
|
||||
|
||||
try:
|
||||
return knx2_to_float(raw_value)
|
||||
except KNXException as exception:
|
||||
_LOGGER.error("Can't convert %s to float (%s)", raw_value, exception)
|
||||
|
||||
|
||||
def convert_percent(raw_value):
|
||||
"""Conversion for scaled byte values.
|
||||
|
||||
1byte percentage scaled KNX Telegram.
|
||||
Defined in KNX 3.7.2 - 3.10.
|
||||
"""
|
||||
value = 0
|
||||
try:
|
||||
value = raw_value[0]
|
||||
except (IndexError, ValueError):
|
||||
# pknx returns a non-iterable type for unsuccessful reads
|
||||
_LOGGER.error("Can't convert %s to percent value", raw_value)
|
||||
|
||||
return round(value * 100 / 255)
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
return None
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue