Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Features

Other features

Home of the useless but fun. 🤡

These are some Spook-specific features that don’t fit in any of the other categories. They are not particularly useful, but they are fun to play with (and maybe you actually have a use case for them).

They originally served as the proof of concept for Spook and left them in for the fun of it.

Actions

Spook offers the following useless actions:

Boo!

This acti will just always scare Home Assistant, causing this action call to fail. Calling this action in any of your automations will thus cause your automation to stop and error.

Screenshot of the Spook Boo! action in the developer tools.

Action properties

Action

Boo! 👻

Action name

spook.boo

Action targets

No

Action response

No response

Spook's influence

Newly added action

Developer tools

Try this action Open your Home Assistant instance and show your actions developer tools with a specific action selected.

Random fail

This action call will randomly fail (and thus randomly stop your automation or script).

Screenshot of the Spook random fail action in the developer tools.

Action properties

Action

Random fail 👻

Action name

spook.random_fail

Action targets

No

Action response

No response

Spook's influence

Newly added action

Developer tools

Try this action Open your Home Assistant instance and show your actions developer tools with a specific action selected.

Wait for a condition

Waits until a condition is true, and carries on straight away if it already is.

Action properties

Action

Wait for a condition 👻

Action name

spook.wait_for_condition

Action targets

No

Action response

Optional response

Spook's influence

Newly added action

Developer tools

Try this action Open your Home Assistant instance and show your actions developer tools with a specific action selected.

Action data parameters

Attribute

Type

Required

Default / Example

condition

condition

Yes

Any condition, built the ordinary way

timeout

string

No

Waits indefinitely when left out

Home Assistant can wait for a template to turn true, and it can wait for a trigger. It cannot wait for a condition, so anything you can express with the condition building blocks has to be rewritten as a template before you can wait on it.

Takes one condition or a list of them, and a list means all of them, the same as anywhere else. Which is also what the visual editor sends, so both shapes have to work.

The other half of what this fixes is that it looks first. wait_for_trigger always waits for something to happen, so if the thing you are waiting for has already happened you wait forever, which is why people wrap it in an if. This checks the condition before waiting, so an automation that arrives late still carries on.

Returns completed when it is given a response_variable, which says whether the condition arrived or the timeout did.

Triggers

Spook offers the following triggers that are not tied to a specific integration:

Cron schedule

Fires on a crontab schedule.

Trigger properties

Trigger

Cron schedule 👻

Trigger name

spook.cron

Spook's influence

Newly added trigger

Trigger options

Attribute

Type

Required

Default / Example

schedule

string

Yes

0 7 * * 1-5

Home Assistant’s own time triggers cover a time of day and a time pattern. Between them they cannot say “every weekday at seven” or “the last Friday of the month”. A crontab expression says either in one line, and anybody who has written a crontab already knows the syntax.

Five fields, in the usual order: minute, hour, day of month, month, day of week. Ranges and steps work, and so do the day-of-week extensions, so MON#2 is the second Monday of the month and 5L the last Friday.

When it fires, trigger.schedule holds the expression and trigger.now the local time it fired at.

One crontab rule catches people out, and it is not ours. Give both a day of the month and a day of the week, and cron combines them with “or”, not “and”. So 0 7 15 * 1 runs at seven on the 15th of the month, and at seven every Monday as well. Leave one of the two as * if you only mean the other.

That “or” holds only while both fields are plain lists of days. Let either one start with a *, a step like */2 included, and the two combine with “and” instead: 0 7 15 * */2 is the 15th, but only when it falls on a Sunday, Tuesday, Thursday or Saturday.

Entity fell silent

Fires when nothing has written to an entity for a while.

Trigger properties

Trigger

Entity fell silent 👻

Trigger name

spook.stale

Targets

Entities, devices, areas, floors and labels

Spook's influence

Newly added trigger

Trigger options

Attribute

Type

Required

Default / Example

for

string

Yes

01:00:00

Home Assistant will tell you an entity went unavailable. Plenty of things die without ever saying so: an integration that quietly stopped polling, a battery device that dropped off the network, an MQTT topic nobody publishes to any more. The state sits there looking perfectly healthy, holding a reading from last Tuesday.

This watches for silence rather than for a value. A sensor that keeps reporting the same 21.5 every minute is alive and is left alone; one that stops reporting altogether fires after the duration you set. Point it at whole areas, floors or labels and the entities underneath are watched one at a time, each firing separately when it falls silent.

