Add zigbee components.

This commit is contained in:
Flyte 2016-01-24 08:02:14 +00:00
parent 85ebd0ab59
commit dda4f84150
7 changed files with 403 additions and 0 deletions

View File

@ -35,6 +35,9 @@ omit =
homeassistant/components/wink.py homeassistant/components/wink.py
homeassistant/components/*/wink.py homeassistant/components/*/wink.py
homeassistant/components/zigbee.py
homeassistant/components/*/zigbee.py
homeassistant/components/zwave.py homeassistant/components/zwave.py
homeassistant/components/*/zwave.py homeassistant/components/*/zwave.py

View File

@ -0,0 +1,19 @@
"""
homeassistant.components.binary_sensor.zigbee
Contains functionality to use a ZigBee device as a binary sensor.
"""
from homeassistant.components.zigbee import (
ZigBeeDigitalIn, ZigBeeDigitalInConfig)
DEPENDENCIES = ["zigbee"]
def setup_platform(hass, config, add_entities, discovery_info=None):
"""
Create and add an entity based on the configuration.
"""
add_entities([
ZigBeeDigitalIn(hass, ZigBeeDigitalInConfig(config))
])

View File

@ -0,0 +1,19 @@
"""
homeassistant.components.light.zigbee
Contains functionality to use a ZigBee device as a light.
"""
from homeassistant.components.zigbee import (
ZigBeeDigitalOut, ZigBeeDigitalOutConfig)
DEPENDENCIES = ["zigbee"]
def setup_platform(hass, config, add_entities, discovery_info=None):
"""
Create and add an entity based on the configuration.
"""
add_entities([
ZigBeeDigitalOut(hass, ZigBeeDigitalOutConfig(config))
])

View File

@ -0,0 +1,68 @@
"""
homeassistant.components.sensor.zigbee
Contains functionality to use a ZigBee device as a sensor.
"""
import logging
from homeassistant.core import JobPriority
from homeassistant.const import TEMP_CELCIUS
from homeassistant.helpers.entity import Entity
from homeassistant.components import zigbee
DEPENDENCIES = ["zigbee"]
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_entities, discovery_info=None):
"""
Uses the 'type' config value to work out which type of ZigBee sensor we're
dealing with and instantiates the relevant classes to handle it.
"""
typ = config.get("type", "").lower()
if not typ:
_LOGGER.exception(
"Must include 'type' when configuring a ZigBee sensor.")
return
try:
sensor_class, config_class = TYPE_CLASSES[typ]
except KeyError:
_LOGGER.exception("Unknown ZigBee sensor type: %s", typ)
return
add_entities([sensor_class(hass, config_class(config))])
class ZigBeeTemperatureSensor(Entity):
"""
Allows usage of an XBee Pro as a temperature sensor.
"""
def __init__(self, hass, config):
self._config = config
self._temp = None
if config.should_poll:
hass.pool.add_job(JobPriority.EVENT_STATE, (self.update, None))
@property
def name(self):
return self._config.name
@property
def state(self):
return self._temp
@property
def unit_of_measurement(self):
return TEMP_CELCIUS
def update(self, *args):
self._temp = zigbee.DEVICE.get_temperature(self._config.address)
self.update_ha_state()
# This must be below the ZigBeeTemperatureSensor which it references.
TYPE_CLASSES = {
"temperature": (ZigBeeTemperatureSensor, zigbee.ZigBeeConfig),
"analog": (zigbee.ZigBeeAnalogIn, zigbee.ZigBeeAnalogInConfig)
}

View File

@ -0,0 +1,19 @@
"""
homeassistant.components.switch.zigbee
Contains functionality to use a ZigBee device as a switch.
"""
from homeassistant.components.zigbee import (
ZigBeeDigitalOut, ZigBeeDigitalOutConfig)
DEPENDENCIES = ["zigbee"]
def setup_platform(hass, config, add_entities, discovery_info=None):
"""
Create and add an entity based on the configuration.
"""
add_entities([
ZigBeeDigitalOut(hass, ZigBeeDigitalOutConfig(config))
])

View File

@ -0,0 +1,272 @@
"""
homeassistant.components.zigbee
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sets up and provides access to a ZigBee device and contains generic entity
classes.
"""
from binascii import unhexlify
import xbee_helper.const as xb_const
from xbee_helper import ZigBee
from homeassistant.core import JobPriority
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.helpers.entity import Entity, ToggleEntity
DOMAIN = "zigbee"
REQUIREMENTS = ("xbee-helper==0.0.6",)
CONF_DEVICE = "device"
CONF_BAUD = "baud"
DEFAULT_DEVICE = "/dev/ttyUSB0"
DEFAULT_BAUD = 9600
DEFAULT_ADC_MAX_VOLTS = 1.2
DEVICE = None
def setup(hass, config):
"""
Set up the connection to the ZigBee device and instantiate the helper
class for it.
"""
global DEVICE
from serial import Serial
usb_device = config[DOMAIN].get(CONF_DEVICE, DEFAULT_DEVICE)
baud = int(config[DOMAIN].get(CONF_BAUD, DEFAULT_BAUD))
ser = Serial(usb_device, baud)
DEVICE = ZigBee(ser)
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, close_serial_port)
return True
def close_serial_port(*args):
"""
Close the serial port we're using to communicate with the ZigBee.
"""
DEVICE.zb.serial.close()
class ZigBeeConfig(object):
"""
Handles the fetching of configuration from the config file for any ZigBee
entity.
"""
def __init__(self, config):
self._config = config
self._should_poll = config.get("poll", True)
@property
def name(self):
"""
The name given to the entity.
"""
return self._config["name"]
@property
def address(self):
"""
If an address has been provided, unhexlify it, otherwise return None
as we're talking to our local ZigBee device.
"""
address = self._config.get("address")
if address is not None:
address = unhexlify(address)
return address
@property
def should_poll(self):
"""
A bool depicting whether HA should repeatedly poll this device for its
value.
"""
return self._should_poll
class ZigBeePinConfig(ZigBeeConfig):
"""
Handles the fetching of configuration from the config file for a ZigBee
GPIO pin.
"""
@property
def pin(self):
"""
The GPIO pin number.
"""
return self._config["pin"]
class ZigBeeDigitalPinConfig(ZigBeePinConfig):
"""
Handles the fetching of configuration from the config file for a ZigBee
GPIO pin set to digital in or out.
"""
def __init__(self, config):
super(ZigBeeDigitalPinConfig, self).__init__(config)
self._bool2state, self._state2bool = self.boolean_maps
@property
def boolean_maps(self):
"""
Create dicts to map booleans to pin high/low and vice versa. Depends on
the config item "on_state" which should be set to "low" or "high".
"""
if self._config.get("on_state", "").lower() == "low":
bool2state = {
True: xb_const.GPIO_DIGITAL_OUTPUT_LOW,
False: xb_const.GPIO_DIGITAL_OUTPUT_HIGH
}
else:
bool2state = {
True: xb_const.GPIO_DIGITAL_OUTPUT_HIGH,
False: xb_const.GPIO_DIGITAL_OUTPUT_LOW
}
state2bool = {v: k for k, v in bool2state.items()}
return bool2state, state2bool
@property
def bool2state(self):
"""
A dictionary mapping booleans to GPIOSetting objects to translate
on/off as being pin high or low.
"""
return self._bool2state
@property
def state2bool(self):
"""
A dictionary mapping GPIOSetting objects to booleans to translate
pin high/low as being on or off.
"""
return self._state2bool
# Create an alias so that ZigBeeDigitalOutConfig has a logical opposite.
ZigBeeDigitalInConfig = ZigBeeDigitalPinConfig
class ZigBeeDigitalOutConfig(ZigBeeDigitalPinConfig):
"""
A subclass of ZigBeeDigitalPinConfig which sets _should_poll to default as
False instead of True. The value will still be overridden by the presence
of a 'poll' config entry.
"""
def __init__(self, config):
super(ZigBeeDigitalOutConfig, self).__init__(config)
self._should_poll = config.get("poll", False)
class ZigBeeAnalogInConfig(ZigBeePinConfig):
"""
Handles the fetching of configuration from the config file for a ZigBee
GPIO pin set to analog in.
"""
@property
def max_voltage(self):
"""
The voltage at which the ADC will report its highest value.
"""
return float(self._config.get("max_volts", DEFAULT_ADC_MAX_VOLTS))
class ZigBeeDigitalIn(ToggleEntity):
"""
ToggleEntity to represent a GPIO pin configured as a digital input.
"""
def __init__(self, hass, config):
self._config = config
self._state = False
if config.should_poll:
hass.pool.add_job(JobPriority.EVENT_STATE, (self.update, None))
@property
def name(self):
return self._config.name
@property
def should_poll(self):
return self._config.should_poll
@property
def is_on(self):
return self._state
def update(self, *args):
"""
Ask the ZigBee device what its output is set to.
"""
pin_state = DEVICE.get_gpio_pin(
self._config.pin,
self._config.address)
self._state = self._config.state2bool[pin_state]
self.update_ha_state()
class ZigBeeDigitalOut(ZigBeeDigitalIn):
"""
Adds functionality to ZigBeeDigitalIn to control an output.
"""
def __init__(self, hass, config):
super(ZigBeeDigitalOut, self).__init__(hass, config)
# Get initial value regardless of whether we should_poll.
# If config.should_poll is True, then it's already been handled in
# our parent class __init__().
if not config.should_poll:
hass.pool.add_job(JobPriority.EVENT_STATE, (self.update, None))
def _set_state(self, state):
DEVICE.set_gpio_pin(
self._config.pin,
self._config.bool2state[state],
self._config.address)
self._state = state
self.update_ha_state()
def turn_on(self, **kwargs):
self._set_state(True)
def turn_off(self, **kwargs):
self._set_state(False)
class ZigBeeAnalogIn(Entity):
"""
Entity to represent a GPIO pin configured as an analog input.
"""
def __init__(self, hass, config):
self._config = config
self._value = None
if config.should_poll:
hass.pool.add_job(JobPriority.EVENT_STATE, (self.update, None))
@property
def name(self):
return self._config.name
@property
def should_poll(self):
return self._config.should_poll
@property
def state(self):
return self._value
@property
def unit_of_measurement(self):
return "%"
def update(self, *args):
"""
Get the latest reading from the ADC.
"""
self._value = DEVICE.read_analog_pin(
self._config.pin,
self._config.max_voltage,
self._config.address,
xb_const.ADC_PERCENTAGE)
self.update_ha_state()

View File

@ -213,5 +213,8 @@ radiotherm==1.2
# homeassistant.components.verisure # homeassistant.components.verisure
vsure==0.4.5 vsure==0.4.5
# homeassistant.components.zigbee
xbee-helper==0.0.6
# homeassistant.components.zwave # homeassistant.components.zwave
pydispatcher==2.0.5 pydispatcher==2.0.5