Return the listeners with the template result for the websocket api (#39925)

This commit is contained in:
J. Nick Koston 2020-09-11 13:18:40 -05:00 committed by GitHub
parent 9b29d09d45
commit 741487a1fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 118 additions and 11 deletions

View file

@ -253,9 +253,11 @@ def handle_render_template(hass, connection, msg):
template.hass = hass
variables = msg.get("variables")
info = None
@callback
def _template_listener(event, updates):
nonlocal info
track_template_result = updates.pop()
result = track_template_result.result
if isinstance(result, TemplateError):
@ -267,7 +269,11 @@ def handle_render_template(hass, connection, msg):
result = None
connection.send_message(messages.event_message(msg["id"], {"result": result}))
connection.send_message(
messages.event_message(
msg["id"], {"result": result, "listeners": info.listeners} # type: ignore
)
)
info = async_track_template_result(
hass, [TrackTemplate(template, variables)], _template_listener

View file

@ -581,6 +581,15 @@ class _TrackTemplateResultInfo:
self._last_info = self._info.copy()
self._create_listeners()
@property
def listeners(self) -> Dict:
"""State changes that will cause a re-render."""
return {
"all": self._all_listener is not None,
"entities": self._last_entities,
"domains": self._last_domains,
}
@property
def _needs_all_listener(self) -> bool:
for track_template_ in self._track_templates:

View file

@ -420,14 +420,20 @@ async def test_render_template_renders_template(
assert msg["id"] == 5
assert msg["type"] == "event"
event = msg["event"]
assert event == {"result": "State is: on"}
assert event == {
"result": "State is: on",
"listeners": {"all": False, "domains": [], "entities": ["light.test"]},
}
hass.states.async_set("light.test", "off")
msg = await websocket_client.receive_json()
assert msg["id"] == 5
assert msg["type"] == "event"
event = msg["event"]
assert event == {"result": "State is: off"}
assert event == {
"result": "State is: off",
"listeners": {"all": False, "domains": [], "entities": ["light.test"]},
}
async def test_render_template_manual_entity_ids_no_longer_needed(
@ -453,14 +459,20 @@ async def test_render_template_manual_entity_ids_no_longer_needed(
assert msg["id"] == 5
assert msg["type"] == "event"
event = msg["event"]
assert event == {"result": "State is: on"}
assert event == {
"result": "State is: on",
"listeners": {"all": False, "domains": [], "entities": ["light.test"]},
}
hass.states.async_set("light.test", "off")
msg = await websocket_client.receive_json()
assert msg["id"] == 5
assert msg["type"] == "event"
event = msg["event"]
assert event == {"result": "State is: off"}
assert event == {
"result": "State is: off",
"listeners": {"all": False, "domains": [], "entities": ["light.test"]},
}
async def test_render_template_with_error(
@ -480,7 +492,10 @@ async def test_render_template_with_error(
assert msg["id"] == 5
assert msg["type"] == "event"
event = msg["event"]
assert event == {"result": None}
assert event == {
"result": None,
"listeners": {"all": True, "domains": [], "entities": []},
}
assert "my_unknown_var" in caplog.text
assert "TemplateError" in caplog.text

View file

@ -809,20 +809,33 @@ async def test_track_template_result_complex(hass):
hass.states.async_set("light.one", "on")
hass.states.async_set("lock.one", "locked")
async_track_template_result(
info = async_track_template_result(
hass, [TrackTemplate(template_complex, None)], specific_run_callback
)
await hass.async_block_till_done()
assert info.listeners == {"all": True, "domains": set(), "entities": set()}
hass.states.async_set("sensor.domain", "light")
await hass.async_block_till_done()
assert len(specific_runs) == 1
assert specific_runs[0].strip() == "['light.one']"
assert info.listeners == {
"all": False,
"domains": {"light"},
"entities": {"sensor.domain"},
}
hass.states.async_set("sensor.domain", "lock")
await hass.async_block_till_done()
assert len(specific_runs) == 2
assert specific_runs[1].strip() == "['lock.one']"
assert info.listeners == {
"all": False,
"domains": {"lock"},
"entities": {"sensor.domain"},
}
hass.states.async_set("sensor.domain", "all")
await hass.async_block_till_done()
@ -830,11 +843,17 @@ async def test_track_template_result_complex(hass):
assert "light.one" in specific_runs[2]
assert "lock.one" in specific_runs[2]
assert "sensor.domain" in specific_runs[2]
assert info.listeners == {"all": True, "domains": set(), "entities": set()}
hass.states.async_set("sensor.domain", "light")
await hass.async_block_till_done()
assert len(specific_runs) == 4
assert specific_runs[3].strip() == "['light.one']"
assert info.listeners == {
"all": False,
"domains": {"light"},
"entities": {"sensor.domain"},
}
hass.states.async_set("light.two", "on")
await hass.async_block_till_done()
@ -842,6 +861,11 @@ async def test_track_template_result_complex(hass):
assert "light.one" in specific_runs[4]
assert "light.two" in specific_runs[4]
assert "sensor.domain" not in specific_runs[4]
assert info.listeners == {
"all": False,
"domains": {"light"},
"entities": {"sensor.domain"},
}
hass.states.async_set("light.three", "on")
await hass.async_block_till_done()
@ -850,26 +874,51 @@ async def test_track_template_result_complex(hass):
assert "light.two" in specific_runs[5]
assert "light.three" in specific_runs[5]
assert "sensor.domain" not in specific_runs[5]
assert info.listeners == {
"all": False,
"domains": {"light"},
"entities": {"sensor.domain"},
}
hass.states.async_set("sensor.domain", "lock")
await hass.async_block_till_done()
assert len(specific_runs) == 7
assert specific_runs[6].strip() == "['lock.one']"
assert info.listeners == {
"all": False,
"domains": {"lock"},
"entities": {"sensor.domain"},
}
hass.states.async_set("sensor.domain", "single_binary_sensor")
await hass.async_block_till_done()
assert len(specific_runs) == 8
assert specific_runs[7].strip() == "unknown"
assert info.listeners == {
"all": False,
"domains": set(),
"entities": {"binary_sensor.single", "sensor.domain"},
}
hass.states.async_set("binary_sensor.single", "binary_sensor_on")
await hass.async_block_till_done()
assert len(specific_runs) == 9
assert specific_runs[8].strip() == "binary_sensor_on"
assert info.listeners == {
"all": False,
"domains": set(),
"entities": {"binary_sensor.single", "sensor.domain"},
}
hass.states.async_set("sensor.domain", "lock")
await hass.async_block_till_done()
assert len(specific_runs) == 10
assert specific_runs[9].strip() == "['lock.one']"
assert info.listeners == {
"all": False,
"domains": {"lock"},
"entities": {"sensor.domain"},
}
async def test_track_template_result_with_wildcard(hass):
@ -893,7 +942,7 @@ async def test_track_template_result_with_wildcard(hass):
hass.states.async_set("cover.office_window", "closed")
hass.states.async_set("cover.office_skylight", "open")
async_track_template_result(
info = async_track_template_result(
hass, [TrackTemplate(template_complex, None)], specific_run_callback
)
await hass.async_block_till_done()
@ -901,6 +950,7 @@ async def test_track_template_result_with_wildcard(hass):
hass.states.async_set("cover.office_window", "open")
await hass.async_block_till_done()
assert len(specific_runs) == 1
assert info.listeners == {"all": True, "domains": set(), "entities": set()}
assert "cover.office_drapes=closed" in specific_runs[0]
assert "cover.office_window=open" in specific_runs[0]
@ -935,11 +985,22 @@ async def test_track_template_result_with_group(hass):
def specific_run_callback(event, updates):
specific_runs.append(updates.pop().result)
async_track_template_result(
info = async_track_template_result(
hass, [TrackTemplate(template_complex, None)], specific_run_callback
)
await hass.async_block_till_done()
assert info.listeners == {
"all": False,
"domains": set(),
"entities": {
"group.power_sensors",
"sensor.power_1",
"sensor.power_2",
"sensor.power_3",
},
}
hass.states.async_set("sensor.power_1", 100.1)
await hass.async_block_till_done()
assert len(specific_runs) == 1
@ -978,10 +1039,11 @@ async def test_track_template_result_and_conditional(hass):
def specific_run_callback(event, updates):
specific_runs.append(updates.pop().result)
async_track_template_result(
info = async_track_template_result(
hass, [TrackTemplate(template, None)], specific_run_callback
)
await hass.async_block_till_done()
assert info.listeners == {"all": False, "domains": set(), "entities": {"light.a"}}
hass.states.async_set("light.b", "on")
await hass.async_block_till_done()
@ -991,11 +1053,21 @@ async def test_track_template_result_and_conditional(hass):
await hass.async_block_till_done()
assert len(specific_runs) == 1
assert specific_runs[0] == "on"
assert info.listeners == {
"all": False,
"domains": set(),
"entities": {"light.a", "light.b"},
}
hass.states.async_set("light.b", "off")
await hass.async_block_till_done()
assert len(specific_runs) == 2
assert specific_runs[1] == "off"
assert info.listeners == {
"all": False,
"domains": set(),
"entities": {"light.a", "light.b"},
}
hass.states.async_set("light.a", "off")
await hass.async_block_till_done()
@ -1051,7 +1123,7 @@ async def test_track_template_result_iterator(hass):
def filter_callback(event, updates):
filter_runs.append(updates.pop().result)
async_track_template_result(
info = async_track_template_result(
hass,
[
TrackTemplate(
@ -1066,6 +1138,11 @@ async def test_track_template_result_iterator(hass):
filter_callback,
)
await hass.async_block_till_done()
assert info.listeners == {
"all": False,
"domains": {"sensor"},
"entities": {"sensor.test"},
}
hass.states.async_set("sensor.test", 6)
await hass.async_block_till_done()