When it fires, trigger.entity_id names the entity that went quiet, trigger.last_reported is when it last spoke, and trigger.for is the duration you configured.

Integration failed to set up

Fires when a configuration entry has been unable to set itself up for a while.

Trigger properties

Trigger

Integration failed to set up 👻

Trigger name

spook.integration_failed

Spook's influence

Newly added trigger

Trigger options

Attribute

Type

Required

Default / Example

for

string

Yes

00:15:00

entry_id

string | list of strings

No

Every configuration entry

An integration that cannot reach its hardware fails to set up, and Home Assistant keeps retrying it on a backoff that tops out at ten minutes. A device switched off overnight therefore fails dozens of times before morning, which is why this trigger waits rather than firing on each attempt. Set for to how long you are willing to let something be broken before you want to hear about it, and the ordinary hiccups at start-up sort themselves out well inside it.

Every configuration entry is watched unless you name some in entry_id. Each one is reported on its own, and only once per spell of trouble: an entry has to come back before it can be reported again.

When it fires, trigger.domain is the integration, trigger.title the name of the entry, trigger.entry_id its identifier, trigger.state the state it is stuck in, trigger.reason whatever Home Assistant recorded about why, and trigger.for the duration you configured.

Repair issue created

Fires when a new repair issue turns up.

Trigger properties

Trigger

Repair issue created 👻

Trigger name

spook.repair_issue_created

Spook's influence

Newly added trigger

Trigger options

Attribute

Type

Required

Default / Example

domain

string | list of strings

No

Defaults to every integration

severity

string | list of strings

No

critical, error, warning

Home Assistant collects repair issues on the repairs page and waits to be visited. This is for the ones you would rather hear about: an integration reporting it needs attention, or Spook finding a reference to something that no longer exists.

Only genuinely new issues fire it. An integration re-reporting an issue that is already there counts as an update rather than a creation, which is what keeps a repair checked on a schedule from firing on every pass.

When it fires, trigger.domain is the integration that reported it, trigger.issue_id names the issue, and trigger.severity, trigger.is_fixable, trigger.breaks_in_ha_version, trigger.learn_more_url and trigger.translation_key carry the rest of what the registry knows.

Repair issue resolved

Fires when a repair issue goes away.

Trigger properties

Trigger

Repair issue resolved 👻

Trigger name

spook.repair_issue_removed

Spook's influence

Newly added trigger

Trigger options

Attribute

Type

Required

Default / Example

domain

string | list of strings

No

Defaults to every integration

Something got fixed, or whatever was complaining stopped complaining. Handy for closing a notification you opened when the issue turned up.

When it fires, trigger.domain and trigger.issue_id say which issue it was.

Conditions

Spook offers the following conditions that are not tied to a specific integration:

Triggered by a user

Passes when a person set this run going.

Condition properties

Condition

Triggered by a user 👻

Condition name

spook.triggered_by_user

Spook's influence

Newly added condition

Condition options

Attribute

Type

Required

Default / Example

person

list of strings

No

Defaults to any user

What this can see is the person behind the trigger. Somebody flipping a switch, tapping something in the app, or calling an action from the API leaves their user account on the state change or event that follows, so an automation reacting to it finds them. A template trigger inherits it too, when the change that made the template true was theirs. A schedule, the sun, or an integration acting on its own leaves nobody.

If the person attribute is not provided, the condition passes for anybody. Name one or more people to narrow it, and Spook matches against the user account each of them is linked to.

Not triggered by a user

Passes when nobody set this run going.

Condition properties

Condition

Not triggered by a user 👻

Condition name

spook.not_triggered_by_user

Spook's influence

Newly added condition

This condition has no options. It passes for anything Spook cannot pin on a person: a schedule, the sun, an integration acting on its own, or a run forced with the Run button.

“Nobody started this” is a different question from “not this particular person”, which is why this condition takes no options. To exclude specific people, wrap Triggered by a user in Home Assistant’s own Not condition:

1
2
3
4
5
condition: not
conditions:
  - condition: spook.triggered_by_user
    options:
      person: person.frenck

That passes for anybody who is not Frenck, and also when nobody was behind it at all, which is what “not Frenck” means.

Cooldown

Passes when this automation or script has not run within a given time.

Condition properties

Condition

Cooldown 👻

Condition name

spook.cooldown

Spook's influence

Newly added condition

Condition options

Attribute

Type

Required

Default / Example

duration

duration

Yes

00:05:00

A built-in cooldown, so an automation does not re-fire more often than you want it to. An automation that has never run passes, because there is no last run to be too close to.

