mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Merge pull request #1821 from home-assistant/service-validations
Service validations
This commit is contained in:
commit
f5ee3e6e13
@ -7,12 +7,15 @@ https://home-assistant.io/components/alarm_control_panel/
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components import verisure
|
from homeassistant.components import verisure
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_CODE, ATTR_CODE_FORMAT, ATTR_ENTITY_ID, SERVICE_ALARM_TRIGGER,
|
ATTR_CODE, ATTR_CODE_FORMAT, ATTR_ENTITY_ID, SERVICE_ALARM_TRIGGER,
|
||||||
SERVICE_ALARM_DISARM, SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_ARM_AWAY)
|
SERVICE_ALARM_DISARM, SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_ARM_AWAY)
|
||||||
from homeassistant.config import load_yaml_config_file
|
from homeassistant.config import load_yaml_config_file
|
||||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
|
|
||||||
@ -38,6 +41,11 @@ ATTR_TO_PROPERTY = [
|
|||||||
ATTR_CODE_FORMAT
|
ATTR_CODE_FORMAT
|
||||||
]
|
]
|
||||||
|
|
||||||
|
ALARM_SERVICE_SCHEMA = vol.Schema({
|
||||||
|
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
||||||
|
vol.Optional(ATTR_CODE): cv.string,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
"""Track states and offer events for sensors."""
|
"""Track states and offer events for sensors."""
|
||||||
@ -51,10 +59,7 @@ def setup(hass, config):
|
|||||||
"""Map services to methods on Alarm."""
|
"""Map services to methods on Alarm."""
|
||||||
target_alarms = component.extract_from_service(service)
|
target_alarms = component.extract_from_service(service)
|
||||||
|
|
||||||
if ATTR_CODE not in service.data:
|
code = service.data.get(ATTR_CODE)
|
||||||
code = None
|
|
||||||
else:
|
|
||||||
code = service.data[ATTR_CODE]
|
|
||||||
|
|
||||||
method = SERVICE_TO_METHOD[service.service]
|
method = SERVICE_TO_METHOD[service.service]
|
||||||
|
|
||||||
@ -68,8 +73,8 @@ def setup(hass, config):
|
|||||||
|
|
||||||
for service in SERVICE_TO_METHOD:
|
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))
|
descriptions.get(service),
|
||||||
|
schema=ALARM_SERVICE_SCHEMA)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,10 +4,18 @@ Provides functionality to launch a web browser on the host machine.
|
|||||||
For more details about this component, please refer to the documentation at
|
For more details about this component, please refer to the documentation at
|
||||||
https://home-assistant.io/components/browser/
|
https://home-assistant.io/components/browser/
|
||||||
"""
|
"""
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
DOMAIN = "browser"
|
DOMAIN = "browser"
|
||||||
SERVICE_BROWSE_URL = "browse_url"
|
SERVICE_BROWSE_URL = "browse_url"
|
||||||
|
|
||||||
|
ATTR_URL = 'url'
|
||||||
|
ATTR_URL_DEFAULT = 'https://www.google.com'
|
||||||
|
|
||||||
|
SERVICE_BROWSE_URL_SCHEMA = vol.Schema({
|
||||||
|
vol.Required(ATTR_URL, default=ATTR_URL_DEFAULT): vol.Url,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
"""Listen for browse_url events."""
|
"""Listen for browse_url events."""
|
||||||
@ -15,8 +23,7 @@ def setup(hass, config):
|
|||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_BROWSE_URL,
|
hass.services.register(DOMAIN, SERVICE_BROWSE_URL,
|
||||||
lambda service:
|
lambda service:
|
||||||
webbrowser.open(
|
webbrowser.open(service.data[ATTR_URL]),
|
||||||
service.data.get(
|
schema=SERVICE_BROWSE_URL_SCHEMA)
|
||||||
'url', 'https://www.google.com')))
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -8,9 +8,12 @@ import logging
|
|||||||
import re
|
import re
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant import core
|
from homeassistant import core
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON)
|
ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON)
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
DOMAIN = "conversation"
|
DOMAIN = "conversation"
|
||||||
|
|
||||||
@ -18,6 +21,10 @@ SERVICE_PROCESS = "process"
|
|||||||
|
|
||||||
ATTR_TEXT = "text"
|
ATTR_TEXT = "text"
|
||||||
|
|
||||||
|
SERVICE_PROCESS_SCHEMA = vol.Schema({
|
||||||
|
vol.Required(ATTR_TEXT): vol.All(cv.string, vol.Lower),
|
||||||
|
})
|
||||||
|
|
||||||
REGEX_TURN_COMMAND = re.compile(r'turn (?P<name>(?: |\w)+) (?P<command>\w+)')
|
REGEX_TURN_COMMAND = re.compile(r'turn (?P<name>(?: |\w)+) (?P<command>\w+)')
|
||||||
|
|
||||||
REQUIREMENTS = ['fuzzywuzzy==0.8.0']
|
REQUIREMENTS = ['fuzzywuzzy==0.8.0']
|
||||||
@ -32,11 +39,7 @@ def setup(hass, config):
|
|||||||
|
|
||||||
def process(service):
|
def process(service):
|
||||||
"""Parse text into commands."""
|
"""Parse text into commands."""
|
||||||
if ATTR_TEXT not in service.data:
|
text = service.data[ATTR_TEXT]
|
||||||
logger.error("Received process service call without a text")
|
|
||||||
return
|
|
||||||
|
|
||||||
text = service.data[ATTR_TEXT].lower()
|
|
||||||
match = REGEX_TURN_COMMAND.match(text)
|
match = REGEX_TURN_COMMAND.match(text)
|
||||||
|
|
||||||
if not match:
|
if not match:
|
||||||
@ -67,6 +70,6 @@ def setup(hass, config):
|
|||||||
logger.error(
|
logger.error(
|
||||||
'Got unsupported command %s from text %s', command, text)
|
'Got unsupported command %s from text %s', command, text)
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_PROCESS, process)
|
hass.services.register(DOMAIN, SERVICE_PROCESS, process,
|
||||||
|
schema=SERVICE_PROCESS_SCHEMA)
|
||||||
return True
|
return True
|
||||||
|
@ -10,8 +10,10 @@ import re
|
|||||||
import threading
|
import threading
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.helpers import validate_config
|
from homeassistant.helpers import validate_config
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.util import sanitize_filename
|
from homeassistant.util import sanitize_filename
|
||||||
|
|
||||||
DOMAIN = "downloader"
|
DOMAIN = "downloader"
|
||||||
@ -21,6 +23,11 @@ SERVICE_DOWNLOAD_FILE = "download_file"
|
|||||||
ATTR_URL = "url"
|
ATTR_URL = "url"
|
||||||
ATTR_SUBDIR = "subdir"
|
ATTR_SUBDIR = "subdir"
|
||||||
|
|
||||||
|
SERVICE_DOWNLOAD_FILE_SCHEMA = vol.Schema({
|
||||||
|
vol.Required(ATTR_URL): vol.Url,
|
||||||
|
vol.Optional(ATTR_SUBDIR): cv.string,
|
||||||
|
})
|
||||||
|
|
||||||
CONF_DOWNLOAD_DIR = 'download_dir'
|
CONF_DOWNLOAD_DIR = 'download_dir'
|
||||||
|
|
||||||
|
|
||||||
@ -48,10 +55,6 @@ def setup(hass, config):
|
|||||||
|
|
||||||
def download_file(service):
|
def download_file(service):
|
||||||
"""Start thread to download file specified in the URL."""
|
"""Start thread to download file specified in the URL."""
|
||||||
if ATTR_URL not in service.data:
|
|
||||||
logger.error("Service called but 'url' parameter not specified.")
|
|
||||||
return
|
|
||||||
|
|
||||||
def do_download():
|
def do_download():
|
||||||
"""Download the file."""
|
"""Download the file."""
|
||||||
try:
|
try:
|
||||||
@ -127,7 +130,7 @@ def setup(hass, config):
|
|||||||
|
|
||||||
threading.Thread(target=do_download).start()
|
threading.Thread(target=do_download).start()
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_DOWNLOAD_FILE,
|
hass.services.register(DOMAIN, SERVICE_DOWNLOAD_FILE, download_file,
|
||||||
download_file)
|
schema=SERVICE_DOWNLOAD_FILE_SCHEMA)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -7,10 +7,13 @@ at https://home-assistant.io/components/garage_door/
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.config import load_yaml_config_file
|
from homeassistant.config import load_yaml_config_file
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
STATE_CLOSED, STATE_OPEN, STATE_UNKNOWN, SERVICE_CLOSE, SERVICE_OPEN,
|
STATE_CLOSED, STATE_OPEN, STATE_UNKNOWN, SERVICE_CLOSE, SERVICE_OPEN,
|
||||||
ATTR_ENTITY_ID)
|
ATTR_ENTITY_ID)
|
||||||
@ -29,6 +32,10 @@ DISCOVERY_PLATFORMS = {
|
|||||||
wink.DISCOVER_GARAGE_DOORS: 'wink'
|
wink.DISCOVER_GARAGE_DOORS: 'wink'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GARAGE_DOOR_SERVICE_SCHEMA = vol.Schema({
|
||||||
|
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
||||||
|
})
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -73,10 +80,11 @@ def setup(hass, config):
|
|||||||
descriptions = load_yaml_config_file(
|
descriptions = load_yaml_config_file(
|
||||||
os.path.join(os.path.dirname(__file__), 'services.yaml'))
|
os.path.join(os.path.dirname(__file__), 'services.yaml'))
|
||||||
hass.services.register(DOMAIN, SERVICE_OPEN, handle_garage_door_service,
|
hass.services.register(DOMAIN, SERVICE_OPEN, handle_garage_door_service,
|
||||||
descriptions.get(SERVICE_OPEN))
|
descriptions.get(SERVICE_OPEN),
|
||||||
|
schema=GARAGE_DOOR_SERVICE_SCHEMA)
|
||||||
hass.services.register(DOMAIN, SERVICE_CLOSE, handle_garage_door_service,
|
hass.services.register(DOMAIN, SERVICE_CLOSE, handle_garage_door_service,
|
||||||
descriptions.get(SERVICE_CLOSE))
|
descriptions.get(SERVICE_CLOSE),
|
||||||
|
schema=GARAGE_DOOR_SERVICE_SCHEMA)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,8 +7,10 @@ https://home-assistant.io/components/ifttt/
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.helpers import validate_config
|
from homeassistant.helpers import validate_config
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -23,6 +25,13 @@ ATTR_VALUE3 = 'value3'
|
|||||||
|
|
||||||
REQUIREMENTS = ['pyfttt==0.3']
|
REQUIREMENTS = ['pyfttt==0.3']
|
||||||
|
|
||||||
|
SERVICE_TRIGGER_SCHEMA = vol.Schema({
|
||||||
|
vol.Required(ATTR_EVENT): cv.string,
|
||||||
|
vol.Optional(ATTR_VALUE1): cv.string,
|
||||||
|
vol.Optional(ATTR_VALUE2): cv.string,
|
||||||
|
vol.Optional(ATTR_VALUE3): cv.string,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
def trigger(hass, event, value1=None, value2=None, value3=None):
|
def trigger(hass, event, value1=None, value2=None, value3=None):
|
||||||
"""Trigger a Maker IFTTT recipe."""
|
"""Trigger a Maker IFTTT recipe."""
|
||||||
@ -44,12 +53,10 @@ def setup(hass, config):
|
|||||||
|
|
||||||
def trigger_service(call):
|
def trigger_service(call):
|
||||||
"""Handle IFTTT trigger service calls."""
|
"""Handle IFTTT trigger service calls."""
|
||||||
event = call.data.get(ATTR_EVENT)
|
event = call.data[ATTR_EVENT]
|
||||||
value1 = call.data.get(ATTR_VALUE1)
|
value1 = call.data.get(ATTR_VALUE1)
|
||||||
value2 = call.data.get(ATTR_VALUE2)
|
value2 = call.data.get(ATTR_VALUE2)
|
||||||
value3 = call.data.get(ATTR_VALUE3)
|
value3 = call.data.get(ATTR_VALUE3)
|
||||||
if event is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import pyfttt as pyfttt
|
import pyfttt as pyfttt
|
||||||
@ -57,6 +64,7 @@ def setup(hass, config):
|
|||||||
except requests.exceptions.RequestException:
|
except requests.exceptions.RequestException:
|
||||||
_LOGGER.exception("Error communicating with IFTTT")
|
_LOGGER.exception("Error communicating with IFTTT")
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_TRIGGER, trigger_service)
|
hass.services.register(DOMAIN, SERVICE_TRIGGER, trigger_service,
|
||||||
|
schema=SERVICE_TRIGGER_SCHEMA)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -6,8 +6,11 @@ at https://home-assistant.io/components/input_boolean/
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_ON)
|
ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_ON)
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.entity import ToggleEntity
|
from homeassistant.helpers.entity import ToggleEntity
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
from homeassistant.util import slugify
|
from homeassistant.util import slugify
|
||||||
@ -22,6 +25,10 @@ CONF_NAME = "name"
|
|||||||
CONF_INITIAL = "initial"
|
CONF_INITIAL = "initial"
|
||||||
CONF_ICON = "icon"
|
CONF_ICON = "icon"
|
||||||
|
|
||||||
|
TOGGLE_SERVICE_SCHEMA = vol.Schema({
|
||||||
|
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
def is_on(hass, entity_id):
|
def is_on(hass, entity_id):
|
||||||
"""Test if input_boolean is True."""
|
"""Test if input_boolean is True."""
|
||||||
@ -75,8 +82,10 @@ def setup(hass, config):
|
|||||||
else:
|
else:
|
||||||
input_b.turn_off()
|
input_b.turn_off()
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_TURN_OFF, toggle_service)
|
hass.services.register(DOMAIN, SERVICE_TURN_OFF, toggle_service,
|
||||||
hass.services.register(DOMAIN, SERVICE_TURN_ON, toggle_service)
|
schema=TOGGLE_SERVICE_SCHEMA)
|
||||||
|
hass.services.register(DOMAIN, SERVICE_TURN_ON, toggle_service,
|
||||||
|
schema=TOGGLE_SERVICE_SCHEMA)
|
||||||
|
|
||||||
component.add_entities(entities)
|
component.add_entities(entities)
|
||||||
|
|
||||||
|
@ -6,7 +6,10 @@ at https://home-assistant.io/components/input_select/
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.const import ATTR_ENTITY_ID
|
from homeassistant.const import ATTR_ENTITY_ID
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
from homeassistant.util import slugify
|
from homeassistant.util import slugify
|
||||||
@ -25,6 +28,11 @@ ATTR_OPTIONS = 'options'
|
|||||||
|
|
||||||
SERVICE_SELECT_OPTION = 'select_option'
|
SERVICE_SELECT_OPTION = 'select_option'
|
||||||
|
|
||||||
|
SERVICE_SELECT_OPTION_SCHEMA = vol.Schema({
|
||||||
|
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
||||||
|
vol.Required(ATTR_OPTION): cv.string,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
def select_option(hass, entity_id, option):
|
def select_option(hass, entity_id, option):
|
||||||
"""Set input_select to False."""
|
"""Set input_select to False."""
|
||||||
@ -79,10 +87,11 @@ def setup(hass, config):
|
|||||||
target_inputs = component.extract_from_service(call)
|
target_inputs = component.extract_from_service(call)
|
||||||
|
|
||||||
for input_select in target_inputs:
|
for input_select in target_inputs:
|
||||||
input_select.select_option(call.data.get(ATTR_OPTION))
|
input_select.select_option(call.data[ATTR_OPTION])
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_SELECT_OPTION,
|
hass.services.register(DOMAIN, SERVICE_SELECT_OPTION,
|
||||||
select_option_service)
|
select_option_service,
|
||||||
|
schema=SERVICE_SELECT_OPTION_SCHEMA)
|
||||||
|
|
||||||
component.add_entities(entities)
|
component.add_entities(entities)
|
||||||
|
|
||||||
|
@ -6,7 +6,10 @@ at https://home-assistant.io/components/input_slider/
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.const import ATTR_ENTITY_ID
|
from homeassistant.const import ATTR_ENTITY_ID
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
from homeassistant.util import slugify
|
from homeassistant.util import slugify
|
||||||
@ -29,6 +32,11 @@ ATTR_STEP = 'step'
|
|||||||
|
|
||||||
SERVICE_SELECT_VALUE = 'select_value'
|
SERVICE_SELECT_VALUE = 'select_value'
|
||||||
|
|
||||||
|
SERVICE_SELECT_VALUE_SCHEMA = vol.Schema({
|
||||||
|
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
||||||
|
vol.Required(ATTR_VALUE): vol.Coerce(int),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
def select_value(hass, entity_id, value):
|
def select_value(hass, entity_id, value):
|
||||||
"""Set input_slider to value."""
|
"""Set input_slider to value."""
|
||||||
@ -81,10 +89,11 @@ def setup(hass, config):
|
|||||||
target_inputs = component.extract_from_service(call)
|
target_inputs = component.extract_from_service(call)
|
||||||
|
|
||||||
for input_slider in target_inputs:
|
for input_slider in target_inputs:
|
||||||
input_slider.select_value(call.data.get(ATTR_VALUE))
|
input_slider.select_value(call.data[ATTR_VALUE])
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_SELECT_VALUE,
|
hass.services.register(DOMAIN, SERVICE_SELECT_VALUE,
|
||||||
select_value_service)
|
select_value_service,
|
||||||
|
schema=SERVICE_SELECT_VALUE_SCHEMA)
|
||||||
|
|
||||||
component.add_entities(entities)
|
component.add_entities(entities)
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ Provides functionality to emulate keyboard presses on host machine.
|
|||||||
For more details about this component, please refer to the documentation at
|
For more details about this component, please refer to the documentation at
|
||||||
https://home-assistant.io/components/keyboard/
|
https://home-assistant.io/components/keyboard/
|
||||||
"""
|
"""
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PLAY_PAUSE,
|
SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PLAY_PAUSE,
|
||||||
SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_VOLUME_DOWN, SERVICE_VOLUME_MUTE,
|
SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_VOLUME_DOWN, SERVICE_VOLUME_MUTE,
|
||||||
@ -12,6 +14,8 @@ from homeassistant.const import (
|
|||||||
DOMAIN = "keyboard"
|
DOMAIN = "keyboard"
|
||||||
REQUIREMENTS = ['pyuserinput==0.1.9']
|
REQUIREMENTS = ['pyuserinput==0.1.9']
|
||||||
|
|
||||||
|
TAP_KEY_SCHEMA = vol.Schema({})
|
||||||
|
|
||||||
|
|
||||||
def volume_up(hass):
|
def volume_up(hass):
|
||||||
"""Press the keyboard button for volume up."""
|
"""Press the keyboard button for volume up."""
|
||||||
@ -52,26 +56,31 @@ def setup(hass, config):
|
|||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_VOLUME_UP,
|
hass.services.register(DOMAIN, SERVICE_VOLUME_UP,
|
||||||
lambda service:
|
lambda service:
|
||||||
keyboard.tap_key(keyboard.volume_up_key))
|
keyboard.tap_key(keyboard.volume_up_key),
|
||||||
|
schema=TAP_KEY_SCHEMA)
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_VOLUME_DOWN,
|
hass.services.register(DOMAIN, SERVICE_VOLUME_DOWN,
|
||||||
lambda service:
|
lambda service:
|
||||||
keyboard.tap_key(keyboard.volume_down_key))
|
keyboard.tap_key(keyboard.volume_down_key),
|
||||||
|
schema=TAP_KEY_SCHEMA)
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_VOLUME_MUTE,
|
hass.services.register(DOMAIN, SERVICE_VOLUME_MUTE,
|
||||||
lambda service:
|
lambda service:
|
||||||
keyboard.tap_key(keyboard.volume_mute_key))
|
keyboard.tap_key(keyboard.volume_mute_key),
|
||||||
|
schema=TAP_KEY_SCHEMA)
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_MEDIA_PLAY_PAUSE,
|
hass.services.register(DOMAIN, SERVICE_MEDIA_PLAY_PAUSE,
|
||||||
lambda service:
|
lambda service:
|
||||||
keyboard.tap_key(keyboard.media_play_pause_key))
|
keyboard.tap_key(keyboard.media_play_pause_key),
|
||||||
|
schema=TAP_KEY_SCHEMA)
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_MEDIA_NEXT_TRACK,
|
hass.services.register(DOMAIN, SERVICE_MEDIA_NEXT_TRACK,
|
||||||
lambda service:
|
lambda service:
|
||||||
keyboard.tap_key(keyboard.media_next_track_key))
|
keyboard.tap_key(keyboard.media_next_track_key),
|
||||||
|
schema=TAP_KEY_SCHEMA)
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_MEDIA_PREVIOUS_TRACK,
|
hass.services.register(DOMAIN, SERVICE_MEDIA_PREVIOUS_TRACK,
|
||||||
lambda service:
|
lambda service:
|
||||||
keyboard.tap_key(keyboard.media_prev_track_key))
|
keyboard.tap_key(keyboard.media_prev_track_key),
|
||||||
|
schema=TAP_KEY_SCHEMA)
|
||||||
return True
|
return True
|
||||||
|
@ -8,10 +8,13 @@ from datetime import timedelta
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.config import load_yaml_config_file
|
from homeassistant.config import load_yaml_config_file
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_CODE, ATTR_CODE_FORMAT, ATTR_ENTITY_ID, STATE_LOCKED, STATE_UNLOCKED,
|
ATTR_CODE, ATTR_CODE_FORMAT, ATTR_ENTITY_ID, STATE_LOCKED, STATE_UNLOCKED,
|
||||||
STATE_UNKNOWN, SERVICE_LOCK, SERVICE_UNLOCK)
|
STATE_UNKNOWN, SERVICE_LOCK, SERVICE_UNLOCK)
|
||||||
@ -33,6 +36,11 @@ DISCOVERY_PLATFORMS = {
|
|||||||
verisure.DISCOVER_LOCKS: 'verisure'
|
verisure.DISCOVER_LOCKS: 'verisure'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOCK_SERVICE_SCHEMA = vol.Schema({
|
||||||
|
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
||||||
|
vol.Optional(ATTR_CODE): cv.string,
|
||||||
|
})
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -75,10 +83,7 @@ def setup(hass, config):
|
|||||||
"""Handle calls to the lock services."""
|
"""Handle calls to the lock services."""
|
||||||
target_locks = component.extract_from_service(service)
|
target_locks = component.extract_from_service(service)
|
||||||
|
|
||||||
if ATTR_CODE not in service.data:
|
code = service.data.get(ATTR_CODE)
|
||||||
code = None
|
|
||||||
else:
|
|
||||||
code = service.data[ATTR_CODE]
|
|
||||||
|
|
||||||
for item in target_locks:
|
for item in target_locks:
|
||||||
if service.service == SERVICE_LOCK:
|
if service.service == SERVICE_LOCK:
|
||||||
@ -92,10 +97,11 @@ def setup(hass, config):
|
|||||||
descriptions = load_yaml_config_file(
|
descriptions = load_yaml_config_file(
|
||||||
os.path.join(os.path.dirname(__file__), 'services.yaml'))
|
os.path.join(os.path.dirname(__file__), 'services.yaml'))
|
||||||
hass.services.register(DOMAIN, SERVICE_UNLOCK, handle_lock_service,
|
hass.services.register(DOMAIN, SERVICE_UNLOCK, handle_lock_service,
|
||||||
descriptions.get(SERVICE_UNLOCK))
|
descriptions.get(SERVICE_UNLOCK),
|
||||||
|
schema=LOCK_SERVICE_SCHEMA)
|
||||||
hass.services.register(DOMAIN, SERVICE_LOCK, handle_lock_service,
|
hass.services.register(DOMAIN, SERVICE_LOCK, handle_lock_service,
|
||||||
descriptions.get(SERVICE_LOCK))
|
descriptions.get(SERVICE_LOCK),
|
||||||
|
schema=LOCK_SERVICE_SCHEMA)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,6 +9,8 @@ import re
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
from homeassistant.components import recorder, sun
|
from homeassistant.components import recorder, sun
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
@ -18,6 +20,7 @@ from homeassistant.core import DOMAIN as HA_DOMAIN
|
|||||||
from homeassistant.core import State
|
from homeassistant.core import State
|
||||||
from homeassistant.helpers.entity import split_entity_id
|
from homeassistant.helpers.entity import split_entity_id
|
||||||
from homeassistant.helpers import template
|
from homeassistant.helpers import template
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
DOMAIN = "logbook"
|
DOMAIN = "logbook"
|
||||||
DEPENDENCIES = ['recorder', 'http']
|
DEPENDENCIES = ['recorder', 'http']
|
||||||
@ -39,6 +42,13 @@ ATTR_MESSAGE = 'message'
|
|||||||
ATTR_DOMAIN = 'domain'
|
ATTR_DOMAIN = 'domain'
|
||||||
ATTR_ENTITY_ID = 'entity_id'
|
ATTR_ENTITY_ID = 'entity_id'
|
||||||
|
|
||||||
|
LOG_MESSAGE_SCHEMA = vol.Schema({
|
||||||
|
vol.Required(ATTR_NAME): cv.string,
|
||||||
|
vol.Required(ATTR_MESSAGE): cv.string,
|
||||||
|
vol.Optional(ATTR_DOMAIN): cv.slug,
|
||||||
|
vol.Optional(ATTR_ENTITY_ID): cv.entity_id,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
def log_entry(hass, name, message, domain=None, entity_id=None):
|
def log_entry(hass, name, message, domain=None, entity_id=None):
|
||||||
"""Add an entry to the logbook."""
|
"""Add an entry to the logbook."""
|
||||||
@ -58,19 +68,17 @@ def setup(hass, config):
|
|||||||
"""Listen for download events to download files."""
|
"""Listen for download events to download files."""
|
||||||
def log_message(service):
|
def log_message(service):
|
||||||
"""Handle sending notification message service calls."""
|
"""Handle sending notification message service calls."""
|
||||||
message = service.data.get(ATTR_MESSAGE)
|
message = service.data[ATTR_MESSAGE]
|
||||||
name = service.data.get(ATTR_NAME)
|
name = service.data[ATTR_NAME]
|
||||||
domain = service.data.get(ATTR_DOMAIN, None)
|
domain = service.data.get(ATTR_DOMAIN)
|
||||||
entity_id = service.data.get(ATTR_ENTITY_ID, None)
|
entity_id = service.data.get(ATTR_ENTITY_ID)
|
||||||
|
|
||||||
if not message or not name:
|
|
||||||
return
|
|
||||||
|
|
||||||
message = template.render(hass, message)
|
message = template.render(hass, message)
|
||||||
log_entry(hass, name, message, domain, entity_id)
|
log_entry(hass, name, message, domain, entity_id)
|
||||||
|
|
||||||
hass.http.register_path('GET', URL_LOGBOOK, _handle_get_logbook)
|
hass.http.register_path('GET', URL_LOGBOOK, _handle_get_logbook)
|
||||||
hass.services.register(DOMAIN, 'log', log_message)
|
hass.services.register(DOMAIN, 'log', log_message,
|
||||||
|
schema=LOG_MESSAGE_SCHEMA)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,11 +8,13 @@ from functools import partial
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
import homeassistant.bootstrap as bootstrap
|
import homeassistant.bootstrap as bootstrap
|
||||||
from homeassistant.config import load_yaml_config_file
|
from homeassistant.config import load_yaml_config_file
|
||||||
from homeassistant.helpers import config_per_platform, template
|
from homeassistant.helpers import config_per_platform, template
|
||||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.const import CONF_NAME
|
from homeassistant.const import CONF_NAME
|
||||||
|
|
||||||
DOMAIN = "notify"
|
DOMAIN = "notify"
|
||||||
@ -32,6 +34,13 @@ ATTR_DATA = 'data'
|
|||||||
|
|
||||||
SERVICE_NOTIFY = "notify"
|
SERVICE_NOTIFY = "notify"
|
||||||
|
|
||||||
|
NOTIFY_SERVICE_SCHEMA = vol.Schema({
|
||||||
|
vol.Required(ATTR_MESSAGE): cv.template,
|
||||||
|
vol.Optional(ATTR_TITLE, default=ATTR_TITLE_DEFAULT): cv.string,
|
||||||
|
vol.Optional(ATTR_TARGET): cv.string,
|
||||||
|
vol.Optional(ATTR_DATA): dict, # nobody seems to be using this (yet)
|
||||||
|
})
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -71,13 +80,7 @@ def setup(hass, config):
|
|||||||
|
|
||||||
def notify_message(notify_service, call):
|
def notify_message(notify_service, call):
|
||||||
"""Handle sending notification message service calls."""
|
"""Handle sending notification message service calls."""
|
||||||
message = call.data.get(ATTR_MESSAGE)
|
message = call.data[ATTR_MESSAGE]
|
||||||
|
|
||||||
if message is None:
|
|
||||||
_LOGGER.error(
|
|
||||||
'Received call to %s without attribute %s',
|
|
||||||
call.service, ATTR_MESSAGE)
|
|
||||||
return
|
|
||||||
|
|
||||||
title = template.render(
|
title = template.render(
|
||||||
hass, call.data.get(ATTR_TITLE, ATTR_TITLE_DEFAULT))
|
hass, call.data.get(ATTR_TITLE, ATTR_TITLE_DEFAULT))
|
||||||
@ -91,7 +94,8 @@ def setup(hass, config):
|
|||||||
service_call_handler = partial(notify_message, notify_service)
|
service_call_handler = partial(notify_message, notify_service)
|
||||||
service_notify = p_config.get(CONF_NAME, SERVICE_NOTIFY)
|
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))
|
descriptions.get(SERVICE_NOTIFY),
|
||||||
|
schema=NOTIFY_SERVICE_SCHEMA)
|
||||||
success = True
|
success = True
|
||||||
|
|
||||||
return success
|
return success
|
||||||
|
@ -7,10 +7,13 @@ https://home-assistant.io/components/rollershutter/
|
|||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.config import load_yaml_config_file
|
from homeassistant.config import load_yaml_config_file
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.components import group
|
from homeassistant.components import group
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
SERVICE_MOVE_UP, SERVICE_MOVE_DOWN, SERVICE_STOP,
|
SERVICE_MOVE_UP, SERVICE_MOVE_DOWN, SERVICE_STOP,
|
||||||
@ -33,6 +36,10 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
ATTR_CURRENT_POSITION = 'current_position'
|
ATTR_CURRENT_POSITION = 'current_position'
|
||||||
|
|
||||||
|
ROLLERSHUTTER_SERVICE_SCHEMA = vol.Schema({
|
||||||
|
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
def is_open(hass, entity_id=None):
|
def is_open(hass, entity_id=None):
|
||||||
"""Return if the roller shutter is open based on the statemachine."""
|
"""Return if the roller shutter is open based on the statemachine."""
|
||||||
@ -85,14 +92,16 @@ def setup(hass, config):
|
|||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_MOVE_UP,
|
hass.services.register(DOMAIN, SERVICE_MOVE_UP,
|
||||||
handle_rollershutter_service,
|
handle_rollershutter_service,
|
||||||
descriptions.get(SERVICE_MOVE_UP))
|
descriptions.get(SERVICE_MOVE_UP),
|
||||||
|
schema=ROLLERSHUTTER_SERVICE_SCHEMA)
|
||||||
hass.services.register(DOMAIN, SERVICE_MOVE_DOWN,
|
hass.services.register(DOMAIN, SERVICE_MOVE_DOWN,
|
||||||
handle_rollershutter_service,
|
handle_rollershutter_service,
|
||||||
descriptions.get(SERVICE_MOVE_DOWN))
|
descriptions.get(SERVICE_MOVE_DOWN),
|
||||||
|
schema=ROLLERSHUTTER_SERVICE_SCHEMA)
|
||||||
hass.services.register(DOMAIN, SERVICE_STOP,
|
hass.services.register(DOMAIN, SERVICE_STOP,
|
||||||
handle_rollershutter_service,
|
handle_rollershutter_service,
|
||||||
descriptions.get(SERVICE_STOP))
|
descriptions.get(SERVICE_STOP),
|
||||||
|
schema=ROLLERSHUTTER_SERVICE_SCHEMA)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,9 +7,12 @@ https://home-assistant.io/components/scene/
|
|||||||
import logging
|
import logging
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID, SERVICE_TURN_ON, CONF_PLATFORM)
|
ATTR_ENTITY_ID, SERVICE_TURN_ON, CONF_PLATFORM)
|
||||||
from homeassistant.helpers import extract_domain_configs
|
from homeassistant.helpers import extract_domain_configs
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
|
|
||||||
@ -19,6 +22,10 @@ STATE = 'scening'
|
|||||||
|
|
||||||
CONF_ENTITIES = "entities"
|
CONF_ENTITIES = "entities"
|
||||||
|
|
||||||
|
SCENE_SERVICE_SCHEMA = vol.Schema({
|
||||||
|
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
||||||
|
})
|
||||||
|
|
||||||
SceneConfig = namedtuple('SceneConfig', ['name', 'states'])
|
SceneConfig = namedtuple('SceneConfig', ['name', 'states'])
|
||||||
|
|
||||||
|
|
||||||
@ -61,7 +68,8 @@ def setup(hass, config):
|
|||||||
for scene in target_scenes:
|
for scene in target_scenes:
|
||||||
scene.activate()
|
scene.activate()
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_scene_service)
|
hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_scene_service,
|
||||||
|
schema=SCENE_SERVICE_SCHEMA)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -109,6 +109,11 @@ CONFIG_SCHEMA = vol.Schema({
|
|||||||
vol.Required(DOMAIN): {cv.slug: _SCRIPT_ENTRY_SCHEMA}
|
vol.Required(DOMAIN): {cv.slug: _SCRIPT_ENTRY_SCHEMA}
|
||||||
}, extra=vol.ALLOW_EXTRA)
|
}, extra=vol.ALLOW_EXTRA)
|
||||||
|
|
||||||
|
SCRIPT_SERVICE_SCHEMA = vol.Schema({})
|
||||||
|
SCRIPT_TURN_ONOFF_SCHEMA = vol.Schema({
|
||||||
|
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
def is_on(hass, entity_id):
|
def is_on(hass, entity_id):
|
||||||
"""Return if the switch is on based on the statemachine."""
|
"""Return if the switch is on based on the statemachine."""
|
||||||
@ -149,7 +154,8 @@ def setup(hass, config):
|
|||||||
alias = cfg.get(CONF_ALIAS, object_id)
|
alias = cfg.get(CONF_ALIAS, object_id)
|
||||||
script = Script(object_id, alias, cfg[CONF_SEQUENCE])
|
script = Script(object_id, alias, cfg[CONF_SEQUENCE])
|
||||||
component.add_entities((script,))
|
component.add_entities((script,))
|
||||||
hass.services.register(DOMAIN, object_id, service_handler)
|
hass.services.register(DOMAIN, object_id, service_handler,
|
||||||
|
schema=SCRIPT_SERVICE_SCHEMA)
|
||||||
|
|
||||||
def turn_on_service(service):
|
def turn_on_service(service):
|
||||||
"""Call a service to turn script on."""
|
"""Call a service to turn script on."""
|
||||||
@ -168,10 +174,12 @@ def setup(hass, config):
|
|||||||
for script in component.extract_from_service(service):
|
for script in component.extract_from_service(service):
|
||||||
script.toggle()
|
script.toggle()
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_TURN_ON, turn_on_service)
|
hass.services.register(DOMAIN, SERVICE_TURN_ON, turn_on_service,
|
||||||
hass.services.register(DOMAIN, SERVICE_TURN_OFF, turn_off_service)
|
schema=SCRIPT_TURN_ONOFF_SCHEMA)
|
||||||
hass.services.register(DOMAIN, SERVICE_TOGGLE, toggle_service)
|
hass.services.register(DOMAIN, SERVICE_TURN_OFF, turn_off_service,
|
||||||
|
schema=SCRIPT_TURN_ONOFF_SCHEMA)
|
||||||
|
hass.services.register(DOMAIN, SERVICE_TOGGLE, toggle_service,
|
||||||
|
schema=SCRIPT_TURN_ONOFF_SCHEMA)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,26 +7,26 @@ https://home-assistant.io/components/shell_command/
|
|||||||
import logging
|
import logging
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from homeassistant.util import slugify
|
import voluptuous as vol
|
||||||
|
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
DOMAIN = 'shell_command'
|
DOMAIN = 'shell_command'
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = vol.Schema({
|
||||||
|
DOMAIN: vol.Schema({
|
||||||
|
cv.slug: cv.string,
|
||||||
|
}),
|
||||||
|
}, extra=vol.ALLOW_EXTRA)
|
||||||
|
|
||||||
|
SHELL_COMMAND_SCHEMA = vol.Schema({})
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
"""Setup the shell_command component."""
|
"""Setup the shell_command component."""
|
||||||
conf = config.get(DOMAIN)
|
conf = config.get(DOMAIN, {})
|
||||||
|
|
||||||
if not isinstance(conf, dict):
|
|
||||||
_LOGGER.error('Expected configuration to be a dictionary')
|
|
||||||
return False
|
|
||||||
|
|
||||||
for name in conf.keys():
|
|
||||||
if name != slugify(name):
|
|
||||||
_LOGGER.error('Invalid service name: %s. Try %s',
|
|
||||||
name, slugify(name))
|
|
||||||
return False
|
|
||||||
|
|
||||||
def service_handler(call):
|
def service_handler(call):
|
||||||
"""Execute a shell command service."""
|
"""Execute a shell command service."""
|
||||||
@ -38,6 +38,6 @@ def setup(hass, config):
|
|||||||
_LOGGER.exception('Error running command')
|
_LOGGER.exception('Error running command')
|
||||||
|
|
||||||
for name in conf.keys():
|
for name in conf.keys():
|
||||||
hass.services.register(DOMAIN, name, service_handler)
|
hass.services.register(DOMAIN, name, service_handler,
|
||||||
|
schema=SHELL_COMMAND_SCHEMA)
|
||||||
return True
|
return True
|
||||||
|
@ -8,10 +8,13 @@ from datetime import timedelta
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.config import load_yaml_config_file
|
from homeassistant.config import load_yaml_config_file
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
from homeassistant.helpers.entity import ToggleEntity
|
from homeassistant.helpers.entity import ToggleEntity
|
||||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE,
|
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE,
|
||||||
ATTR_ENTITY_ID)
|
ATTR_ENTITY_ID)
|
||||||
@ -50,6 +53,10 @@ PROP_TO_ATTR = {
|
|||||||
'today_power_mw': ATTR_TODAY_MWH,
|
'today_power_mw': ATTR_TODAY_MWH,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SWITCH_SERVICE_SCHEMA = vol.Schema({
|
||||||
|
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
||||||
|
})
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -102,11 +109,14 @@ def setup(hass, config):
|
|||||||
descriptions = load_yaml_config_file(
|
descriptions = load_yaml_config_file(
|
||||||
os.path.join(os.path.dirname(__file__), 'services.yaml'))
|
os.path.join(os.path.dirname(__file__), 'services.yaml'))
|
||||||
hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_switch_service,
|
hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_switch_service,
|
||||||
descriptions.get(SERVICE_TURN_OFF))
|
descriptions.get(SERVICE_TURN_OFF),
|
||||||
|
schema=SWITCH_SERVICE_SCHEMA)
|
||||||
hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_switch_service,
|
hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_switch_service,
|
||||||
descriptions.get(SERVICE_TURN_ON))
|
descriptions.get(SERVICE_TURN_ON),
|
||||||
|
schema=SWITCH_SERVICE_SCHEMA)
|
||||||
hass.services.register(DOMAIN, SERVICE_TOGGLE, handle_switch_service,
|
hass.services.register(DOMAIN, SERVICE_TOGGLE, handle_switch_service,
|
||||||
descriptions.get(SERVICE_TOGGLE))
|
descriptions.get(SERVICE_TOGGLE),
|
||||||
|
schema=SWITCH_SERVICE_SCHEMA)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -7,14 +7,16 @@ https://home-assistant.io/components/thermostat/
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
|
|
||||||
from homeassistant.config import load_yaml_config_file
|
from homeassistant.config import load_yaml_config_file
|
||||||
import homeassistant.util as util
|
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.helpers.temperature import convert
|
from homeassistant.helpers.temperature import convert
|
||||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
||||||
from homeassistant.components import (ecobee, zwave)
|
from homeassistant.components import (ecobee, zwave)
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID, ATTR_TEMPERATURE, STATE_ON, STATE_OFF, STATE_UNKNOWN,
|
ATTR_ENTITY_ID, ATTR_TEMPERATURE, STATE_ON, STATE_OFF, STATE_UNKNOWN,
|
||||||
TEMP_CELCIUS)
|
TEMP_CELCIUS)
|
||||||
@ -48,6 +50,19 @@ DISCOVERY_PLATFORMS = {
|
|||||||
zwave.DISCOVER_THERMOSTATS: 'zwave'
|
zwave.DISCOVER_THERMOSTATS: 'zwave'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SET_AWAY_MODE_SCHEMA = vol.Schema({
|
||||||
|
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
||||||
|
vol.Required(ATTR_AWAY_MODE): cv.boolean,
|
||||||
|
})
|
||||||
|
SET_TEMPERATURE_SCHEMA = vol.Schema({
|
||||||
|
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
||||||
|
vol.Required(ATTR_TEMPERATURE): vol.Coerce(float),
|
||||||
|
})
|
||||||
|
SET_FAN_MODE_SCHEMA = vol.Schema({
|
||||||
|
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
||||||
|
vol.Required(ATTR_FAN): cv.boolean,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
def set_away_mode(hass, away_mode, entity_id=None):
|
def set_away_mode(hass, away_mode, entity_id=None):
|
||||||
"""Turn all or specified thermostat away mode on."""
|
"""Turn all or specified thermostat away mode on."""
|
||||||
@ -97,13 +112,7 @@ def setup(hass, config):
|
|||||||
"""Set away mode on target thermostats."""
|
"""Set away mode on target thermostats."""
|
||||||
target_thermostats = component.extract_from_service(service)
|
target_thermostats = component.extract_from_service(service)
|
||||||
|
|
||||||
away_mode = service.data.get(ATTR_AWAY_MODE)
|
away_mode = service.data[ATTR_AWAY_MODE]
|
||||||
|
|
||||||
if away_mode is None:
|
|
||||||
_LOGGER.error(
|
|
||||||
"Received call to %s without attribute %s",
|
|
||||||
SERVICE_SET_AWAY_MODE, ATTR_AWAY_MODE)
|
|
||||||
return
|
|
||||||
|
|
||||||
for thermostat in target_thermostats:
|
for thermostat in target_thermostats:
|
||||||
if away_mode:
|
if away_mode:
|
||||||
@ -115,20 +124,14 @@ def setup(hass, config):
|
|||||||
|
|
||||||
hass.services.register(
|
hass.services.register(
|
||||||
DOMAIN, SERVICE_SET_AWAY_MODE, away_mode_set_service,
|
DOMAIN, SERVICE_SET_AWAY_MODE, away_mode_set_service,
|
||||||
descriptions.get(SERVICE_SET_AWAY_MODE))
|
descriptions.get(SERVICE_SET_AWAY_MODE),
|
||||||
|
schema=SET_AWAY_MODE_SCHEMA)
|
||||||
|
|
||||||
def temperature_set_service(service):
|
def temperature_set_service(service):
|
||||||
"""Set temperature on the target thermostats."""
|
"""Set temperature on the target thermostats."""
|
||||||
target_thermostats = component.extract_from_service(service)
|
target_thermostats = component.extract_from_service(service)
|
||||||
|
|
||||||
temperature = util.convert(
|
temperature = service.data[ATTR_TEMPERATURE]
|
||||||
service.data.get(ATTR_TEMPERATURE), float)
|
|
||||||
|
|
||||||
if temperature is None:
|
|
||||||
_LOGGER.error(
|
|
||||||
"Received call to %s without attribute %s",
|
|
||||||
SERVICE_SET_TEMPERATURE, ATTR_TEMPERATURE)
|
|
||||||
return
|
|
||||||
|
|
||||||
for thermostat in target_thermostats:
|
for thermostat in target_thermostats:
|
||||||
thermostat.set_temperature(convert(
|
thermostat.set_temperature(convert(
|
||||||
@ -139,19 +142,14 @@ def setup(hass, config):
|
|||||||
|
|
||||||
hass.services.register(
|
hass.services.register(
|
||||||
DOMAIN, SERVICE_SET_TEMPERATURE, temperature_set_service,
|
DOMAIN, SERVICE_SET_TEMPERATURE, temperature_set_service,
|
||||||
descriptions.get(SERVICE_SET_TEMPERATURE))
|
descriptions.get(SERVICE_SET_TEMPERATURE),
|
||||||
|
schema=SET_TEMPERATURE_SCHEMA)
|
||||||
|
|
||||||
def fan_mode_set_service(service):
|
def fan_mode_set_service(service):
|
||||||
"""Set fan mode on target thermostats."""
|
"""Set fan mode on target thermostats."""
|
||||||
target_thermostats = component.extract_from_service(service)
|
target_thermostats = component.extract_from_service(service)
|
||||||
|
|
||||||
fan_mode = service.data.get(ATTR_FAN)
|
fan_mode = service.data[ATTR_FAN]
|
||||||
|
|
||||||
if fan_mode is None:
|
|
||||||
_LOGGER.error(
|
|
||||||
"Received call to %s without attribute %s",
|
|
||||||
SERVICE_SET_FAN_MODE, ATTR_FAN)
|
|
||||||
return
|
|
||||||
|
|
||||||
for thermostat in target_thermostats:
|
for thermostat in target_thermostats:
|
||||||
if fan_mode:
|
if fan_mode:
|
||||||
@ -163,7 +161,8 @@ def setup(hass, config):
|
|||||||
|
|
||||||
hass.services.register(
|
hass.services.register(
|
||||||
DOMAIN, SERVICE_SET_FAN_MODE, fan_mode_set_service,
|
DOMAIN, SERVICE_SET_FAN_MODE, fan_mode_set_service,
|
||||||
descriptions.get(SERVICE_SET_FAN_MODE))
|
descriptions.get(SERVICE_SET_FAN_MODE),
|
||||||
|
schema=SET_FAN_MODE_SCHEMA)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ class TestComponentHistory(unittest.TestCase):
|
|||||||
logbook.ATTR_NAME: 'Alarm',
|
logbook.ATTR_NAME: 'Alarm',
|
||||||
logbook.ATTR_MESSAGE: 'is triggered',
|
logbook.ATTR_MESSAGE: 'is triggered',
|
||||||
logbook.ATTR_DOMAIN: 'switch',
|
logbook.ATTR_DOMAIN: 'switch',
|
||||||
logbook.ATTR_ENTITY_ID: 'test_switch'
|
logbook.ATTR_ENTITY_ID: 'switch.test_switch'
|
||||||
}, True)
|
}, True)
|
||||||
self.hass.pool.block_till_done()
|
self.hass.pool.block_till_done()
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ class TestComponentHistory(unittest.TestCase):
|
|||||||
self.assertEqual('is triggered', last_call.data.get(
|
self.assertEqual('is triggered', last_call.data.get(
|
||||||
logbook.ATTR_MESSAGE))
|
logbook.ATTR_MESSAGE))
|
||||||
self.assertEqual('switch', last_call.data.get(logbook.ATTR_DOMAIN))
|
self.assertEqual('switch', last_call.data.get(logbook.ATTR_DOMAIN))
|
||||||
self.assertEqual('test_switch', last_call.data.get(
|
self.assertEqual('switch.test_switch', last_call.data.get(
|
||||||
logbook.ATTR_ENTITY_ID))
|
logbook.ATTR_ENTITY_ID))
|
||||||
|
|
||||||
def test_service_call_create_log_book_entry_no_message(self):
|
def test_service_call_create_log_book_entry_no_message(self):
|
||||||
|
@ -5,6 +5,7 @@ import unittest
|
|||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
from subprocess import SubprocessError
|
from subprocess import SubprocessError
|
||||||
|
|
||||||
|
from homeassistant.bootstrap import _setup_component
|
||||||
from homeassistant.components import shell_command
|
from homeassistant.components import shell_command
|
||||||
|
|
||||||
from tests.common import get_test_home_assistant
|
from tests.common import get_test_home_assistant
|
||||||
@ -25,11 +26,11 @@ class TestShellCommand(unittest.TestCase):
|
|||||||
"""Test if able to call a configured service."""
|
"""Test if able to call a configured service."""
|
||||||
with tempfile.TemporaryDirectory() as tempdirname:
|
with tempfile.TemporaryDirectory() as tempdirname:
|
||||||
path = os.path.join(tempdirname, 'called.txt')
|
path = os.path.join(tempdirname, 'called.txt')
|
||||||
self.assertTrue(shell_command.setup(self.hass, {
|
assert _setup_component(self.hass, shell_command.DOMAIN, {
|
||||||
'shell_command': {
|
shell_command.DOMAIN: {
|
||||||
'test_service': "date > {}".format(path)
|
'test_service': "date > {}".format(path)
|
||||||
}
|
}
|
||||||
}))
|
})
|
||||||
|
|
||||||
self.hass.services.call('shell_command', 'test_service',
|
self.hass.services.call('shell_command', 'test_service',
|
||||||
blocking=True)
|
blocking=True)
|
||||||
@ -38,16 +39,17 @@ class TestShellCommand(unittest.TestCase):
|
|||||||
|
|
||||||
def test_config_not_dict(self):
|
def test_config_not_dict(self):
|
||||||
"""Test if config is not a dict."""
|
"""Test if config is not a dict."""
|
||||||
self.assertFalse(shell_command.setup(self.hass, {
|
assert not _setup_component(self.hass, shell_command.DOMAIN, {
|
||||||
'shell_command': ['some', 'weird', 'list']
|
shell_command.DOMAIN: ['some', 'weird', 'list']
|
||||||
}))
|
})
|
||||||
|
|
||||||
def test_config_not_valid_service_names(self):
|
def test_config_not_valid_service_names(self):
|
||||||
"""Test if config contains invalid service names."""
|
"""Test if config contains invalid service names."""
|
||||||
self.assertFalse(shell_command.setup(self.hass, {
|
assert not _setup_component(self.hass, shell_command.DOMAIN, {
|
||||||
'shell_command': {
|
shell_command.DOMAIN: {
|
||||||
'this is invalid because space': 'touch bla.txt'
|
'this is invalid because space': 'touch bla.txt'
|
||||||
}}))
|
}
|
||||||
|
})
|
||||||
|
|
||||||
@patch('homeassistant.components.shell_command.subprocess.call',
|
@patch('homeassistant.components.shell_command.subprocess.call',
|
||||||
side_effect=SubprocessError)
|
side_effect=SubprocessError)
|
||||||
@ -56,11 +58,11 @@ class TestShellCommand(unittest.TestCase):
|
|||||||
"""Test subprocess."""
|
"""Test subprocess."""
|
||||||
with tempfile.TemporaryDirectory() as tempdirname:
|
with tempfile.TemporaryDirectory() as tempdirname:
|
||||||
path = os.path.join(tempdirname, 'called.txt')
|
path = os.path.join(tempdirname, 'called.txt')
|
||||||
self.assertTrue(shell_command.setup(self.hass, {
|
assert _setup_component(self.hass, shell_command.DOMAIN, {
|
||||||
'shell_command': {
|
shell_command.DOMAIN: {
|
||||||
'test_service': "touch {}".format(path)
|
'test_service': "touch {}".format(path)
|
||||||
}
|
}
|
||||||
}))
|
})
|
||||||
|
|
||||||
self.hass.services.call('shell_command', 'test_service',
|
self.hass.services.call('shell_command', 'test_service',
|
||||||
blocking=True)
|
blocking=True)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user