mirror of
https://github.com/jpawlowski/hass.tibber_prices.git
synced 2026-05-28 18:43:40 +00:00
docs(scheduling-actions): enhance dishwasher and appliance scheduling examples
Some checks are pending
Deploy Docusaurus Documentation (Dual Sites) / Build and Deploy Documentation Sites (push) Waiting to run
Some checks are pending
Deploy Docusaurus Documentation (Dual Sites) / Build and Deploy Documentation Sites (push) Waiting to run
Updated the dishwasher automation to include planning and execution steps using input_datetime helpers for better reliability across restarts. Clarified the use of find_cheapest_schedule for independent appliances and added caution regarding task order. Improved messaging in notifications for planned schedules. Impact: Users can now schedule appliances more reliably, ensuring planned start times persist through Home Assistant restarts. docs(sidebars): rename automation section for clarity Changed the sidebar label from '🤖 Automations' to '🤖 Automations & Usage' to better reflect the content and improve user navigation. Impact: Users will find it easier to locate automation-related documentation.
This commit is contained in:
parent
3057642cba
commit
0162394263
3 changed files with 1057 additions and 303 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -32,7 +32,8 @@ flowchart TD
|
||||||
|
|
||||||
- **Dishwasher, washing machine, dryer** → `find_cheapest_block` (must run X hours straight)
|
- **Dishwasher, washing machine, dryer** → `find_cheapest_block` (must run X hours straight)
|
||||||
- **EV charging, battery, pool pump** → `find_cheapest_hours` (total runtime matters, not continuity)
|
- **EV charging, battery, pool pump** → `find_cheapest_hours` (total runtime matters, not continuity)
|
||||||
- **Multiple appliances sharing a circuit** → `find_cheapest_schedule` (prevents overlap + manages gaps)
|
- **Multiple independent appliances** → `find_cheapest_schedule` (prevents overlap + manages gaps)
|
||||||
|
- **Sequential chain (A must finish before B)** → 2× `find_cheapest_block` (use A's end as B's search start)
|
||||||
- **"When should I NOT run this?"** → `find_most_expensive_block` or `find_most_expensive_hours`
|
- **"When should I NOT run this?"** → `find_most_expensive_block` or `find_most_expensive_hours`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -424,12 +425,15 @@ response_variable: result
|
||||||
|
|
||||||
### Use in Automations
|
### Use in Automations
|
||||||
|
|
||||||
|
**Prerequisite:** Create an `input_datetime.dishwasher_start` helper (type: Date and time) in **Settings → Helpers**.
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Show YAML: Dishwasher Automation</summary>
|
<summary>Show YAML: Dishwasher Automation (Plan + Execute)</summary>
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
automation:
|
automation:
|
||||||
- alias: "Start dishwasher at cheapest time"
|
# Step 1: Plan the cheapest start time every evening
|
||||||
|
- alias: "Dishwasher - Plan Cheapest Start"
|
||||||
trigger:
|
trigger:
|
||||||
- platform: time
|
- platform: time
|
||||||
at: "20:00:00"
|
at: "20:00:00"
|
||||||
|
|
@ -443,19 +447,31 @@ automation:
|
||||||
response_variable: result
|
response_variable: result
|
||||||
- if: "{{ result.window_found }}"
|
- if: "{{ result.window_found }}"
|
||||||
then:
|
then:
|
||||||
- service: automation.turn_on
|
- service: input_datetime.set_datetime
|
||||||
target:
|
target:
|
||||||
entity_id: automation.run_dishwasher
|
entity_id: input_datetime.dishwasher_start
|
||||||
- delay:
|
data:
|
||||||
hours: "{{ ((result.window.start | as_datetime - now()) .total_seconds() / 3600) | int }}"
|
datetime: "{{ result.window.start }}"
|
||||||
minutes: "{{ (((result.window.start | as_datetime - now()).total_seconds() % 3600) / 60) | int }}"
|
|
||||||
- service: switch.turn_on
|
# Step 2: Start the dishwasher at the stored time (survives HA restarts)
|
||||||
target:
|
- alias: "Dishwasher - Start at Planned Time"
|
||||||
entity_id: switch.dishwasher_smart_plug
|
trigger:
|
||||||
|
- platform: time
|
||||||
|
at: input_datetime.dishwasher_start
|
||||||
|
action:
|
||||||
|
# Option A: Smart appliance via Home Connect (see tip below)
|
||||||
|
# Option B: Smart plug
|
||||||
|
- service: switch.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: switch.dishwasher_smart_plug
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
:::tip Home Connect instead of smart plugs
|
||||||
|
If you have a **Bosch/Siemens appliance with Home Connect**, you can start the program directly instead of using a smart plug. See [Automation Examples — Home Connect tip](automation-examples.md#dishwasher-find-cheapest-2-hour-window-tonight) for exact service call syntax for both the official and alternative integration.
|
||||||
|
:::
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Find Cheapest Hours
|
## Find Cheapest Hours
|
||||||
|
|
@ -714,6 +730,10 @@ response_variable: result
|
||||||
|
|
||||||
If you call `find_cheapest_block` separately for each appliance, they might all find the **same** cheap time window. `find_cheapest_schedule` solves this by tracking which intervals are already claimed — each appliance gets its own non-overlapping slot.
|
If you call `find_cheapest_block` separately for each appliance, they might all find the **same** cheap time window. `find_cheapest_schedule` solves this by tracking which intervals are already claimed — each appliance gets its own non-overlapping slot.
|
||||||
|
|
||||||
|
:::caution No ordering guarantee
|
||||||
|
`find_cheapest_schedule` optimizes purely for **price** — it does not guarantee task order. The dryer could be scheduled before the washing machine if that's cheaper. For sequential workflows (washing machine → dryer), use **two sequential `find_cheapest_block` calls** where the second call starts after the first result ends. See [Automation Examples — Sequential Scheduling](automation-examples.md#washing-machine--dryer-sequential-scheduling) for a complete example.
|
||||||
|
:::
|
||||||
|
|
||||||
### Gap Minutes
|
### Gap Minutes
|
||||||
|
|
||||||
Use `gap_minutes` to add a mandatory pause between appliances:
|
Use `gap_minutes` to add a mandatory pause between appliances:
|
||||||
|
|
@ -784,16 +804,29 @@ response_variable: peak
|
||||||
|
|
||||||
## Practical Examples
|
## Practical Examples
|
||||||
|
|
||||||
|
:::tip Restart-safe automations
|
||||||
|
All examples below use `input_datetime` helpers to store planned start times. This ensures your schedule **survives HA restarts** — unlike `delay` or `wait_for_trigger` which are lost on restart.
|
||||||
|
|
||||||
|
**Setup:** Create an `input_datetime` helper per appliance in **Settings → Devices & Services → Helpers → Create Helper → Date and/or time** (choose "Date and time").
|
||||||
|
:::
|
||||||
|
|
||||||
### Overnight Appliance Scheduling
|
### Overnight Appliance Scheduling
|
||||||
|
|
||||||
Schedule dishwasher + washing machine to run overnight at cheapest prices, with a 15-minute gap between them:
|
Schedule dishwasher + washing machine to run overnight at cheapest prices, with a 15-minute gap between them. These appliances are **independent** — either can run first.
|
||||||
|
|
||||||
|
:::tip Sequential appliances (e.g., washer → dryer)?
|
||||||
|
If one appliance **must** finish before another starts, don't use `find_cheapest_schedule` — it doesn't guarantee order. Use **two sequential `find_cheapest_block` calls** instead. See [Automation Examples — Sequential Scheduling](automation-examples.md#washing-machine--dryer-sequential-scheduling).
|
||||||
|
:::
|
||||||
|
|
||||||
|
**Prerequisites:** Create `input_datetime.dishwasher_start` and `input_datetime.washing_machine_start` helpers.
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Show YAML: Overnight Appliance Scheduling</summary>
|
<summary>Show YAML: Overnight Appliance Scheduling (Plan + Execute)</summary>
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
automation:
|
automation:
|
||||||
- alias: "Schedule overnight appliances"
|
# Planning automation — runs every evening
|
||||||
|
- alias: "Laundry - Plan Overnight Schedule"
|
||||||
trigger:
|
trigger:
|
||||||
- platform: time
|
- platform: time
|
||||||
at: "21:00:00"
|
at: "21:00:00"
|
||||||
|
|
@ -812,25 +845,67 @@ automation:
|
||||||
response_variable: schedule
|
response_variable: schedule
|
||||||
- if: "{{ schedule.all_tasks_scheduled }}"
|
- if: "{{ schedule.all_tasks_scheduled }}"
|
||||||
then:
|
then:
|
||||||
|
# Store start times in helpers (survives HA restarts)
|
||||||
|
- 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.washing_machine_start
|
||||||
|
data:
|
||||||
|
datetime: >
|
||||||
|
{{ schedule.tasks | selectattr('name', 'eq', 'washing_machine')
|
||||||
|
| map(attribute='start') | first }}
|
||||||
- service: notify.mobile_app
|
- service: notify.mobile_app
|
||||||
data:
|
data:
|
||||||
title: "Appliance Schedule Ready"
|
title: "🧺 Laundry Planned"
|
||||||
message: >
|
message: >
|
||||||
Dishwasher: {{ schedule.tasks[0].start | as_datetime | as_local }}
|
Dishwasher: {{ (schedule.tasks | selectattr('name', 'eq', 'dishwasher')
|
||||||
Washing machine: {{ schedule.tasks[1].start | as_datetime | as_local }}
|
| map(attribute='start') | first) | as_datetime | as_local
|
||||||
Total cost: {{ schedule.total_estimated_cost | round(2) }} ct
|
| as_timestamp | timestamp_custom('%H:%M') }}
|
||||||
|
Washing machine: {{ (schedule.tasks | selectattr('name', 'eq', 'washing_machine')
|
||||||
|
| 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 — trigger at stored times
|
||||||
|
- alias: "Dishwasher - Start at Planned Time"
|
||||||
|
trigger:
|
||||||
|
- platform: time
|
||||||
|
at: input_datetime.dishwasher_start
|
||||||
|
action:
|
||||||
|
# Use Home Connect or smart plug — see automation-examples.md
|
||||||
|
- service: switch.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: switch.dishwasher_smart_plug
|
||||||
|
|
||||||
|
- alias: "Washing Machine - Start at Planned Time"
|
||||||
|
trigger:
|
||||||
|
- platform: time
|
||||||
|
at: input_datetime.washing_machine_start
|
||||||
|
action:
|
||||||
|
- service: switch.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: switch.washing_machine_smart_plug
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### EV Charging During Cheapest 4 Hours
|
### EV Charging During Cheapest 4 Hours
|
||||||
|
|
||||||
|
**Prerequisite:** Create `input_datetime.ev_charge_start` helper.
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Show YAML: EV Charging in Cheapest 4 Hours</summary>
|
<summary>Show YAML: EV Charging in Cheapest 4 Hours</summary>
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
automation:
|
automation:
|
||||||
- alias: "Smart EV charging"
|
- alias: "EV - Plan Cheapest Charging"
|
||||||
trigger:
|
trigger:
|
||||||
- platform: time
|
- platform: time
|
||||||
at: "18:00:00"
|
at: "18:00:00"
|
||||||
|
|
@ -849,18 +924,34 @@ automation:
|
||||||
response_variable: charging
|
response_variable: charging
|
||||||
- if: "{{ charging.intervals_found }}"
|
- if: "{{ charging.intervals_found }}"
|
||||||
then:
|
then:
|
||||||
|
# Store first segment start time
|
||||||
|
- service: input_datetime.set_datetime
|
||||||
|
target:
|
||||||
|
entity_id: input_datetime.ev_charge_start
|
||||||
|
data:
|
||||||
|
datetime: "{{ charging.schedule.segments[0].start }}"
|
||||||
- service: notify.mobile_app
|
- service: notify.mobile_app
|
||||||
data:
|
data:
|
||||||
title: "EV Charging Plan"
|
title: "🔌 EV Charging Planned"
|
||||||
message: >
|
message: >
|
||||||
{{ charging.schedule.segment_count }} charging sessions planned.
|
{{ charging.schedule.segment_count }} sessions:
|
||||||
Average price: {{ charging.schedule.price_mean | round(1) }} ct/kWh
|
{% for seg in charging.schedule.segments %}
|
||||||
Savings vs. peak: {{ charging.price_comparison.price_difference | round(1) }} ct/kWh
|
• {{ 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) }} {{ charging.price_unit }})
|
||||||
|
{% endfor %}
|
||||||
|
Savings vs. peak: {{ charging.price_comparison.price_difference
|
||||||
|
| round(1) }} {{ charging.price_unit }}
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
:::tip Simpler alternative for EV charging
|
||||||
|
If your charger can't pause/resume, use `find_cheapest_block` instead for one contiguous window. See the [Automation Examples](automation-examples.md#ev-charging-cheapest-4-hours-overnight) for a complete example.
|
||||||
|
:::
|
||||||
|
|
||||||
### Peak Price Warning
|
### Peak Price Warning
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
@ -868,10 +959,10 @@ automation:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
automation:
|
automation:
|
||||||
- alias: "Peak price warning"
|
- alias: "Peak Price - Morning Warning"
|
||||||
trigger:
|
trigger:
|
||||||
- platform: time
|
- platform: time
|
||||||
at: "08:00:00"
|
at: "07:00:00"
|
||||||
action:
|
action:
|
||||||
- service: tibber_prices.find_most_expensive_block
|
- service: tibber_prices.find_most_expensive_block
|
||||||
data:
|
data:
|
||||||
|
|
@ -884,10 +975,13 @@ automation:
|
||||||
data:
|
data:
|
||||||
title: "⚡ Expensive Period Today"
|
title: "⚡ Expensive Period Today"
|
||||||
message: >
|
message: >
|
||||||
Avoid high-power appliances between
|
Avoid heavy loads between
|
||||||
{{ peak.window.start | as_datetime | as_local }}
|
{{ peak.window.start | as_datetime | as_local
|
||||||
and {{ peak.window.end | as_datetime | as_local }}.
|
| as_timestamp | timestamp_custom('%H:%M') }}
|
||||||
Average price: {{ peak.window.price_mean | round(1) }} ct/kWh
|
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 }}
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ const sidebars: SidebarsConfig = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'category',
|
type: 'category',
|
||||||
label: '🤖 Automations',
|
label: '🤖 Automations & Usage',
|
||||||
link: { type: 'doc', id: 'automation-examples' },
|
link: { type: 'doc', id: 'automation-examples' },
|
||||||
items: ['automation-examples'],
|
items: ['automation-examples'],
|
||||||
collapsible: true,
|
collapsible: true,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue