2019-02-13 20:21:14 +00:00
|
|
|
"""Support for EnOcean devices."""
|
2016-08-25 04:35:09 +00:00
|
|
|
import logging
|
2016-05-29 21:28:03 +00:00
|
|
|
|
2016-08-25 04:35:09 +00:00
|
|
|
import voluptuous as vol
|
|
|
|
|
|
|
|
from homeassistant.const import CONF_DEVICE
|
|
|
|
import homeassistant.helpers.config_validation as cv
|
2016-05-29 21:28:03 +00:00
|
|
|
|
2016-08-25 04:35:09 +00:00
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
DOMAIN = 'enocean'
|
2016-05-29 21:28:03 +00:00
|
|
|
|
|
|
|
ENOCEAN_DONGLE = None
|
|
|
|
|
2016-08-25 04:35:09 +00:00
|
|
|
CONFIG_SCHEMA = vol.Schema({
|
|
|
|
DOMAIN: vol.Schema({
|
|
|
|
vol.Required(CONF_DEVICE): cv.string,
|
|
|
|
}),
|
|
|
|
}, extra=vol.ALLOW_EXTRA)
|
|
|
|
|
2016-05-29 21:28:03 +00:00
|
|
|
|
|
|
|
def setup(hass, config):
|
2017-04-30 05:04:49 +00:00
|
|
|
"""Set up the EnOcean component."""
|
2016-05-29 21:28:03 +00:00
|
|
|
global ENOCEAN_DONGLE
|
|
|
|
|
2016-08-25 04:35:09 +00:00
|
|
|
serial_dev = config[DOMAIN].get(CONF_DEVICE)
|
2016-05-29 21:28:03 +00:00
|
|
|
|
|
|
|
ENOCEAN_DONGLE = EnOceanDongle(hass, serial_dev)
|
2016-08-25 04:35:09 +00:00
|
|
|
|
2016-05-29 21:28:03 +00:00
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
class EnOceanDongle:
|
|
|
|
"""Representation of an EnOcean dongle."""
|
|
|
|
|
|
|
|
def __init__(self, hass, ser):
|
|
|
|
"""Initialize the EnOcean dongle."""
|
|
|
|
from enocean.communicators.serialcommunicator import SerialCommunicator
|
2017-04-30 05:04:49 +00:00
|
|
|
self.__communicator = SerialCommunicator(
|
|
|
|
port=ser, callback=self.callback)
|
2016-05-29 21:28:03 +00:00
|
|
|
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)
|
|
|
|
|
2016-10-30 21:18:53 +00:00
|
|
|
# pylint: disable=no-self-use
|
|
|
|
def _combine_hex(self, data):
|
2016-05-29 21:28:03 +00:00
|
|
|
"""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):
|
2017-05-02 20:47:20 +00:00
|
|
|
"""Handle EnOcean device's callback.
|
2016-05-29 21:28:03 +00:00
|
|
|
|
2017-04-30 05:04:49 +00:00
|
|
|
This is the callback function called by python-enocan whenever there
|
|
|
|
is an incoming packet.
|
2016-05-29 21:28:03 +00:00
|
|
|
"""
|
|
|
|
from enocean.protocol.packet import RadioPacket
|
|
|
|
if isinstance(temp, RadioPacket):
|
2017-11-04 11:58:02 +00:00
|
|
|
_LOGGER.debug("Received radio packet: %s", temp)
|
2016-05-29 21:28:03 +00:00
|
|
|
rxtype = None
|
|
|
|
value = None
|
2018-07-09 09:05:25 +00:00
|
|
|
channel = 0
|
2016-05-29 21:28:03 +00:00
|
|
|
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)
|
2018-07-09 09:05:25 +00:00
|
|
|
elif temp.data[2] & 0x60 == 0x60:
|
2016-05-29 21:28:03 +00:00
|
|
|
rxtype = "switch_status"
|
2018-07-09 09:05:25 +00:00
|
|
|
channel = temp.data[2] & 0x1F
|
2016-05-29 21:28:03 +00:00
|
|
|
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":
|
2017-11-04 11:58:02 +00:00
|
|
|
if temp.sender_int == self._combine_hex(device.dev_id):
|
2016-05-29 21:28:03 +00:00
|
|
|
device.value_changed(value, temp.data[1])
|
|
|
|
if rxtype == "power" and device.stype == "powersensor":
|
2017-11-04 11:58:02 +00:00
|
|
|
if temp.sender_int == self._combine_hex(device.dev_id):
|
2016-05-29 21:28:03 +00:00
|
|
|
device.value_changed(value)
|
|
|
|
if rxtype == "power" and device.stype == "switch":
|
2017-11-04 11:58:02 +00:00
|
|
|
if temp.sender_int == self._combine_hex(device.dev_id):
|
2016-05-29 21:28:03 +00:00
|
|
|
if value > 10:
|
|
|
|
device.value_changed(1)
|
2018-07-09 09:05:25 +00:00
|
|
|
if rxtype == "switch_status" and device.stype == "switch" and \
|
|
|
|
channel == device.channel:
|
2017-11-04 11:58:02 +00:00
|
|
|
if temp.sender_int == self._combine_hex(device.dev_id):
|
2016-05-29 21:28:03 +00:00
|
|
|
device.value_changed(value)
|
|
|
|
if rxtype == "dimmerstatus" and device.stype == "dimmer":
|
2017-11-04 11:58:02 +00:00
|
|
|
if temp.sender_int == self._combine_hex(device.dev_id):
|
2016-05-29 21:28:03 +00:00
|
|
|
device.value_changed(value)
|
|
|
|
|
|
|
|
|
|
|
|
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)
|