mirror of
https://github.com/home-assistant/core.git
synced 2025-07-25 22:27:07 +00:00
Pilight component (#2742)
* New component to interface with a pilight-daemon for RF send/receive * Fix bug that changed the received data, add connected flag, clean up * New pilight switch component * New optional whitelist filter to filter uninteressting devices * Add pilight * PEP8: too long lines, white spaces * To keep up the good coverage ... * PEP 257 * pylint enhancements * pylint enhancements * PEP 257 * Better HA config validation and cleanup following code review for #2742 * Fix requirenments to require fixed pilight version * Change config validation to use voluptuous * Pilight switch exclude not needed due to wildcard pilight exclude * Enhance configuration parsing using voluptuous
This commit is contained in:
parent
d80c05b6b6
commit
dc9f990ad2
@ -88,6 +88,9 @@ omit =
|
|||||||
homeassistant/components/homematic.py
|
homeassistant/components/homematic.py
|
||||||
homeassistant/components/*/homematic.py
|
homeassistant/components/*/homematic.py
|
||||||
|
|
||||||
|
homeassistant/components/pilight.py
|
||||||
|
homeassistant/components/*/pilight.py
|
||||||
|
|
||||||
homeassistant/components/knx.py
|
homeassistant/components/knx.py
|
||||||
homeassistant/components/switch/knx.py
|
homeassistant/components/switch/knx.py
|
||||||
homeassistant/components/binary_sensor/knx.py
|
homeassistant/components/binary_sensor/knx.py
|
||||||
|
112
homeassistant/components/pilight.py
Normal file
112
homeassistant/components/pilight.py
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
"""
|
||||||
|
Component to create an interface to a Pilight daemon (https://pilight.org/).
|
||||||
|
|
||||||
|
Pilight can be used to send and receive signals from a radio frequency
|
||||||
|
module (RF receiver).
|
||||||
|
|
||||||
|
RF commands received by the daemon are put on the HA event bus.
|
||||||
|
RF commands can also be send with a pilight.send service call.
|
||||||
|
"""
|
||||||
|
# pylint: disable=import-error
|
||||||
|
import logging
|
||||||
|
import socket
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
from homeassistant.helpers.config_validation import ensure_list
|
||||||
|
from homeassistant.const import EVENT_HOMEASSISTANT_START
|
||||||
|
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||||
|
from homeassistant.const import CONF_HOST, CONF_PORT
|
||||||
|
|
||||||
|
REQUIREMENTS = ['pilight==0.0.2']
|
||||||
|
|
||||||
|
DOMAIN = "pilight"
|
||||||
|
EVENT = 'pilight_received'
|
||||||
|
SERVICE_NAME = 'send'
|
||||||
|
|
||||||
|
CONF_WHITELIST = 'whitelist'
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = vol.Schema({
|
||||||
|
DOMAIN: vol.Schema({
|
||||||
|
vol.Required(CONF_HOST, default='127.0.0.1'): cv.string,
|
||||||
|
vol.Required(CONF_PORT, default=5000): vol.Coerce(int),
|
||||||
|
vol.Optional(CONF_WHITELIST): {cv.string: [cv.string]}
|
||||||
|
}),
|
||||||
|
}, extra=vol.ALLOW_EXTRA)
|
||||||
|
|
||||||
|
# The pilight code schema depends on the protocol
|
||||||
|
# Thus only require to have the protocol information
|
||||||
|
ATTR_PROTOCOL = 'protocol'
|
||||||
|
RF_CODE_SCHEMA = vol.Schema({vol.Required(ATTR_PROTOCOL): cv.string},
|
||||||
|
extra=vol.ALLOW_EXTRA)
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def setup(hass, config):
|
||||||
|
"""Setup pilight component."""
|
||||||
|
from pilight import pilight
|
||||||
|
|
||||||
|
try:
|
||||||
|
pilight_client = pilight.Client(host=config[DOMAIN][CONF_HOST],
|
||||||
|
port=config[DOMAIN][CONF_PORT])
|
||||||
|
except (socket.error, socket.timeout) as err:
|
||||||
|
_LOGGER.error(
|
||||||
|
"Unable to connect to %s on port %s: %s",
|
||||||
|
config[CONF_HOST], config[CONF_PORT], err)
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Start / stop pilight-daemon connection with HA start/stop
|
||||||
|
def start_pilight_client(_):
|
||||||
|
"""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."""
|
||||||
|
pilight_client.stop()
|
||||||
|
|
||||||
|
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_pilight_client)
|
||||||
|
|
||||||
|
def send_code(call):
|
||||||
|
"""Send RF code to the pilight-daemon."""
|
||||||
|
message_data = call.data
|
||||||
|
|
||||||
|
# Patch data because of bug:
|
||||||
|
# https://github.com/pilight/pilight/issues/296
|
||||||
|
# Protocol has to be in a list otherwise segfault in pilight-daemon
|
||||||
|
message_data["protocol"] = ensure_list(message_data["protocol"])
|
||||||
|
|
||||||
|
try:
|
||||||
|
pilight_client.send_code(message_data)
|
||||||
|
except IOError:
|
||||||
|
_LOGGER.error('Pilight send failed for %s', str(message_data))
|
||||||
|
|
||||||
|
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
|
||||||
|
whitelist = config[DOMAIN].get('whitelist', False)
|
||||||
|
|
||||||
|
def handle_received_code(data):
|
||||||
|
"""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['message'])
|
||||||
|
|
||||||
|
# No whitelist defined, put data on event bus
|
||||||
|
if not whitelist:
|
||||||
|
hass.bus.fire(EVENT, data)
|
||||||
|
# Check if data matches the defined whitelist
|
||||||
|
elif all(data[key] in whitelist[key] for key in whitelist):
|
||||||
|
hass.bus.fire(EVENT, data)
|
||||||
|
|
||||||
|
pilight_client.set_callback(handle_received_code)
|
||||||
|
|
||||||
|
return True
|
110
homeassistant/components/switch/pilight.py
Normal file
110
homeassistant/components/switch/pilight.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
"""
|
||||||
|
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
|
||||||
|
|
||||||
|
DEPENDENCIES = ['pilight']
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
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', {})
|
||||||
|
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))))
|
||||||
|
|
||||||
|
add_devices_callback(devices)
|
||||||
|
|
||||||
|
|
||||||
|
class PilightSwitch(SwitchDevice):
|
||||||
|
"""Representation of a pilight switch."""
|
||||||
|
|
||||||
|
# pylint: disable=too-many-arguments, too-many-instance-attributes
|
||||||
|
def __init__(self, hass, name, code_on, code_off,
|
||||||
|
code_on_receive, code_off_receive):
|
||||||
|
"""Initialize the switch."""
|
||||||
|
self._hass = hass
|
||||||
|
self._name = name
|
||||||
|
self._state = False
|
||||||
|
self._code_on = code_on
|
||||||
|
self._code_off = code_off
|
||||||
|
self._code_on_receive = code_on_receive
|
||||||
|
self._code_off_receive = code_off_receive
|
||||||
|
|
||||||
|
if any(self._code_on_receive) or any(self._code_off_receive):
|
||||||
|
hass.bus.listen(pilight.EVENT, self._handle_code)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""Get the name of the switch."""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def should_poll(self):
|
||||||
|
"""No polling needed, state set when correct code is received."""
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self):
|
||||||
|
"""Return true if switch is on."""
|
||||||
|
return self._state
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
# Check if a on code is defined to turn this switch on
|
||||||
|
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
|
||||||
|
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
|
||||||
|
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):
|
||||||
|
"""Turn the switch on by calling pilight.send service with on code."""
|
||||||
|
self._hass.services.call(pilight.DOMAIN, pilight.SERVICE_NAME,
|
||||||
|
self._code_on, blocking=True)
|
||||||
|
self._state = True
|
||||||
|
self.update_ha_state()
|
||||||
|
|
||||||
|
def turn_off(self):
|
||||||
|
"""Turn the switch on by calling pilight.send service with off code."""
|
||||||
|
self._hass.services.call(pilight.DOMAIN, pilight.SERVICE_NAME,
|
||||||
|
self._code_off, blocking=True)
|
||||||
|
self._state = False
|
||||||
|
self.update_ha_state()
|
@ -229,6 +229,9 @@ pexpect==4.0.1
|
|||||||
# homeassistant.components.light.hue
|
# homeassistant.components.light.hue
|
||||||
phue==0.8
|
phue==0.8
|
||||||
|
|
||||||
|
# homeassistant.components.pilight
|
||||||
|
pilight==0.0.2
|
||||||
|
|
||||||
# homeassistant.components.media_player.plex
|
# homeassistant.components.media_player.plex
|
||||||
# homeassistant.components.sensor.plex
|
# homeassistant.components.sensor.plex
|
||||||
plexapi==2.0.2
|
plexapi==2.0.2
|
||||||
|
Loading…
x
Reference in New Issue
Block a user