1 """Support for MQTT room presence detection."""
3 from __future__
import annotations
5 from datetime
import timedelta
6 from functools
import lru_cache
10 import voluptuous
as vol
15 PLATFORM_SCHEMA
as SENSOR_PLATFORM_SCHEMA,
34 _LOGGER = logging.getLogger(__name__)
36 ATTR_DISTANCE =
"distance"
39 CONF_AWAY_TIMEOUT =
"away_timeout"
41 DEFAULT_AWAY_TIMEOUT = 0
42 DEFAULT_NAME =
"Room Sensor"
44 DEFAULT_TOPIC =
"room_presence"
46 PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
48 vol.Required(CONF_DEVICE_ID): cv.string,
49 vol.Required(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
50 vol.Optional(CONF_AWAY_TIMEOUT, default=DEFAULT_AWAY_TIMEOUT): cv.positive_int,
51 vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
52 vol.Optional(CONF_UNIQUE_ID): cv.string,
54 ).extend(mqtt.MQTT_RO_SCHEMA.schema)
57 @lru_cache(maxsize=256)
59 """Return a slugified version of string, uppercased."""
63 MQTT_PAYLOAD = vol.Schema(
68 vol.Required(ATTR_ID): cv.string,
69 vol.Required(ATTR_DISTANCE): vol.Coerce(float),
71 extra=vol.ALLOW_EXTRA,
80 async_add_entities: AddEntitiesCallback,
81 discovery_info: DiscoveryInfoType |
None =
None,
83 """Set up MQTT room Sensor."""
87 if not await mqtt.async_wait_for_mqtt_client(hass):
88 _LOGGER.error(
"MQTT integration is not available")
93 config.get(CONF_NAME),
94 config[CONF_STATE_TOPIC],
95 config[CONF_DEVICE_ID],
97 config[CONF_AWAY_TIMEOUT],
98 config.get(CONF_UNIQUE_ID),
105 """Representation of a room sensor that is updated via MQTT."""
114 unique_id: str |
None,
116 """Initialize the sensor."""
125 timedelta(seconds=consider_home)
if consider_home
else None
131 """Subscribe to MQTT events."""
134 def update_state(device_id, room, distance):
135 """Update the sensor state."""
138 self.
_updated_updated = dt_util.utcnow()
143 def message_received(msg):
144 """Handle new MQTT messages."""
147 except vol.MultipleInvalid
as error:
148 _LOGGER.debug(
"Skipping update because of malformatted data: %s", error)
152 if device.get(CONF_DEVICE_ID) == self.
_device_id_device_id:
154 update_state(**device)
160 timediff = dt_util.utcnow() - self.
_updated_updated
162 device.get(ATTR_ROOM) == self.
_state_state
163 or device.get(ATTR_DISTANCE) < self.
_distance_distance
164 or timediff.total_seconds() >= self.
_timeout_timeout
166 update_state(**device)
168 await mqtt.async_subscribe(self.
hasshass, self.
_state_topic_state_topic, message_received, 1)
172 """Return the name of the sensor."""
173 return self.
_name_name
177 """Return the state attributes."""
178 return {ATTR_DISTANCE: self.
_distance_distance}
182 """Return the current room of the entity."""
186 """Update the state for absent devices."""
192 self.
_state_state = STATE_NOT_HOME
196 """Parse the room presence update."""
197 parts = topic.split(
"/")
200 distance = data.get(
"distance")
201 return {ATTR_DEVICE_ID: device_id, ATTR_ROOM: room, ATTR_DISTANCE: distance}
None async_added_to_hass(self)
None __init__(self, str|None name, str state_topic, str device_id, int timeout, int consider_home, str|None unique_id)
def extra_state_attributes(self)
None async_write_ha_state(self)
str _slugify_upper(str string)
None async_setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback async_add_entities, DiscoveryInfoType|None discovery_info=None)
dict[str, Any] _parse_update_data(str topic, dict[str, Any] data)