Use voluptuous for Pilight switch (#3819)

* Migrate to voluptuous

* Add protocol

* Update
This commit is contained in:
Fabian Affolter 2016-10-31 13:18:47 +01:00 committed by GitHub
parent a89e635bf3
commit 4484a7a94b
5 changed files with 96 additions and 74 deletions

View file

@ -1,5 +1,5 @@
"""
Component to create an interface to a Pilight daemon (https://pilight.org/).
Component to create an interface to a Pilight daemon.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/pilight/
@ -12,40 +12,38 @@ import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.const import (
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, CONF_HOST, CONF_PORT,
CONF_WHITELIST)
CONF_WHITELIST, CONF_PROTOCOL)
REQUIREMENTS = ['pilight==0.1.1']
_LOGGER = logging.getLogger(__name__)
ATTR_PROTOCOL = 'protocol'
DEFAULT_HOST = '127.0.0.1'
DEFAULT_PORT = 5000
DOMAIN = 'pilight'
EVENT = 'pilight_received'
# The pilight code schema depends on the protocol
# Thus only require to have the protocol information
# Ensure that protocol is in a list otherwise segfault in pilight-daemon
# https://github.com/pilight/pilight/issues/296
RF_CODE_SCHEMA = vol.Schema({vol.Required(ATTR_PROTOCOL):
vol.All(cv.ensure_list, [cv.string])},
extra=vol.ALLOW_EXTRA)
# The Pilight code schema depends on the protocol. Thus only require to have
# the protocol information. Ensure that protocol is in a list otherwise
# segfault in pilight-daemon, https://github.com/pilight/pilight/issues/296
RF_CODE_SCHEMA = vol.Schema({
vol.Required(CONF_PROTOCOL): vol.All(cv.ensure_list, [cv.string]),
}, extra=vol.ALLOW_EXTRA)
SERVICE_NAME = 'send'
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_HOST, default=DEFAULT_HOST): cv.string,
vol.Required(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_WHITELIST, default={}): {cv.string: [cv.string]}
}),
}, extra=vol.ALLOW_EXTRA)
def setup(hass, config):
"""Setup the pilight component."""
"""Setup the Pilight component."""
from pilight import pilight
host = config[DOMAIN][CONF_HOST]
@ -58,23 +56,22 @@ def setup(hass, config):
host, port, err)
return False
# Start / stop pilight-daemon connection with HA start/stop
def start_pilight_client(_):
"""Called once when home assistant starts."""
"""Called once when Home Assistant starts."""
pilight_client.start()
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_pilight_client)
def stop_pilight_client(_):
"""Called once when home assistant stops."""
"""Called once when Home Assistant stops."""
pilight_client.stop()
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_pilight_client)
def send_code(call):
"""Send RF code to the pilight-daemon."""
# Change type to dict from mappingproxy
# since data has to be JSON serializable
# Change type to dict from mappingproxy since data has to be JSON
# serializable
message_data = dict(call.data)
try:
@ -82,8 +79,8 @@ def setup(hass, config):
except IOError:
_LOGGER.error('Pilight send failed for %s', str(message_data))
hass.services.register(DOMAIN, SERVICE_NAME,
send_code, schema=RF_CODE_SCHEMA)
hass.services.register(
DOMAIN, SERVICE_NAME, send_code, schema=RF_CODE_SCHEMA)
# Publish received codes on the HA event bus
# A whitelist of codes to be published in the event bus
@ -93,9 +90,7 @@ def setup(hass, config):
"""Called when RF codes are received."""
# Unravel dict of dicts to make event_data cut in automation rule
# possible
data = dict(
{'protocol': data['protocol'],
'uuid': data['uuid']},
data = dict({'protocol': data['protocol'], 'uuid': data['uuid']},
**data['message'])
# No whitelist defined, put data on event bus

View file

@ -1,5 +1,5 @@
"""
Support for pilight sensors.
Support for Pilight sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.pilight/
@ -9,8 +9,7 @@ import logging
import voluptuous as vol
from homeassistant.const import (
CONF_NAME, STATE_UNKNOWN, CONF_UNIT_OF_MEASUREMENT,
CONF_PAYLOAD)
CONF_NAME, STATE_UNKNOWN, CONF_UNIT_OF_MEASUREMENT, CONF_PAYLOAD)
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.helpers.entity import Entity
import homeassistant.components.pilight as pilight
@ -18,11 +17,13 @@ import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
CONF_VARIABLE = 'variable'
DEFAULT_NAME = 'Pilight Sensor'
DEPENDENCIES = ['pilight']
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required("variable"): cv.string,
vol.Required(CONF_VARIABLE): cv.string,
vol.Required(CONF_PAYLOAD): vol.Schema(dict),
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_UNIT_OF_MEASUREMENT, default=None): cv.string,
@ -31,18 +32,18 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup pilight Sensor."""
"""Set up Pilight Sensor."""
add_devices([PilightSensor(
hass=hass,
name=config.get(CONF_NAME),
variable=config.get("variable"),
variable=config.get(CONF_VARIABLE),
payload=config.get(CONF_PAYLOAD),
unit_of_measurement=config.get(CONF_UNIT_OF_MEASUREMENT)
)])
class PilightSensor(Entity):
"""Representation of a sensor that can be updated using pilight."""
"""Representation of a sensor that can be updated using Pilight."""
def __init__(self, hass, name, variable, payload, unit_of_measurement):
"""Initialize the sensor."""

View file

@ -1,44 +1,77 @@
"""
Support for switching devices via pilight to on and off.
Support for switching devices via Pilight to on and off.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/switch.pilight/
"""
import logging
from homeassistant.helpers.config_validation import ensure_list
import homeassistant.components.pilight as pilight
from homeassistant.components.switch import SwitchDevice
import voluptuous as vol
DEPENDENCIES = ['pilight']
import homeassistant.helpers.config_validation as cv
import homeassistant.components.pilight as pilight
from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA)
from homeassistant.const import (CONF_NAME, CONF_ID, CONF_SWITCHES, CONF_STATE)
_LOGGER = logging.getLogger(__name__)
CONF_OFF_CODE = 'off_code'
CONF_OFF_CODE_RECIEVE = 'off_code_receive'
CONF_ON_CODE = 'on_code'
CONF_ON_CODE_RECIEVE = 'on_code_receive'
CONF_SYSTEMCODE = 'systemcode'
CONF_UNIT = 'unit'
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
"""Setup the pilight platform."""
# Find and return switches controlled by pilight
switches = config.get('switches', {})
DEPENDENCIES = ['pilight']
COMMAND_SCHEMA = pilight.RF_CODE_SCHEMA.extend({
vol.Optional('on'): cv.positive_int,
vol.Optional('off'): cv.positive_int,
vol.Optional(CONF_UNIT): cv.string,
vol.Optional(CONF_ID): cv.positive_int,
vol.Optional(CONF_STATE): cv.string,
vol.Optional(CONF_SYSTEMCODE): cv.string,
})
SWITCHES_SCHEMA = vol.Schema({
vol.Required(CONF_ON_CODE): COMMAND_SCHEMA,
vol.Required(CONF_OFF_CODE): COMMAND_SCHEMA,
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_OFF_CODE_RECIEVE): COMMAND_SCHEMA,
vol.Optional(CONF_ON_CODE_RECIEVE): COMMAND_SCHEMA,
})
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_SWITCHES):
vol.Schema({cv.string: SWITCHES_SCHEMA}),
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Pilight platform."""
switches = config.get(CONF_SWITCHES)
devices = []
for dev_name, properties in switches.items():
devices.append(
PilightSwitch(
hass,
properties.get('name', dev_name),
properties.get('on_code'),
properties.get('off_code'),
ensure_list(properties.get('on_code_receive', False)),
ensure_list(properties.get('off_code_receive', False))))
properties.get(CONF_NAME, dev_name),
properties.get(CONF_ON_CODE),
properties.get(CONF_OFF_CODE),
properties.get(CONF_ON_CODE_RECIEVE),
properties.get(CONF_OFF_CODE_RECIEVE)
)
)
add_devices_callback(devices)
add_devices(devices)
class PilightSwitch(SwitchDevice):
"""Representation of a pilight switch."""
"""Representation of a Pilight switch."""
def __init__(self, hass, name, code_on, code_off,
code_on_receive, code_off_receive):
def __init__(self, hass, name, code_on, code_off, code_on_receive,
code_off_receive):
"""Initialize the switch."""
self._hass = hass
self._name = name
@ -69,29 +102,22 @@ class PilightSwitch(SwitchDevice):
def _handle_code(self, call):
"""Check if received code by the pilight-daemon.
If the code matches the receive on / off codes of this switch
the switch state is changed accordingly.
If the code matches the receive on/off codes of this switch the switch
state is changed accordingly.
"""
# Check if a on code is defined to turn this switch on
# - True if off_code/on_code is contained in received code dict, not
# all items have to match.
# - Call turn on/off only once, even if more than one code is received
if any(self._code_on_receive):
for on_code in self._code_on_receive: # Loop through codes
# True if on_code is contained in received code dict, not
# all items have to match
for on_code in self._code_on_receive:
if on_code.items() <= call.data.items():
self.turn_on()
# Call turn on only once, even when more than one on
# code is received
break
# Check if a off code is defined to turn this switch off
if any(self._code_off_receive):
for off_code in self._code_off_receive: # Loop through codes
# True if off_code is contained in received code dict, not
# all items have to match
for off_code in self._code_off_receive:
if off_code.items() <= call.data.items():
self.turn_off()
# Call turn off only once, even when more than one off
# code is received
break
def turn_on(self):

