aneeshd / schedule_state

Home Assistant (HA) sensor that returns a string based on a defined schedule, enabling further automations

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Schedule State for Home Assistant

A custom component for Home Assistant that simplifies the creation of schedule-based automations.

It takes a very different approach from other projects of this type, because it only provides scheduling functionality. It does not try to replace the powerful automations framework that Home Assistant provides.

Instead, it separates the concepts of when you want something done, and what to do when that state arrives. Use schedule_state to handle the when, and standard Home Assistant tools to handle the what.

schedule_state allows you to create sensors that provide arbitrary string values, based on the time of day and other criteria. Home Assistant can then use the state of these sensors to trigger other automations.


GitHub Release GitHub Activity GitHub Issues License

hacs Project Maintenance BuyMeCoffee

Community Forum


Installation

Using HACS:

  1. Go to the HACS integration page
  2. Click on "Explore & Download Repositories"
  3. Search for "Schedule State" and add it
  4. Restart Home Assistant

Configuration

Configuration is done via YAML files. It seems safer to keep your custom configuration in a separate file, so add an entry in configuration.yaml:

sensor: !include sensors.yaml

Put configuration for schedule_state, and any other custom sensors, in sensors.yaml.

Create a schedule_state sensor:

  - platform: schedule_state
    name: Some descriptive name
    # refresh: "6:00:00"                # this is the default
    # default_state: default            # this is the default
    # icon: mdi:calendar-check          # this is the default
    # error_icon: mdi:calendar-alert    # this is the default
    # minutes_to_refresh_on_error: 5    # this is the default
    # allow_wrap: False                 # this is the default

By default, the sensor returns the name provided as the default_state. Configuration is built up in layers of events. Events have a start time and end time, and cause the sensor to report a new state name.

All times are interpreted as belonging to the local timezone.

The start and end times can be adjusted with an offset, in minutes, using the start_offset/end_offset settings. This helps define events such as "x minutes before sunrise", in a simpler way than using complicated templates. These offsets can also be specified as templates.

This simple configuration will cause the sensor to report sleep or awake depending on the time of day. You can create automations that trigger on the sensor state. Or, you can use the value of the sensor state as a condition in your automations.

Each state can have a custom icon. Pick any icon that you can find on pictogrammers.com and prefix the name with mdi:. Note: the last icon assigned to a state is used for all occurrences of that state.

    events:
      - start: "0:00"
        end: "5:30"
        state: sleep
        icon: mdi:sleep
      - start: "5:30"
        end: "22:30"
        state: awake
        icon: mdi:walk
      - start: "22:30"
        state: sleep

If start is not provided, it defaults to the start of the day, and if end is not provided, it defaults to the end of the day.

By default, it is required that end times are larger than start times. However, by setting the allow_wrap attribute to True, events can be made to "wrap" to the next day. This can be a more convenient or intuitive way to specify events that run from evening to morning.

For example, the sleep state in the example could be specified as follows:

    events:
      - start: "22:30"
        end: "5:30"
        state: sleep
        icon: mdi:sleep
        allow_wrap: True
      - start: "5:30"
        end: "22:30"
        state: awake
        icon: mdi:walk

There can be some unexpected behaviour, however, when using conditions and templates in conjunction with this feature (see below).

The allow_wrap setting can be specified at either the sensor level or for a particular event.

Note: Make sure that times are quoted!

schedule1

Events can be defined in any order. If an event is created that overlaps with existing events, the existing events are re-sized to accommodate the new event.

To continue the previous example:

      - start: "13:30"
        end: "14:30"
        state: sleep

This will carve out a 1 hour naptime, and create two awake times (5:30-13:30 and 14:30-22:30).

schedule2

Finally, events can be conditional. Any condition logic supported by Home Assistant can be used. This makes it easy to make different schedules for weekends or holidays.

      # weekends
      - start: "0:00"
        end: "8:00"
        state: sleep
        condition: &weekend
          condition: time
          weekday:
            - sat
            - sun

Note that Home Assistant will evaluate conditions based on the current time of day. In this particular example, the condition evaluates to true if the current time is between 0:00:00 on Saturday and 23:59:59 on Sunday. You will need to take this into account if you use the allow_wrap feature described above to conditionally wrap an event from Friday evening to Saturday morning, for example.

Keep in mind that the evaluation order of the events is top-to-bottom, and all of them are evaluated. This means that if multiple events match the settings, it's the last one that "wins". For example, if you put this as your last event in your configuration, it can be used to override all the events above it, only by using a binary_sensor (note how there are no start and end parameters - matching all day if condition passes):

      - state: away_mode
        condition:
          - condition: state
            entity_id: binary_sensor.family_at_home
            state: 'off'

Conditions are re-evaluated whenever the state of any entities referenced in the condition change.

The icon of the schedule_state sensor will change to an "alert" (mdi:calendar-alert) if an error is detected while evaluating the condition. This indicates that the condition definition may need to be examined.

Templates

State values, start, and end times can be specified using templates. Simply use a template for any state, default_state, start, or end definition.

In previous versions, it was necessary to use the attributes start_template/end_template instead of start/end.

The template value must evaluate to one of these data types:

Format Comments
datetime A string that HA or Python can understand as a datetime. Only the time portion is used.
time A string that HA or Python can understand as a time.
timestamp A number that will be interpreted as a timestamp. This will be converted to a datetime and only the time portion is used.

All times are converted to the local timezone.

