Monitor Sensor Thresholds
Temperature spikes, humidity creeps, CO2 builds up. Numeric sensors in Home Assistant report values continuously. You want a notification when a value crosses a limit, but only once per crossing. Not a flood while the value stays high.
The Code
from hassette import App, AppConfig, C, D, states
class ThresholdConfig(AppConfig):
entity_id: str = "sensor.living_room_temperature"
threshold: float = 28.0
notify_target: str = "mobile_app_my_phone"
class SensorThresholdApp(App[ThresholdConfig]):
"""Alert when a sensor value exceeds a configured threshold."""
async def on_initialize(self) -> None:
await self.bus.on_state_change(
self.app_config.entity_id,
handler=self.on_threshold_exceeded,
changed_to=C.Comparison("gt", self.app_config.threshold),
name="threshold_monitor",
)
async def on_threshold_exceeded(
self,
new_state: D.StateNew[states.SensorState],
entity_id: D.EntityId,
) -> None:
value = new_state.value
unit = new_state.attributes.unit_of_measurement or ""
name = new_state.attributes.friendly_name or entity_id
self.logger.warning("%s crossed threshold: %s%s", name, value, unit)
await self.api.call_service(
"notify",
self.app_config.notify_target,
title="Sensor Alert",
message=f"{name} is now {value}{unit} (threshold: {self.app_config.threshold}{unit})",
)
Run It
Save the code as sensor_threshold.py in your apps directory and register it in hassette.toml, overriding any config defaults in the .config block:
[hassette.apps.sensor_threshold]
filename = "sensor_threshold.py"
class_name = "SensorThresholdApp"
[hassette.apps.sensor_threshold.config]
entity_id = "sensor.living_room_temperature"
threshold = 28.0
The section name (sensor_threshold) is the app key the hassette CLI commands below take via --app. App Configuration covers registration in full.
How It Works
Every App instance carries self.bus (delivers HA events to handlers), self.api (calls HA services), and self.app_config (the validated config) — Hassette provides them at startup. Handlers are async def; Hassette runs the event loop.
ThresholdConfig exposes entity_id, threshold, and notify_target as settings. Each instance reads its own values from hassette.toml. The same app class watches different sensors in different rooms without code changes.
C.Comparison("gt", threshold) passed to changed_to is a gate. C is an alias for hassette.event_handling.conditions, a module of value-comparison functions. The bus evaluates the condition before invoking the handler. HA delivers state values as strings, so numeric comparisons coerce the value to a float first; values that can't convert (like "unavailable") evaluate to False and are dropped. Events where the new state value is not greater than the threshold are dropped. The handler fires only on the crossing itself, not on every subsequent reading above the limit.
D is an alias for hassette.event_handling.dependencies — Hassette inspects handler parameter types at registration and passes the extracted values in automatically. D.StateNew[states.SensorState] delivers the new state as a typed object. SensorState.value is a str (HA state values are always strings); the typed model provides .attributes with fields like unit_of_measurement and friendly_name. D.EntityId delivers the entity ID as a plain string. The handler declares what it needs, and the framework fills it in.
name= on on_state_change is required — it labels the listener in logs and in hassette listener output. Omitting it raises ListenerNameRequiredError at registration time.
self.api.call_service("notify", ...) sends the alert. new_state.attributes.unit_of_measurement and new_state.attributes.friendly_name come directly from the typed model, so the message reads naturally without manual attribute dict lookups.
Verify It's Working
Run these from your project directory while Hassette is running. Check that the handler registered with the threshold condition:
hassette listener --app sensor_threshold
Expected output shows one listener named threshold_monitor with the > 28.0 condition.
To force a crossing without waiting for the real sensor, set the value by hand in HA under Developer Tools → States. Then confirm the handler fired:
hassette log --app sensor_threshold --since 1h
The log shows the warning line with the sensor name, value, and unit.
Variations
Below-limit alert. Change "gt" to "lt" to alert when a value drops below the threshold. Battery level and water pressure are natural fits. Alert when either falls too low.
Alert once until recovery (hysteresis). A second listener with changed_to=C.Comparison("le", threshold) fires when the sensor recovers back to or below the limit. Store a flag on self when recovery fires, and check it in on_threshold_exceeded before sending. The notification skips if the sensor has not yet cleared since the last alert.
Multiple sensors. Pass a glob pattern like "sensor.temp_*" to on_state_change to cover a group of sensors with one registration. Alternatively, loop over a list[str] config field and call on_state_change once per entity. D.EntityId in the handler identifies which sensor triggered each alert.
See Also
- Filtering. Full reference for
C.Comparisonand all other conditions. - Dependency Injection. How
D.StateNewandD.EntityIdwork. - States. Typed state models and the
SensorStateattributes.