mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 08:47:57 +00:00
Add support for a wider variety of EnOcean devices (#22052)
* Implement EnOcean temperature and humidity sensors. * Bump EnOcean version to 0.50 * Refactor components for more generic device handling * Move radio packet data interpretation to specific devices * Update CODEOWNERS * Implement code review changes
This commit is contained in:
parent
fef1dc8c54
commit
96735e41af
@ -66,6 +66,7 @@ homeassistant/components/egardia/* @jeroenterheerdt
|
||||
homeassistant/components/eight_sleep/* @mezz64
|
||||
homeassistant/components/emby/* @mezz64
|
||||
homeassistant/components/enigma2/* @fbradyirl
|
||||
homeassistant/components/enocean/* @bdurrer
|
||||
homeassistant/components/ephember/* @ttroy50
|
||||
homeassistant/components/epsonworkforce/* @ThaStealth
|
||||
homeassistant/components/eq3btsmart/* @rytilahti
|
||||
|
@ -4,13 +4,13 @@ import logging
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import CONF_DEVICE
|
||||
from homeassistant.helpers.entity import Entity
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DOMAIN = 'enocean'
|
||||
|
||||
ENOCEAN_DONGLE = None
|
||||
DATA_ENOCEAN = 'enocean'
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
DOMAIN: vol.Schema({
|
||||
@ -18,14 +18,15 @@ CONFIG_SCHEMA = vol.Schema({
|
||||
}),
|
||||
}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
SIGNAL_RECEIVE_MESSAGE = 'enocean.receive_message'
|
||||
SIGNAL_SEND_MESSAGE = 'enocean.send_message'
|
||||
|
||||
|
||||
def setup(hass, config):
|
||||
"""Set up the EnOcean component."""
|
||||
global ENOCEAN_DONGLE
|
||||
|
||||
serial_dev = config[DOMAIN].get(CONF_DEVICE)
|
||||
|
||||
ENOCEAN_DONGLE = EnOceanDongle(hass, serial_dev)
|
||||
dongle = EnOceanDongle(hass, serial_dev)
|
||||
hass.data[DATA_ENOCEAN] = dongle
|
||||
|
||||
return True
|
||||
|
||||
@ -39,87 +40,53 @@ class EnOceanDongle:
|
||||
self.__communicator = SerialCommunicator(
|
||||
port=ser, callback=self.callback)
|
||||
self.__communicator.start()
|
||||
self.__devices = []
|
||||
self.hass = hass
|
||||
self.hass.helpers.dispatcher.dispatcher_connect(
|
||||
SIGNAL_SEND_MESSAGE, self._send_message_callback)
|
||||
|
||||
def register_device(self, dev):
|
||||
"""Register another device."""
|
||||
self.__devices.append(dev)
|
||||
|
||||
def send_command(self, command):
|
||||
"""Send a command from the EnOcean dongle."""
|
||||
def _send_message_callback(self, command):
|
||||
"""Send a command through the EnOcean dongle."""
|
||||
self.__communicator.send(command)
|
||||
|
||||
# pylint: disable=no-self-use
|
||||
def _combine_hex(self, data):
|
||||
"""Combine list of integer values to one big integer."""
|
||||
output = 0x00
|
||||
for i, j in enumerate(reversed(data)):
|
||||
output |= (j << i * 8)
|
||||
return output
|
||||
|
||||
def callback(self, temp):
|
||||
def callback(self, packet):
|
||||
"""Handle EnOcean device's callback.
|
||||
|
||||
This is the callback function called by python-enocan whenever there
|
||||
is an incoming packet.
|
||||
"""
|
||||
from enocean.protocol.packet import RadioPacket
|
||||
if isinstance(temp, RadioPacket):
|
||||
_LOGGER.debug("Received radio packet: %s", temp)
|
||||
rxtype = None
|
||||
value = None
|
||||
channel = 0
|
||||
if temp.data[6] == 0x30:
|
||||
rxtype = "wallswitch"
|
||||
value = 1
|
||||
elif temp.data[6] == 0x20:
|
||||
rxtype = "wallswitch"
|
||||
value = 0
|
||||
elif temp.data[4] == 0x0c:
|
||||
rxtype = "power"
|
||||
value = temp.data[3] + (temp.data[2] << 8)
|
||||
elif temp.data[2] & 0x60 == 0x60:
|
||||
rxtype = "switch_status"
|
||||
channel = temp.data[2] & 0x1F
|
||||
if temp.data[3] == 0xe4:
|
||||
value = 1
|
||||
elif temp.data[3] == 0x80:
|
||||
value = 0
|
||||
elif temp.data[0] == 0xa5 and temp.data[1] == 0x02:
|
||||
rxtype = "dimmerstatus"
|
||||
value = temp.data[2]
|
||||
for device in self.__devices:
|
||||
if rxtype == "wallswitch" and device.stype == "listener":
|
||||
if temp.sender_int == self._combine_hex(device.dev_id):
|
||||
device.value_changed(value, temp.data[1])
|
||||
if rxtype == "power" and device.stype == "powersensor":
|
||||
if temp.sender_int == self._combine_hex(device.dev_id):
|
||||
device.value_changed(value)
|
||||
if rxtype == "power" and device.stype == "switch":
|
||||
if temp.sender_int == self._combine_hex(device.dev_id):
|
||||
if value > 10:
|
||||
device.value_changed(1)
|
||||
if rxtype == "switch_status" and device.stype == "switch" and \
|
||||
channel == device.channel:
|
||||
if temp.sender_int == self._combine_hex(device.dev_id):
|
||||
device.value_changed(value)
|
||||
if rxtype == "dimmerstatus" and device.stype == "dimmer":
|
||||
if temp.sender_int == self._combine_hex(device.dev_id):
|
||||
device.value_changed(value)
|
||||
if isinstance(packet, RadioPacket):
|
||||
_LOGGER.debug("Received radio packet: %s", packet)
|
||||
self.hass.helpers.dispatcher.dispatcher_send(
|
||||
SIGNAL_RECEIVE_MESSAGE, packet)
|
||||
|
||||
|
||||
class EnOceanDevice():
|
||||
class EnOceanDevice(Entity):
|
||||
"""Parent class for all devices associated with the EnOcean component."""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, dev_id, dev_name="EnOcean device"):
|
||||
"""Initialize the device."""
|
||||
ENOCEAN_DONGLE.register_device(self)
|
||||
self.stype = ""
|
||||
self.sensorid = [0x00, 0x00, 0x00, 0x00]
|
||||
self.dev_id = dev_id
|
||||
self.dev_name = dev_name
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Register callbacks."""
|
||||
self.hass.helpers.dispatcher.async_dispatcher_connect(
|
||||
SIGNAL_RECEIVE_MESSAGE, self._message_received_callback)
|
||||
|
||||
def _message_received_callback(self, packet):
|
||||
"""Handle incoming packets."""
|
||||
from enocean.utils import combine_hex
|
||||
if packet.sender_int == combine_hex(self.dev_id):
|
||||
self.value_changed(packet)
|
||||
|
||||
def value_changed(self, packet):
|
||||
"""Update the internal state of the device when a packet arrives."""
|
||||
|
||||
# pylint: disable=no-self-use
|
||||
def send_command(self, data, optional, packet_type):
|
||||
"""Send a command via the EnOcean dongle."""
|
||||
from enocean.protocol.packet import Packet
|
||||
packet = Packet(packet_type, data=data, optional=optional)
|
||||
ENOCEAN_DONGLE.send_command(packet)
|
||||
self.hass.helpers.dispatcher.dispatcher_send(
|
||||
SIGNAL_SEND_MESSAGE, packet)
|
||||
|
@ -3,16 +3,17 @@ import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.binary_sensor import (
|
||||
BinarySensorDevice, PLATFORM_SCHEMA, DEVICE_CLASSES_SCHEMA)
|
||||
from homeassistant.components import enocean
|
||||
from homeassistant.const import (
|
||||
CONF_NAME, CONF_ID, CONF_DEVICE_CLASS)
|
||||
from homeassistant.components.binary_sensor import (
|
||||
DEVICE_CLASSES_SCHEMA, PLATFORM_SCHEMA, BinarySensorDevice)
|
||||
from homeassistant.const import CONF_DEVICE_CLASS, CONF_ID, CONF_NAME
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_NAME = 'EnOcean binary sensor'
|
||||
DEPENDENCIES = ['enocean']
|
||||
EVENT_BUTTON_PRESSED = 'button_pressed'
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_ID): vol.All(cv.ensure_list, [vol.Coerce(int)]),
|
||||
@ -24,61 +25,80 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
"""Set up the Binary Sensor platform for EnOcean."""
|
||||
dev_id = config.get(CONF_ID)
|
||||
devname = config.get(CONF_NAME)
|
||||
dev_name = config.get(CONF_NAME)
|
||||
device_class = config.get(CONF_DEVICE_CLASS)
|
||||
|
||||
add_entities([EnOceanBinarySensor(dev_id, devname, device_class)])
|
||||
add_entities([EnOceanBinarySensor(dev_id, dev_name, device_class)])
|
||||
|
||||
|
||||
class EnOceanBinarySensor(enocean.EnOceanDevice, BinarySensorDevice):
|
||||
"""Representation of EnOcean binary sensors such as wall switches."""
|
||||
"""Representation of EnOcean binary sensors such as wall switches.
|
||||
|
||||
def __init__(self, dev_id, devname, device_class):
|
||||
Supported EEPs (EnOcean Equipment Profiles):
|
||||
- F6-02-01 (Light and Blind Control - Application Style 2)
|
||||
- F6-02-02 (Light and Blind Control - Application Style 1)
|
||||
"""
|
||||
|
||||
def __init__(self, dev_id, dev_name, device_class):
|
||||
"""Initialize the EnOcean binary sensor."""
|
||||
enocean.EnOceanDevice.__init__(self)
|
||||
self.stype = 'listener'
|
||||
self.dev_id = dev_id
|
||||
super().__init__(dev_id, dev_name)
|
||||
self._device_class = device_class
|
||||
self.which = -1
|
||||
self.onoff = -1
|
||||
self.devname = devname
|
||||
self._device_class = device_class
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the default name for the binary sensor."""
|
||||
return self.devname
|
||||
return self.dev_name
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return the class of this sensor."""
|
||||
return self._device_class
|
||||
|
||||
def value_changed(self, value, value2):
|
||||
def value_changed(self, packet):
|
||||
"""Fire an event with the data that have changed.
|
||||
|
||||
This method is called when there is an incoming packet associated
|
||||
with this platform.
|
||||
|
||||
Example packet data:
|
||||
- 2nd button pressed
|
||||
['0xf6', '0x10', '0x00', '0x2d', '0xcf', '0x45', '0x30']
|
||||
- button released
|
||||
['0xf6', '0x00', '0x00', '0x2d', '0xcf', '0x45', '0x20']
|
||||
"""
|
||||
# Energy Bow
|
||||
pushed = None
|
||||
|
||||
if packet.data[6] == 0x30:
|
||||
pushed = 1
|
||||
elif packet.data[6] == 0x20:
|
||||
pushed = 0
|
||||
|
||||
self.schedule_update_ha_state()
|
||||
if value2 == 0x70:
|
||||
|
||||
action = packet.data[1]
|
||||
if action == 0x70:
|
||||
self.which = 0
|
||||
self.onoff = 0
|
||||
elif value2 == 0x50:
|
||||
elif action == 0x50:
|
||||
self.which = 0
|
||||
self.onoff = 1
|
||||
elif value2 == 0x30:
|
||||
elif action == 0x30:
|
||||
self.which = 1
|
||||
self.onoff = 0
|
||||
elif value2 == 0x10:
|
||||
elif action == 0x10:
|
||||
self.which = 1
|
||||
self.onoff = 1
|
||||
elif value2 == 0x37:
|
||||
elif action == 0x37:
|
||||
self.which = 10
|
||||
self.onoff = 0
|
||||
elif value2 == 0x15:
|
||||
elif action == 0x15:
|
||||
self.which = 10
|
||||
self.onoff = 1
|
||||
self.hass.bus.fire('button_pressed', {'id': self.dev_id,
|
||||
'pushed': value,
|
||||
'which': self.which,
|
||||
'onoff': self.onoff})
|
||||
self.hass.bus.fire(EVENT_BUTTON_PRESSED,
|
||||
{'id': self.dev_id,
|
||||
'pushed': pushed,
|
||||
'which': self.which,
|
||||
'onoff': self.onoff})
|
||||
|
@ -4,10 +4,10 @@ import math
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.light import (
|
||||
Light, ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, PLATFORM_SCHEMA)
|
||||
from homeassistant.const import (CONF_NAME, CONF_ID)
|
||||
from homeassistant.components import enocean
|
||||
from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS, PLATFORM_SCHEMA, SUPPORT_BRIGHTNESS, Light)
|
||||
from homeassistant.const import CONF_ID, CONF_NAME
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@ -28,29 +28,26 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
"""Set up the EnOcean light platform."""
|
||||
sender_id = config.get(CONF_SENDER_ID)
|
||||
devname = config.get(CONF_NAME)
|
||||
dev_name = config.get(CONF_NAME)
|
||||
dev_id = config.get(CONF_ID)
|
||||
|
||||
add_entities([EnOceanLight(sender_id, devname, dev_id)])
|
||||
add_entities([EnOceanLight(sender_id, dev_id, dev_name)])
|
||||
|
||||
|
||||
class EnOceanLight(enocean.EnOceanDevice, Light):
|
||||
"""Representation of an EnOcean light source."""
|
||||
|
||||
def __init__(self, sender_id, devname, dev_id):
|
||||
def __init__(self, sender_id, dev_id, dev_name):
|
||||
"""Initialize the EnOcean light source."""
|
||||
enocean.EnOceanDevice.__init__(self)
|
||||
super().__init__(dev_id, dev_name)
|
||||
self._on_state = False
|
||||
self._brightness = 50
|
||||
self._sender_id = sender_id
|
||||
self.dev_id = dev_id
|
||||
self._devname = devname
|
||||
self.stype = 'dimmer'
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the device if any."""
|
||||
return self._devname
|
||||
return self.dev_name
|
||||
|
||||
@property
|
||||
def brightness(self):
|
||||
@ -94,8 +91,14 @@ class EnOceanLight(enocean.EnOceanDevice, Light):
|
||||
self.send_command(command, [], 0x01)
|
||||
self._on_state = False
|
||||
|
||||
def value_changed(self, val):
|
||||
"""Update the internal state of this device."""
|
||||
self._brightness = math.floor(val / 100.0 * 256.0)
|
||||
self._on_state = bool(val != 0)
|
||||
self.schedule_update_ha_state()
|
||||
def value_changed(self, packet):
|
||||
"""Update the internal state of this device.
|
||||
|
||||
Dimmer devices like Eltako FUD61 send telegram in different RORGs.
|
||||
We only care about the 4BS (0xA5).
|
||||
"""
|
||||
if packet.data[0] == 0xa5 and packet.data[1] == 0x02:
|
||||
val = packet.data[2]
|
||||
self._brightness = math.floor(val / 100.0 * 256.0)
|
||||
self._on_state = bool(val != 0)
|
||||
self.schedule_update_ha_state()
|
||||
|
@ -3,8 +3,8 @@
|
||||
"name": "Enocean",
|
||||
"documentation": "https://www.home-assistant.io/components/enocean",
|
||||
"requirements": [
|
||||
"enocean==0.40"
|
||||
"enocean==0.50"
|
||||
],
|
||||
"dependencies": [],
|
||||
"codeowners": []
|
||||
"codeowners": ["@bdurrer"]
|
||||
}
|
||||
|
@ -3,58 +3,201 @@ import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||
from homeassistant.const import (CONF_NAME, CONF_ID, POWER_WATT)
|
||||
from homeassistant.helpers.entity import Entity
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.components import enocean
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||
from homeassistant.const import (
|
||||
CONF_DEVICE_CLASS, CONF_ID, CONF_NAME, DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, POWER_WATT)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONF_MAX_TEMP = 'max_temp'
|
||||
CONF_MIN_TEMP = 'min_temp'
|
||||
CONF_RANGE_FROM = 'range_from'
|
||||
CONF_RANGE_TO = 'range_to'
|
||||
|
||||
DEFAULT_NAME = 'EnOcean sensor'
|
||||
|
||||
DEVICE_CLASS_POWER = 'powersensor'
|
||||
|
||||
SENSOR_TYPES = {
|
||||
DEVICE_CLASS_HUMIDITY: {
|
||||
'name': 'Humidity',
|
||||
'unit': '%',
|
||||
'icon': 'mdi:water-percent',
|
||||
'class': DEVICE_CLASS_HUMIDITY,
|
||||
},
|
||||
DEVICE_CLASS_POWER: {
|
||||
'name': 'Power',
|
||||
'unit': POWER_WATT,
|
||||
'icon': 'mdi:power-plug',
|
||||
'class': DEVICE_CLASS_POWER,
|
||||
},
|
||||
DEVICE_CLASS_TEMPERATURE: {
|
||||
'name': 'Temperature',
|
||||
'unit': TEMP_CELSIUS,
|
||||
'icon': 'mdi:thermometer',
|
||||
'class': DEVICE_CLASS_TEMPERATURE,
|
||||
},
|
||||
}
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_ID): vol.All(cv.ensure_list, [vol.Coerce(int)]),
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_DEVICE_CLASS, default=DEVICE_CLASS_POWER): cv.string,
|
||||
vol.Optional(CONF_MAX_TEMP, default=40): vol.Coerce(int),
|
||||
vol.Optional(CONF_MIN_TEMP, default=0): vol.Coerce(int),
|
||||
vol.Optional(CONF_RANGE_FROM, default=255): cv.positive_int,
|
||||
vol.Optional(CONF_RANGE_TO, default=0): cv.positive_int,
|
||||
})
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
"""Set up an EnOcean sensor device."""
|
||||
dev_id = config.get(CONF_ID)
|
||||
devname = config.get(CONF_NAME)
|
||||
dev_name = config.get(CONF_NAME)
|
||||
dev_class = config.get(CONF_DEVICE_CLASS)
|
||||
|
||||
add_entities([EnOceanSensor(dev_id, devname)])
|
||||
if dev_class == DEVICE_CLASS_TEMPERATURE:
|
||||
temp_min = config.get(CONF_MIN_TEMP)
|
||||
temp_max = config.get(CONF_MAX_TEMP)
|
||||
range_from = config.get(CONF_RANGE_FROM)
|
||||
range_to = config.get(CONF_RANGE_TO)
|
||||
add_entities([EnOceanTemperatureSensor(
|
||||
dev_id, dev_name, temp_min, temp_max, range_from, range_to)])
|
||||
|
||||
elif dev_class == DEVICE_CLASS_HUMIDITY:
|
||||
add_entities([EnOceanHumiditySensor(dev_id, dev_name)])
|
||||
|
||||
elif dev_class == DEVICE_CLASS_POWER:
|
||||
add_entities([EnOceanPowerSensor(dev_id, dev_name)])
|
||||
|
||||
|
||||
class EnOceanSensor(enocean.EnOceanDevice, Entity):
|
||||
"""Representation of an EnOcean sensor device such as a power meter."""
|
||||
class EnOceanSensor(enocean.EnOceanDevice):
|
||||
"""Representation of an EnOcean sensor device such as a power meter."""
|
||||
|
||||
def __init__(self, dev_id, devname):
|
||||
def __init__(self, dev_id, dev_name, sensor_type):
|
||||
"""Initialize the EnOcean sensor device."""
|
||||
enocean.EnOceanDevice.__init__(self)
|
||||
self.stype = "powersensor"
|
||||
self.power = None
|
||||
self.dev_id = dev_id
|
||||
self.which = -1
|
||||
self.onoff = -1
|
||||
self.devname = devname
|
||||
super().__init__(dev_id, dev_name)
|
||||
self._sensor_type = sensor_type
|
||||
self._device_class = SENSOR_TYPES[self._sensor_type]['class']
|
||||
self._dev_name = '{} {}'.format(
|
||||
SENSOR_TYPES[self._sensor_type]['name'], dev_name)
|
||||
self._unit_of_measurement = SENSOR_TYPES[self._sensor_type]['unit']
|
||||
self._icon = SENSOR_TYPES[self._sensor_type]['icon']
|
||||
self._state = None
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the device."""
|
||||
return 'Power %s' % self.devname
|
||||
return self._dev_name
|
||||
|
||||
def value_changed(self, value):
|
||||
"""Update the internal state of the device."""
|
||||
self.power = value
|
||||
self.schedule_update_ha_state()
|
||||
@property
|
||||
def icon(self):
|
||||
"""Icon to use in the frontend."""
|
||||
return self._icon
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return the device class of the sensor."""
|
||||
return self._device_class
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state of the device."""
|
||||
return self.power
|
||||
return self._state
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return the unit of measurement."""
|
||||
return POWER_WATT
|
||||
return self._unit_of_measurement
|
||||
|
||||
def value_changed(self, packet):
|
||||
"""Update the internal state of the sensor."""
|
||||
|
||||
|
||||
class EnOceanPowerSensor(EnOceanSensor):
|
||||
"""Representation of an EnOcean power sensor.
|
||||
|
||||
EEPs (EnOcean Equipment Profiles):
|
||||
- A5-12-01 (Automated Meter Reading, Electricity)
|
||||
"""
|
||||
|
||||
def __init__(self, dev_id, dev_name):
|
||||
"""Initialize the EnOcean power sensor device."""
|
||||
super().__init__(dev_id, dev_name, DEVICE_CLASS_POWER)
|
||||
|
||||
def value_changed(self, packet):
|
||||
"""Update the internal state of the sensor."""
|
||||
if packet.rorg != 0xA5:
|
||||
return
|
||||
packet.parse_eep(0x12, 0x01)
|
||||
if packet.parsed['DT']['raw_value'] == 1:
|
||||
# this packet reports the current value
|
||||
raw_val = packet.parsed['MR']['raw_value']
|
||||
divisor = packet.parsed['DIV']['raw_value']
|
||||
self._state = raw_val / (10 ** divisor)
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
|
||||
class EnOceanTemperatureSensor(EnOceanSensor):
|
||||
"""Representation of an EnOcean temperature sensor device.
|
||||
|
||||
EEPs (EnOcean Equipment Profiles):
|
||||
- A5-02-01 to A5-02-1B All 8 Bit Temperature Sensors of A5-02
|
||||
- A5-10-01 to A5-10-14 (Room Operating Panels)
|
||||
- A5-04-01 (Temp. and Humidity Sensor, Range 0°C to +40°C and 0% to 100%)
|
||||
- A5-04-02 (Temp. and Humidity Sensor, Range -20°C to +60°C and 0% to 100%)
|
||||
- A5-10-10 (Temp. and Humidity Sensor and Set Point)
|
||||
- A5-10-12 (Temp. and Humidity Sensor, Set Point and Occupancy Control)
|
||||
- 10 Bit Temp. Sensors are not supported (A5-02-20, A5-02-30)
|
||||
|
||||
For the following EEPs the scales must be set to "0 to 250":
|
||||
- A5-04-01
|
||||
- A5-04-02
|
||||
- A5-10-10 to A5-10-14
|
||||
"""
|
||||
|
||||
def __init__(self, dev_id, dev_name, scale_min, scale_max,
|
||||
range_from, range_to):
|
||||
"""Initialize the EnOcean temperature sensor device."""
|
||||
super().__init__(dev_id, dev_name, DEVICE_CLASS_TEMPERATURE)
|
||||
self._scale_min = scale_min
|
||||
self._scale_max = scale_max
|
||||
self.range_from = range_from
|
||||
self.range_to = range_to
|
||||
|
||||
def value_changed(self, packet):
|
||||
"""Update the internal state of the sensor."""
|
||||
if packet.data[0] != 0xa5:
|
||||
return
|
||||
temp_scale = self._scale_max - self._scale_min
|
||||
temp_range = self.range_to - self.range_from
|
||||
raw_val = packet.data[3]
|
||||
temperature = temp_scale / temp_range * (raw_val - self.range_from)
|
||||
temperature += self._scale_min
|
||||
self._state = round(temperature, 1)
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
|
||||
class EnOceanHumiditySensor(EnOceanSensor):
|
||||
"""Representation of an EnOcean humidity sensor device.
|
||||
|
||||
EEPs (EnOcean Equipment Profiles):
|
||||
- A5-04-01 (Temp. and Humidity Sensor, Range 0°C to +40°C and 0% to 100%)
|
||||
- A5-04-02 (Temp. and Humidity Sensor, Range -20°C to +60°C and 0% to 100%)
|
||||
- A5-10-10 to A5-10-14 (Room Operating Panels)
|
||||
"""
|
||||
|
||||
def __init__(self, dev_id, dev_name):
|
||||
"""Initialize the EnOcean humidity sensor device."""
|
||||
super().__init__(dev_id, dev_name, DEVICE_CLASS_HUMIDITY)
|
||||
|
||||
def value_changed(self, packet):
|
||||
"""Update the internal state of the sensor."""
|
||||
if packet.rorg != 0xA5:
|
||||
return
|
||||
humidity = packet.data[2] * 100 / 250
|
||||
self._state = round(humidity, 1)
|
||||
self.schedule_update_ha_state()
|
||||
|
@ -3,16 +3,16 @@ import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.switch import PLATFORM_SCHEMA
|
||||
from homeassistant.const import (CONF_NAME, CONF_ID)
|
||||
from homeassistant.components import enocean
|
||||
from homeassistant.helpers.entity import ToggleEntity
|
||||
from homeassistant.components.switch import PLATFORM_SCHEMA
|
||||
from homeassistant.const import CONF_ID, CONF_NAME
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity import ToggleEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_NAME = 'EnOcean Switch'
|
||||
CONF_CHANNEL = 'channel'
|
||||
DEFAULT_NAME = 'EnOcean Switch'
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_ID): vol.All(cv.ensure_list, [vol.Coerce(int)]),
|
||||
@ -23,26 +23,23 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
"""Set up the EnOcean switch platform."""
|
||||
dev_id = config.get(CONF_ID)
|
||||
devname = config.get(CONF_NAME)
|
||||
channel = config.get(CONF_CHANNEL)
|
||||
dev_id = config.get(CONF_ID)
|
||||
dev_name = config.get(CONF_NAME)
|
||||
|
||||
add_entities([EnOceanSwitch(dev_id, devname, channel)])
|
||||
add_entities([EnOceanSwitch(dev_id, dev_name, channel)])
|
||||
|
||||
|
||||
class EnOceanSwitch(enocean.EnOceanDevice, ToggleEntity):
|
||||
"""Representation of an EnOcean switch device."""
|
||||
|
||||
def __init__(self, dev_id, devname, channel):
|
||||
def __init__(self, dev_id, dev_name, channel):
|
||||
"""Initialize the EnOcean switch device."""
|
||||
enocean.EnOceanDevice.__init__(self)
|
||||
self.dev_id = dev_id
|
||||
self._devname = devname
|
||||
super().__init__(dev_id, dev_name)
|
||||
self._light = None
|
||||
self._on_state = False
|
||||
self._on_state2 = False
|
||||
self.channel = channel
|
||||
self.stype = "switch"
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
@ -52,7 +49,7 @@ class EnOceanSwitch(enocean.EnOceanDevice, ToggleEntity):
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the device name."""
|
||||
return self._devname
|
||||
return self.dev_name
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
"""Turn on the switch."""
|
||||
@ -74,7 +71,24 @@ class EnOceanSwitch(enocean.EnOceanDevice, ToggleEntity):
|
||||
packet_type=0x01)
|
||||
self._on_state = False
|
||||
|
||||
def value_changed(self, val):
|
||||
def value_changed(self, packet):
|
||||
"""Update the internal state of the switch."""
|
||||
self._on_state = val
|
||||
self.schedule_update_ha_state()
|
||||
if packet.data[0] == 0xa5:
|
||||
# power meter telegram, turn on if > 10 watts
|
||||
packet.parse_eep(0x12, 0x01)
|
||||
if packet.parsed['DT']['raw_value'] == 1:
|
||||
raw_val = packet.parsed['MR']['raw_value']
|
||||
divisor = packet.parsed['DIV']['raw_value']
|
||||
watts = raw_val / (10 ** divisor)
|
||||
if watts > 1:
|
||||
self._on_state = True
|
||||
self.schedule_update_ha_state()
|
||||
elif packet.data[0] == 0xd2:
|
||||
# actuator status telegram
|
||||
packet.parse_eep(0x01, 0x01)
|
||||
if packet.parsed['CMD']['raw_value'] == 4:
|
||||
channel = packet.parsed['IO']['raw_value']
|
||||
output = packet.parsed['OV']['raw_value']
|
||||
if channel == self.channel:
|
||||
self._on_state = output > 0
|
||||
self.schedule_update_ha_state()
|
||||
|
@ -383,7 +383,7 @@ elkm1-lib==0.7.13
|
||||
emulated_roku==0.1.8
|
||||
|
||||
# homeassistant.components.enocean
|
||||
enocean==0.40
|
||||
enocean==0.50
|
||||
|
||||
# homeassistant.components.entur_public_transport
|
||||
enturclient==0.2.0
|
||||
|
@ -90,6 +90,9 @@ eebrightbox==0.0.4
|
||||
# homeassistant.components.emulated_roku
|
||||
emulated_roku==0.1.8
|
||||
|
||||
# homeassistant.components.enocean
|
||||
enocean==0.50
|
||||
|
||||
# homeassistant.components.season
|
||||
ephem==3.7.6.0
|
||||
|
||||
|
@ -58,6 +58,7 @@ TEST_REQUIREMENTS = (
|
||||
'dsmr_parser',
|
||||
'eebrightbox',
|
||||
'emulated_roku',
|
||||
'enocean',
|
||||
'ephem',
|
||||
'evohomeclient',
|
||||
'feedparser-homeassistant',
|
||||
|
Loading…
x
Reference in New Issue
Block a user