It replaces the most copy-pasted template condition there is:

{{ now() - this.attributes.last_triggered >= timedelta(minutes=5) }}

Chance

Passes a set percentage of the time, chosen at random on every check.

Condition properties

Condition

Chance 👻

Condition name

spook.chance

Spook's influence

Newly added condition

Condition options

Attribute

Type

Required

Default / Example

percentage

float

Yes

20

For when you want a bit of variation rather than the same thing every evening.

Triggered by an automation

Passes when another automation set this run going.

Condition properties

Condition

Triggered by an automation 👻

Condition name

spook.triggered_by_automation

Spook's influence

Newly added condition

Condition options

Attribute

Type

Required

Default / Example

automation

list of strings

No

Defaults to any automation

Home Assistant gives every automation run its own context, and everything that run writes carries it along. So an automation reacting to a change another automation made can find out who did it, and it keeps working when there is a script in between, because the script runs under the automation’s context rather than making one of its own.

What Home Assistant does not do is turn a context back into the automation it belongs to, so Spook listens for automations announcing themselves and remembers the mapping for the last few hundred runs. That is far more than enough: the run being asked about happened a fraction of a second earlier.

If the automation attribute is not provided, the condition passes for any automation. Name one or more to narrow it.

Repair issue outstanding

Passes while a repair issue is outstanding.

Condition properties

Condition

Repair issue outstanding 👻

Condition name

spook.repair_issue_present

Spook's influence

Newly added condition

Condition options

Attribute

Type

Required

Default / Example

domain

string | list of strings

No

Defaults to every integration

severity

string | list of strings

No

critical, error, warning

For holding something back until the house is in order: not running a nightly job while an integration is complaining, or nagging once a day for as long as anything is wrong.

Leave the options out and any outstanding issue passes it. Name integrations, severities, or both, and all the named parts have to match the same issue.

Run allowance left

Passes while this automation has runs to spare.

Condition properties

Condition

Run allowance left 👻

Condition name

spook.quota

Spook's influence

Newly added condition

Condition options

Attribute

Type

Required

Default / Example

limit

integer

Yes

5

period

string

Yes

24:00:00

The counterpart to Cooldown: that one spaces runs out, this one caps how many there are. Handy for something that is fine now and then but not fifty times a day, like a notification about a door that keeps being opened.

The window rolls. A limit of five over a day means no more than five runs in any twenty-four hours, not five between midnights, so the allowance comes back gradually as the oldest run drops out of the window rather than all at once.

Runs are counted from what actually ran. Home Assistant does not announce an automation whose conditions turned the run down, so an attempt something else held back costs nothing.

Triggers in order

Fires when several triggers happen one after another, in the order given.

Trigger properties

Trigger

Triggers in order

Trigger name

spook.sequence

Spook's influence

Newly added trigger

Trigger options

Attribute

Type

Required

Default / Example

steps

trigger

Yes

Two or more triggers, in the order they have to happen

timeout

string

No

Waits indefinitely when left out

reset

trigger

No

Nothing abandons a run when left out

Home Assistant fires on one thing happening. It does not fire on one thing happening after another, which is most of what a house actually does: the door opened and then somebody moved in the hall, the washing machine started and then went quiet, the alarm armed and then a window opened. Written by hand that needs a helper entity per step and an automation to set each one.

Only the step being waited for is listening. So a later step firing before an earlier one is not a match, and neither is the same step firing twice. Two steps is the minimum, because one trigger in order is just a trigger.

Once the last step lands the trigger fires and goes back to waiting for the first, so it works as many times as you like.

When it fires, trigger.steps holds what each step reported, in order, and trigger.duration is how long the whole thing took. The step that completed the sequence is also lifted to the top, so trigger.entity_id, trigger.from_state and trigger.to_state describe the thing that finished it, and spook.triggered_by_user and friends find the person behind it there.

While a condition holds

Fires when a condition turns true, and keeps firing on an interval for as long as it stays true.

Trigger properties

Trigger

While a condition holds

Trigger name

spook.while

Spook's influence

Newly added trigger

Trigger options

Attribute

Type

Required

Default / Example

condition

condition

Yes

Any condition, built the ordinary way

every

string

Yes

00:10:00

The nagging trigger. The garage is open and you want to hear about it again in ten minutes, and ten minutes after that, until somebody closes it.

Home Assistant can already do this inside a script, with a repeat holding a while and a delay. What that costs is a script run held open for as long as the garage is open: the automation’s mode has to allow for it, a reload ends it, and core stops a loop that has gone round ten thousand times. As a trigger none of that applies, because nothing is being held open between one firing and the next.

