Skip to content

Vacation Mode Toggle

Watch an input_boolean helper in Home Assistant and use its state to start and stop a presence-simulation loop — no redeployment needed to toggle the behavior.

The Code

import random

from hassette import App, AppConfig
from hassette.scheduler import ScheduledJob

VACATION_TOGGLE = "input_boolean.vacation_mode"
LIGHTS = ("light.living_room", "light.kitchen", "light.bedroom")
CHECK_INTERVAL = 900  # seconds (15 minutes)
PRESENCE_JOB_NAME = "vacation_presence_sim"


class VacationModeConfig(AppConfig):
    vacation_toggle: str = VACATION_TOGGLE
    lights: tuple[str, ...] = LIGHTS
    check_interval: float = CHECK_INTERVAL


class VacationMode(App[VacationModeConfig]):
    _presence_job: ScheduledJob | None = None

    async def on_initialize(self) -> None:
        await self.bus.on_state_change(
            self.app_config.vacation_toggle,
            changed_to="on",
            handler=self.on_vacation_start,
            name="vacation_start",
        )
        await self.bus.on_state_change(
            self.app_config.vacation_toggle,
            changed_to="off",
            handler=self.on_vacation_end,
            name="vacation_end",
        )

    async def on_vacation_start(self) -> None:
        self.logger.info("Vacation mode enabled — starting presence simulation")
        self._presence_job = await self.scheduler.run_every(
            self.simulate_presence,
            seconds=self.app_config.check_interval,
            name=PRESENCE_JOB_NAME,
        )

    async def on_vacation_end(self) -> None:
        self.logger.info("Vacation mode disabled — stopping presence simulation")
        if self._presence_job is not None:
            self._presence_job.cancel()
            self._presence_job = None
        for light in self.app_config.lights:
            await self.api.turn_off(light, domain="light")

    async def simulate_presence(self) -> None:
        light = random.choice(self.app_config.lights)
        state = await self.api.get_state(f"{light}")
        if state.value is True:
            await self.api.turn_off(light, domain="light")
            self.logger.debug("Presence sim: turned off %s", light)
        else:
            await self.api.turn_on(light, domain="light")
            self.logger.debug("Presence sim: turned on %s", light)

How It Works

  • Two on_state_change subscriptions watch input_boolean.vacation_mode — one fires when it turns on, the other when it turns off.
  • When vacation mode turns on, run_every schedules simulate_presence to run on a fixed interval, and the returned ScheduledJob is stored on the instance.
  • Each tick, simulate_presence picks a random light and toggles it — on if currently off, off if currently on — to create irregular activity.
  • When vacation mode turns off, the stored job is cancelled and all lights are turned off to restore a clean state.
  • The entity IDs and interval are configurable through VacationModeConfig, so you can adjust the light list and simulation frequency without touching the code.

Variations

Provision the helper from code — instead of creating input_boolean.vacation_mode manually in the HA UI, use api.create_input_boolean in on_initialize to provision it automatically on first run. See Managing Helpers for the idempotent-bootstrap pattern.

Schedule vacation windows — replace the manual toggle with run_cron entries that enable and disable presence simulation at fixed times each day (e.g., evening hours only). See Scheduler Methods for cron syntax.

See Also

  • Managing Helpers — create and manage input_boolean and other helper types from your app
  • Buson_state_change filtering, debounce, and throttle options
  • States — read entity state from the local cache without an API call