add percentage (DPT_Scaling) KNX sensors (#8168)

* add percentage (DPT_Scaling) KNX sensors

1. moved basic functionality to KNXSensorBaseClass instead of
KNXSensorFloatClass
2. added "if" clause in setup for a "percentage" sensor type and added KNXSensorDPTScalingClass

* support-knx-percentage-sensor: lint correction

Updated convert method base sensor class to avoid lint warning
(R201 - Method could be a function)

* added PLATFORM_SCHEMA for configuration

1. added SCHEMA extension for defined keywords
2. moved fixed data for internal settings out of sensor logic
3. moved everything into standard KNXSensor object
4. added parsing of extra config parameters in __init__

* correct lint errors on support-knx-percentage-sensor
This commit is contained in:
Will W 2017-06-27 14:25:54 +09:00 committed by Paulus Schoutsen
parent 596093d564
commit 88b9503962

View File

@ -4,109 +4,125 @@ Sensors of a KNX Device.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/knx/
"""
from enum import Enum
import logging
import voluptuous as vol
from homeassistant.const import (
TEMP_CELSIUS, TEMPERATURE, CONF_TYPE, ILLUMINANCE, SPEED_MS, CONF_MINIMUM,
CONF_MAXIMUM)
CONF_NAME, CONF_MAXIMUM, CONF_MINIMUM,
CONF_TYPE, TEMP_CELSIUS
)
from homeassistant.components.knx import (KNXConfig, KNXGroupAddress)
from homeassistant.components.sensor import PLATFORM_SCHEMA
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['knx']
# Speed units
SPEED_METERPERSECOND = 'm/s' # type: str
DEFAULT_NAME = "KNX sensor"
# Illuminance units
ILLUMINANCE_LUX = 'lx' # type: str
# Predefined Minimum, Maximum Values for Sensors
# Temperature as defined in KNX Standard 3.10 - 9.001 DPT_Value_Temp
KNX_TEMP_MIN = -273
KNX_TEMP_MAX = 670760
# Luminance(LUX) as Defined in KNX Standard 3.10 - 9.004 DPT_Value_Lux
KNX_LUX_MIN = 0
KNX_LUX_MAX = 670760
# Speed m/s as defined in KNX Standard 3.10 - 9.005 DPT_Value_Wsp
KNX_SPEED_MS_MIN = 0
KNX_SPEED_MS_MAX = 670760
CONF_TEMPERATURE = 'temperature'
CONF_ADDRESS = 'address'
CONF_ILLUMINANCE = 'illuminance'
CONF_PERCENTAGE = 'percentage'
CONF_SPEED_MS = 'speed_ms'
def setup_platform(hass, config, add_entities, discovery_info=None):
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)
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the KNX Sensor platform."""
# KNX Datapoint 9.001 DPT_Value_Temp
if config[CONF_TYPE] == TEMPERATURE:
minimum_value, maximum_value = \
update_and_define_min_max(config, KNX_TEMP_MIN, KNX_TEMP_MAX)
add_entities([
KNXSensorFloatClass(
hass, KNXConfig(config), TEMP_CELSIUS, minimum_value,
maximum_value)
])
# Add KNX Speed Sensors(Like Wind Speed)
# KNX Datapoint 9.005 DPT_Value_Wsp
elif config[CONF_TYPE] == SPEED_MS:
minimum_value, maximum_value = \
update_and_define_min_max(
config, KNX_SPEED_MS_MIN, KNX_SPEED_MS_MAX)
add_entities([
KNXSensorFloatClass(hass, KNXConfig(config), SPEED_METERPERSECOND,
minimum_value, maximum_value)
])
# Add KNX Illuminance Sensors(Lux)
# KNX Datapoint 9.004 DPT_Value_Lux
elif config[CONF_TYPE] == ILLUMINANCE:
minimum_value, maximum_value = \
update_and_define_min_max(config, KNX_LUX_MIN, KNX_LUX_MAX)
add_entities([
KNXSensorFloatClass(hass, KNXConfig(config), ILLUMINANCE_LUX,
minimum_value, maximum_value)
])
add_devices([KNXSensor(hass, KNXConfig(config))])
def update_and_define_min_max(config, minimum_default, maximum_default):
"""Determine a min/max value defined in the configuration."""
minimum_value = minimum_default
maximum_value = maximum_default
if config.get(CONF_MINIMUM):
minimum_value = config.get(CONF_MINIMUM)
class KNXSensor(KNXGroupAddress):
"""Representation of a KNX Sensor device."""
if config.get(CONF_MAXIMUM):
maximum_value = config.get(CONF_MAXIMUM)
return minimum_value, maximum_value
class KNXSensorBaseClass():
"""Sensor Base Class for all KNX Sensors."""
@property
def cache(self):
"""We don't want to cache any Sensor Value."""
return False
class KNXSensorFloatClass(KNXGroupAddress, KNXSensorBaseClass):
"""
Base Implementation of a 2byte Floating Point KNX Telegram.
Defined in KNX 3.7.2 - 3.10
"""
def __init__(self, hass, config, unit_of_measurement, minimum_sensor_value,
maximum_sensor_value):
def __init__(self, hass, config):
"""Initialize a KNX Float Sensor."""
self._unit_of_measurement = unit_of_measurement
self._minimum_value = minimum_sensor_value
self._maximum_value = maximum_sensor_value
self._value = None
# 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)
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()
# 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)
)
self._value = None
@property
def state(self):
"""Return the Value of the KNX Sensor."""
@ -119,13 +135,49 @@ class KNXSensorFloatClass(KNXGroupAddress, KNXSensorBaseClass):
def update(self):
"""Update KNX sensor."""
from knxip.conversion import knx2_to_float
super().update()
self._value = None
if self._data:
value = 0 if self._data == 0 else knx2_to_float(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
@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
return knx2_to_float(raw_value)
def convert_percent(raw_value):
"""Conversion for scaled byte values.
1byte percentage scaled KNX Telegram.
Defined in KNX 3.7.2 - 3.10.
"""
summed_value = 0
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 round(summed_value * 100 / 255)