hass.tibber_prices/docs/user/versioned_docs/version-v0.31.0/automation-examples.md

56 KiB
Raw Blame History

Automation Examples

This guide shows you how to practically use all the features of the Tibber Prices integration — sensors, binary sensors, and scheduling actions — for real-world energy optimization.

:::tip Entity ID tip <home_name> is a placeholder for your Tibber home display name in Home Assistant. Entity IDs are derived from the displayed name (localized), so the exact slug may differ. Can't find a sensor? Use the Entity Reference (All Languages) to search by name in your language. :::

:::caution Adapt before using Replace all Entity IDs (e.g., sensor.<home_name>_..., switch.dishwasher) with IDs from your own setup. Adjust thresholds, temperatures, and device names to match your devices. :::


Two Approaches to Energy Optimization

The integration provides two complementary ways to save on electricity. Understanding when to use which is key:

flowchart TD
    Q["What kind of device?"] -->|"Fixed runtime<br/>(dishwasher, dryer)"| A["🗓️ Scheduling Actions<br/>find_cheapest_block / schedule"]
    Q -->|"Flexible runtime<br/>(EV, pool pump)"| B["🗓️ Scheduling Actions<br/>find_cheapest_hours"]
    Q -->|"Continuous / modulating<br/>(heat pump, battery)"| C["📊 Sensor-Based<br/>React in real time"]
    Q -->|"Multiple appliances<br/>shared circuit"| D["🗓️ Scheduling Actions<br/>find_cheapest_schedule"]

    A --> E["Plan once → execute on time"]
    B --> E
    D --> E
    C --> F["Adjust continuously<br/>as prices change"]

    style A fill:#e6f7ff,stroke:#00b9e7
    style B fill:#e6f7ff,stroke:#00b9e7
    style D fill:#e6f7ff,stroke:#00b9e7
    style C fill:#e6fff5,stroke:#00c853

Scheduling Actions vs. Sensor-Based Automations

Scheduling Actions (find_*) Sensor-Based (binary sensors + trends)
How it works Call action once → get exact start/end times React when sensor state changes
Best for Appliances with fixed runtime, precise planning Continuous or modulating devices
Typical devices Dishwasher, washing machine, dryer, EV Heat pump, home battery, water heater
Planning horizon Up to 48h ahead, with full cost estimate Real-time, reacts as prices evolve
Complexity Simple: one action call, clear result Flexible: combine multiple sensors
Multi-appliance Built-in overlap prevention Manual coordination required
HA restart Schedule survives via input_datetime pattern Sensors auto-recover, no state lost

:::tip Rule of thumb "I need to start my device at an exact time" → Use a scheduling action. "I need my device to react as prices change throughout the day" → Use sensors. "I want both" → Combine them (see Advanced Combinations). :::


Appliance Scheduling with Actions

Use scheduling actions when you know how long a device needs to run and want the integration to find the cheapest window for it.

The Restart-Safe Pattern

All action-based automations in this guide use input_datetime helpers to store the planned start time. This ensures the schedule survives HA restarts — unlike delay which is lost when HA restarts.

Setup: Create an input_datetime helper per appliance in Settings → Devices & Services → Helpers → Create Helper → Date and/or time (choose "Date and time").

sequenceDiagram
    participant T as ⏰ Time Trigger
    participant A as 📋 Planning Automation
    participant S as ⚡ Scheduling Action
    participant H as 💾 input_datetime Helper
    participant X as ▶️ Execution Automation

    T->>A: 20:00 — "Plan tonight"
    A->>S: find_cheapest_block (2h, 22:0006:00)
    S-->>A: Best window: 02:0004:00
    A->>H: Store start time: 02:00
    Note over H: Survives HA restart ✓
    H->>X: 02:00 — time trigger fires
    X->>X: Start appliance<br/>(Home Connect / smart plug)

Dishwasher: Find Cheapest 2-Hour Window Tonight

The classic use case. Every evening at 20:00, the automation plans when to start the dishwasher overnight.

:::info Blueprint available These blueprints are automatically installed with the integration. Find them under Settings → Automations → Blueprints. You can also import them manually:

Variant Import
Smart Plug Import
Home Connect Import
Home Connect Alt Import
:::

Prerequisite: Create an input_datetime helper named input_datetime.dishwasher_start (type: Date and time).

Show YAML: Dishwasher — Plan + Execute (2 automations)
automation:
    # Automation 1: Plan the cheapest time (runs every evening)
    - alias: "Dishwasher - Plan Cheapest Start Time"
      trigger:
          - platform: time
            at: "20:00:00"
      action:
          - service: tibber_prices.find_cheapest_block
            data:
                duration: "02:00:00"
                search_start_time: "22:00:00"
                search_end_time: "06:00:00"
                search_end_day_offset: 1
            response_variable: result
          - if: "{{ result.window_found }}"
            then:
                - service: input_datetime.set_datetime
                  target:
                      entity_id: input_datetime.dishwasher_start
                  data:
                      datetime: "{{ result.window.start }}"
                - service: notify.mobile_app
                  data:
                      title: "🍽️ Dishwasher Planned"
                      message: >
                          Start at {{ result.window.start | as_datetime | as_local
                          | as_timestamp | timestamp_custom('%H:%M') }}.
                          Avg price: {{ result.window.price_mean | round(1) }}
                          {{ result.price_unit }}.                          

    # Automation 2: Execute at the planned time
    - alias: "Dishwasher - Start at Planned Time"
      trigger:
          - platform: time
            at: input_datetime.dishwasher_start
      action:
          - service: switch.turn_on
            target:
                entity_id: switch.dishwasher_smart_plug

