React to a Service Call
Intercept a Home Assistant service call and run custom logic in response. This recipe mirrors brightness and color temperature from a primary light to an accent light whenever someone turns the primary light on.
The code
from pydantic_settings import SettingsConfigDict
from hassette import App, AppConfig, P
from hassette.events import CallServiceEvent
class LightGroupConfig(AppConfig):
model_config = SettingsConfigDict(env_prefix="light_group_")
primary_light: str = "light.living_room_main"
accent_light: str = "light.living_room_accent"
class LightGroupApp(App[LightGroupConfig]):
"""Mirror accent light whenever the primary light is turned on."""
async def on_initialize(self) -> None:
await self.bus.on_call_service(
domain="light",
service="turn_on",
where=P.ServiceDataWhere({"entity_id": self.app_config.primary_light}),
handler=self.on_primary_turned_on,
name="primary_light_on",
)
async def on_primary_turned_on(self, event: CallServiceEvent) -> None:
service_data = event.payload.data.service_data
brightness = service_data.get("brightness")
color_temp = service_data.get("color_temp")
self.logger.info(
"Primary light turned on (brightness=%s, color_temp=%s) — syncing accent",
brightness,
color_temp,
)
call_data: dict[str, object] = {"entity_id": self.app_config.accent_light}
if brightness is not None:
call_data["brightness"] = brightness
if color_temp is not None:
call_data["color_temp"] = color_temp
await self.api.call_service("light", "turn_on", service_data=call_data)
How it works
on_call_service(domain="light", service="turn_on", ...)subscribes only tolight.turn_oncalls — no other service types reach the handler.P.ServiceDataWhere({"entity_id": ...})narrows the subscription further, so the handler only fires when the call targets the configured primary light.- The handler receives a
CallServiceEvent.event.payload.data.service_datais the dict of arguments the caller passed — brightness, color temperature, transitions, and so on. - The handler forwards whichever parameters were present to
light.turn_onon the accent light, leaving out keys that were not set in the original call. - Config fields (
primary_light,accent_light) let you change entity IDs via environment variables (LIGHT_GROUP_PRIMARY_LIGHT,LIGHT_GROUP_ACCENT_LIGHT) without touching code.
Variations
Watch any entity in a group — replace the exact entity ID in ServiceDataWhere with a glob pattern:
where=P.ServiceDataWhere({"entity_id": "light.living_room_*"})
React to turn-off too — add a second subscription for service="turn_off" pointing to its own handler, and call light.turn_off on the accent light there.
See Also
- Filtering & Advanced Subscriptions — full reference for
on_call_service,P.ServiceDataWhere, andP.ServiceMatches - Bus Overview — subscription options, debounce, throttle, and
once