It fires the moment the condition arrives, not while it already holds, so loading an automation whose condition happens to be true does not set it off. Counting starts again at one each time the condition comes back.

When it fires, trigger.times is how many times it has fired this spell, starting at one, and trigger.every is the interval. The first firing also carries the state change that made the condition turn, in the same way Condition turned true does, so spook.triggered_by_user finds the person who caused it there. The firings after that carry nobody, because nobody makes a clock come round.

Watchdog

Fires when something that was expected to happen does not happen in time.

Trigger properties

Trigger

Watchdog

Trigger name

spook.watchdog

Spook's influence

Newly added trigger

Trigger options

Attribute

Type

Required

Default / Example

arm

trigger

Yes

What starts the watch

expect

trigger

Yes

What is expected to follow

within

string

Yes

00:02:00

Every trigger Home Assistant has fires because something happened. The interesting failures are the other kind: the back door opened and nobody walked into the hall, the washing machine started and never finished, the nightly backup began and never reported in. Written by hand that is a helper entity, a timer, and two automations to keep them in step.

Arming starts a clock. The expected trigger arriving before it runs out calls the watch off and nothing fires. The clock running out is what fires it.

Arming again while already waiting starts the wait over rather than running a second one, because the wait is measured from the arming and the latest one is the one that counts. The expected trigger arriving while nothing is being waited for does nothing at all.

When it fires, trigger.armed_by is what the arming trigger reported and trigger.within is the wait it was given. Nothing else is carried, and no user: a watchdog fires because nothing happened, at a moment a clock came round, and nobody makes a clock come round. An automation that wants to name the person can read trigger.armed_by.

Entity will not settle

Fires when an entity changes state more often than it should within a stretch of time.

Trigger properties

Trigger

Entity will not settle

Trigger name

spook.flapping

Targets

Entities, devices, areas, floors and labels

Spook's influence

Newly added trigger

Trigger options

Attribute

Type

Required

Default / Example

changes

integer

Yes

Between 2 and 1000

within

string

Yes

00:05:00

A sensor that goes on and off and on again, a device that drops off the network and comes back, a binary sensor sitting right on its threshold. Each change on its own looks perfectly fine, and Home Assistant has no way to say “this one has changed five times in five minutes and something is wrong with it”.

Changes of state, not writes. An entity reporting the same value over and over is chatty, not unsettled, so attribute changes do not count. Going to unavailable and back does count, which is the case this exists for. An entity appearing or disappearing does not: that is not it going back and forth.

The window slides, so this is always about the most recent changes rather than a fresh count every five minutes.

It reports once per spell. Once it has said an entity will not settle it stays quiet until that entity does settle, because a storm of alerts about a storm helps nobody. Settling means the last few changes no longer fall inside the window, and the next time it starts up is news again. A change that still leaves the last few inside the window is the same spell carrying on, however long it has been since the one before it.

When it fires, trigger.entity_id names the entity, trigger.from_state and trigger.to_state are the change that tipped it over, and trigger.changes and trigger.within are what was asked for.

Condition turned true

Fires when a condition goes from false to true.

Trigger properties

Trigger

Condition turned true 👻

Trigger name

spook.condition_met

Spook's influence

Newly added trigger

Trigger options

Attribute

Type

Required

Default / Example

condition

condition

Yes

Any condition, built the ordinary way

A condition is true or false, and the moment it turns is worth reacting to. Home Assistant has a trigger for a template turning true and one for a state arriving, but nothing that takes the condition building blocks, so anything more involved than a single state has to be rewritten as a template.

Takes one condition or a list of them, and a list means all of them, the same as anywhere else. Which is also what the visual editor sends, so both shapes have to work.

Only the turn counts. A condition that is already true when the automation loads is not a change, so this does not fire for it, the same as the template trigger. And going back to false is not a turn either.

When it can tell which change turned the condition, trigger.entity_id names that entity and trigger.from_state and trigger.to_state are what it moved between, the same three the template trigger hands over. Which is also what carries the user through: an automation starts a fresh context, so spook.triggered_by_user and friends read the person off trigger.to_state.

It can tell only when every part of the condition announces its own turns, which means state, numeric_state and zone conditions without a for: and without a value_template, plus any and, or or not built out of those. Anything else and all three are empty, because a condition with a part that turns quietly gets discovered by the next unrelated change, and naming that change would be naming the wrong one.