Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions homeassistant/components/automation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@
import voluptuous as vol

from homeassistant.setup import async_prepare_setup_platform
from homeassistant.core import CoreState
from homeassistant.core import CoreState, EventOrigin
from homeassistant.loader import bind_hass
from homeassistant.const import (
ATTR_ENTITY_ID, CONF_PLATFORM, STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF,
SERVICE_TOGGLE, SERVICE_RELOAD, EVENT_HOMEASSISTANT_START, CONF_ID)
SERVICE_TOGGLE, SERVICE_RELOAD, EVENT_HOMEASSISTANT_START, CONF_ID,
EVENT_AUTOMATION_TRIGGER, ATTR_DOMAIN, ATTR_NAME)
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import extract_domain_configs, script, condition
from homeassistant.helpers.entity import ToggleEntity
Expand Down Expand Up @@ -286,6 +287,11 @@ async def async_trigger(self, variables, skip_condition=False,
"""
if skip_condition or self._cond_func(variables):
self.async_set_context(context)
self.hass.bus.async_fire(EVENT_AUTOMATION_TRIGGER, {
ATTR_NAME: self._name,
ATTR_DOMAIN: DOMAIN,
Comment thread
hunterjm marked this conversation as resolved.
Outdated
ATTR_ENTITY_ID: self.entity_id,
}, EventOrigin.local, context)
Comment thread
hunterjm marked this conversation as resolved.
Outdated
await self._async_action(self.entity_id, variables, context)
self._last_triggered = utcnow()
await self.async_update_ha_state()
Expand Down Expand Up @@ -370,8 +376,6 @@ def _async_get_action(hass, config, name):
async def action(entity_id, variables, context):
"""Execute an action."""
_LOGGER.info('Executing %s', name)
hass.components.logbook.async_log_entry(
name, 'has been triggered', DOMAIN, entity_id)
await script_obj.async_run(variables, context)

return action
Expand Down
12 changes: 8 additions & 4 deletions homeassistant/components/image_processing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,14 @@ async def async_scan_service(service):
"""Service handler for scan."""
image_entities = component.async_extract_from_service(service)

update_task = [entity.async_update_ha_state(True) for
entity in image_entities]
if update_task:
await asyncio.wait(update_task, loop=hass.loop)
update_tasks = []
for entity in image_entities:
entity.async_set_context(service.context)
update_tasks.append(
entity.async_update_ha_state(True))

if update_tasks:
await asyncio.wait(update_tasks, loop=hass.loop)

hass.services.async_register(
DOMAIN, SERVICE_SCAN, async_scan_service,
Expand Down
14 changes: 13 additions & 1 deletion homeassistant/components/logbook.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
ATTR_DOMAIN, ATTR_ENTITY_ID, ATTR_HIDDEN, ATTR_NAME, ATTR_SERVICE,
CONF_EXCLUDE, CONF_INCLUDE, EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP, EVENT_LOGBOOK_ENTRY, EVENT_STATE_CHANGED,
HTTP_BAD_REQUEST, STATE_NOT_HOME, STATE_OFF, STATE_ON)
EVENT_AUTOMATION_TRIGGER, HTTP_BAD_REQUEST, STATE_NOT_HOME,
STATE_OFF, STATE_ON)
from homeassistant.core import (
DOMAIN as HA_DOMAIN, State, callback, split_entity_id)
from homeassistant.components.alexa.smart_home import EVENT_ALEXA_SMART_HOME
Expand Down Expand Up @@ -316,6 +317,17 @@ def humanify(hass, events):
'context_user_id': event.context.user_id
}

elif event.event_type == EVENT_AUTOMATION_TRIGGER:
yield {
'when': event.time_fired,
'name': event.data.get(ATTR_NAME),
'message': "has been triggered",
'domain': event.data.get(ATTR_DOMAIN),
Comment thread
hunterjm marked this conversation as resolved.
Outdated
'entity_id': event.data.get(ATTR_ENTITY_ID),
'context_id': event.context.id,
'context_user_id': event.context.user_id
}


def _get_related_entity_ids(session, entity_filter):
from homeassistant.components.recorder.models import States
Expand Down
13 changes: 11 additions & 2 deletions homeassistant/components/script.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@

import voluptuous as vol

from homeassistant.core import EventOrigin
from homeassistant.const import (
ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON,
SERVICE_TOGGLE, SERVICE_RELOAD, STATE_ON, CONF_ALIAS)
SERVICE_TOGGLE, SERVICE_RELOAD, STATE_ON, CONF_ALIAS,
EVENT_SCRIPT_RUN, ATTR_DOMAIN, ATTR_NAME)
from homeassistant.loader import bind_hass
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.helpers.entity_component import EntityComponent
Expand Down Expand Up @@ -170,8 +172,15 @@ def is_on(self):

async def async_turn_on(self, **kwargs):
"""Turn the script on."""
context = kwargs.get('context')
self.async_set_context(context)
self.hass.bus.async_fire(EVENT_SCRIPT_RUN, {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we have a bit of a weird thing right now with scripts that include a delay that get turned on . It means that we don't always start a new script, but sometimes just continue it. (it's kinda weird honestly). So we might want to check that.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add an attribute to the event data to determine if it is a new run or a continuation based on self.script.is_running in the component, then add it to logbook for "{{name}} started" and "{{name}} continued" for scripts. I currently only added automations to logbook.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like scripts running again to bypass delay has been disabled based on this: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/script.py#L118-L119

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, then that's not a problem then !

ATTR_NAME: self.script.name,
ATTR_DOMAIN: DOMAIN,
ATTR_ENTITY_ID: self.entity_id,
}, EventOrigin.local, context)
Comment thread
hunterjm marked this conversation as resolved.
Outdated
await self.script.async_run(
kwargs.get(ATTR_VARIABLES), kwargs.get('context'))
kwargs.get(ATTR_VARIABLES), context)

async def async_turn_off(self, **kwargs):
"""Turn script off."""
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@
EVENT_LOGBOOK_ENTRY = 'logbook_entry'
EVENT_THEMES_UPDATED = 'themes_updated'
EVENT_TIMER_OUT_OF_SYNC = 'timer_out_of_sync'
EVENT_AUTOMATION_TRIGGER = 'automation_trigger'
Comment thread
hunterjm marked this conversation as resolved.
Outdated
EVENT_SCRIPT_RUN = 'script_run'
Comment thread
hunterjm marked this conversation as resolved.
Outdated

# #### DEVICE CLASSES ####
DEVICE_CLASS_BATTERY = 'battery'
Expand Down
64 changes: 61 additions & 3 deletions tests/components/automation/test_init.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
"""The tests for the automation component."""
import asyncio
from datetime import timedelta
from unittest.mock import patch
from unittest.mock import patch, Mock

import pytest

from homeassistant.core import State, CoreState
from homeassistant.core import State, CoreState, Context
from homeassistant.setup import async_setup_component
import homeassistant.components.automation as automation
from homeassistant.const import (
ATTR_ENTITY_ID, STATE_ON, STATE_OFF, EVENT_HOMEASSISTANT_START)
ATTR_ENTITY_ID, STATE_ON, STATE_OFF, EVENT_HOMEASSISTANT_START,
EVENT_AUTOMATION_TRIGGER)
from homeassistant.exceptions import HomeAssistantError
import homeassistant.util.dt as dt_util

Expand Down Expand Up @@ -342,6 +343,63 @@ async def test_automation_calling_two_actions(hass, calls):
assert calls[1].data['position'] == 1


async def test_shared_context(hass, calls):
"""Test that the shared context is passed down the chain."""
assert await async_setup_component(hass, automation.DOMAIN, {
automation.DOMAIN: [
{
'alias': 'hello',
'trigger': {
'platform': 'event',
'event_type': 'test_event',
},
'action': {'event': 'test_event2'}
},
{
'alias': 'bye',
'trigger': {
'platform': 'event',
'event_type': 'test_event2',
},
'action': {
'service': 'test.automation',
}
}
]
})

context = Context()
automation_mock = Mock()
event_mock = Mock()

hass.bus.async_listen('test_event2', automation_mock)
hass.bus.async_listen(EVENT_AUTOMATION_TRIGGER, event_mock)
hass.bus.async_fire('test_event', context=context)
await hass.async_block_till_done()

# Ensure events was fired
assert automation_mock.call_count == 1
assert event_mock.call_count == 2

# Ensure context carries through the event
args, kwargs = automation_mock.call_args
assert args[0].context == context

for call in event_mock.call_args_list:
args, kwargs = call
assert args[0].context == context

# Ensure the automation state shares the same context
state = hass.states.get('automation.hello')
assert state is not None
assert state.context == context

# Ensure the service call from the second automation
# shares the same context
assert len(calls) == 1
assert calls[0].context == context


async def test_services(hass, calls):
"""Test the automation services for turning entities on/off."""
entity_id = 'automation.hello'
Expand Down
32 changes: 29 additions & 3 deletions tests/components/test_logbook.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
from homeassistant.components import sun
import homeassistant.core as ha
from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_SERVICE,
ATTR_ENTITY_ID, ATTR_SERVICE, ATTR_DOMAIN, ATTR_NAME,
EVENT_STATE_CHANGED, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP,
ATTR_HIDDEN, STATE_NOT_HOME, STATE_ON, STATE_OFF)
EVENT_AUTOMATION_TRIGGER, ATTR_HIDDEN, STATE_NOT_HOME, STATE_ON, STATE_OFF)
import homeassistant.util.dt as dt_util
from homeassistant.components import logbook, recorder
from homeassistant.components.alexa.smart_home import EVENT_ALEXA_SMART_HOME
Expand Down Expand Up @@ -731,7 +731,33 @@ async def test_humanify_homekit_changed_event(hass):
assert event1['entity_id'] == 'lock.front_door'

assert event2['name'] == 'HomeKit'
assert event1['domain'] == DOMAIN_HOMEKIT
assert event2['domain'] == DOMAIN_HOMEKIT
assert event2['message'] == \
'send command set_cover_position to 75 for Window'
assert event2['entity_id'] == 'cover.window'


async def test_humanify_automation_trigger_event(hass):
"""Test humanifying Automation Trigger event."""
event1, event2 = list(logbook.humanify(hass, [
ha.Event(EVENT_AUTOMATION_TRIGGER, {
ATTR_ENTITY_ID: 'automation.hello',
ATTR_DOMAIN: 'automation',
ATTR_NAME: 'Hello Automation',
}),
ha.Event(EVENT_AUTOMATION_TRIGGER, {
ATTR_ENTITY_ID: 'automation.bye',
ATTR_DOMAIN: 'automation',
ATTR_NAME: 'Bye Automation',
}),
]))

assert event1['name'] == 'Hello Automation'
assert event1['domain'] == 'automation'
assert event1['message'] == 'has been triggered'
assert event1['entity_id'] == 'automation.hello'

assert event2['name'] == 'Bye Automation'
assert event2['domain'] == 'automation'
assert event2['message'] == 'has been triggered'
assert event2['entity_id'] == 'automation.bye'
39 changes: 37 additions & 2 deletions tests/components/test_script.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
"""The tests for the Script component."""
# pylint: disable=protected-access
import unittest
from unittest.mock import patch
from unittest.mock import patch, Mock

from homeassistant.components import script
from homeassistant.components.script import DOMAIN
from homeassistant.const import (
ATTR_ENTITY_ID, SERVICE_RELOAD, SERVICE_TOGGLE, SERVICE_TURN_OFF)
ATTR_ENTITY_ID, SERVICE_RELOAD, SERVICE_TOGGLE, SERVICE_TURN_OFF,
EVENT_SCRIPT_RUN)
from homeassistant.core import Context, callback, split_entity_id
from homeassistant.loader import bind_hass
from homeassistant.setup import setup_component
Expand Down Expand Up @@ -254,3 +255,37 @@ def test_reload_service(self):

assert self.hass.states.get("script.test2") is not None
assert self.hass.services.has_service(script.DOMAIN, 'test2')

def test_shared_context(self):
"""Test that the shared context is passed down the chain."""
event = 'test_event'
context = Context()

event_mock = Mock()
run_mock = Mock()

self.hass.bus.listen(event, event_mock)
self.hass.bus.listen(EVENT_SCRIPT_RUN, run_mock)

assert setup_component(self.hass, 'script', {
'script': {
'test': {
'sequence': [{'event': event}]
}
}
})

turn_on(self.hass, ENTITY_ID, context=context)
self.hass.block_till_done()

# Ensure context carries through the event
args, kwargs = event_mock.call_args
assert args[0].context == context

args, kwargs = run_mock.call_args
assert args[0].context == context

# Ensure the script state shares the same context
state = self.hass.states.get('script.test')
assert state is not None
assert state.context == context