View file

@ -114,6 +114,7 @@ CONF_PIN = 'pin'
CONF_PLATFORM = 'platform'
CONF_PORT = 'port'
CONF_PREFIX = 'prefix'
CONF_PROTOCOL = 'protocol'
CONF_QUOTE = 'quote'
CONF_RECIPIENT = 'recipient'
CONF_RESOURCE = 'resource'

View file

@ -11,8 +11,8 @@ HASS = None
def fire_pilight_message(protocol, data):
"""Fire the fake pilight message."""
message = {pilight.ATTR_PROTOCOL: protocol}
"""Fire the fake Pilight message."""
message = {pilight.CONF_PROTOCOL: protocol}
message.update(data)
HASS.bus.fire(pilight.EVENT, message)
@ -67,23 +67,23 @@ def test_disregard_wrong_payload():
'platform': 'pilight',
'name': 'test_2',
'variable': 'test',
'payload': {'uuid': '1-2-3-4',
'protocol': 'test-protocol_2'}
'payload': {
'uuid': '1-2-3-4',
'protocol': 'test-protocol_2'
}
}
})
# Try set value from data with incorrect payload
fire_pilight_message(protocol='test-protocol_2',
data={'test': 'data',
'uuid': '0-0-0-0'})
data={'test': 'data', 'uuid': '0-0-0-0'})
HASS.block_till_done()
state = HASS.states.get('sensor.test_2')
assert state.state == 'unknown'
# Try set value from data with partially matched payload
fire_pilight_message(protocol='wrong-protocol',
data={'test': 'data',
'uuid': '1-2-3-4'})
data={'test': 'data', 'uuid': '1-2-3-4'})
HASS.block_till_done()
state = HASS.states.get('sensor.test_2')
assert state.state == 'unknown'
@ -113,8 +113,7 @@ def test_variable_missing(caplog):
# Create code without sensor variable
fire_pilight_message(protocol='test-protocol',
data={'uuid': '1-2-3-4',
'other_variable': 3.141})
data={'uuid': '1-2-3-4', 'other_variable': 3.141})
HASS.block_till_done()
logs = caplog.text