Initial support for EnOcean (#2177)
* Initial support for EnOcean Tested to work with: - Eltako FUD61 dimmer - Eltako FT55 battery-less switch - Permundo PSC234 (switch and power monitor) * Rerun gen_requirements_all.pypull/2182/head
parent
03e8627b12
commit
bf940bd1f3
|
@ -75,6 +75,9 @@ omit =
|
|||
homeassistant/components/zwave.py
|
||||
homeassistant/components/*/zwave.py
|
||||
|
||||
homeassistant/components/enocean.py
|
||||
homeassistant/components/*/enocean.py
|
||||
|
||||
homeassistant/components/alarm_control_panel/alarmdotcom.py
|
||||
homeassistant/components/alarm_control_panel/nx584.py
|
||||
homeassistant/components/binary_sensor/arest.py
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
"""
|
||||
Support for EnOcean binary sensors.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/binary_sensor.enocean/
|
||||
"""
|
||||
|
||||
from homeassistant.components.binary_sensor import BinarySensorDevice
|
||||
from homeassistant.components import enocean
|
||||
from homeassistant.const import CONF_NAME
|
||||
|
||||
DEPENDENCIES = ["enocean"]
|
||||
|
||||
CONF_ID = "id"
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup the Binary Sensor platform fo EnOcean."""
|
||||
dev_id = config.get(CONF_ID, None)
|
||||
devname = config.get(CONF_NAME, "EnOcean binary sensor")
|
||||
add_devices([EnOceanBinarySensor(dev_id, devname)])
|
||||
|
||||
|
||||
class EnOceanBinarySensor(enocean.EnOceanDevice, BinarySensorDevice):
|
||||
"""Representation of EnOcean binary sensors such as wall switches."""
|
||||
|
||||
def __init__(self, dev_id, devname):
|
||||
"""Initialize the EnOcean binary sensor."""
|
||||
enocean.EnOceanDevice.__init__(self)
|
||||
self.stype = "listener"
|
||||
self.dev_id = dev_id
|
||||
self.which = -1
|
||||
self.onoff = -1
|
||||
self.devname = devname
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""The default name for the binary sensor."""
|
||||
return self.devname
|
||||
|
||||
def value_changed(self, value, value2):
|
||||
"""Fire an event with the data that have changed.
|
||||
|
||||
This method is called when there is an incoming packet associated
|
||||
with this platform.
|
||||
"""
|
||||
self.update_ha_state()
|
||||
if value2 == 0x70:
|
||||
self.which = 0
|
||||
self.onoff = 0
|
||||
elif value2 == 0x50:
|
||||
self.which = 0
|
||||
self.onoff = 1
|
||||
elif value2 == 0x30:
|
||||
self.which = 1
|
||||
self.onoff = 0
|
||||
elif value2 == 0x10:
|
||||
self.which = 1
|
||||
self.onoff = 1
|
||||
self.hass.bus.fire('button_pressed', {"id": self.dev_id,
|
||||
'pushed': value,
|
||||
'which': self.which,
|
||||
'onoff': self.onoff})
|
|
@ -0,0 +1,117 @@
|
|||
"""
|
||||
EnOcean Component.
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/EnOcean/
|
||||
"""
|
||||
|
||||
DOMAIN = "enocean"
|
||||
|
||||
REQUIREMENTS = ['enocean==0.31']
|
||||
|
||||
CONF_DEVICE = "device"
|
||||
|
||||
ENOCEAN_DONGLE = None
|
||||
|
||||
|
||||
def setup(hass, config):
|
||||
"""Setup the EnOcean component."""
|
||||
global ENOCEAN_DONGLE
|
||||
|
||||
serial_dev = config[DOMAIN].get(CONF_DEVICE, "/dev/ttyUSB0")
|
||||
|
||||
ENOCEAN_DONGLE = EnOceanDongle(hass, serial_dev)
|
||||
return True
|
||||
|
||||
|
||||
class EnOceanDongle:
|
||||
"""Representation of an EnOcean dongle."""
|
||||
|
||||
def __init__(self, hass, ser):
|
||||
"""Initialize the EnOcean dongle."""
|
||||
from enocean.communicators.serialcommunicator import SerialCommunicator
|
||||
self.__communicator = SerialCommunicator(port=ser,
|
||||
callback=self.callback)
|
||||
self.__communicator.start()
|
||||
self.__devices = []
|
||||
|
||||
def register_device(self, dev):
|
||||
"""Register another device."""
|
||||
self.__devices.append(dev)
|
||||
|
||||
def send_command(self, command):
|
||||
"""Send a command from the EnOcean dongle."""
|
||||
self.__communicator.send(command)
|
||||
|
||||
def _combine_hex(self, data): # pylint: disable=no-self-use
|
||||
"""Combine list of integer values to one big integer."""
|
||||
output = 0x00
|
||||
for i, j in enumerate(reversed(data)):
|
||||
output |= (j << i * 8)
|
||||
return output
|
||||
|
||||
# pylint: disable=too-many-branches
|
||||
def callback(self, temp):
|
||||
"""Callback function for EnOcean Device.
|
||||
|
||||
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):
|
||||
rxtype = None
|
||||
value = None
|
||||
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:
|
||||
rxtype = "switch_status"
|
||||
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 == self._combine_hex(device.dev_id):
|
||||
device.value_changed(value, temp.data[1])
|
||||
if rxtype == "power" and device.stype == "powersensor":
|
||||
if temp.sender == self._combine_hex(device.dev_id):
|
||||
device.value_changed(value)
|
||||
if rxtype == "power" and device.stype == "switch":
|
||||
if temp.sender == self._combine_hex(device.dev_id):
|
||||
if value > 10:
|
||||
device.value_changed(1)
|
||||
if rxtype == "switch_status" and device.stype == "switch":
|
||||
if temp.sender == self._combine_hex(device.dev_id):
|
||||
device.value_changed(value)
|
||||
if rxtype == "dimmerstatus" and device.stype == "dimmer":
|
||||
if temp.sender == self._combine_hex(device.dev_id):
|
||||
device.value_changed(value)
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
class EnOceanDevice():
|
||||
"""Parent class for all devices associated with the EnOcean component."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the device."""
|
||||
ENOCEAN_DONGLE.register_device(self)
|
||||
self.stype = ""
|
||||
self.sensorid = [0x00, 0x00, 0x00, 0x00]
|
||||
|
||||
# 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)
|
|
@ -0,0 +1,92 @@
|
|||
"""
|
||||
Support for EnOcean light sources.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/light.enocean/
|
||||
"""
|
||||
|
||||
import logging
|
||||
import math
|
||||
|
||||
from homeassistant.components.light import Light, ATTR_BRIGHTNESS
|
||||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.components import enocean
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ["enocean"]
|
||||
|
||||
CONF_ID = "id"
|
||||
CONF_SENDER_ID = "sender_id"
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup the EnOcean light platform."""
|
||||
sender_id = config.get(CONF_SENDER_ID, None)
|
||||
devname = config.get(CONF_NAME, "Enocean actuator")
|
||||
dev_id = config.get(CONF_ID, [0x00, 0x00, 0x00, 0x00])
|
||||
|
||||
add_devices([EnOceanLight(sender_id, devname, dev_id)])
|
||||
|
||||
|
||||
class EnOceanLight(enocean.EnOceanDevice, Light):
|
||||
"""Representation of an EnOcean light source."""
|
||||
|
||||
def __init__(self, sender_id, devname, dev_id):
|
||||
"""Initialize the EnOcean light source."""
|
||||
enocean.EnOceanDevice.__init__(self)
|
||||
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
|
||||
|
||||
@property
|
||||
def brightness(self):
|
||||
"""Brightness of the light.
|
||||
|
||||
This method is optional. Removing it indicates to Home Assistant
|
||||
that brightness is not supported for this light.
|
||||
"""
|
||||
return self._brightness
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""If light is on."""
|
||||
return self._on_state
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
"""Turn the light source on or sets a specific dimmer value."""
|
||||
brightness = kwargs.get(ATTR_BRIGHTNESS)
|
||||
if brightness is not None:
|
||||
self._brightness = brightness
|
||||
|
||||
bval = math.floor(self._brightness / 256.0 * 100.0)
|
||||
if bval == 0:
|
||||
bval = 1
|
||||
command = [0xa5, 0x02, bval, 0x01, 0x09]
|
||||
command.extend(self._sender_id)
|
||||
command.extend([0x00])
|
||||
self.send_command(command, [], 0x01)
|
||||
self._on_state = True
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
"""Turn the light source off."""
|
||||
command = [0xa5, 0x02, 0x00, 0x01, 0x09]
|
||||
command.extend(self._sender_id)
|
||||
command.extend([0x00])
|
||||
self.send_command(command, [], 0x01)
|
||||
self._on_state = False
|
||||
|
||||
def value_changed(self, val):
|
||||
"""Update the internal state of this device in HA."""
|
||||
self._brightness = math.floor(val / 100.0 * 256.0)
|
||||
self._on_state = bool(val != 0)
|
||||
self.update_ha_state()
|
|
@ -0,0 +1,55 @@
|
|||
"""
|
||||
Support for EnOcean sensors.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/sensor.enocean/
|
||||
"""
|
||||
|
||||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.components import enocean
|
||||
|
||||
DEPENDENCIES = ["enocean"]
|
||||
|
||||
CONF_ID = "id"
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup an EnOcean sensor device."""
|
||||
dev_id = config.get(CONF_ID, None)
|
||||
devname = config.get(CONF_NAME, None)
|
||||
add_devices([EnOceanSensor(dev_id, devname)])
|
||||
|
||||
|
||||
class EnOceanSensor(enocean.EnOceanDevice, Entity):
|
||||
"""Representation of an EnOcean sensor device such as a power meter."""
|
||||
|
||||
def __init__(self, dev_id, devname):
|
||||
"""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
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the device."""
|
||||
return 'Power %s' % self.devname
|
||||
|
||||
def value_changed(self, value):
|
||||
"""Update the internal state of the device."""
|
||||
self.power = value
|
||||
self.update_ha_state()
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state of the device."""
|
||||
return self.power
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return the unit of measurement."""
|
||||
return "W"
|
|
@ -0,0 +1,76 @@
|
|||
"""
|
||||
Support for EnOcean switches.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/switch.enocean/
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.components import enocean
|
||||
from homeassistant.helpers.entity import ToggleEntity
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ["enocean"]
|
||||
|
||||
CONF_ID = "id"
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup the EnOcean switch platform."""
|
||||
dev_id = config.get(CONF_ID, None)
|
||||
devname = config.get(CONF_NAME, "Enocean actuator")
|
||||
|
||||
add_devices([EnOceanSwitch(dev_id, devname)])
|
||||
|
||||
|
||||
class EnOceanSwitch(enocean.EnOceanDevice, ToggleEntity):
|
||||
"""Representation of an EnOcean switch device."""
|
||||
|
||||
def __init__(self, dev_id, devname):
|
||||
"""Initialize the EnOcean switch device."""
|
||||
enocean.EnOceanDevice.__init__(self)
|
||||
self.dev_id = dev_id
|
||||
self._devname = devname
|
||||
self._light = None
|
||||
self._on_state = False
|
||||
self._on_state2 = False
|
||||
self.stype = "switch"
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return whether the switch is on or off."""
|
||||
return self._on_state
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the device name."""
|
||||
return self._devname
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
"""Turn on the switch."""
|
||||
optional = [0x03, ]
|
||||
optional.extend(self.dev_id)
|
||||
optional.extend([0xff, 0x00])
|
||||
self.send_command(data=[0xD2, 0x01, 0x00, 0x64, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00], optional=optional,
|
||||
packet_type=0x01)
|
||||
self._on_state = True
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
"""Turn off the switch."""
|
||||
optional = [0x03, ]
|
||||
optional.extend(self.dev_id)
|
||||
optional.extend([0xff, 0x00])
|
||||
self.send_command(data=[0xD2, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00], optional=optional,
|
||||
packet_type=0x01)
|
||||
self._on_state = False
|
||||
|
||||
def value_changed(self, val):
|
||||
"""Update the internal state of the switch."""
|
||||
self._on_state = val
|
||||
self.update_ha_state()
|
|
@ -55,6 +55,9 @@ dweepy==0.2.0
|
|||
# homeassistant.components.sensor.eliqonline
|
||||
eliqonline==1.0.12
|
||||
|
||||
# homeassistant.components.enocean
|
||||
enocean==0.31
|
||||
|
||||
# homeassistant.components.http
|
||||
eventlet==0.19.0
|
||||
|
||||
|
|
Loading…
Reference in New Issue