forked from home-assistant/core
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtest_blueprint.py
216 lines (168 loc) · 6.52 KB
/
test_blueprint.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
"""Test built-in blueprints."""
import asyncio
import contextlib
from datetime import timedelta
import pathlib
from unittest.mock import patch
import pytest
from homeassistant.components import automation
from homeassistant.components.blueprint import models
from homeassistant.core import HomeAssistant, callback
from homeassistant.setup import async_setup_component
from homeassistant.util import dt as dt_util, yaml
from tests.common import async_fire_time_changed, async_mock_service
BUILTIN_BLUEPRINT_FOLDER = pathlib.Path(automation.__file__).parent / "blueprints"
@contextlib.contextmanager
def patch_blueprint(blueprint_path: str, data_path):
"""Patch blueprint loading from a different source."""
orig_load = models.DomainBlueprints._load_blueprint
@callback
def mock_load_blueprint(self, path):
if path != blueprint_path:
pytest.fail(f"Unexpected blueprint {path}")
return orig_load(self, path)
return models.Blueprint(
yaml.load_yaml(data_path), expected_domain=self.domain, path=path
)
with patch(
"homeassistant.components.blueprint.models.DomainBlueprints._load_blueprint",
mock_load_blueprint,
):
yield
async def test_notify_leaving_zone(hass: HomeAssistant) -> None:
"""Test notifying leaving a zone blueprint."""
def set_person_state(state, extra={}):
hass.states.async_set(
"person.test_person", state, {"friendly_name": "Paulus", **extra}
)
set_person_state("School")
assert await async_setup_component(
hass, "zone", {"zone": {"name": "School", "latitude": 1, "longitude": 2}}
)
with patch_blueprint(
"notify_leaving_zone.yaml",
BUILTIN_BLUEPRINT_FOLDER / "notify_leaving_zone.yaml",
):
assert await async_setup_component(
hass,
"automation",
{
"automation": {
"use_blueprint": {
"path": "notify_leaving_zone.yaml",
"input": {
"person_entity": "person.test_person",
"zone_entity": "zone.school",
"notify_device": "abcdefgh",
},
}
}
},
)
with patch(
"homeassistant.components.mobile_app.device_action.async_call_action_from_config"
) as mock_call_action:
# Leaving zone to no zone
set_person_state("not_home")
await hass.async_block_till_done()
assert len(mock_call_action.mock_calls) == 1
_hass, config, variables, _context = mock_call_action.mock_calls[0][1]
message_tpl = config.pop("message")
assert config == {
"alias": "Notify that a person has left the zone",
"domain": "mobile_app",
"type": "notify",
"device_id": "abcdefgh",
}
message_tpl.hass = hass
assert message_tpl.async_render(variables) == "Paulus has left School"
# Should not increase when we go to another zone
set_person_state("bla")
await hass.async_block_till_done()
assert len(mock_call_action.mock_calls) == 1
# Should not increase when we go into the zone
set_person_state("School")
await hass.async_block_till_done()
assert len(mock_call_action.mock_calls) == 1
# Should not increase when we move in the zone
set_person_state("School", {"extra_key": "triggers change with same state"})
await hass.async_block_till_done()
assert len(mock_call_action.mock_calls) == 1
# Should increase when leaving zone for another zone
set_person_state("Just Outside School")
await hass.async_block_till_done()
assert len(mock_call_action.mock_calls) == 2
# Verify trigger works
await hass.services.async_call(
"automation",
"trigger",
{"entity_id": "automation.automation_0"},
blocking=True,
)
assert len(mock_call_action.mock_calls) == 3
async def test_motion_light(hass: HomeAssistant) -> None:
"""Test motion light blueprint."""
hass.states.async_set("binary_sensor.kitchen", "off")
with patch_blueprint(
"motion_light.yaml",
BUILTIN_BLUEPRINT_FOLDER / "motion_light.yaml",
):
assert await async_setup_component(
hass,
"automation",
{
"automation": {
"use_blueprint": {
"path": "motion_light.yaml",
"input": {
"light_target": {"entity_id": "light.kitchen"},
"motion_entity": "binary_sensor.kitchen",
},
}
}
},
)
turn_on_calls = async_mock_service(hass, "light", "turn_on")
turn_off_calls = async_mock_service(hass, "light", "turn_off")
# Turn on motion
hass.states.async_set("binary_sensor.kitchen", "on")
# Can't block till done because delay is active
# So wait 10 event loop iterations to process script
for _ in range(10):
await asyncio.sleep(0)
assert len(turn_on_calls) == 1
# Test light doesn't turn off if motion stays
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=200))
for _ in range(10):
await asyncio.sleep(0)
assert len(turn_off_calls) == 0
# Test light turns off off 120s after last motion
hass.states.async_set("binary_sensor.kitchen", "off")
for _ in range(10):
await asyncio.sleep(0)
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=120))
await hass.async_block_till_done()
assert len(turn_off_calls) == 1
# Test restarting the script
hass.states.async_set("binary_sensor.kitchen", "on")
for _ in range(10):
await asyncio.sleep(0)
assert len(turn_on_calls) == 2
assert len(turn_off_calls) == 1
hass.states.async_set("binary_sensor.kitchen", "off")
for _ in range(10):
await asyncio.sleep(0)
hass.states.async_set("binary_sensor.kitchen", "on")
for _ in range(15):
await asyncio.sleep(0)
assert len(turn_on_calls) == 3
assert len(turn_off_calls) == 1
# Verify trigger works
await hass.services.async_call(
"automation",
"trigger",
{"entity_id": "automation.automation_0"},
)
for _ in range(25):
await asyncio.sleep(0)
assert len(turn_on_calls) == 4