Skip to content

Commit

Permalink
Add support for automation config panel (#7509)
Browse files Browse the repository at this point in the history
* Add support for automation config

* Build fromtend

* Lint
  • Loading branch information
balloob committed May 10, 2017
1 parent d86dfb6 commit 5d820ec
Show file tree
Hide file tree
Showing 16 changed files with 144 additions and 26 deletions.
36 changes: 31 additions & 5 deletions homeassistant/components/automation/__init__.py
Expand Up @@ -16,7 +16,7 @@
from homeassistant import config as conf_util
from homeassistant.const import (
ATTR_ENTITY_ID, CONF_PLATFORM, STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF,
SERVICE_TOGGLE, SERVICE_RELOAD, EVENT_HOMEASSISTANT_START)
SERVICE_TOGGLE, SERVICE_RELOAD, EVENT_HOMEASSISTANT_START, CONF_ID)
from homeassistant.components import logbook
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import extract_domain_configs, script, condition
Expand All @@ -26,6 +26,7 @@
from homeassistant.loader import get_platform
from homeassistant.util.dt import utcnow
import homeassistant.helpers.config_validation as cv
from homeassistant.components.frontend import register_built_in_panel

DOMAIN = 'automation'
ENTITY_ID_FORMAT = DOMAIN + '.{}'
Expand Down Expand Up @@ -81,6 +82,7 @@ def _platform_validator(config):
_CONDITION_SCHEMA = vol.All(cv.ensure_list, [cv.CONDITION_SCHEMA])

PLATFORM_SCHEMA = vol.Schema({
CONF_ID: cv.string,
CONF_ALIAS: cv.string,
vol.Optional(CONF_INITIAL_STATE): cv.boolean,
vol.Optional(CONF_HIDE_ENTITY, default=DEFAULT_HIDE_ENTITY): cv.boolean,
Expand Down Expand Up @@ -139,6 +141,14 @@ def reload(hass):
hass.services.call(DOMAIN, SERVICE_RELOAD)


def async_reload(hass):
"""Reload the automation from config.
Returns a coroutine object.
"""
return hass.services.async_call(DOMAIN, SERVICE_RELOAD)


@asyncio.coroutine
def async_setup(hass, config):
"""Set up the automation."""
Expand Down Expand Up @@ -215,15 +225,20 @@ def reload_service_handler(service_call):
DOMAIN, service, turn_onoff_service_handler,
descriptions.get(service), schema=SERVICE_SCHEMA)

if 'frontend' in hass.config.components:
register_built_in_panel(hass, 'automation', 'Automations',
'mdi:playlist-play')

return True


class AutomationEntity(ToggleEntity):
"""Entity to show status of entity."""

def __init__(self, name, async_attach_triggers, cond_func, async_action,
hidden, initial_state):
def __init__(self, automation_id, name, async_attach_triggers, cond_func,
async_action, hidden, initial_state):
"""Initialize an automation entity."""
self._id = automation_id
self._name = name
self._async_attach_triggers = async_attach_triggers
self._async_detach_triggers = None
Expand Down Expand Up @@ -346,6 +361,16 @@ def async_enable(self):
self.async_trigger)
yield from self.async_update_ha_state()

@property
def device_state_attributes(self):
"""Return automation attributes."""
if self._id is None:
return None

return {
CONF_ID: self._id
}


@asyncio.coroutine
def _async_process_config(hass, config, component):
Expand All @@ -359,6 +384,7 @@ def _async_process_config(hass, config, component):
conf = config[config_key]

for list_no, config_block in enumerate(conf):
automation_id = config_block.get(CONF_ID)
name = config_block.get(CONF_ALIAS) or "{} {}".format(config_key,
list_no)

Expand All @@ -383,8 +409,8 @@ def cond_func(variables):
config_block.get(CONF_TRIGGER, []), name
)
entity = AutomationEntity(
name, async_attach_triggers, cond_func, action, hidden,
initial_state)
automation_id, name, async_attach_triggers, cond_func, action,
hidden, initial_state)

entities.append(entity)

Expand Down
88 changes: 75 additions & 13 deletions homeassistant/components/config/__init__.py
Expand Up @@ -5,7 +5,7 @@
import voluptuous as vol

from homeassistant.core import callback
from homeassistant.const import EVENT_COMPONENT_LOADED
from homeassistant.const import EVENT_COMPONENT_LOADED, CONF_ID
from homeassistant.setup import (
async_prepare_setup_platform, ATTR_COMPONENT)
from homeassistant.components.frontend import register_built_in_panel
Expand All @@ -14,8 +14,8 @@

DOMAIN = 'config'
DEPENDENCIES = ['http']
SECTIONS = ('core', 'group', 'hassbian')
ON_DEMAND = ('zwave', )
SECTIONS = ('core', 'group', 'hassbian', 'automation')
ON_DEMAND = ('zwave')


@asyncio.coroutine
Expand Down Expand Up @@ -60,7 +60,7 @@ def component_loaded(event):
return True


class EditKeyBasedConfigView(HomeAssistantView):
class BaseEditConfigView(HomeAssistantView):
"""Configure a Group endpoint."""

