Initial commit for rollershutter component
This commit is contained in:
parent
d4f0f0ffd3
commit
8269e843f2
4 changed files with 351 additions and 0 deletions
116
homeassistant/components/rollershutter/__init__.py
Normal file
116
homeassistant/components/rollershutter/__init__.py
Normal file
|
@ -0,0 +1,116 @@
|
|||
"""
|
||||
homeassistant.components.rollershutter
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Rollershutter component.
|
||||
|
||||
"""
|
||||
import os
|
||||
import logging
|
||||
|
||||
from homeassistant.config import load_yaml_config_file
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.components import group
|
||||
from homeassistant.const import (
|
||||
SERVICE_MOVE_UP, SERVICE_MOVE_DOWN, SERVICE_MOVE_STOP,
|
||||
STATE_OPEN, ATTR_ENTITY_ID)
|
||||
|
||||
|
||||
DOMAIN = 'rollershutter'
|
||||
DEPENDENCIES = []
|
||||
SCAN_INTERVAL = 15
|
||||
|
||||
GROUP_NAME_ALL_ROLLERSHUTTERS = 'all rollershutters'
|
||||
ENTITY_ID_ALL_ROLLERSHUTTERS = group.ENTITY_ID_FORMAT.format(
|
||||
'all_rollershutters')
|
||||
|
||||
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
||||
|
||||
# Maps discovered services to their platforms
|
||||
DISCOVERY_PLATFORMS = {}
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def is_open(hass, entity_id=None):
|
||||
""" Returns if the rollershutter is open based on the statemachine. """
|
||||
entity_id = entity_id or ENTITY_ID_ALL_ROLLERSHUTTERS
|
||||
return hass.states.is_state(entity_id, STATE_OPEN)
|
||||
|
||||
|
||||
def move_up(hass, entity_id=None):
|
||||
""" Moves all or specified rollershutter up. """
|
||||
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
|
||||
hass.services.call(DOMAIN, SERVICE_MOVE_UP, data)
|
||||
|
||||
|
||||
def move_down(hass, entity_id=None):
|
||||
""" Moves all or specified rollershutter down. """
|
||||
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
|
||||
hass.services.call(DOMAIN, SERVICE_MOVE_DOWN, data)
|
||||
|
||||
|
||||
def move_stop(hass, entity_id=None):
|
||||
""" Stops all or specified rollershutter. """
|
||||
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
|
||||
hass.services.call(DOMAIN, SERVICE_MOVE_STOP, data)
|
||||
|
||||
|
||||
def setup(hass, config):
|
||||
""" Track states and offer events for rollershutters. """
|
||||
component = EntityComponent(
|
||||
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS,
|
||||
GROUP_NAME_ALL_ROLLERSHUTTERS)
|
||||
component.setup(config)
|
||||
|
||||
def handle_rollershutter_service(service):
|
||||
""" Handles calls to the rollershutter services. """
|
||||
target_rollershutters = component.extract_from_service(service)
|
||||
|
||||
for rollershutter in target_rollershutters:
|
||||
if service.service == SERVICE_MOVE_UP:
|
||||
rollershutter.move_up()
|
||||
elif service.service == SERVICE_MOVE_DOWN:
|
||||
rollershutter.move_down()
|
||||
elif service.service == SERVICE_MOVE_STOP:
|
||||
rollershutter.move_stop()
|
||||
|
||||
if rollershutter.should_poll:
|
||||
rollershutter.update_ha_state(True)
|
||||
|
||||
descriptions = load_yaml_config_file(
|
||||
os.path.join(os.path.dirname(__file__), 'services.yaml'))
|
||||
|
||||
hass.services.register(DOMAIN, SERVICE_MOVE_UP,
|
||||
handle_rollershutter_service,
|
||||
descriptions.get(SERVICE_MOVE_UP))
|
||||
hass.services.register(DOMAIN, SERVICE_MOVE_DOWN,
|
||||
handle_rollershutter_service,
|
||||
descriptions.get(SERVICE_MOVE_DOWN))
|
||||
hass.services.register(DOMAIN, SERVICE_MOVE_STOP,
|
||||
handle_rollershutter_service,
|
||||
descriptions.get(SERVICE_MOVE_STOP))
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class RollershutterDevice(Entity):
|
||||
""" Represents a rollershutter within Home Assistant. """
|
||||
# pylint: disable=no-self-use
|
||||
|
||||
@property
|
||||
def state_attributes(self):
|
||||
""" Returns optional state attributes. """
|
||||
return None
|
||||
|
||||
def move_up(self, **kwargs):
|
||||
""" Moves the device UP. """
|
||||
raise NotImplementedError()
|
||||
|
||||
def move_down(self, **kwargs):
|
||||
""" Moves the device DOWN. """
|
||||
raise NotImplementedError()
|
||||
|
||||
def move_stop(self, **kwargs):
|
||||
""" Moves the device to STOP. """
|
||||
raise NotImplementedError()
|
119
homeassistant/components/rollershutter/mqtt.py
Normal file
119
homeassistant/components/rollershutter/mqtt.py
Normal file
|
@ -0,0 +1,119 @@
|
|||
"""
|
||||
homeassistant.components.rollershutter.mqtt
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Allows to configure a MQTT rollershutter.
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/rollershutter.mqtt/
|
||||
"""
|
||||
import logging
|
||||
import homeassistant.components.mqtt as mqtt
|
||||
from homeassistant.components.rollershutter import RollershutterDevice
|
||||
from homeassistant.const import (STATE_OPEN, STATE_CLOSED, STATE_UNKNOWN)
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ['mqtt']
|
||||
|
||||
DEFAULT_NAME = "MQTT Shutter"
|
||||
DEFAULT_QOS = 0
|
||||
DEFAULT_PAYLOAD_UP = "UP"
|
||||
DEFAULT_PAYLOAD_DOWN = "DOWN"
|
||||
DEFAULT_PAYLOAD_STOP = "STOP"
|
||||
|
||||
ATTR_CURRENT_POSITION = 'current_position'
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
""" Add MQTT Roller Shutter """
|
||||
|
||||
if config.get('command_topic') is None:
|
||||
_LOGGER.error("Missing required variable: command_topic")
|
||||
return False
|
||||
|
||||
add_devices_callback([MqttRollershutter(
|
||||
hass,
|
||||
config.get('name', DEFAULT_NAME),
|
||||
config.get('state_topic'),
|
||||
config.get('command_topic'),
|
||||
config.get('qos', DEFAULT_QOS),
|
||||
config.get('payload_up', DEFAULT_PAYLOAD_UP),
|
||||
config.get('payload_down', DEFAULT_PAYLOAD_DOWN),
|
||||
config.get('payload_stop', DEFAULT_PAYLOAD_STOP),
|
||||
config.get('state_format'))])
|
||||
|
||||
|
||||
# pylint: disable=too-many-arguments, too-many-instance-attributes
|
||||
class MqttRollershutter(RollershutterDevice):
|
||||
""" Represents a rollershutter that can be togggled using MQTT """
|
||||
def __init__(self, hass, name, state_topic, command_topic, qos,
|
||||
payload_up, payload_down, payload_stop, state_format):
|
||||
self._state = -1
|
||||
self._hass = hass
|
||||
self._name = name
|
||||
self._state_topic = state_topic
|
||||
self._command_topic = command_topic
|
||||
self._qos = qos
|
||||
self._payload_up = payload_up
|
||||
self._payload_down = payload_down
|
||||
self._payload_stop = payload_stop
|
||||
self._parse = mqtt.FmtParser(state_format)
|
||||
|
||||
if self._state_topic:
|
||||
def message_received(topic, payload, qos):
|
||||
""" A new MQTT message has been received. """
|
||||
value = self._parse(payload)
|
||||
if value.isnumeric():
|
||||
if 0 <= int(value) <= 100:
|
||||
self._state = int(value)
|
||||
self.update_ha_state()
|
||||
|
||||
mqtt.subscribe(hass, self._state_topic, message_received,
|
||||
self._qos)
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
""" No polling needed """
|
||||
return False
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""" The name of the rollershutter """
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
""" Returns the state of the device. """
|
||||
if self._state == -1:
|
||||
return STATE_UNKNOWN
|
||||
elif self._state == 0:
|
||||
return STATE_CLOSED
|
||||
else:
|
||||
return STATE_OPEN
|
||||
|
||||
@property
|
||||
def is_open(self):
|
||||
""" True if device is open. """
|
||||
return self.state == STATE_OPEN
|
||||
|
||||
def move_up(self, **kwargs):
|
||||
""" Moves the device UP. """
|
||||
mqtt.publish(self.hass, self._command_topic, self._payload_up,
|
||||
self._qos)
|
||||
|
||||
def move_down(self, **kwargs):
|
||||
""" Moves the device DOWN. """
|
||||
mqtt.publish(self.hass, self._command_topic, self._payload_down,
|
||||
self._qos)
|
||||
|
||||
def move_stop(self, **kwargs):
|
||||
""" Moves the device to STOP. """
|
||||
mqtt.publish(self.hass, self._command_topic, self._payload_stop,
|
||||
self._qos)
|
||||
|
||||
@property
|
||||
def state_attributes(self):
|
||||
""" Return the state attributes. """
|
||||
state_attr = {
|
||||
ATTR_CURRENT_POSITION: self._state,
|
||||
}
|
||||
return state_attr
|
0
homeassistant/components/rollershutter/services.yaml
Normal file
0
homeassistant/components/rollershutter/services.yaml
Normal file
116
tests/components/rollershutter/test_mqtt.py
Normal file
116
tests/components/rollershutter/test_mqtt.py
Normal file
|
@ -0,0 +1,116 @@
|
|||
"""
|
||||
tests.components.rollershutter.test_mqtt
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Tests mqtt rollershutter.
|
||||
"""
|
||||
import unittest
|
||||
|
||||
from homeassistant.const import STATE_OPEN, STATE_CLOSED, STATE_UNKNOWN
|
||||
import homeassistant.core as ha
|
||||
import homeassistant.components.rollershutter as rollershutter
|
||||
from tests.common import mock_mqtt_component, fire_mqtt_message
|
||||
|
||||
|
||||
class TestRollershutterMQTT(unittest.TestCase):
|
||||
""" Test the MQTT rollershutter. """
|
||||
|
||||
def setUp(self): # pylint: disable=invalid-name
|
||||
self.hass = ha.HomeAssistant()
|
||||
self.mock_publish = mock_mqtt_component(self.hass)
|
||||
|
||||
def tearDown(self): # pylint: disable=invalid-name
|
||||
""" Stop down stuff we started. """
|
||||
self.hass.stop()
|
||||
|
||||
def test_controlling_state_via_topic(self):
|
||||
self.assertTrue(rollershutter.setup(self.hass, {
|
||||
'rollershutter': {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'state-topic',
|
||||
'command_topic': 'command-topic',
|
||||
'qos': 0,
|
||||
'payload_up': 'UP',
|
||||
'payload_down': 'DOWN',
|
||||
'payload_stop': 'STOP'
|
||||
}
|
||||
}))
|
||||
|
||||
state = self.hass.states.get('rollershutter.test')
|
||||
self.assertEqual(STATE_UNKNOWN, state.state)
|
||||
|
||||
fire_mqtt_message(self.hass, 'state-topic', '0')
|
||||
self.hass.pool.block_till_done()
|
||||
|
||||
state = self.hass.states.get('rollershutter.test')
|
||||
self.assertEqual(STATE_CLOSED, state.state)
|
||||
|
||||
fire_mqtt_message(self.hass, 'state-topic', '50')
|
||||
self.hass.pool.block_till_done()
|
||||
|
||||
state = self.hass.states.get('rollershutter.test')
|
||||
self.assertEqual(STATE_OPEN, state.state)
|
||||
|
||||
fire_mqtt_message(self.hass, 'state-topic', '100')
|
||||
self.hass.pool.block_till_done()
|
||||
|
||||
state = self.hass.states.get('rollershutter.test')
|
||||
self.assertEqual(STATE_OPEN, state.state)
|
||||
|
||||
def test_sending_mqtt_commands(self):
|
||||
self.assertTrue(rollershutter.setup(self.hass, {
|
||||
'rollershutter': {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'state-topic',
|
||||
'command_topic': 'command-topic',
|
||||
'qos': 2
|
||||
}
|
||||
}))
|
||||
|
||||
state = self.hass.states.get('rollershutter.test')
|
||||
self.assertEqual(STATE_UNKNOWN, state.state)
|
||||
|
||||
rollershutter.move_up(self.hass, 'rollershutter.test')
|
||||
self.hass.pool.block_till_done()
|
||||
|
||||
self.assertEqual(('command-topic', 'UP', 2),
|
||||
self.mock_publish.mock_calls[-1][1])
|
||||
state = self.hass.states.get('rollershutter.test')
|
||||
self.assertEqual(STATE_UNKNOWN, state.state)
|
||||
|
||||
def test_state_attributes_current_position(self):
|
||||
self.assertTrue(rollershutter.setup(self.hass, {
|
||||
'rollershutter': {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'state-topic',
|
||||
'command_topic': 'command-topic',
|
||||
'payload_up': 'UP',
|
||||
'payload_down': 'DOWN',
|
||||
'payload_stop': 'STOP'
|
||||
}
|
||||
}))
|
||||
|
||||
current_position = self.hass.states.get(
|
||||
'rollershutter.test').attributes['current_position']
|
||||
self.assertEqual(-1, current_position)
|
||||
|
||||
fire_mqtt_message(self.hass, 'state-topic', '0')
|
||||
self.hass.pool.block_till_done()
|
||||
current_position = self.hass.states.get(
|
||||
'rollershutter.test').attributes['current_position']
|
||||
self.assertEqual(0, current_position)
|
||||
|
||||
fire_mqtt_message(self.hass, 'state-topic', '50')
|
||||
self.hass.pool.block_till_done()
|
||||
current_position = self.hass.states.get(
|
||||
'rollershutter.test').attributes['current_position']
|
||||
self.assertEqual(50, current_position)
|
||||
|
||||
fire_mqtt_message(self.hass, 'state-topic', '101')
|
||||
self.hass.pool.block_till_done()
|
||||
current_position = self.hass.states.get(
|
||||
'rollershutter.test').attributes['current_position']
|
||||
self.assertEqual(50, current_position)
|
Loading…
Add table
Reference in a new issue