From 4e3bd5f2a902aeead54f5f55c7c4ca0943517556 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 26 Sep 2015 23:17:04 -0700 Subject: [PATCH] Add service descriptions --- .../alarm_control_panel/__init__.py | 13 +++-- .../alarm_control_panel/services.yaml | 0 homeassistant/components/demo.py | 2 +- .../components/device_tracker/__init__.py | 5 +- .../components/device_tracker/services.yaml | 0 homeassistant/components/frontend/version.py | 2 +- .../frontend/www_static/frontend.html | 29 ++++++----- .../www_static/home-assistant-polymer | 2 +- homeassistant/components/light/__init__.py | 22 ++++---- homeassistant/components/light/services.yaml | 52 +++++++++++++++++++ .../components/media_player/__init__.py | 26 +++++++--- .../components/media_player/services.yaml | 0 homeassistant/components/notify/__init__.py | 8 ++- homeassistant/components/notify/services.yaml | 0 homeassistant/components/switch/__init__.py | 12 +++-- homeassistant/components/switch/services.yaml | 0 .../components/thermostat/__init__.py | 13 +++-- .../components/thermostat/services.yaml | 0 homeassistant/core.py | 41 +++++++++++++-- tests/test_core.py | 2 +- 20 files changed, 180 insertions(+), 49 deletions(-) create mode 100644 homeassistant/components/alarm_control_panel/services.yaml create mode 100644 homeassistant/components/device_tracker/services.yaml create mode 100644 homeassistant/components/light/services.yaml create mode 100644 homeassistant/components/media_player/services.yaml create mode 100644 homeassistant/components/notify/services.yaml create mode 100644 homeassistant/components/switch/services.yaml create mode 100644 homeassistant/components/thermostat/services.yaml diff --git a/homeassistant/components/alarm_control_panel/__init__.py b/homeassistant/components/alarm_control_panel/__init__.py index 8755d65e26e..49eb5eafba7 100644 --- a/homeassistant/components/alarm_control_panel/__init__.py +++ b/homeassistant/components/alarm_control_panel/__init__.py @@ -4,12 +4,15 @@ homeassistant.components.alarm_control_panel Component to interface with a alarm control panel. """ import logging -from homeassistant.helpers.entity import Entity -from homeassistant.helpers.entity_component import EntityComponent +import os + from homeassistant.components import verisure from homeassistant.const import ( ATTR_ENTITY_ID, SERVICE_ALARM_DISARM, SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_ARM_AWAY) +from homeassistant.config import load_yaml_config_file +from homeassistant.helpers.entity import Entity +from homeassistant.helpers.entity_component import EntityComponent DOMAIN = 'alarm_control_panel' DEPENDENCIES = [] @@ -59,8 +62,12 @@ def setup(hass, config): for alarm in target_alarms: getattr(alarm, method)(code) + descriptions = load_yaml_config_file( + os.path.join(os.path.dirname(__file__), 'services.yaml')) + for service in SERVICE_TO_METHOD: - hass.services.register(DOMAIN, service, alarm_service_handler) + hass.services.register(DOMAIN, service, alarm_service_handler, + descriptions.get(service)) return True diff --git a/homeassistant/components/alarm_control_panel/services.yaml b/homeassistant/components/alarm_control_panel/services.yaml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/homeassistant/components/demo.py b/homeassistant/components/demo.py index f0ce2259dc4..f22135ec5bc 100644 --- a/homeassistant/components/demo.py +++ b/homeassistant/components/demo.py @@ -60,7 +60,7 @@ def setup(hass, config): {'camera': { 'platform': 'generic', 'name': 'IP Camera', - 'still_image_url': 'http://194.218.96.92/jpg/image.jpg', + 'still_image_url': 'http://home-assistant.io/demo/webcam.jpg', }}) # Setup scripts diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index 27e9417ab5b..d716422b38c 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -175,7 +175,10 @@ def setup(hass, config): ATTR_GPS, ATTR_GPS_ACCURACY, ATTR_BATTERY)} tracker.see(**args) - hass.services.register(DOMAIN, SERVICE_SEE, see_service) + descriptions = load_yaml_config_file( + os.path.join(os.path.dirname(__file__), 'services.yaml')) + hass.services.register(DOMAIN, SERVICE_SEE, see_service, + descriptions.get(SERVICE_SEE)) return True diff --git a/homeassistant/components/device_tracker/services.yaml b/homeassistant/components/device_tracker/services.yaml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index 721eeaf78b7..b6cebfbb296 100644 --- a/homeassistant/components/frontend/version.py +++ b/homeassistant/components/frontend/version.py @@ -1,2 +1,2 @@ """ DO NOT MODIFY. Auto-generated by build_frontend script """ -VERSION = "5ba745608f6271b781fc41c1e8f8deb0" +VERSION = "1dae3a2fb4ea4ff853d0d0c647f0ac51" diff --git a/homeassistant/components/frontend/www_static/frontend.html b/homeassistant/components/frontend/www_static/frontend.html index d7c8ed1a8cc..656c9e44797 100644 --- a/homeassistant/components/frontend/www_static/frontend.html +++ b/homeassistant/components/frontend/www_static/frontend.html @@ -5733,14 +5733,19 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN a { color: var(--accent-color); - } \ No newline at end of file + } \ No newline at end of file diff --git a/homeassistant/components/frontend/www_static/home-assistant-polymer b/homeassistant/components/frontend/www_static/home-assistant-polymer index 78cf25f6057..72bceff0fcf 160000 --- a/homeassistant/components/frontend/www_static/home-assistant-polymer +++ b/homeassistant/components/frontend/www_static/home-assistant-polymer @@ -1 +1 @@ -Subproject commit 78cf25f605724ae84d8e23d0e7ab97845f53725a +Subproject commit 72bceff0fcfe7ba440cc1676ec0fe77ac0f6e200 diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index d2f8033add7..8d09910093b 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -52,14 +52,14 @@ import logging import os import csv -from homeassistant.helpers.entity_component import EntityComponent -from homeassistant.helpers.entity import ToggleEntity - -import homeassistant.util as util -import homeassistant.util.color as color_util +from homeassistant.components import group, discovery, wink, isy994 +from homeassistant.config import load_yaml_config_file from homeassistant.const import ( STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID) -from homeassistant.components import group, discovery, wink, isy994 +from homeassistant.helpers.entity import ToggleEntity +from homeassistant.helpers.entity_component import EntityComponent +import homeassistant.util as util +import homeassistant.util.color as color_util DOMAIN = "light" @@ -275,11 +275,13 @@ def setup(hass, config): light.update_ha_state(True) # Listen for light on and light off service calls - hass.services.register(DOMAIN, SERVICE_TURN_ON, - handle_light_service) + descriptions = load_yaml_config_file( + os.path.join(os.path.dirname(__file__), 'services.yaml')) + hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_light_service, + descriptions.get(SERVICE_TURN_ON)) - hass.services.register(DOMAIN, SERVICE_TURN_OFF, - handle_light_service) + hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_light_service, + descriptions.get(SERVICE_TURN_OFF)) return True diff --git a/homeassistant/components/light/services.yaml b/homeassistant/components/light/services.yaml new file mode 100644 index 00000000000..ed8b4b663ea --- /dev/null +++ b/homeassistant/components/light/services.yaml @@ -0,0 +1,52 @@ +# Describes the format for available light services + +turn_on: + description: Turn a light on + + fields: + entity_id: + description: Name(s) of entities to turn on + example: 'light.kitchen' + + transition: + description: Duration in seconds it takes to get to next state + example: 60 + + rgb_color: + description: Color for the light in RGB-format + example: '[255, 100, 100]' + + xy_color: + description: Color for the light in XY-format + example: '[0.52, 0.43]' + + brightness: + description: Number between 0..255 indicating brightness + example: 120 + + profile: + description: Name of a light profile to use + example: relax + + flash: + description: If the light should flash + values: + - short + - long + + effect: + description: Light effect + values: + - colorloop + +turn_off: + description: Turn a light off + + fields: + entity_id: + description: Name(s) of entities to turn off + example: 'light.kitchen' + + transition: + description: Duration in seconds it takes to get to next state + example: 60 diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 19ff0540c6b..143473e2fde 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -5,8 +5,10 @@ homeassistant.components.media_player Component to interface with various media players. """ import logging +import os from homeassistant.components import discovery +from homeassistant.config import load_yaml_config_file from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity_component import EntityComponent from homeassistant.const import ( @@ -186,6 +188,9 @@ def setup(hass, config): component.setup(config) + descriptions = load_yaml_config_file( + os.path.join(os.path.dirname(__file__), 'services.yaml')) + def media_player_service_handler(service): """ Maps services to methods on MediaPlayerDevice. """ target_players = component.extract_from_service(service) @@ -199,7 +204,8 @@ def setup(hass, config): player.update_ha_state(True) for service in SERVICE_TO_METHOD: - hass.services.register(DOMAIN, service, media_player_service_handler) + hass.services.register(DOMAIN, service, media_player_service_handler, + descriptions.get(service)) def volume_set_service(service): """ Set specified volume on the media player. """ @@ -216,7 +222,8 @@ def setup(hass, config): if player.should_poll: player.update_ha_state(True) - hass.services.register(DOMAIN, SERVICE_VOLUME_SET, volume_set_service) + hass.services.register(DOMAIN, SERVICE_VOLUME_SET, volume_set_service, + descriptions.get(SERVICE_VOLUME_SET)) def volume_mute_service(service): """ Mute (true) or unmute (false) the media player. """ @@ -233,7 +240,8 @@ def setup(hass, config): if player.should_poll: player.update_ha_state(True) - hass.services.register(DOMAIN, SERVICE_VOLUME_MUTE, volume_mute_service) + hass.services.register(DOMAIN, SERVICE_VOLUME_MUTE, volume_mute_service, + descriptions.get(SERVICE_VOLUME_MUTE)) def media_seek_service(service): """ Seek to a position. """ @@ -250,7 +258,8 @@ def setup(hass, config): if player.should_poll: player.update_ha_state(True) - hass.services.register(DOMAIN, SERVICE_MEDIA_SEEK, media_seek_service) + hass.services.register(DOMAIN, SERVICE_MEDIA_SEEK, media_seek_service, + descriptions.get(SERVICE_MEDIA_SEEK)) def play_youtube_video_service(service, media_id=None): """ Plays specified media_id on the media player. """ @@ -268,14 +277,17 @@ def setup(hass, config): hass.services.register( DOMAIN, "start_fireplace", - lambda service: play_youtube_video_service(service, "eyU3bRy2x44")) + lambda service: play_youtube_video_service(service, "eyU3bRy2x44"), + descriptions.get('start_fireplace')) hass.services.register( DOMAIN, "start_epic_sax", - lambda service: play_youtube_video_service(service, "kxopViU98Xo")) + lambda service: play_youtube_video_service(service, "kxopViU98Xo"), + descriptions.get('start_epic_sax')) hass.services.register( - DOMAIN, SERVICE_YOUTUBE_VIDEO, play_youtube_video_service) + DOMAIN, SERVICE_YOUTUBE_VIDEO, play_youtube_video_service, + descriptions.get(SERVICE_YOUTUBE_VIDEO)) return True diff --git a/homeassistant/components/media_player/services.yaml b/homeassistant/components/media_player/services.yaml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/homeassistant/components/notify/__init__.py b/homeassistant/components/notify/__init__.py index ee53159d5e6..e6cdf372dc8 100644 --- a/homeassistant/components/notify/__init__.py +++ b/homeassistant/components/notify/__init__.py @@ -6,7 +6,9 @@ Provides functionality to notify people. """ from functools import partial import logging +import os +from homeassistant.config import load_yaml_config_file from homeassistant.loader import get_component from homeassistant.helpers import config_per_platform @@ -36,6 +38,9 @@ def setup(hass, config): """ Sets up notify services. """ success = False + descriptions = load_yaml_config_file( + os.path.join(os.path.dirname(__file__), 'services.yaml')) + for platform, p_config in config_per_platform(config, DOMAIN, _LOGGER): # get platform notify_implementation = get_component( @@ -69,7 +74,8 @@ def setup(hass, config): # register service service_call_handler = partial(notify_message, notify_service) service_notify = p_config.get(CONF_NAME, SERVICE_NOTIFY) - hass.services.register(DOMAIN, service_notify, service_call_handler) + hass.services.register(DOMAIN, service_notify, service_call_handler, + descriptions.get(service_notify)) success = True return success diff --git a/homeassistant/components/notify/services.yaml b/homeassistant/components/notify/services.yaml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/homeassistant/components/switch/__init__.py b/homeassistant/components/switch/__init__.py index b6dd31b48c2..0fa345747f9 100644 --- a/homeassistant/components/switch/__init__.py +++ b/homeassistant/components/switch/__init__.py @@ -3,9 +3,11 @@ homeassistant.components.switch ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Component to interface with various switches that can be controlled remotely. """ -import logging from datetime import timedelta +import logging +import os +from homeassistant.config import load_yaml_config_file from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.entity import ToggleEntity @@ -83,8 +85,12 @@ def setup(hass, config): if switch.should_poll: switch.update_ha_state(True) - hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_switch_service) - hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_switch_service) + descriptions = load_yaml_config_file( + os.path.join(os.path.dirname(__file__), 'services.yaml')) + hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_switch_service, + descriptions.get(SERVICE_TURN_OFF)) + hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_switch_service, + descriptions.get(SERVICE_TURN_ON)) return True diff --git a/homeassistant/components/switch/services.yaml b/homeassistant/components/switch/services.yaml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/homeassistant/components/thermostat/__init__.py b/homeassistant/components/thermostat/__init__.py index e9d3c50451b..7d26c78f0f8 100644 --- a/homeassistant/components/thermostat/__init__.py +++ b/homeassistant/components/thermostat/__init__.py @@ -5,9 +5,11 @@ homeassistant.components.thermostat Provides functionality to interact with thermostats. """ import logging +import os from homeassistant.helpers.entity_component import EntityComponent +from homeassistant.config import load_yaml_config_file import homeassistant.util as util from homeassistant.helpers.entity import Entity from homeassistant.helpers.temperature import convert @@ -101,11 +103,16 @@ def setup(hass, config): for thermostat in target_thermostats: thermostat.update_ha_state(True) - hass.services.register( - DOMAIN, SERVICE_SET_AWAY_MODE, thermostat_service) + descriptions = load_yaml_config_file( + os.path.join(os.path.dirname(__file__), 'services.yaml')) hass.services.register( - DOMAIN, SERVICE_SET_TEMPERATURE, thermostat_service) + DOMAIN, SERVICE_SET_AWAY_MODE, thermostat_service, + descriptions.get(SERVICE_SET_AWAY_MODE)) + + hass.services.register( + DOMAIN, SERVICE_SET_TEMPERATURE, thermostat_service, + descriptions.get(SERVICE_SET_TEMPERATURE)) return True diff --git a/homeassistant/components/thermostat/services.yaml b/homeassistant/components/thermostat/services.yaml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/homeassistant/core.py b/homeassistant/core.py index d0494e070f6..3ea5b43e986 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -525,6 +525,28 @@ class StateMachine(object): from_state, to_state) +# pylint: disable=too-few-public-methods +class Service(object): + """ Represents a service. """ + + __slots__ = ['func', 'description', 'fields'] + + def __init__(self, func, description, fields): + self.func = func + self.description = description or '' + self.fields = fields or {} + + def as_dict(self): + """ Return dictionary representation of this service. """ + return { + 'description': self.description, + 'fields': self.fields, + } + + def __call__(self, call): + self.func(call) + + # pylint: disable=too-few-public-methods class ServiceCall(object): """ Represents a call to a service. """ @@ -559,20 +581,29 @@ class ServiceRegistry(object): def services(self): """ Dict with per domain a list of available services. """ with self._lock: - return {domain: list(self._services[domain].keys()) + return {domain: {key: value.as_dict() for key, value + in self._services[domain].items()} for domain in self._services} def has_service(self, domain, service): """ Returns True if specified service exists. """ return service in self._services.get(domain, []) - def register(self, domain, service, service_func): - """ Register a service. """ + def register(self, domain, service, service_func, description=None): + """ + Register a service. + + Description is a dict containing key 'description' to describe + the service and a key 'fields' to describe the fields. + """ + description = description or {} + service_obj = Service(service_func, description.get('description'), + description.get('fields', {})) with self._lock: if domain in self._services: - self._services[domain][service] = service_func + self._services[domain][service] = service_obj else: - self._services[domain] = {service: service_func} + self._services[domain] = {service: service_obj} self._bus.fire( EVENT_SERVICE_REGISTERED, diff --git a/tests/test_core.py b/tests/test_core.py index 30ef03ac1b4..01ede9e138e 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -441,7 +441,7 @@ class TestServiceRegistry(unittest.TestCase): def test_services(self): expected = { - 'test_domain': ['test_service'] + 'test_domain': {'test_service': {'description': '', 'fields': {}}} } self.assertEqual(expected, self.services.services)