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
|
||||
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in New Issue