Raise ConditionError for zone errors (#46253)

* Raise ConditionError for zone errors

* Do not test missing state

* Handle multiple entities/zones
pull/46784/head
Anders Melchiorsen 2021-02-19 13:14:47 +01:00 committed by GitHub
parent 40068c2f1b
commit e7e3e09063
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 127 additions and 18 deletions

View File

@ -48,8 +48,11 @@ async def async_attach_trigger(hass, config, action, automation_info):
return
zone_state = hass.states.get(zone_entity_id)
from_match = condition.zone(hass, zone_state, from_state)
to_match = condition.zone(hass, zone_state, to_state)
from_match = (
condition.zone(hass, zone_state, from_state) if from_state else False
)
to_match = condition.zone(hass, zone_state, to_state) if to_state else False
if (
trigger_event == EVENT_ENTER

View File

@ -58,7 +58,7 @@ async def async_attach_trigger(
zone_state = hass.states.get(zone_entity_id)
from_match = condition.zone(hass, zone_state, from_s) if from_s else False
to_match = condition.zone(hass, zone_state, to_s)
to_match = condition.zone(hass, zone_state, to_s) if to_s else False
if (
event == EVENT_ENTER

View File

@ -588,23 +588,36 @@ def zone(
Async friendly.
"""
if zone_ent is None:
raise ConditionError("No zone specified")
if isinstance(zone_ent, str):
zone_ent_id = zone_ent
zone_ent = hass.states.get(zone_ent)
if zone_ent is None:
return False
if isinstance(entity, str):
entity = hass.states.get(entity)
if zone_ent is None:
raise ConditionError(f"Unknown zone {zone_ent_id}")
if entity is None:
return False
raise ConditionError("No entity specified")
if isinstance(entity, str):
entity_id = entity
entity = hass.states.get(entity)
if entity is None:
raise ConditionError(f"Unknown entity {entity_id}")
else:
entity_id = entity.entity_id
latitude = entity.attributes.get(ATTR_LATITUDE)
longitude = entity.attributes.get(ATTR_LONGITUDE)
if latitude is None or longitude is None:
return False
if latitude is None:
raise ConditionError(f"Entity {entity_id} has no 'latitude' attribute")
if longitude is None:
raise ConditionError(f"Entity {entity_id} has no 'longitude' attribute")
return zone_cmp.in_zone(
zone_ent, latitude, longitude, entity.attributes.get(ATTR_GPS_ACCURACY, 0)
@ -622,13 +635,26 @@ def zone_from_config(
def if_in_zone(hass: HomeAssistant, variables: TemplateVarsType = None) -> bool:
"""Test if condition."""
return all(
any(
zone(hass, zone_entity_id, entity_id)
for zone_entity_id in zone_entity_ids
)
for entity_id in entity_ids
)
errors = []
all_ok = True
for entity_id in entity_ids:
entity_ok = False
for zone_entity_id in zone_entity_ids:
try:
if zone(hass, zone_entity_id, entity_id):
entity_ok = True
except ConditionError as ex:
errors.append(str(ex))
if not entity_ok:
all_ok = False
# Raise the errors only if no definitive result was found
if errors and not all_ok:
raise ConditionError("Error in 'zone' condition: " + ", ".join(errors))
return all_ok
return if_in_zone

View File

@ -811,6 +811,86 @@ async def test_numeric_state_using_input_number(hass):
)
async def test_zone_raises(hass):
"""Test that zone raises ConditionError on errors."""
test = await condition.async_from_config(
hass,
{
"condition": "zone",
"entity_id": "device_tracker.cat",
"zone": "zone.home",
},
)
with pytest.raises(ConditionError, match="Unknown zone"):
test(hass)
hass.states.async_set(
"zone.home",
"zoning",
{"name": "home", "latitude": 2.1, "longitude": 1.1, "radius": 10},
)
with pytest.raises(ConditionError, match="Unknown entity"):
test(hass)
hass.states.async_set(
"device_tracker.cat",
"home",
{"friendly_name": "cat"},
)
with pytest.raises(ConditionError, match="latitude"):
test(hass)
hass.states.async_set(
"device_tracker.cat",
"home",
{"friendly_name": "cat", "latitude": 2.1},
)
with pytest.raises(ConditionError, match="longitude"):
test(hass)
hass.states.async_set(
"device_tracker.cat",
"home",
{"friendly_name": "cat", "latitude": 2.1, "longitude": 1.1},
)
# All okay, now test multiple failed conditions
assert test(hass)
test = await condition.async_from_config(
hass,
{
"condition": "zone",
"entity_id": ["device_tracker.cat", "device_tracker.dog"],
"zone": ["zone.home", "zone.work"],
},
)
with pytest.raises(ConditionError, match="dog"):
test(hass)
with pytest.raises(ConditionError, match="work"):
test(hass)
hass.states.async_set(
"zone.work",
"zoning",
{"name": "work", "latitude": 20, "longitude": 10, "radius": 25000},
)
hass.states.async_set(
"device_tracker.dog",
"work",
{"friendly_name": "dog", "latitude": 20.1, "longitude": 10.1},
)
assert test(hass)
async def test_zone_multiple_entities(hass):
"""Test with multiple entities in condition."""
test = await condition.async_from_config(