forked from home-assistant/core
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
570d1e7d8f | ||
|
|
23be039b7f | ||
|
|
859632d636 | ||
|
|
874cbf808e | ||
|
|
56b5ddb06d | ||
|
|
df371d99dc |
@@ -2,7 +2,7 @@
|
||||
"domain": "frontend",
|
||||
"name": "Home Assistant Frontend",
|
||||
"documentation": "https://www.home-assistant.io/integrations/frontend",
|
||||
"requirements": ["home-assistant-frontend==20201001.0"],
|
||||
"requirements": ["home-assistant-frontend==20201001.1"],
|
||||
"dependencies": [
|
||||
"api",
|
||||
"auth",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "SmartThings",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/smartthings",
|
||||
"requirements": ["pysmartapp==0.3.2", "pysmartthings==0.7.3"],
|
||||
"requirements": ["pysmartapp==0.3.2", "pysmartthings==0.7.4"],
|
||||
"dependencies": ["webhook"],
|
||||
"after_dependencies": ["cloud"],
|
||||
"codeowners": ["@andrewsayre"]
|
||||
|
||||
@@ -488,7 +488,6 @@ async def async_process_ha_core_config(hass: HomeAssistant, config: Dict) -> Non
|
||||
CONF_UNIT_SYSTEM,
|
||||
CONF_EXTERNAL_URL,
|
||||
CONF_INTERNAL_URL,
|
||||
CONF_MEDIA_DIRS,
|
||||
]
|
||||
):
|
||||
hac.config_source = SOURCE_YAML
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Constants used by Home Assistant components."""
|
||||
MAJOR_VERSION = 0
|
||||
MINOR_VERSION = 116
|
||||
PATCH_VERSION = "0b3"
|
||||
PATCH_VERSION = "0b5"
|
||||
__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||
__version__ = f"{__short_version__}.{PATCH_VERSION}"
|
||||
REQUIRED_PYTHON_VER = (3, 7, 1)
|
||||
|
||||
@@ -587,17 +587,18 @@ class DomainStates:
|
||||
class TemplateState(State):
|
||||
"""Class to represent a state object in a template."""
|
||||
|
||||
__slots__ = ("_hass", "_state")
|
||||
__slots__ = ("_hass", "_state", "_collect")
|
||||
|
||||
# Inheritance is done so functions that check against State keep working
|
||||
# pylint: disable=super-init-not-called
|
||||
def __init__(self, hass, state):
|
||||
def __init__(self, hass, state, collect=True):
|
||||
"""Initialize template state."""
|
||||
self._hass = hass
|
||||
self._state = state
|
||||
self._collect = collect
|
||||
|
||||
def _collect_state(self):
|
||||
if _RENDER_INFO in self._hass.data:
|
||||
if self._collect and _RENDER_INFO in self._hass.data:
|
||||
self._hass.data[_RENDER_INFO].entities.add(self._state.entity_id)
|
||||
|
||||
# Jinja will try __getitem__ first and it avoids the need
|
||||
@@ -606,7 +607,7 @@ class TemplateState(State):
|
||||
"""Return a property as an attribute for jinja."""
|
||||
if item in _COLLECTABLE_STATE_ATTRIBUTES:
|
||||
# _collect_state inlined here for performance
|
||||
if _RENDER_INFO in self._hass.data:
|
||||
if self._collect and _RENDER_INFO in self._hass.data:
|
||||
self._hass.data[_RENDER_INFO].entities.add(self._state.entity_id)
|
||||
return getattr(self._state, item)
|
||||
if item == "entity_id":
|
||||
@@ -697,7 +698,7 @@ def _collect_state(hass: HomeAssistantType, entity_id: str) -> None:
|
||||
def _state_generator(hass: HomeAssistantType, domain: Optional[str]) -> Generator:
|
||||
"""State generator for a domain or all states."""
|
||||
for state in sorted(hass.states.async_all(domain), key=attrgetter("entity_id")):
|
||||
yield TemplateState(hass, state)
|
||||
yield TemplateState(hass, state, collect=False)
|
||||
|
||||
|
||||
def _get_state_if_valid(
|
||||
|
||||
@@ -13,7 +13,7 @@ defusedxml==0.6.0
|
||||
distro==1.5.0
|
||||
emoji==0.5.4
|
||||
hass-nabucasa==0.37.0
|
||||
home-assistant-frontend==20201001.0
|
||||
home-assistant-frontend==20201001.1
|
||||
importlib-metadata==1.6.0;python_version<'3.8'
|
||||
jinja2>=2.11.2
|
||||
netdisco==2.8.2
|
||||
|
||||
@@ -753,7 +753,7 @@ hole==0.5.1
|
||||
holidays==0.10.3
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20201001.0
|
||||
home-assistant-frontend==20201001.1
|
||||
|
||||
# homeassistant.components.zwave
|
||||
homeassistant-pyozw==0.1.10
|
||||
@@ -1653,7 +1653,7 @@ pysmappee==0.2.13
|
||||
pysmartapp==0.3.2
|
||||
|
||||
# homeassistant.components.smartthings
|
||||
pysmartthings==0.7.3
|
||||
pysmartthings==0.7.4
|
||||
|
||||
# homeassistant.components.smarty
|
||||
pysmarty==0.8
|
||||
|
||||
@@ -376,7 +376,7 @@ hole==0.5.1
|
||||
holidays==0.10.3
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20201001.0
|
||||
home-assistant-frontend==20201001.1
|
||||
|
||||
# homeassistant.components.zwave
|
||||
homeassistant-pyozw==0.1.10
|
||||
@@ -800,7 +800,7 @@ pysmappee==0.2.13
|
||||
pysmartapp==0.3.2
|
||||
|
||||
# homeassistant.components.smartthings
|
||||
pysmartthings==0.7.3
|
||||
pysmartthings==0.7.4
|
||||
|
||||
# homeassistant.components.soma
|
||||
pysoma==0.0.10
|
||||
|
||||
@@ -1284,7 +1284,7 @@ async def test_track_template_result_iterator(hass):
|
||||
assert info.listeners == {
|
||||
"all": False,
|
||||
"domains": {"sensor"},
|
||||
"entities": {"sensor.test"},
|
||||
"entities": set(),
|
||||
}
|
||||
|
||||
hass.states.async_set("sensor.test", 6)
|
||||
@@ -1488,6 +1488,80 @@ async def test_track_template_rate_limit_five(hass):
|
||||
assert refresh_runs == ["0", "1"]
|
||||
|
||||
|
||||
async def test_track_template_has_default_rate_limit(hass):
|
||||
"""Test template has a rate limit by default."""
|
||||
hass.states.async_set("sensor.zero", "any")
|
||||
template_refresh = Template("{{ states | list | count }}", hass)
|
||||
|
||||
refresh_runs = []
|
||||
|
||||
@ha.callback
|
||||
def refresh_listener(event, updates):
|
||||
refresh_runs.append(updates.pop().result)
|
||||
|
||||
info = async_track_template_result(
|
||||
hass,
|
||||
[TrackTemplate(template_refresh, None)],
|
||||
refresh_listener,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
info.async_refresh()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert refresh_runs == ["1"]
|
||||
hass.states.async_set("sensor.one", "any")
|
||||
await hass.async_block_till_done()
|
||||
assert refresh_runs == ["1"]
|
||||
info.async_refresh()
|
||||
assert refresh_runs == ["1", "2"]
|
||||
hass.states.async_set("sensor.two", "any")
|
||||
await hass.async_block_till_done()
|
||||
assert refresh_runs == ["1", "2"]
|
||||
hass.states.async_set("sensor.three", "any")
|
||||
await hass.async_block_till_done()
|
||||
assert refresh_runs == ["1", "2"]
|
||||
|
||||
|
||||
async def test_track_template_unavailable_sates_has_default_rate_limit(hass):
|
||||
"""Test template watching for unavailable states has a rate limit by default."""
|
||||
hass.states.async_set("sensor.zero", "unknown")
|
||||
template_refresh = Template(
|
||||
"{{ states | selectattr('state', 'in', ['unavailable', 'unknown', 'none']) | list | count }}",
|
||||
hass,
|
||||
)
|
||||
|
||||
refresh_runs = []
|
||||
|
||||
@ha.callback
|
||||
def refresh_listener(event, updates):
|
||||
refresh_runs.append(updates.pop().result)
|
||||
|
||||
info = async_track_template_result(
|
||||
hass,
|
||||
[TrackTemplate(template_refresh, None)],
|
||||
refresh_listener,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
info.async_refresh()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert refresh_runs == ["1"]
|
||||
hass.states.async_set("sensor.one", "unknown")
|
||||
await hass.async_block_till_done()
|
||||
assert refresh_runs == ["1"]
|
||||
info.async_refresh()
|
||||
assert refresh_runs == ["1", "2"]
|
||||
hass.states.async_set("sensor.two", "any")
|
||||
await hass.async_block_till_done()
|
||||
assert refresh_runs == ["1", "2"]
|
||||
hass.states.async_set("sensor.three", "unknown")
|
||||
await hass.async_block_till_done()
|
||||
assert refresh_runs == ["1", "2"]
|
||||
info.async_refresh()
|
||||
await hass.async_block_till_done()
|
||||
assert refresh_runs == ["1", "2", "3"]
|
||||
|
||||
|
||||
async def test_specifically_referenced_entity_is_not_rate_limited(hass):
|
||||
"""Test template rate limit of 5 seconds."""
|
||||
hass.states.async_set("sensor.one", "none")
|
||||
|
||||
@@ -155,9 +155,25 @@ def test_iterating_all_states(hass):
|
||||
hass.states.async_set("sensor.temperature", 10)
|
||||
|
||||
info = render_to_info(hass, tmpl_str)
|
||||
assert_result_info(
|
||||
info, "10happy", entities=["test.object", "sensor.temperature"], all_states=True
|
||||
)
|
||||
assert_result_info(info, "10happy", entities=[], all_states=True)
|
||||
|
||||
|
||||
def test_iterating_all_states_unavailable(hass):
|
||||
"""Test iterating all states unavailable."""
|
||||
hass.states.async_set("test.object", "on")
|
||||
|
||||
tmpl_str = "{{ states | selectattr('state', 'in', ['unavailable', 'unknown', 'none']) | list | count }}"
|
||||
|
||||
info = render_to_info(hass, tmpl_str)
|
||||
|
||||
assert info.all_states is True
|
||||
assert info.rate_limit == template.DEFAULT_RATE_LIMIT
|
||||
|
||||
hass.states.async_set("test.object", "unknown")
|
||||
hass.states.async_set("sensor.temperature", 10)
|
||||
|
||||
info = render_to_info(hass, tmpl_str)
|
||||
assert_result_info(info, "1", entities=[], all_states=True)
|
||||
|
||||
|
||||
def test_iterating_domain_states(hass):
|
||||
@@ -176,7 +192,7 @@ def test_iterating_domain_states(hass):
|
||||
assert_result_info(
|
||||
info,
|
||||
"open10",
|
||||
entities=["sensor.back_door", "sensor.temperature"],
|
||||
entities=[],
|
||||
domains=["sensor"],
|
||||
)
|
||||
|
||||
@@ -1426,9 +1442,7 @@ async def test_expand(hass):
|
||||
info = render_to_info(
|
||||
hass, "{{ expand(states.group) | map(attribute='entity_id') | join(', ') }}"
|
||||
)
|
||||
assert_result_info(
|
||||
info, "test.object", {"test.object", "group.new_group"}, ["group"]
|
||||
)
|
||||
assert_result_info(info, "test.object", {"test.object"}, ["group"])
|
||||
assert info.rate_limit == template.DEFAULT_RATE_LIMIT
|
||||
|
||||
info = render_to_info(
|
||||
@@ -1587,7 +1601,7 @@ async def test_async_render_to_info_with_wildcard_matching_entity_id(hass):
|
||||
"""Test tracking template with a wildcard."""
|
||||
template_complex_str = r"""
|
||||
|
||||
{% for state in states %}
|
||||
{% for state in states.cover %}
|
||||
{% if state.entity_id | regex_match('.*\.office_') %}
|
||||
{{ state.entity_id }}={{ state.state }}
|
||||
{% endif %}
|
||||
@@ -1599,13 +1613,9 @@ async def test_async_render_to_info_with_wildcard_matching_entity_id(hass):
|
||||
hass.states.async_set("cover.office_skylight", "open")
|
||||
info = render_to_info(hass, template_complex_str)
|
||||
|
||||
assert not info.domains
|
||||
assert info.entities == {
|
||||
"cover.office_drapes",
|
||||
"cover.office_window",
|
||||
"cover.office_skylight",
|
||||
}
|
||||
assert info.all_states is True
|
||||
assert info.domains == {"cover"}
|
||||
assert info.entities == set()
|
||||
assert info.all_states is False
|
||||
assert info.rate_limit == template.DEFAULT_RATE_LIMIT
|
||||
|
||||
|
||||
@@ -1629,13 +1639,7 @@ async def test_async_render_to_info_with_wildcard_matching_state(hass):
|
||||
info = render_to_info(hass, template_complex_str)
|
||||
|
||||
assert not info.domains
|
||||
assert info.entities == {
|
||||
"cover.x_skylight",
|
||||
"binary_sensor.door",
|
||||
"cover.office_drapes",
|
||||
"cover.office_window",
|
||||
"cover.office_skylight",
|
||||
}
|
||||
assert info.entities == set()
|
||||
assert info.all_states is True
|
||||
assert info.rate_limit == template.DEFAULT_RATE_LIMIT
|
||||
|
||||
@@ -1643,13 +1647,7 @@ async def test_async_render_to_info_with_wildcard_matching_state(hass):
|
||||
info = render_to_info(hass, template_complex_str)
|
||||
|
||||
assert not info.domains
|
||||
assert info.entities == {
|
||||
"cover.x_skylight",
|
||||
"binary_sensor.door",
|
||||
"cover.office_drapes",
|
||||
"cover.office_window",
|
||||
"cover.office_skylight",
|
||||
}
|
||||
assert info.entities == set()
|
||||
assert info.all_states is True
|
||||
assert info.rate_limit == template.DEFAULT_RATE_LIMIT
|
||||
|
||||
@@ -1666,12 +1664,7 @@ async def test_async_render_to_info_with_wildcard_matching_state(hass):
|
||||
info = render_to_info(hass, template_cover_str)
|
||||
|
||||
assert info.domains == {"cover"}
|
||||
assert info.entities == {
|
||||
"cover.x_skylight",
|
||||
"cover.office_drapes",
|
||||
"cover.office_window",
|
||||
"cover.office_skylight",
|
||||
}
|
||||
assert info.entities == set()
|
||||
assert info.all_states is False
|
||||
assert info.rate_limit == template.DEFAULT_RATE_LIMIT
|
||||
|
||||
@@ -1965,9 +1958,7 @@ def test_generate_filter_iterators(hass):
|
||||
{% endfor %}
|
||||
""",
|
||||
)
|
||||
assert_result_info(
|
||||
info, "sensor.test_sensor=off,", ["sensor.test_sensor"], ["sensor"]
|
||||
)
|
||||
assert_result_info(info, "sensor.test_sensor=off,", [], ["sensor"])
|
||||
|
||||
info = render_to_info(
|
||||
hass,
|
||||
@@ -1977,9 +1968,7 @@ def test_generate_filter_iterators(hass):
|
||||
{% endfor %}
|
||||
""",
|
||||
)
|
||||
assert_result_info(
|
||||
info, "sensor.test_sensor=value,", ["sensor.test_sensor"], ["sensor"]
|
||||
)
|
||||
assert_result_info(info, "sensor.test_sensor=value,", [], ["sensor"])
|
||||
|
||||
|
||||
def test_generate_select(hass):
|
||||
@@ -2001,7 +1990,7 @@ def test_generate_select(hass):
|
||||
assert_result_info(
|
||||
info,
|
||||
"sensor.test_sensor",
|
||||
["sensor.test_sensor", "sensor.test_sensor_on"],
|
||||
[],
|
||||
["sensor"],
|
||||
)
|
||||
assert info.domains_lifecycle == {"sensor"}
|
||||
@@ -2542,7 +2531,9 @@ async def test_lights(hass):
|
||||
|
||||
tmp = template.Template(tmpl, hass)
|
||||
info = tmp.async_render_to_info()
|
||||
assert info.entities == set(states)
|
||||
assert info.entities == set()
|
||||
assert info.domains == {"light"}
|
||||
|
||||
assert "lights are on" in info.result()
|
||||
for i in range(10):
|
||||
assert f"sensor{i}" in info.result()
|
||||
|
||||
@@ -369,6 +369,36 @@ async def test_loading_configuration_from_storage(hass, hass_storage):
|
||||
assert hass.config.config_source == SOURCE_STORAGE
|
||||
|
||||
|
||||
async def test_loading_configuration_from_storage_with_yaml_only(hass, hass_storage):
|
||||
"""Test loading core and YAML config onto hass object."""
|
||||
hass_storage["core.config"] = {
|
||||
"data": {
|
||||
"elevation": 10,
|
||||
"latitude": 55,
|
||||
"location_name": "Home",
|
||||
"longitude": 13,
|
||||
"time_zone": "Europe/Copenhagen",
|
||||
"unit_system": "metric",
|
||||
},
|
||||
"key": "core.config",
|
||||
"version": 1,
|
||||
}
|
||||
await config_util.async_process_ha_core_config(
|
||||
hass, {"media_dirs": {"mymedia": "/usr"}, "allowlist_external_dirs": "/etc"}
|
||||
)
|
||||
|
||||
assert hass.config.latitude == 55
|
||||
assert hass.config.longitude == 13
|
||||
assert hass.config.elevation == 10
|
||||
assert hass.config.location_name == "Home"
|
||||
assert hass.config.units.name == CONF_UNIT_SYSTEM_METRIC
|
||||
assert hass.config.time_zone.zone == "Europe/Copenhagen"
|
||||
assert len(hass.config.allowlist_external_dirs) == 3
|
||||
assert "/etc" in hass.config.allowlist_external_dirs
|
||||
assert hass.config.media_dirs == {"mymedia": "/usr"}
|
||||
assert hass.config.config_source == SOURCE_STORAGE
|
||||
|
||||
|
||||
async def test_updating_configuration(hass, hass_storage):
|
||||
"""Test updating configuration stores the new configuration."""
|
||||
core_data = {
|
||||
|
||||
Reference in New Issue
Block a user