Raise ConditionError for zone errors (#46253)
* Raise ConditionError for zone errors * Do not test missing state * Handle multiple entities/zonespull/46784/head
parent
40068c2f1b
commit
e7e3e09063
|
@ -48,8 +48,11 @@ async def async_attach_trigger(hass, config, action, automation_info):
|
||||||
return
|
return
|
||||||
|
|
||||||
zone_state = hass.states.get(zone_entity_id)
|
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 (
|
if (
|
||||||
trigger_event == EVENT_ENTER
|
trigger_event == EVENT_ENTER
|
||||||
|
|
|
@ -58,7 +58,7 @@ async def async_attach_trigger(
|
||||||
|
|
||||||
zone_state = hass.states.get(zone_entity_id)
|
zone_state = hass.states.get(zone_entity_id)
|
||||||
from_match = condition.zone(hass, zone_state, from_s) if from_s else False
|
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 (
|
if (
|
||||||
event == EVENT_ENTER
|
event == EVENT_ENTER
|
||||||
|
|
|
@ -588,23 +588,36 @@ def zone(
|
||||||
|
|
||||||
Async friendly.
|
Async friendly.
|
||||||
"""
|
"""
|
||||||
|
if zone_ent is None:
|
||||||
|
raise ConditionError("No zone specified")
|
||||||
|
|
||||||
if isinstance(zone_ent, str):
|
if isinstance(zone_ent, str):
|
||||||
|
zone_ent_id = zone_ent
|
||||||
zone_ent = hass.states.get(zone_ent)
|
zone_ent = hass.states.get(zone_ent)
|
||||||
|
|
||||||
if zone_ent is None:
|
if zone_ent is None:
|
||||||
return False
|
raise ConditionError(f"Unknown zone {zone_ent_id}")
|
||||||
|
|
||||||
if isinstance(entity, str):
|
|
||||||
entity = hass.states.get(entity)
|
|
||||||
|
|
||||||
if entity is None:
|
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)
|
latitude = entity.attributes.get(ATTR_LATITUDE)
|
||||||
longitude = entity.attributes.get(ATTR_LONGITUDE)
|
longitude = entity.attributes.get(ATTR_LONGITUDE)
|
||||||
|
|
||||||
if latitude is None or longitude is None:
|
if latitude is None:
|
||||||
return False
|
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(
|
return zone_cmp.in_zone(
|
||||||
zone_ent, latitude, longitude, entity.attributes.get(ATTR_GPS_ACCURACY, 0)
|
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:
|
def if_in_zone(hass: HomeAssistant, variables: TemplateVarsType = None) -> bool:
|
||||||
"""Test if condition."""
|
"""Test if condition."""
|
||||||
return all(
|
errors = []
|
||||||
any(
|
|
||||||
zone(hass, zone_entity_id, entity_id)
|
all_ok = True
|
||||||
for zone_entity_id in zone_entity_ids
|
for entity_id in entity_ids:
|
||||||
)
|
entity_ok = False
|
||||||
for entity_id in entity_ids
|
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
|
return if_in_zone
|
||||||
|
|
||||||
|
|
|
@ -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):
|
async def test_zone_multiple_entities(hass):
|
||||||
"""Test with multiple entities in condition."""
|
"""Test with multiple entities in condition."""
|
||||||
test = await condition.async_from_config(
|
test = await condition.async_from_config(
|
||||||
|
|
Loading…
Reference in New Issue