mirror of
https://github.com/home-assistant/core.git
synced 2025-07-08 05:47:10 +00:00
Validate MQTT device tracker location data before assigning (#141980)
* Validate MQTT device tracker location data before assigning * Log warning for invalid gps_accuracy
This commit is contained in:
parent
bf0d2e9bd2
commit
ea38639395
@ -28,7 +28,12 @@ from homeassistant.helpers.typing import ConfigType, VolSchemaType
|
|||||||
|
|
||||||
from . import subscription
|
from . import subscription
|
||||||
from .config import MQTT_BASE_SCHEMA
|
from .config import MQTT_BASE_SCHEMA
|
||||||
from .const import CONF_JSON_ATTRS_TOPIC, CONF_PAYLOAD_RESET, CONF_STATE_TOPIC
|
from .const import (
|
||||||
|
CONF_JSON_ATTRS_TEMPLATE,
|
||||||
|
CONF_JSON_ATTRS_TOPIC,
|
||||||
|
CONF_PAYLOAD_RESET,
|
||||||
|
CONF_STATE_TOPIC,
|
||||||
|
)
|
||||||
from .entity import MqttEntity, async_setup_entity_entry_helper
|
from .entity import MqttEntity, async_setup_entity_entry_helper
|
||||||
from .models import MqttValueTemplate, ReceiveMessage
|
from .models import MqttValueTemplate, ReceiveMessage
|
||||||
from .schemas import MQTT_ENTITY_COMMON_SCHEMA
|
from .schemas import MQTT_ENTITY_COMMON_SCHEMA
|
||||||
@ -151,16 +156,54 @@ class MqttDeviceTracker(MqttEntity, TrackerEntity):
|
|||||||
self, extra_state_attributes: dict[str, Any]
|
self, extra_state_attributes: dict[str, Any]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Extract the location from the extra state attributes."""
|
"""Extract the location from the extra state attributes."""
|
||||||
self._attr_latitude = extra_state_attributes.get(ATTR_LATITUDE)
|
|
||||||
self._attr_longitude = extra_state_attributes.get(ATTR_LONGITUDE)
|
|
||||||
if (
|
if (
|
||||||
ATTR_LATITUDE in extra_state_attributes
|
ATTR_LATITUDE in extra_state_attributes
|
||||||
or ATTR_LONGITUDE in extra_state_attributes
|
or ATTR_LONGITUDE in extra_state_attributes
|
||||||
):
|
):
|
||||||
# Reset manual set location
|
latitude: float | None
|
||||||
|
longitude: float | None
|
||||||
|
gps_accuracy: int
|
||||||
|
# Reset manually set location to allow automatic zone detection
|
||||||
self._attr_location_name = None
|
self._attr_location_name = None
|
||||||
|
if isinstance(
|
||||||
|
latitude := extra_state_attributes.get(ATTR_LATITUDE), (int, float)
|
||||||
|
) and isinstance(
|
||||||
|
longitude := extra_state_attributes.get(ATTR_LONGITUDE), (int, float)
|
||||||
|
):
|
||||||
|
self._attr_latitude = latitude
|
||||||
|
self._attr_longitude = longitude
|
||||||
|
else:
|
||||||
|
# Invalid or incomplete coordinates, reset location
|
||||||
|
self._attr_latitude = None
|
||||||
|
self._attr_longitude = None
|
||||||
|
_LOGGER.warning(
|
||||||
|
"Extra state attributes received at % and template %s "
|
||||||
|
"contain invalid or incomplete location info. Got %s",
|
||||||
|
self._config.get(CONF_JSON_ATTRS_TEMPLATE),
|
||||||
|
self._config.get(CONF_JSON_ATTRS_TOPIC),
|
||||||
|
extra_state_attributes,
|
||||||
|
)
|
||||||
|
|
||||||
|
if ATTR_GPS_ACCURACY in extra_state_attributes:
|
||||||
|
if isinstance(
|
||||||
|
gps_accuracy := extra_state_attributes[ATTR_GPS_ACCURACY],
|
||||||
|
(int, float),
|
||||||
|
):
|
||||||
|
self._attr_location_accuracy = gps_accuracy
|
||||||
|
else:
|
||||||
|
_LOGGER.warning(
|
||||||
|
"Extra state attributes received at % and template %s "
|
||||||
|
"contain invalid GPS accuracy setting, "
|
||||||
|
"gps_accuracy was set to 0 as the default. Got %s",
|
||||||
|
self._config.get(CONF_JSON_ATTRS_TEMPLATE),
|
||||||
|
self._config.get(CONF_JSON_ATTRS_TOPIC),
|
||||||
|
extra_state_attributes,
|
||||||
|
)
|
||||||
|
self._attr_location_accuracy = 0
|
||||||
|
|
||||||
|
else:
|
||||||
|
self._attr_location_accuracy = 0
|
||||||
|
|
||||||
self._attr_location_accuracy = extra_state_attributes.get(ATTR_GPS_ACCURACY, 0)
|
|
||||||
self._attr_extra_state_attributes = {
|
self._attr_extra_state_attributes = {
|
||||||
attribute: value
|
attribute: value
|
||||||
for attribute, value in extra_state_attributes.items()
|
for attribute, value in extra_state_attributes.items()
|
||||||
|
@ -450,14 +450,82 @@ async def test_setting_device_tracker_location_via_lat_lon_message(
|
|||||||
assert state.attributes["latitude"] == 50.1
|
assert state.attributes["latitude"] == 50.1
|
||||||
assert state.attributes["longitude"] == -2.1
|
assert state.attributes["longitude"] == -2.1
|
||||||
assert state.attributes["gps_accuracy"] == 0
|
assert state.attributes["gps_accuracy"] == 0
|
||||||
|
assert state.attributes["source_type"] == "gps"
|
||||||
assert state.state == STATE_NOT_HOME
|
assert state.state == STATE_NOT_HOME
|
||||||
|
|
||||||
|
# incomplete coordinates results in unknown state
|
||||||
async_fire_mqtt_message(hass, "attributes-topic", '{"longitude": -117.22743}')
|
async_fire_mqtt_message(hass, "attributes-topic", '{"longitude": -117.22743}')
|
||||||
state = hass.states.get("device_tracker.test")
|
state = hass.states.get("device_tracker.test")
|
||||||
|
assert "latitude" not in state.attributes
|
||||||
|
assert "longitude" not in state.attributes
|
||||||
|
assert state.attributes["source_type"] == "gps"
|
||||||
assert state.state == STATE_UNKNOWN
|
assert state.state == STATE_UNKNOWN
|
||||||
|
|
||||||
async_fire_mqtt_message(hass, "attributes-topic", '{"latitude":32.87336}')
|
async_fire_mqtt_message(hass, "attributes-topic", '{"latitude":32.87336}')
|
||||||
state = hass.states.get("device_tracker.test")
|
state = hass.states.get("device_tracker.test")
|
||||||
|
assert "latitude" not in state.attributes
|
||||||
|
assert "longitude" not in state.attributes
|
||||||
|
assert state.attributes["source_type"] == "gps"
|
||||||
|
assert state.state == STATE_UNKNOWN
|
||||||
|
|
||||||
|
# invalid coordinates results in unknown state
|
||||||
|
async_fire_mqtt_message(
|
||||||
|
hass, "attributes-topic", '{"longitude": -117.22743, "latitude":null}'
|
||||||
|
)
|
||||||
|
state = hass.states.get("device_tracker.test")
|
||||||
|
assert "latitude" not in state.attributes
|
||||||
|
assert "longitude" not in state.attributes
|
||||||
|
assert state.attributes["source_type"] == "gps"
|
||||||
|
assert state.state == STATE_UNKNOWN
|
||||||
|
|
||||||
|
# Test number validation
|
||||||
|
async_fire_mqtt_message(
|
||||||
|
hass,
|
||||||
|
"attributes-topic",
|
||||||
|
'{"latitude": "32.87336","longitude": "-117.22743", "gps_accuracy": "1.5", "source_type": "router"}',
|
||||||
|
)
|
||||||
|
state = hass.states.get("device_tracker.test")
|
||||||
|
assert "latitude" not in state.attributes
|
||||||
|
assert "longitude" not in state.attributes
|
||||||
|
assert "gps_accuracy" not in state.attributes
|
||||||
|
# assert source_type is overridden by discovery
|
||||||
|
assert state.attributes["source_type"] == "router"
|
||||||
|
assert state.state == STATE_UNKNOWN
|
||||||
|
|
||||||
|
# Test with invalid GPS accuracy should default to 0,
|
||||||
|
# but location updates as expected
|
||||||
|
async_fire_mqtt_message(
|
||||||
|
hass,
|
||||||
|
"attributes-topic",
|
||||||
|
'{"latitude": 32.871234,"longitude": -117.21234, "gps_accuracy": "invalid", "source_type": "router"}',
|
||||||
|
)
|
||||||
|
state = hass.states.get("device_tracker.test")
|
||||||
|
assert state.state == STATE_NOT_HOME
|
||||||
|
assert state.attributes["latitude"] == 32.871234
|
||||||
|
assert state.attributes["longitude"] == -117.21234
|
||||||
|
assert state.attributes["gps_accuracy"] == 0
|
||||||
|
assert state.attributes["source_type"] == "router"
|
||||||
|
|
||||||
|
# Test with invalid latitude
|
||||||
|
async_fire_mqtt_message(
|
||||||
|
hass,
|
||||||
|
"attributes-topic",
|
||||||
|
'{"latitude": null,"longitude": "-117.22743", "gps_accuracy": 1, "source_type": "router"}',
|
||||||
|
)
|
||||||
|
state = hass.states.get("device_tracker.test")
|
||||||
|
assert "latitude" not in state.attributes
|
||||||
|
assert "longitude" not in state.attributes
|
||||||
|
assert state.state == STATE_UNKNOWN
|
||||||
|
|
||||||
|
# Test with invalid longitude
|
||||||
|
async_fire_mqtt_message(
|
||||||
|
hass,
|
||||||
|
"attributes-topic",
|
||||||
|
'{"latitude": 32.87336,"longitude": "unknown", "gps_accuracy": 1, "source_type": "router"}',
|
||||||
|
)
|
||||||
|
state = hass.states.get("device_tracker.test")
|
||||||
|
assert "latitude" not in state.attributes
|
||||||
|
assert "longitude" not in state.attributes
|
||||||
assert state.state == STATE_UNKNOWN
|
assert state.state == STATE_UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user