How it works:

  1. At 20:00, calls find_cheapest_block to find the cheapest contiguous 2h window between 22:00 and 06:00
  2. Stores the start time in an input_datetime helper (survives HA restarts)
  3. A second automation triggers at the stored time and starts the appliance

:::tip Home Connect appliances For Bosch/Siemens appliances with Home Connect, use the dedicated Home Connect blueprints above instead — they handle StartInRelative / FinishInRelative automatically. You can also import a blueprint and use "Take Control" in the automation editor to customize it. :::

Washing Machine: Cheapest Window Overnight

Same pattern as the dishwasher — change the duration to match your program (ECO 40-60 ~1:30h, Cotton 60°C ~2:00h) and swap the entity IDs.

:::info Blueprint available These blueprints are automatically installed with the integration. Find them under Settings → Automations → Blueprints. You can also import them manually:

Variant Import
Smart Plug Import
Home Connect Import
Home Connect Alt Import
:::

Prerequisite: Create an input_datetime helper named input_datetime.washing_machine_start (type: Date and time).

The YAML is identical to the dishwasher example above — just adjust duration, helper name, and switch entity. Use the blueprints above for a one-click setup, including Home Connect support.

Dryer: Cheapest Window Overnight

Same pattern again — dryer programs are typically shorter (45 min1:15h depending on load and program).

:::info Blueprint available These blueprints are automatically installed with the integration. Find them under Settings → Automations → Blueprints. You can also import them manually:

Variant Import
Smart Plug Import
Home Connect Import
Home Connect Alt Import
:::

Prerequisite: Create an input_datetime helper named input_datetime.dryer_start (type: Date and time).

The YAML is identical to the dishwasher example above — just change duration to "01:00:00", helper name, and switch entity.

Two Independent Appliances: No Overlap, Each at Its Cheapest