def __init__(self, component, config_type, path, key_schema, data_schema,
Expand All @@ -73,13 +73,29 @@ def __init__(self, component, config_type, path, key_schema, data_schema,
self.data_schema = data_schema
self.post_write_hook = post_write_hook

def _empty_config(self):
"""Empty config if file not found."""
raise NotImplementedError

def _get_value(self, data, config_key):
"""Get value."""
raise NotImplementedError

def _write_value(self, data, config_key, new_value):
"""Set value."""
raise NotImplementedError

@asyncio.coroutine
def get(self, request, config_key):
"""Fetch device specific config."""
hass = request.app['hass']
current = yield from hass.loop.run_in_executor(
None, _read, hass.config.path(self.path))
return self.json(current.get(config_key, {}))
current = yield from self.read_config(hass)
value = self._get_value(current, config_key)

if value is None:
return self.json_message('Resource not found', 404)

return self.json(value)

@asyncio.coroutine
def post(self, request, config_key):
Expand All @@ -104,10 +120,10 @@ def post(self, request, config_key):
hass = request.app['hass']
path = hass.config.path(self.path)

current = yield from hass.loop.run_in_executor(None, _read, path)
current.setdefault(config_key, {}).update(data)
current = yield from self.read_config(hass)
self._write_value(current, config_key, data)

yield from hass.loop.run_in_executor(None, _write, path, current)
yield from hass.async_add_job(_write, path, current)

if self.post_write_hook is not None:
hass.async_add_job(self.post_write_hook(hass))
Expand All @@ -116,13 +132,59 @@ def post(self, request, config_key):
'result': 'ok',
})

@asyncio.coroutine
def read_config(self, hass):
"""Read the config."""
current = yield from hass.async_add_job(
_read, hass.config.path(self.path))
if not current:
current = self._empty_config()
return current


class EditKeyBasedConfigView(BaseEditConfigView):
"""Configure a list of entries."""

def _empty_config(self):
"""Return an empty config."""
return {}

def _get_value(self, data, config_key):
"""Get value."""
return data.get(config_key, {})

def _write_value(self, data, config_key, new_value):
"""Set value."""
data.setdefault(config_key, {}).update(new_value)


class EditIdBasedConfigView(BaseEditConfigView):
"""Configure key based config entries."""

def _empty_config(self):
"""Return an empty config."""
return []

def _get_value(self, data, config_key):
"""Get value."""
return next(
(val for val in data if val.get(CONF_ID) == config_key), None)

def _write_value(self, data, config_key, new_value):
"""Set value."""
value = self._get_value(data, config_key)

if value is None:
value = {CONF_ID: config_key}
data.append(value)

value.update(new_value)


def _read(path):
"""Read YAML helper."""
if not os.path.isfile(path):
with open(path, 'w'):
pass
return {}
return None

return load_yaml(path)

Expand Down
20 changes: 20 additions & 0 deletions homeassistant/components/config/automation.py
@@ -0,0 +1,20 @@
"""Provide configuration end points for Z-Wave."""
import asyncio

from homeassistant.components.config import EditIdBasedConfigView
from homeassistant.components.automation import (
PLATFORM_SCHEMA, DOMAIN, async_reload)
import homeassistant.helpers.config_validation as cv


CONFIG_PATH = 'automations.yaml'


@asyncio.coroutine
def async_setup(hass):
"""Set up the Automation config API."""
hass.http.register_view(EditIdBasedConfigView(
DOMAIN, 'config', CONFIG_PATH, cv.string,
PLATFORM_SCHEMA, post_write_hook=async_reload
))
return True
7 changes: 4 additions & 3 deletions homeassistant/components/frontend/version.py
@@ -1,18 +1,19 @@
"""DO NOT MODIFY. Auto-generated by script/fingerprint_frontend."""

FINGERPRINTS = {
"compatibility.js": "83d9c77748dafa9db49ae77d7f3d8fb0",
"core.js": "5d08475f03adb5969bd31855d5ca0cfd",
"compatibility.js": "8e4c44b5f4288cc48ec1ba94a9bec812",
"core.js": "8cc30e2ad9ee3df44fe7a17507099d88",
"frontend.html": "5999c8fac69c503b846672cae75a12b0",
"mdi.html": "f407a5a57addbe93817ee1b244d33fbe",
"micromarkdown-js.html": "93b5ec4016f0bba585521cf4d18dec1a",
"panels/ha-panel-automation.html": "cc6fe23a97c1974b9f4165a7692bb280",
"panels/ha-panel-config.html": "59d9eb28758b497a4d9b2428f978b9b1",
"panels/ha-panel-dev-event.html": "2db9c218065ef0f61d8d08db8093cad2",
"panels/ha-panel-dev-info.html": "61610e015a411cfc84edd2c4d489e71d",
"panels/ha-panel-dev-service.html": "415552027cb083badeff5f16080410ed",
"panels/ha-panel-dev-state.html": "d70314913b8923d750932367b1099750",
"panels/ha-panel-dev-template.html": "567fbf86735e1b891e40c2f4060fec9b",
"panels/ha-panel-hassio.html": "23d175b6744c20e2fdf475b6efdaa1d3",
"panels/ha-panel-hassio.html": "41fc94a5dc9247ed7efa112614491c71",
"panels/ha-panel-history.html": "89062c48c76206cad1cec14ddbb1cbb1",
"panels/ha-panel-iframe.html": "d920f0aa3c903680f2f8795e2255daab",
"panels/ha-panel-logbook.html": "6dd6a16f52117318b202e60f98400163",
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.
2 changes: 1 addition & 1 deletion homeassistant/components/frontend/www_static/core.js

Large diffs are not rendered by default.

Binary file modified homeassistant/components/frontend/www_static/core.js.gz
Binary file not shown.

Large diffs are not rendered by default.

Binary file not shown.

0 comments on commit 5d820ec

Please sign in to comment.