This is an example of a sensor that provides day or night states based on data from the sun integration.

    events:
      - state: night
      - start: "{{ as_timestamp(state_attr('sun.sun', 'next_rising')) }}"
        end: "{{ as_timestamp(state_attr('sun.sun', 'next_setting')) }}"
        state: day

Template values are refreshed at every refresh interval, or whenever the state of any entities referenced in the template change.

Sometimes, errors can occur when evaluating valid templates. This is because Home Assistant may not yet have loaded the entities on which the template depends. schedule_state will re-evaluate the template again in a few minutes to guard against this condition. This gives HA some time to start everything up.

Of course, it is possible that the template is not valid, but schedule_state cannot yet differentiate between these two scenarios. The icon of the schedule_state sensor will change to an "alert" (mdi:calendar-alert) to indicate that the template definition may need to be examined. In a future version, this force-refresh strategy may incorporate a timeout.

schedule_state will ask Home Assistant to delay loading it until after the following integrations have been loaded:

  • binary_sensor
  • input_boolean
  • input_button
  • input_datetime
  • input_number
  • input_select
  • input_text
  • sun
  • zone

State and Attributes

state

The state contains the current state of the schedule.

Attributes

These custom attributes are available, in addition to the more common friendly_name and icon attributes.

Attribute Description
states All states known to the sensor
next_state The state that will occur after the current state
start Start time of current state
end End time of current state
friendly_start Start time of current state as a string, formatted using your local conventions
friendly_end End time of current state as a string, formatted using your local conventions
errors List of any events with configuration errors

start and end return datetime.time objects, allowing templates to access .hour, .minute, and other attributes of the time object.

friendly_end will return 'midnight' if the event ends at the end of the day instead of the less useful '23:59:59.999999' returned by end in earlier versions.

Custom Attributes

schedule_state can be configured to publish user-defined attributes with each event.

  - platform: schedule_state
    extra_attributes:
      # attributes and their default values can be defined here - templates are allowed for values
      fan_mode: "off"
      swing_mode: "off"
    events:
      - start: "5:00" # intentional overlap
        end: "22:30"
        state: "temp_day"
        fan_mode: "high"
        # swing_mode will use default value ("off")
      - start: "23:19"
        state: "temp_evening"
        # attribute values - templates are allowed for values
        swing_mode: "horizontal"
        fan_mode: "{{ iif(is_state('input_boolean.mode', 'on'), 'mid', 'very-high', 'broken') }}"

Attributes are evaluated with the same top-to-bottom logic as states. If an attribute is not provided for an event, the attribute value reverts to the default.

Tip: You can add Home Assistant-specific attributes too. For example if you schedule target temperature values, this will make it display a graph:

    extra_attributes:
      unit_of_measurement: "°C"
      state_class: measurement

Lovelace Ideas

Here is an example of using a Jinja template in Lovelace to display the current state and the time at which it expires:

chip_with_template:

  - type: custom:mushroom-chips-card
    chips:
      - type: template
        entity: sensor.my_schedule
        content: >-
          ⏱️ {{ states(config.entity) | title }} until {{
          state_attr(config.entity, 'friendly_end') }}

This example uses a Mushroom card with a template chip, but the concept works with any Lovelace card that supports templates.

Services

recalculate

Forces the schedule to be recalculated. This is useful if you have conditionals or templates in the schedule definition, and you would like the schedule to be updated based on these changes.

This is cleaner than re-loading, as it prevents the sensor from becoming "unavailable".

Note: schedule_state will (should) automatically reload the schedule definition if any referenced conditionals or templates have been updated. As a result, this service should not be needed if everything is working properly.

set_override

Temporarily schedule the sensor to report a given value, overriding the schedule definition.

The override can be specified in four different ways:

Data Provided Meaning
duration Override for duration minutes, starting now.
start, duration Override for duration minutes, starting at the next occurrence of start.
end, duration Override for duration minutes, ending at the next occurrence of end.
start, end Override from the next occurrence to start to the next occurrence of end.

Other data required for this service call:

Data Meaning
state The state to apply for this override
id Optionally provide an ID for the override.
icon Optionally provide an icon for this override
extra_attributes Optionally provide a dict of extra attributes for this override

If ID is provided, and there is already an override with that ID, the override is modified in-place. This maintains the override's ordering in the evaluation order.

Providing an ID also allows the override to be removed later via a service call.

remove_override

Remove the override with the specified ID.

Data Meaning
id The override to remove

clear_overrides

Clears any overrides defined for the schedule.

Development Notes

There are (at least) 3 modes in which development and testing can be performed.

  • Standalone using pytest, schedule_state, and its dependencies
  • Home Assistant devcontainer
  • A real Home Assistant instance

This section provides some notes on each type of development environment, mostly as a reminder for me.

pytest

Use poetry and the provided pyproject.toml to setup the development environment

  • poetry install
  • poetry shell
  • pytest tests

devcontainer

  • Open schedule_state repo in VS Code
  • Re-open in container
  • Use standard devcontainer development methods

On a real HA instance

Code changes require HA to be re-started, so try to get as much fixed as possible using either of the two previous methods.

Enable logging for schedule_state:

logger:
  default: warning
  logs:
    custom_components.schedule_state: debug

Contributions are welcome!

If you want to contribute to this please read the Contribution guidelines


About

Home Assistant (HA) sensor that returns a string based on a defined schedule, enabling further automations

License:Apache License 2.0


Languages

Language:Python 100.0%