When running multiple independent appliances overnight (e.g., dishwasher + dryer that don't depend on each other), find_cheapest_schedule ensures they don't overlap and each gets its own cheapest slot.

Prerequisite: Create input_datetime.dishwasher_start and input_datetime.dryer_start helpers.

Show YAML: Multi-Appliance Overnight Schedule
automation:
    - alias: "Kitchen + Laundry - Plan Overnight Schedule"
      description: "Schedule dishwasher + dryer overnight without overlap"
      trigger:
          - platform: time
            at: "21:00:00"
      action:
          - service: tibber_prices.find_cheapest_schedule
            data:
                tasks:
                    - name: dishwasher
                      duration: "02:00:00"
                    - name: dryer
                      duration: "01:00:00"
                gap_minutes: 15
                search_start_time: "22:00:00"
                search_end_time: "07:00:00"
                search_end_day_offset: 1
            response_variable: schedule
          - if: "{{ schedule.all_tasks_scheduled }}"
            then:
                - service: input_datetime.set_datetime
                  target:
                      entity_id: input_datetime.dishwasher_start
                  data:
                      datetime: >
                          {{ schedule.tasks | selectattr('name', 'eq', 'dishwasher')
                          | map(attribute='start') | first }}                          
                - service: input_datetime.set_datetime
                  target:
                      entity_id: input_datetime.dryer_start
                  data:
                      datetime: >
                          {{ schedule.tasks | selectattr('name', 'eq', 'dryer')
                          | map(attribute='start') | first }}                          
                - service: notify.mobile_app
                  data:
                      title: "🧺 Appliances Planned"
                      message: >
                          Dishwasher: {{ (schedule.tasks | selectattr('name', 'eq', 'dishwasher')
                          | map(attribute='start') | first) | as_datetime | as_local
                          | as_timestamp | timestamp_custom('%H:%M') }}
                          Dryer: {{ (schedule.tasks | selectattr('name', 'eq', 'dryer')
                          | map(attribute='start') | first) | as_datetime | as_local
                          | as_timestamp | timestamp_custom('%H:%M') }}
                          Total cost: ~{{ schedule.total_estimated_cost | round(1) }}
                          {{ schedule.currency }}                          

    # Execution automations (one per appliance)
    - alias: "Dishwasher - Start at Planned Time"
      trigger:
          - platform: time
            at: input_datetime.dishwasher_start
      action:
          # For Home Connect, use the dedicated blueprints instead
          - service: switch.turn_on
            target:
                entity_id: switch.dishwasher_smart_plug

    - alias: "Dryer - Start at Planned Time"
      trigger:
          - platform: time
            at: input_datetime.dryer_start
      action:
          - service: switch.turn_on
            target:
                entity_id: switch.dryer_smart_plug

Why find_cheapest_schedule instead of separate find_cheapest_block calls? If you call find_cheapest_block separately for each appliance, they might both pick the same cheap window. find_cheapest_schedule reserves each slot exclusively — the dryer gets the next-cheapest window after the dishwasher claims its slot, with a 15-minute gap between them.

:::tip sequential: true for ordered workflows By default, find_cheapest_schedule optimizes purely for price — the dryer might be scheduled before the dishwasher. This is fine for independent appliances. For sequential workflows (e.g., washing machine → dryer), add sequential: true — see the next example. :::

Washing Machine → Dryer: Sequential Scheduling

When the dryer must run after the washing machine, use sequential: true to guarantee declaration-order scheduling. The scheduler places each task after the previous one finishes (plus gap).

Prerequisite: Create input_datetime.washing_machine_start and input_datetime.dryer_start helpers.

Show YAML: Sequential Washer → Dryer
automation:
    - alias: "Laundry - Plan Sequential Wash + Dry"
      description: "Washing machine first, then dryer — guaranteed order"
      trigger:
          - platform: time
            at: "21:00:00"
      action:
          - service: tibber_prices.find_cheapest_schedule
            data:
                sequential: true
                gap_minutes: 15
                search_start_time: "22:00:00"
                search_end_time: "08:00:00"
                search_end_day_offset: 1
                tasks:
                    # Order matters! Washer runs first, dryer after.
                    - name: washing_machine
                      duration: "01:30:00"
                    - name: dryer
                      duration: "01:00:00"
            response_variable: schedule

          - if: "{{ schedule.all_tasks_scheduled }}"
            then:
                - service: input_datetime.set_datetime
                  target:
                      entity_id: input_datetime.washing_machine_start
                  data:
                      datetime: "{{ schedule.tasks[0].start }}"
                - service: input_datetime.set_datetime
                  target:
                      entity_id: input_datetime.dryer_start
                  data:
                      datetime: "{{ schedule.tasks[1].start }}"
                - service: notify.mobile_app
                  data:
                      title: "🧺 Laundry Planned"
                      message: >
                          Washing: {{ schedule.tasks[0].start | as_datetime
                          | as_local | as_timestamp
                          | timestamp_custom('%H:%M') }}{{ schedule.tasks[0].end
                          | as_datetime | as_local | as_timestamp
                          | timestamp_custom('%H:%M') }}
                          ({{ schedule.tasks[0].price_mean | round(1) }}
                          {{ schedule.price_unit }})
                          Dryer: {{ schedule.tasks[1].start | as_datetime
                          | as_local | as_timestamp
                          | timestamp_custom('%H:%M') }}{{ schedule.tasks[1].end
                          | as_datetime | as_local | as_timestamp
                          | timestamp_custom('%H:%M') }}
                          ({{ schedule.tasks[1].price_mean | round(1) }}
                          {{ schedule.price_unit }})
                          Total: {{ schedule.total_estimated_cost | round(2) }}
                          {{ schedule.price_unit }}                          

    # Execution automations
    - alias: "Washing Machine - Start at Planned Time"
      trigger:
          - platform: time
            at: input_datetime.washing_machine_start
      action:
          # For Home Connect, use the dedicated blueprints instead
          - service: switch.turn_on
            target:
                entity_id: switch.washing_machine_smart_plug

    - alias: "Dryer - Start at Planned Time"
      trigger:
          - platform: time
            at: input_datetime.dryer_start
      action:
          # For Home Connect, use the dedicated blueprints instead
          - service: switch.turn_on
            target:
                entity_id: switch.dryer_smart_plug

How it works:

  1. With sequential: true, the scheduler places the washing machine first at its cheapest window
  2. The dryer's search window starts after the washer ends + 15 min gap — guaranteed order
  3. The 15-minute gap gives you time to transfer laundry (adjust or remove as needed)

Laundry Day Pipeline: Multiple Loads with Price Optimization

When you have a full laundry day (23 loads that each need washing and drying), managing the timing manually is tedious. The Laundry Day Pipeline blueprint automates the entire process:

sequenceDiagram
    participant U as 👤 You
    participant B as 🤖 Blueprint
    participant T as ⚡ Tibber Prices
    participant W as 👕 Washer
    participant D as 🌀 Dryer

    U->>B: Turn on "Laundry Day" toggle
    B->>T: find_cheapest_block (wash 1)
    T-->>B: Best window: 08:1509:45
    B->>W: Start wash 1
    Note over W: Washing... (90 min)
    B-->>U: 📱 "Wash 1 done! Transfer to dryer"
    U->>U: Transfer laundry
    B->>T: find_cheapest_block (dry 1)
    T-->>B: Best window: 10:1511:15
    B->>D: Start dryer 1
    Note over D,W: Pipeline: dryer runs while...
    B->>T: find_cheapest_block (wash 2)
    T-->>B: Best window: 10:1511:45
    B->>W: Start wash 2
    Note over W: Washing... (90 min)
    B-->>U: 📱 "Wash 2 done! Transfer"

What the blueprint handles:

  • Price optimization: Each cycle is scheduled at the cheapest available window
  • Pipeline mode (optional): Next wash starts while the dryer runs, cutting total time significantly
  • Transfer reminders: Notifications when each wash finishes
  • Automatic completion: Toggle turns off when all loads are done
  • Cancellation: Turn off the toggle at any time to stop the pipeline

What you need:

Helper Type Settings
input_boolean.laundry_day Toggle Starts/stops laundry day
input_number.laundry_loads Number Min: 1, Max: 5, Step: 1

Choose your variant and import:

The blueprint comes in three variants depending on how you control your appliances. Pick the one that matches your setup:

Variant Control Method Import
Smart Plug Smart plug switches (any brand) Import
Home Connect HA Core Home Connect integration Import
Home Connect Alt HC Alt HACS integration Import

After importing:

  1. Create the two helpers (input_boolean + input_number)
  2. Create an automation from the blueprint
  3. Configure your appliances, program durations, and deadline
  4. On laundry day: set the load count, turn on the toggle, and let the blueprint handle the rest

:::info Pipeline mode vs. sequential Without pipeline (default): Each wash + dry cycle completes fully before the next one starts. Safe for all setups.

With pipeline: The next wash starts immediately after the dryer begins. This overlaps washer and dryer operation, saving roughly one dryer cycle per load. Only enable this when your wash duration ≥ dryer duration — otherwise the dryer from the previous load might still be running when the next dryer needs to start.

Example with 3 loads (wash 90 min, dry 60 min, 15 min transfer):

  • Sequential: ~8h 45min
  • Pipeline: ~6h 15min — saves 2.5 hours! :::

:::caution About program durations The blueprint uses estimated durations (not real-time appliance feedback). Set your durations based on typical program times and add a small buffer (~5 min). If your actual program finishes earlier or later, the timing will drift slightly — this is acceptable for price optimization purposes. :::

EV Charging: Cheapest 4 Hours Overnight

For EV charging, you usually don't need one contiguous block — the charger can pause and resume. find_cheapest_hours picks the cheapest individual intervals.

:::info Blueprint available This blueprint is automatically installed with the integration. You can also import it manually: Import :::

Prerequisite: Create input_datetime.ev_charge_start helper. For multi-segment charging, see the note below.

Show YAML: EV Charging — Cheapest 4 Hours
automation:
    - alias: "EV - Plan Cheapest Charging Overnight"
      trigger:
          - platform: time
            at: "18:00:00"
      condition:
          - condition: numeric_state
            entity_id: sensor.ev_battery_level
            below: 80
      action:
          - service: tibber_prices.find_cheapest_hours
            data:
                duration: "04:00:00"
                min_segment_duration: "00:30:00"
                search_start_time: "18:00:00"
                search_end_time: "07:00:00"
                search_end_day_offset: 1
            response_variable: result
          - if: "{{ result.intervals_found }}"
            then:
                # Store start of first segment as charging start
                - service: input_datetime.set_datetime
                  target:
                      entity_id: input_datetime.ev_charge_start
                  data:
                      datetime: "{{ result.schedule.segments[0].start }}"
                - service: notify.mobile_app
                  data:
                      title: "🔌 EV Charging Planned"
                      message: >
                          {{ result.schedule.segment_count }} charging sessions:
                          {% for seg in result.schedule.segments %}
                          • {{ seg.start | as_datetime | as_local | as_timestamp
                          | timestamp_custom('%H:%M') }}{{ seg.end | as_datetime | as_local
                          | as_timestamp | timestamp_custom('%H:%M') }}
                          ({{ seg.price_mean | round(1) }} {{ result.price_unit }})
                          {% endfor %}
                          Savings vs. peak: {{ result.price_comparison.price_difference
                          | round(1) }} {{ result.price_unit }}                          

:::tip Simple alternative for EV charging If your EV charger can't pause/resume or you prefer simplicity, use find_cheapest_block instead — it finds one contiguous window, just like the dishwasher example above. :::

Peak Price Warning: Know When NOT to Run Appliances

Get a morning warning about the most expensive period today:

Show YAML: Peak Price Warning
automation:
    - alias: "Peak Price - Morning Warning"
      trigger:
          - platform: time
            at: "07:00:00"
      action:
          - service: tibber_prices.find_most_expensive_block
            data:
                duration: "02:00:00"
                search_scope: today
            response_variable: peak
          - if: "{{ peak.window_found }}"
            then:
                - service: notify.mobile_app
                  data:
                      title: "⚡ Expensive Period Today"
                      message: >
                          Avoid heavy loads between
                          {{ peak.window.start | as_datetime | as_local
                          | as_timestamp | timestamp_custom('%H:%M') }}
                          and {{ peak.window.end | as_datetime | as_local
                          | as_timestamp | timestamp_custom('%H:%M') }}.
                          Average price: {{ peak.window.price_mean | round(1) }}
                          {{ peak.price_unit }}                          

Real-Time Optimization with Sensors

Use sensors for devices that run continuously and can modulate their power draw — like heat pumps that adjust target temperature, or home batteries that switch between charging and discharging.

Heat Pump: Temperature Based on Price Level

The simplest real-time approach: adjust the heat pump target temperature based on the current price rating.

:::info Blueprint available This blueprint is automatically installed with the integration. You can also import it manually: Import :::

Show YAML: Heat Pump — Price-Based Temperature
automation:
    - alias: "Heat Pump - Adjust Temperature by Price"
      description: "Higher target when cheap, lower when expensive"
      mode: restart
      trigger:
          # Triggers every 15 minutes when price updates
          - platform: state
            entity_id: sensor.<home_name>_current_electricity_price
      action:
          - variables:
              level: >
                  {{ state_attr('sensor.<home_name>_current_electricity_price',
                  'rating_level') }}                  
          - choose:
                - conditions: "{{ level in ['VERY_CHEAP'] }}"
                  sequence:
                      - service: climate.set_temperature
                        target:
                            entity_id: climate.heat_pump
                        data:
                            temperature: 23
                - conditions: "{{ level in ['CHEAP'] }}"
                  sequence:
                      - service: climate.set_temperature
                        target:
                            entity_id: climate.heat_pump
                        data:
                            temperature: 22
                - conditions: "{{ level in ['EXPENSIVE', 'VERY_EXPENSIVE'] }}"
                  sequence:
                      - service: climate.set_temperature
                        target:
                            entity_id: climate.heat_pump
                        data:
                            temperature: 19
            default:
                - service: climate.set_temperature
                  target:
                      entity_id: climate.heat_pump
                  data:
                      temperature: 20.5

Heat Pump: Smart Boost with Trend Awareness

A more sophisticated approach: combine the best price period with trend sensors to boost during the full cheap window, not just the detected period.

:::info Blueprint available This blueprint is automatically installed with the integration. You can also import it manually: Import :::

Why? On V-shaped price days, the Best Price Period may cover only 12 hours, but prices remain favorable for 46 hours. By checking the price level and trend, you can extend the boost.

gantt
    title V-Shaped Price Day — Best Price vs Full Cheap Window
    dateFormat HH:mm
    axisFormat %H:%M

    section Price Level
    NORMAL              :done,    n1, 06:00, 2h
    CHEAP               :active,  c1, 08:00, 2h
    VERY CHEAP          :crit,    vc, 10:00, 2h
    CHEAP               :active,  c2, 12:00, 2h
    NORMAL              :done,    n2, 14:00, 2h
    EXPENSIVE           :         e1, 16:00, 2h

    section Detected
    Best Price Period   :crit,    bp, 10:00, 2h

    section This Automation
    Boost while cheap + trend OK :active, goal, 08:00, 6h
Show YAML: Heat Pump — Extended Cheap Window Boost
automation:
    - alias: "Heat Pump - Boost During Full Cheap Window"
      description: "Run heat pump boost during cheap period, even beyond the detected best price period"
      mode: restart
      trigger:
          # Start: Best price period begins
          - platform: state
            entity_id: binary_sensor.<home_name>_best_price_period
            to: "on"
          # Re-evaluate: Every 15 minutes when price updates
          - platform: state
            entity_id: sensor.<home_name>_current_electricity_price
      condition:
          # Continue while EITHER is true:
          - condition: or
            conditions:
                # Path 1: Inside a best price period
                - condition: state
                  entity_id: binary_sensor.<home_name>_best_price_period
                  state: "on"
                # Path 2: Price is still cheap AND trend is stable or falling
                - condition: and
                  conditions:
                      - condition: template
                        value_template: >
                            {{ state_attr('sensor.<home_name>_current_electricity_price',
                            'rating_level') in ['VERY_CHEAP', 'CHEAP'] }}                            
                      - condition: template
                        value_template: >
                            {{ state_attr('sensor.<home_name>_price_outlook_1h',
                            'trend_value') | int(0) <= 0 }}                            
      action:
          - service: climate.set_temperature
            target:
                entity_id: climate.heat_pump
            data:
                temperature: 22

    - alias: "Heat Pump - Return to Normal"
      description: "Reset heat pump when cheap window ends"
      trigger:
          - platform: state
            entity_id: sensor.<home_name>_current_electricity_price
      condition:
          # Only reset when NOT in cheap conditions
          - condition: template
            value_template: >
                {{ not is_state('binary_sensor.<home_name>_best_price_period', 'on')
                and state_attr('sensor.<home_name>_current_electricity_price',
                'rating_level') not in ['VERY_CHEAP', 'CHEAP'] }}                
      action:
          - service: climate.set_temperature
            target:
                entity_id: climate.heat_pump
            data:
                temperature: 20.5

:::tip Why "rising" means "act now" A common misconception: "rising" does NOT mean "too late". The Price Outlook sensors compare your current price to the future average. rising means your current price is lower than the future average — so now is actually a good time to consume. See Trend Sensors for details. :::

Home Battery: Charge Cheap, Discharge Expensive

Use the best price period for charging and the peak price period for discharging:

:::info Blueprint available This blueprint is automatically installed with the integration. You can also import it manually: Import :::

Show YAML: Home Battery — Charge/Discharge Cycle
automation:
    - alias: "Battery - Charge During Best Price"
      trigger:
          - platform: state
            entity_id: binary_sensor.<home_name>_best_price_period
            to: "on"
      condition:
          # Only if volatility makes it worthwhile
          - condition: template
            value_template: >
                {{ state_attr('binary_sensor.<home_name>_best_price_period',
                'volatility') != 'low' }}                
          - condition: numeric_state
            entity_id: sensor.home_battery_soc
            below: 90
      action:
          - service: switch.turn_on
            target:
                entity_id: switch.battery_grid_charging

    - alias: "Battery - Discharge During Peak Price"
      trigger:
          - platform: state
            entity_id: binary_sensor.<home_name>_peak_price_period
            to: "on"
      condition:
          - condition: numeric_state
            entity_id: sensor.home_battery_soc
            above: 20
      action:
          - service: switch.turn_on
            target:
                entity_id: switch.battery_grid_discharge

    - alias: "Battery - Stop Charge/Discharge Outside Periods"
      trigger:
          - platform: state
            entity_id: binary_sensor.<home_name>_best_price_period
            to: "off"
          - platform: state
            entity_id: binary_sensor.<home_name>_peak_price_period
            to: "off"
      action:
          - service: switch.turn_off
            target:
                entity_id:
                    - switch.battery_grid_charging
                    - switch.battery_grid_discharge

Water Heater: Pre-Heat Before the Cheapest Window Ends

Heat your water tank during the cheap window so it's ready when prices rise:

:::info Blueprint available This blueprint is automatically installed with the integration. You can also import it manually: Import :::

Show YAML: Water Heater — Pre-Heat During Cheap Prices
automation:
    - alias: "Water Heater - Boost During Best Price"
      trigger:
          - platform: state
            entity_id: binary_sensor.<home_name>_best_price_period
            to: "on"
      action:
          - service: water_heater.set_temperature
            target:
                entity_id: water_heater.boiler
            data:
                temperature: 60

    - alias: "Water Heater - Return to Eco After Period"
      trigger:
          - platform: state
            entity_id: binary_sensor.<home_name>_best_price_period
            to: "off"
      action:
          - service: water_heater.set_temperature
            target:
                entity_id: water_heater.boiler
            data:
                temperature: 45

Combining Sensors and Actions

The most powerful automations combine planning with actions and real-time awareness with sensors. Here are patterns for when you need both.

Volatility-Aware Appliance Scheduling

Only schedule an appliance at a specific cheap time if price variations are meaningful. On days with flat prices, just run whenever convenient.

Show YAML: Volatility-Aware Dishwasher Scheduling
automation:
    - alias: "Dishwasher - Smart Scheduling with Volatility Check"
      trigger:
          - platform: time
            at: "20:00:00"
      condition:
          - condition: state
            entity_id: switch.dishwasher_smart_plug
            state: "off"
      action:
          - variables:
              volatility: >
                  {{ state_attr('sensor.<home_name>_today_s_price_volatility',
                  'price_volatility') }}                  
          - choose:
                # Meaningful price variation → find the cheapest window
                - conditions: "{{ volatility != 'low' }}"
                  sequence:
                      - service: tibber_prices.find_cheapest_block
                        data:
                            duration: "02:00:00"
                            search_start_time: "22:00:00"
                            search_end_time: "06:00:00"
                            search_end_day_offset: 1
                        response_variable: result
                      - if: "{{ result.window_found }}"
                        then:
                            - service: input_datetime.set_datetime
                              target:
                                  entity_id: input_datetime.dishwasher_start
                              data:
                                  datetime: "{{ result.window.start }}"
                            - service: notify.mobile_app
                              data:
                                  message: >
                                      Dishwasher planned for
                                      {{ result.window.start | as_datetime | as_local
                                      | as_timestamp | timestamp_custom('%H:%M') }}
                                      (saving ~{{ result.price_comparison.price_difference
                                      | round(1) }} {{ result.price_unit }} vs. worst time).                                      
            # Low volatility → prices are flat, just start at 22:00
            default:
                - service: input_datetime.set_datetime
                  target:
                      entity_id: input_datetime.dishwasher_start
                  data:
                      datetime: "{{ today_at('22:00') }}"
                - service: notify.mobile_app
                  data:
                      message: >
                          Prices are flat today — dishwasher starting at 22:00
                          (scheduling wouldn't save much).                          

EV Charging: Action-Planned with Sensor Fallback

Plan overnight charging with find_cheapest_hours. If the plan fails or the EV arrives home late, fall back to sensor-based charging during any best price period.

Show YAML: EV Charging — Planned + Fallback
automation:
    # Primary: Plan optimal charging when arriving home
    - alias: "EV - Plan Charging on Arrival"
      trigger:
          - platform: state
            entity_id: device_tracker.ev
            to: "home"
      condition:
          - condition: numeric_state
            entity_id: sensor.ev_battery_level
            below: 80
      action:
          - service: tibber_prices.find_cheapest_hours
            data:
                duration: "04:00:00"
                min_segment_duration: "00:30:00"
                search_start_offset_minutes: 0
                search_end_time: "07:00:00"
                search_end_day_offset: 1
            response_variable: plan
          - if: "{{ plan.intervals_found }}"
            then:
                # Store first segment start for the charger control automation
                - service: input_datetime.set_datetime
                  target:
                      entity_id: input_datetime.ev_charge_start
                  data:
                      datetime: "{{ plan.schedule.segments[0].start }}"
                - service: input_boolean.turn_on
                  target:
                      entity_id: input_boolean.ev_charging_planned

    # Fallback: Charge during any best price period if no plan exists
    - alias: "EV - Fallback Charging During Best Price"
      trigger:
          - platform: state
            entity_id: binary_sensor.<home_name>_best_price_period
            to: "on"
      condition:
          - condition: state
            entity_id: input_boolean.ev_charging_planned
            state: "off"
          - condition: numeric_state
            entity_id: sensor.ev_battery_level
            below: 80
          - condition: state
            entity_id: device_tracker.ev
            state: "home"
      action:
          - service: switch.turn_on
            target:
                entity_id: switch.ev_charger

    - alias: "EV - Stop Fallback Charging"
      trigger:
          - platform: state
            entity_id: binary_sensor.<home_name>_best_price_period
            to: "off"
      condition:
          - condition: state
            entity_id: input_boolean.ev_charging_planned
            state: "off"
      action:
          - service: switch.turn_off
            target:
                entity_id: switch.ev_charger

Multi-Window Trend Strategy for Continuous Devices

Combine short-term and long-term outlook sensors for nuanced decisions. Useful for heat pumps and other devices where you want to decide: boost now, wait for cheaper prices, or reduce consumption?

Short-term (1h) Long-term (6h) Interpretation Action
rising rising Prices only go up from here Boost now — this is the cheapest it gets
falling rising Brief dip coming, then up Wait — a dip is imminent
any falling Cheaper hours ahead Reduce — save energy for later
stable stable Flat prices, no urgency Normal — no benefit to shifting
Show YAML: Multi-Window Trend Strategy
automation:
    - alias: "Heat Pump - Multi-Window Trend Boost"
      description: >
          Rising = current price is LOWER than future average = act now.
          Falling = current price is HIGHER than future average = wait.          
      mode: restart
      trigger:
          - platform: state
            entity_id: sensor.<home_name>_price_outlook_1h
          - platform: state
            entity_id: sensor.<home_name>_price_outlook_6h
      condition:
          # Skip if best price period is active (separate automation handles that)
          - condition: state
            entity_id: binary_sensor.<home_name>_best_price_period
            state: "off"
      action:
          - variables:
              t1: "{{ state_attr('sensor.<home_name>_price_outlook_1h', 'trend_value') | int(0) }}"
              t6: "{{ state_attr('sensor.<home_name>_price_outlook_6h', 'trend_value') | int(0) }}"
          - choose:
                # Both rising → prices only go up, boost NOW
                - conditions: "{{ t1 >= 1 and t6 >= 1 }}"
                  sequence:
                      - service: climate.set_temperature
                        target:
                            entity_id: climate.heat_pump
                        data:
                            temperature: 22
                # Short-term falling + long-term rising → brief dip, wait
                - conditions: "{{ t1 <= -1 and t6 >= 1 }}"
                  sequence:
                      - service: climate.set_temperature
                        target:
                            entity_id: climate.heat_pump
                        data:
                            temperature: 20
                # Long-term falling → cheaper hours ahead, reduce
                - conditions: "{{ t6 <= -1 }}"
                  sequence:
                      - service: climate.set_temperature
                        target:
                            entity_id: climate.heat_pump
                        data:
                            temperature: 19
            # Stable on both → maintain normal
            default:
                - service: climate.set_temperature
                  target:
                      entity_id: climate.heat_pump
                  data:
                      temperature: 20.5

Volatility-Aware Automations

On days with flat prices, the "cheapest" and "most expensive" hours may differ by fractions of a cent. These patterns help you avoid unnecessary actions when price variations don't matter.

Best Practice: Check Volatility Before Acting

Show YAML: EV Charging — Volatility + Absolute Price Check
automation:
    - alias: "EV Charging - Smart Strategy"
      description: "Trust best price classification on volatile days, use absolute price on flat days"
      trigger:
          - platform: state
            entity_id: binary_sensor.<home_name>_best_price_period
            to: "on"
      condition:
          - condition: numeric_state
            entity_id: sensor.ev_battery_level
            below: 80
          # Strategy: Meaningful volatility OR genuinely cheap price
          - condition: or
            conditions:
                # Volatile day → trust the Best Price detection
                - condition: template
                  value_template: >
                      {{ state_attr('sensor.<home_name>_today_s_price_volatility',
                      'price_volatility') != 'low' }}                      
                # Flat day → charge if price is below absolute threshold
                - condition: numeric_state
                  entity_id: sensor.<home_name>_current_electricity_price
                  below: 0.18
      action:
          - service: switch.turn_on
            target:
                entity_id: switch.ev_charger

Why this works:

  • On volatile days: Charges during detected best price periods (meaningful savings)
  • On flat days: Charges whenever price drops below your personal threshold (e.g., 18 ct/kWh) — no point optimizing for 0.5 ct difference
  • The price_volatility attribute is language-independent (values: low, moderate, high, very_high)

:::tip Simplest volatility check You can also check the volatility attribute directly on the best_price_period binary sensor. This is especially useful for periods that span across midnight:

condition:
    - condition: template
      value_template: >
          {{ state_attr('binary_sensor.<home_name>_best_price_period',
          'volatility') != 'low' }}          

:::

Using min_distance_from_avg in Scheduling Actions

The scheduling actions have a built-in volatility check: the min_distance_from_avg parameter. It ensures the found window is meaningfully cheaper (or more expensive) than average:

Show YAML: Scheduling with Minimum Distance
automation:
    - alias: "Dishwasher - Only Schedule If Meaningful Savings"
      trigger:
          - platform: time
            at: "20:00:00"
      action:
          - service: tibber_prices.find_cheapest_block
            data:
                duration: "02:00:00"
                search_start_time: "22:00:00"
                search_end_time: "06:00:00"
                search_end_day_offset: 1
                min_distance_from_avg: 10.0
            response_variable: result
          - if: "{{ result.window_found }}"
            then:
                - service: input_datetime.set_datetime
                  target:
                      entity_id: input_datetime.dishwasher_start
                  data:
                      datetime: "{{ result.window.start }}"
            else:
                # Prices are too flat — no meaningful savings
                # Just set a convenient default time
                - service: input_datetime.set_datetime
                  target:
                      entity_id: input_datetime.dishwasher_start
                  data:
                      datetime: "{{ today_at('22:00') + timedelta(days=0) }}"

Notification Patterns

Daily Scheduling Summary

Send one notification with the full picture for the next day:

Show YAML: Daily Summary Notification
automation:
    - alias: "Daily Price Summary"
      trigger:
          - platform: time
            at: "18:00:00"
      action:
          # Find cheapest and most expensive windows
          - service: tibber_prices.find_cheapest_block
            data:
                duration: "02:00:00"
                search_scope: remaining_today
            response_variable: cheap
          - service: tibber_prices.find_most_expensive_block
            data:
                duration: "02:00:00"
                search_scope: remaining_today
            response_variable: expensive
          - service: notify.mobile_app
            data:
                title: "⚡ Tonight's Price Summary"
                message: >
                    {% if cheap.window_found %}
                    Cheapest 2h: {{ cheap.window.start | as_datetime | as_local
                    | as_timestamp | timestamp_custom('%H:%M') }}{{ cheap.window.end
                    | as_datetime | as_local | as_timestamp | timestamp_custom('%H:%M') }}
                    ({{ cheap.window.price_mean | round(1) }} {{ cheap.price_unit }})
                    {% endif %}
                    {% if expensive.window_found %}
                    Most expensive 2h: {{ expensive.window.start | as_datetime | as_local
                    | as_timestamp | timestamp_custom('%H:%M') }}{{ expensive.window.end
                    | as_datetime | as_local | as_timestamp | timestamp_custom('%H:%M') }}
                    ({{ expensive.window.price_mean | round(1) }} {{ expensive.price_unit }})
                    {% endif %}
                    {% if cheap.window_found and expensive.window_found %}
                    Price spread: {{ (expensive.window.price_mean - cheap.window.price_mean)
                    | round(1) }} {{ cheap.price_unit }}
                    {% endif %}                    

Notify When Best Price Period Starts

Show YAML: Cheap Window Notification
automation:
    - alias: "Notify - Best Price Period Started"
      trigger:
          - platform: state
            entity_id: binary_sensor.<home_name>_best_price_period
            to: "on"
      action:
          - service: notify.mobile_app
            data:
                title: "⚡ Cheap Electricity Now!"
                message: >
                    Best price period started.
                    Price: {{ states('sensor.<home_name>_current_electricity_price') }}
                    {{ state_attr('sensor.<home_name>_current_electricity_price',
                    'unit_of_measurement') }}.
                    Duration: {{ state_attr('binary_sensor.<home_name>_best_price_period',
                    'duration_minutes') }} min.
                    Period avg: {{ state_attr('binary_sensor.<home_name>_best_price_period',
                    'price_mean') }}
                    {{ state_attr('sensor.<home_name>_current_electricity_price',
                    'unit_of_measurement') }}.                    

Quick Reference

When to Use Which Approach

Scenario Best approach Why
Dishwasher tonight find_cheapest_block Fixed 2h runtime, needs exact start time
Washer → dryer (must be sequential) find_cheapest_schedule sequential: true + gap_minutes for guaranteed order
Dishwasher + dryer (independent) find_cheapest_schedule Multiple appliances, prevent overlap
EV charging by morning find_cheapest_hours Flexible, can split into segments
Heat pump all day Sensors (rating_level) Continuous, adjusts every 15 min
Home battery Sensors (best/peak period) Reacts to detected cheap/expensive windows
Water heater boost Sensors (best_price_period) Simple on/off during cheap period
"Should I start the dryer now?" find_cheapest_block with remaining_today Ad-hoc: "Is there a cheaper time left today?"
Pool pump 4h daily find_cheapest_hours Flexible, doesn't need continuity
Seasonal heat pump tuning Runtime override entities Adjust flex/thresholds via automation

Sensor Combination Quick Reference

What You Want What to Check
"Is it cheap right now?" rating_level attribute on price sensor (VERY_CHEAP, CHEAP)
"Am I in a cheap/expensive period?" binary_sensor.<home_name>_best_price_period / peak_price_period state
"Will prices go up or down?" Price Outlook sensor state (rising, falling, stable)
"Is the price variation meaningful?" price_volatility attribute on volatility sensor (or volatility on period sensor)
"When is the next cheap window?" find_cheapest_block with search_scope: remaining_today
"Find the cheapest time for my 2h appliance" find_cheapest_block with duration: "02:00:00"
"When should I NOT run my appliance?" find_most_expensive_block

Scheduling Actions — Choosing the Right One

Your Situation Action Key Parameter
One appliance, must run uninterrupted find_cheapest_block duration
One appliance, can pause/resume find_cheapest_hours duration, min_segment_duration
Multiple independent appliances, no overlap find_cheapest_schedule tasks, gap_minutes
Sequential chain (A must finish before B) find_cheapest_schedule sequential: true, gap_minutes
Find the worst time (avoid it) find_most_expensive_block duration

Scheduling Actions — Full Guide for all parameters, response formats, and advanced options (power profiles, relaxation, outlier smoothing).


Charts & Visualizations

Looking for chart configurations? See the Chart Examples Guide for ApexCharts card configurations, rolling window modes, and more.