Compare commits
105 Commits
8828281ae2
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 644cb28e88 | |||
| 7cae5a8c8b | |||
| 31c1258760 | |||
| 7cb07af473 | |||
| 25ab4ceeef | |||
| ba3ba34db8 | |||
| c54f583968 | |||
| 27224a4115 | |||
| ff8324af1f | |||
| 6731257b83 | |||
| 8b533ca95a | |||
| b46d820c20 | |||
| f5f085f155 | |||
| 63b6283fdb | |||
| 9e446e0d57 | |||
| d0e3a6c3e5 | |||
| 8b2ff535de | |||
| acdc7259a7 | |||
| c297012592 | |||
| 52dc97dd3d | |||
| 532f42b310 | |||
| ad5bdf91b9 | |||
| 0cb3145257 | |||
| 54144a03b8 | |||
| af3779a573 | |||
| 3094620985 | |||
| 8057b3abf1 | |||
| 677debfc27 | |||
| 910de6ed54 | |||
| bc6799c126 | |||
| 9f9de0524a | |||
| eacd137a7c | |||
| 63288dbb4b | |||
| c394d6b974 | |||
| fa64223630 | |||
| f2ac6064b5 | |||
| bd134bafef | |||
| 4e0819d4ff | |||
| 0d112fb4d0 | |||
| 067d5c6a63 | |||
| 5a7d25fd3c | |||
| bee2028f0b | |||
| 1525cc0070 | |||
| 812199889e | |||
| 7d5b31f9ab | |||
| bf6f2feba6 | |||
| ae8b1ea6f8 | |||
| 16b68da838 | |||
| 02d63e3d2b | |||
| 7b3bf5b375 | |||
| da78422f5e | |||
| ba0c34672c | |||
| 1805b4c107 | |||
| c366831dd0 | |||
| 6310ee4055 | |||
| 0e23bceb79 | |||
| 19dcab272e | |||
| 1fdcf410ed | |||
| 83fc33f809 | |||
| 1e96c41971 | |||
| 6c3def6996 | |||
| 68db35cae3 | |||
| 4e13ccf658 | |||
| 1063166faa | |||
| 750dee2996 | |||
| 7c6c62cff4 | |||
| e0ec8391d9 | |||
| 6208f6fb49 | |||
| 9cb7550f78 | |||
| 56f21f79e1 | |||
| 6b302b7c65 | |||
| a7580b851a | |||
| 3d2cfe6437 | |||
| 40cb13662a | |||
| 9b833aef16 | |||
| 4030b5995f | |||
| cd9bc28e9e | |||
| c36e47d2ee | |||
| 94f0cd4d8a | |||
| 158beed9c5 | |||
| 9c184ed0b7 | |||
| df734d9259 | |||
| 9f1dd20f06 | |||
| 8bd20bda42 | |||
| 30fffd8651 | |||
| 5a0232eeba | |||
| 3608da8a4c | |||
| 7e38ef2e42 | |||
| 72e0e583be | |||
| 70d01bfabf | |||
| 2ae9723fa3 | |||
| 9fbc1626d7 | |||
| cea0e599f9 | |||
| b7caa8d158 | |||
| 542af023c8 | |||
| 9697dd2925 | |||
| fa79ffc0e6 | |||
| 5bbf0a5082 | |||
| 7b7dc22245 | |||
| 2c3e5bb540 | |||
| 0f3743c1cf | |||
| ea97b824e7 | |||
| ef9200f65b | |||
| 83f8908a3f | |||
| 98adadbaaa |
+1
-1
@@ -1 +1 @@
|
|||||||
2026.4.3
|
2026.5.1
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
name: ha
|
||||||
|
description: "Use when creating, updating, or reviewing Home Assistant YAML automations, scripts, templates, dashboards, and includes in this repository. Trigger phrases: ha, home assistant, automation, script, yaml, lovelace, include, configuration.yaml."
|
||||||
|
tools: [read, edit, search, todo, execute]
|
||||||
|
user-invocable: true
|
||||||
|
---
|
||||||
|
You are a Home Assistant specialist for this exact repository.
|
||||||
|
|
||||||
|
Your primary goal is to produce reliable, maintainable YAML changes that match this installation's structure and conventions.
|
||||||
|
|
||||||
|
## Core Principles
|
||||||
|
- Target latest Home Assistant behavior and YAML syntax.
|
||||||
|
- Always prefer YAML-managed configuration over UI-generated storage unless explicitly requested.
|
||||||
|
- Keep changes minimal, safe, and consistent with nearby files.
|
||||||
|
- Never move secrets into tracked YAML files; keep secrets in secrets.yaml.
|
||||||
|
|
||||||
|
## Repository-Specific Layout
|
||||||
|
Follow these include rules from configuration.yaml:
|
||||||
|
- Automations: include/automations/ via include_dir_merge_list
|
||||||
|
- Scripts: include/scripts/ via include_dir_merge_named
|
||||||
|
- Shell commands: include/shell_commands/ via include_dir_merge_named
|
||||||
|
- Templates: include/templates/ via include_dir_merge_list
|
||||||
|
- Sensors: include/sensors/ via include_dir_merge_list
|
||||||
|
- Binary sensors: include/binary_sensors/ via include_dir_merge_list
|
||||||
|
- Inputs live under include/input/*/
|
||||||
|
|
||||||
|
When adding new logic:
|
||||||
|
- Put automations in include/automations/*.yaml (do not edit root automations.yaml unless asked).
|
||||||
|
- Put scripts in include/scripts/*.yaml (do not edit root scripts.yaml unless asked).
|
||||||
|
- Put shell commands in include/shell_commands/*.yaml.
|
||||||
|
- Keep Lovelace in YAML mode and edit existing YAML dashboards/views files.
|
||||||
|
|
||||||
|
## Automation Authoring Rules
|
||||||
|
- Use clear alias names and deterministic triggers.
|
||||||
|
- Prefer idempotent actions and guard conditions to avoid repeated/duplicate notifications.
|
||||||
|
- Use choose/conditions for branching instead of duplicated automations.
|
||||||
|
- Reuse existing helper entities and naming patterns where possible.
|
||||||
|
- Keep time/date logic explicit (timezone-aware assumptions for Europe/Copenhagen).
|
||||||
|
|
||||||
|
## YAML Quality Rules
|
||||||
|
- Preserve existing indentation and style in the target file.
|
||||||
|
- Avoid broad refactors when only a local change is needed.
|
||||||
|
- Keep comments short and only where they clarify non-obvious logic.
|
||||||
|
- If multiple files are touched, ensure references are consistent across automations/scripts/shell commands/dashboard buttons.
|
||||||
|
|
||||||
|
## Validation and Safety
|
||||||
|
- After edits, run a quick repository sanity check (at minimum inspect YAML structure and obvious syntax issues).
|
||||||
|
- If executable checks are available, run them and report results.
|
||||||
|
- Call out assumptions or runtime dependencies (custom components, integrations, mobile notify targets).
|
||||||
|
- Do not remove or overwrite unrelated user changes.
|
||||||
|
|
||||||
|
## Output Expectations
|
||||||
|
When asked to implement changes:
|
||||||
|
1. Make the YAML edits directly in the correct include files.
|
||||||
|
2. Explain exactly which files were changed and why.
|
||||||
|
3. Summarize any validation performed.
|
||||||
|
4. Mention any manual Home Assistant reload/restart steps required (automation/script reload vs full restart).
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
---
|
||||||
|
name: Madplan
|
||||||
|
description: "Use when creating or updating weekly dinner meal plans for this family, importing Valdemarsro recipes into Mealie, avoiding duplicates, and producing a Bilka ToGo shopping list. Trigger phrases: madplan, mealie, Valdemarsro, aftensmad, indkøbsliste, Bilka ToGo, ugeplan."
|
||||||
|
tools: [read, edit, search, web, todo, execute]
|
||||||
|
user-invocable: true
|
||||||
|
---
|
||||||
|
You are a specialist meal-planning agent for this Home Assistant + Mealie setup.
|
||||||
|
|
||||||
|
Your goal is to create practical weekly DINNER plans that match family nutrition needs, reuse leftovers smartly, and keep shopping simple.
|
||||||
|
|
||||||
|
## Family Profile
|
||||||
|
- Family of 4: Anne (49), Claus (54), Andreas (16), Daniel (14).
|
||||||
|
- Andreas: wants high protein and fewer carbs.
|
||||||
|
- Daniel: elite swimmer, trains 7x/week, needs high carbs.
|
||||||
|
- Anne and Claus: weight maintenance.
|
||||||
|
|
||||||
|
## Fixed Weekly Structure
|
||||||
|
- Dinner only.
|
||||||
|
- Sunday: cook double portion; serve leftovers Tuesday.
|
||||||
|
- Monday: cook double portion; serve leftovers Wednesday.
|
||||||
|
- Thursday: light, fast, flexible meal (family may eat at different times).
|
||||||
|
- Friday: "lækker mad" (for example ribeye-style dinner).
|
||||||
|
- Saturday: "lækker mad" (for example homemade salmon sushi).
|
||||||
|
|
||||||
|
## Recipe Source Rules
|
||||||
|
- Prefer Valdemarsro recipes.
|
||||||
|
- Always import planned recipes into Mealie by default.
|
||||||
|
- Fetch recipe pages and map to Mealie-friendly names/slugs before finalizing the plan.
|
||||||
|
- Never add duplicate recipes already present in Mealie or existing week plan.
|
||||||
|
- Keep the plan realistic for one weekly grocery run.
|
||||||
|
|
||||||
|
## Shopping Rules
|
||||||
|
- Build one combined weekly shopping list suitable for Bilka ToGo.
|
||||||
|
- Group clearly by category (produce, meat/fish, dairy, pantry, frozen, etc.).
|
||||||
|
- Use plain item names and practical quantities for 4 people, accounting for double-portion days.
|
||||||
|
- If exact quantities are unknown from source, provide best-effort estimates and mark them clearly.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
1. Read current Mealie data and detect already planned dinners and existing imported recipes.
|
||||||
|
2. Propose or update a week plan that follows the fixed Sunday-Monday leftovers pattern.
|
||||||
|
3. Validate nutrition balance across the week:
|
||||||
|
- Ensure protein-forward options exist for Andreas.
|
||||||
|
- Ensure carb availability for Daniel (sides/add-ons where relevant).
|
||||||
|
- Keep Anne/Claus portions and sides weight-maintenance friendly.
|
||||||
|
4. Import all missing planned recipes into Mealie references before publishing the final plan.
|
||||||
|
5. Produce a Bilka ToGo shopping list for the final plan.
|
||||||
|
|
||||||
|
## Output Format
|
||||||
|
Always return:
|
||||||
|
1. Weekly dinner plan (Sun-Sat) with leftover links Tue/Wed.
|
||||||
|
2. Brief nutrition notes per day (protein/carbs adjustment hints per person).
|
||||||
|
3. "Imported to Mealie" section (added vs skipped as duplicate).
|
||||||
|
4. Bilka ToGo shopping list grouped by category.
|
||||||
|
5. Optional prep plan for Friday pickup/delivery and Sunday prep.
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
- Do not plan breakfast/lunch unless explicitly requested.
|
||||||
|
- Do not ignore the fixed leftovers pattern.
|
||||||
|
- Do not leave planned recipes unimported in Mealie unless blocked by missing source data.
|
||||||
|
- Do not include duplicate recipe imports.
|
||||||
|
- Keep recommendations practical for a busy training week.
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
# Ignore everything globally
|
# Ignore everything globally
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
._*
|
||||||
custom_components/
|
custom_components/
|
||||||
blueprints/
|
blueprints/
|
||||||
dwains-dashboard/
|
dwains-dashboard/
|
||||||
@@ -37,6 +38,10 @@ configuration_minimal.yaml
|
|||||||
!.HA_VERSION
|
!.HA_VERSION
|
||||||
!customize
|
!customize
|
||||||
|
|
||||||
|
# --- Re-ignore macOS metadata files inside whitelisted dirs ---
|
||||||
|
include/**/.DS_Store
|
||||||
|
include/**/._*
|
||||||
|
|
||||||
# --- Whitelist directories ---
|
# --- Whitelist directories ---
|
||||||
!www/
|
!www/
|
||||||
!include/
|
!include/
|
||||||
@@ -65,6 +70,7 @@ configuration_minimal.yaml
|
|||||||
/oldscripts.yaml
|
/oldscripts.yaml
|
||||||
|
|
||||||
# --- Local media snapshots and downloads ---
|
# --- Local media snapshots and downloads ---
|
||||||
|
/www/snapshots/
|
||||||
/www/affalddk/
|
/www/affalddk/
|
||||||
/www/community/
|
/www/community/
|
||||||
/www/indkorsel_snapshot.jpg
|
/www/indkorsel_snapshot.jpg
|
||||||
@@ -75,4 +81,7 @@ configuration_minimal.yaml
|
|||||||
|
|
||||||
# --- Local helper scripts not used in git ---
|
# --- Local helper scripts not used in git ---
|
||||||
/python_scripts/scene_generator.py
|
/python_scripts/scene_generator.py
|
||||||
|
|
||||||
|
# --- Temporary backup workspace ---
|
||||||
|
/tmp_backups/
|
||||||
/python_scripts/update_climate.py
|
/python_scripts/update_climate.py
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
## Installation of home-assistant on Synology
|
## Installation of home-assistant on Synology
|
||||||
|
|
||||||
Follow https://www.home-assistant.io/installation/alternative/:
|
Follow https://www.home-assistant.io/installation/alternative/:
|
||||||
|
|||||||
+3
-3
@@ -6,7 +6,7 @@ default_config:
|
|||||||
|
|
||||||
homeassistant:
|
homeassistant:
|
||||||
name: !secret name
|
name: !secret name
|
||||||
external_url: "http://anneclaus.duckdns.org:8123"
|
external_url: "https://ha.anneclaus.dk"
|
||||||
internal_url: "http://dethlefsen:8123"
|
internal_url: "http://dethlefsen:8123"
|
||||||
auth_providers:
|
auth_providers:
|
||||||
- type: homeassistant
|
- type: homeassistant
|
||||||
@@ -27,6 +27,7 @@ http:
|
|||||||
trusted_proxies:
|
trusted_proxies:
|
||||||
- 127.0.0.1
|
- 127.0.0.1
|
||||||
- 10.0.0.142
|
- 10.0.0.142
|
||||||
|
- 172.17.0.0/16 # Docker bridge (NPM)
|
||||||
|
|
||||||
logger:
|
logger:
|
||||||
default: warning
|
default: warning
|
||||||
@@ -38,6 +39,7 @@ logger:
|
|||||||
homeassistant.components.discovery: error
|
homeassistant.components.discovery: error
|
||||||
homeassistant.components.dlna_dmr: error
|
homeassistant.components.dlna_dmr: error
|
||||||
async_upnp_client: error
|
async_upnp_client: error
|
||||||
|
automower_ble: critical
|
||||||
|
|
||||||
recorder:
|
recorder:
|
||||||
purge_keep_days: 7
|
purge_keep_days: 7
|
||||||
@@ -110,7 +112,6 @@ cover:
|
|||||||
|
|
||||||
template: !include_dir_merge_list include/templates/
|
template: !include_dir_merge_list include/templates/
|
||||||
group: !include_dir_merge_named include/groups/
|
group: !include_dir_merge_named include/groups/
|
||||||
mqtt: !include include/mqtt.yaml
|
|
||||||
sensor: !include_dir_merge_list include/sensors/
|
sensor: !include_dir_merge_list include/sensors/
|
||||||
automation: !include_dir_merge_list include/automations/
|
automation: !include_dir_merge_list include/automations/
|
||||||
binary_sensor: !include_dir_merge_list include/binary_sensors/
|
binary_sensor: !include_dir_merge_list include/binary_sensors/
|
||||||
@@ -120,7 +121,6 @@ input_number: !include_dir_merge_named include/input/number/
|
|||||||
input_select: !include_dir_merge_named include/input/select/
|
input_select: !include_dir_merge_named include/input/select/
|
||||||
input_boolean: !include_dir_merge_named include/input/boolean/
|
input_boolean: !include_dir_merge_named include/input/boolean/
|
||||||
input_text: !include_dir_merge_named include/input/text/
|
input_text: !include_dir_merge_named include/input/text/
|
||||||
command_line: !include_dir_merge_list include/command_line/
|
|
||||||
light: !include_dir_merge_list include/lights/
|
light: !include_dir_merge_list include/lights/
|
||||||
panel_iframe: !include_dir_merge_named include/panels/
|
panel_iframe: !include_dir_merge_named include/panels/
|
||||||
script: !include_dir_merge_named include/scripts/
|
script: !include_dir_merge_named include/scripts/
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
title: Person Status
|
|
||||||
path: person-status
|
|
||||||
icon: mdi:account-group
|
|
||||||
type: sections
|
|
||||||
max_columns: 1 # én kolonne for fuld bredde
|
|
||||||
|
|
||||||
sections:
|
|
||||||
|
|
||||||
# 👨👩👧👦 Personer (2x2 grid)
|
|
||||||
- type: grid
|
|
||||||
columns: 2
|
|
||||||
square: false
|
|
||||||
cards:
|
|
||||||
|
|
||||||
- type: entities
|
|
||||||
title: Daniel
|
|
||||||
entities:
|
|
||||||
- entity: person.daniel_schusler_dethlefsen
|
|
||||||
- entity: input_select.daniel_status
|
|
||||||
|
|
||||||
- type: entities
|
|
||||||
title: Claus
|
|
||||||
entities:
|
|
||||||
- entity: person.claus_dethlefsen
|
|
||||||
- entity: input_select.claus_status
|
|
||||||
|
|
||||||
- type: entities
|
|
||||||
title: Anne
|
|
||||||
entities:
|
|
||||||
- entity: person.anne_schusler_dethlefsen
|
|
||||||
- entity: input_select.anne_status
|
|
||||||
|
|
||||||
- type: entities
|
|
||||||
title: Andreas
|
|
||||||
entities:
|
|
||||||
- entity: person.andreas_schusler_dethlefsen
|
|
||||||
- entity: input_select.andreas_status
|
|
||||||
|
|
||||||
|
|
||||||
# 🗺️ Popup kort nederst
|
|
||||||
- type: grid
|
|
||||||
cards:
|
|
||||||
- type: custom:button-card
|
|
||||||
name: Åbn kort
|
|
||||||
icon: mdi:map
|
|
||||||
show_state: false
|
|
||||||
styles:
|
|
||||||
card:
|
|
||||||
- border-radius: 16px
|
|
||||||
- padding: 14px
|
|
||||||
- background: linear-gradient(135deg, rgba(0,0,0,0.05), rgba(0,0,0,0.1))
|
|
||||||
icon:
|
|
||||||
- color: var(--primary-color)
|
|
||||||
name:
|
|
||||||
- font-size: 14px
|
|
||||||
- font-weight: 600
|
|
||||||
tap_action:
|
|
||||||
action: call-service
|
|
||||||
service: browser_mod.popup
|
|
||||||
service_data:
|
|
||||||
title: 📍 Familien
|
|
||||||
size: fullscreen
|
|
||||||
style:
|
|
||||||
--popup-border-radius: 20px
|
|
||||||
--popup-background-color: rgba(0,0,0,0.9)
|
|
||||||
content:
|
|
||||||
type: map
|
|
||||||
default_zoom: 12
|
|
||||||
hours_to_show: 6
|
|
||||||
aspect_ratio: 1
|
|
||||||
entities:
|
|
||||||
- entity: device_tracker.andreas_iphone_12
|
|
||||||
name: Andreas
|
|
||||||
- entity: device_tracker.daniels_iphone_7_3
|
|
||||||
name: Daniel
|
|
||||||
- entity: device_tracker.annes_iphone_xs_4
|
|
||||||
name: Anne
|
|
||||||
- entity: device_tracker.claus_iphone_15pro
|
|
||||||
name: Claus
|
|
||||||
@@ -4,6 +4,31 @@ icon: mdi:bed-king-outline
|
|||||||
type: sections
|
type: sections
|
||||||
|
|
||||||
sections:
|
sections:
|
||||||
|
- type: grid
|
||||||
|
cards:
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Godnat
|
||||||
|
icon: mdi:weather-night
|
||||||
|
tap_action:
|
||||||
|
action: call-service
|
||||||
|
service: script.godnat_sovevaerelse
|
||||||
|
hold_action:
|
||||||
|
action: call-service
|
||||||
|
service: light.turn_off
|
||||||
|
service_data:
|
||||||
|
entity_id: light.sovevaerelse
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 64px
|
||||||
|
- background-color: "#1a1a2e"
|
||||||
|
- color: "#c8b8f0"
|
||||||
|
icon:
|
||||||
|
- color: "#c8b8f0"
|
||||||
|
- width: 24px
|
||||||
|
name:
|
||||||
|
- font-size: 14px
|
||||||
|
- color: "#c8b8f0"
|
||||||
|
|
||||||
- type: grid
|
- type: grid
|
||||||
cards:
|
cards:
|
||||||
- type: heading
|
- type: heading
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ cards:
|
|||||||
[[[
|
[[[
|
||||||
var slug = states['sensor.dagens_aftensmad_slug'].state;
|
var slug = states['sensor.dagens_aftensmad_slug'].state;
|
||||||
if (slug && slug !== '' && slug !== 'unknown') {
|
if (slug && slug !== '' && slug !== 'unknown') {
|
||||||
return 'http://anneclaus.dk:9925/g/home/r/' + slug;
|
return 'https://mealie.anneclaus.dk/g/home/r/' + slug;
|
||||||
}
|
}
|
||||||
return 'http://anneclaus.dk:9925';
|
return 'https://mealie.anneclaus.dk';
|
||||||
]]]
|
]]]
|
||||||
styles:
|
styles:
|
||||||
card:
|
card:
|
||||||
@@ -41,7 +41,7 @@ cards:
|
|||||||
- color: white
|
- color: white
|
||||||
- padding-top: 4px
|
- padding-top: 4px
|
||||||
|
|
||||||
# 🎵 Musik i køkken + Der er mad
|
# 🎵 Musik i køkken + Vi laver mad + Der er mad
|
||||||
- type: grid
|
- type: grid
|
||||||
columns: 2
|
columns: 2
|
||||||
square: false
|
square: false
|
||||||
@@ -57,6 +57,13 @@ cards:
|
|||||||
data:
|
data:
|
||||||
source: "1 Family Mix"
|
source: "1 Family Mix"
|
||||||
|
|
||||||
|
- type: button
|
||||||
|
name: Vi laver mad
|
||||||
|
icon: mdi:chef-hat
|
||||||
|
tap_action:
|
||||||
|
action: call-service
|
||||||
|
service: script.vi_laver_mad
|
||||||
|
|
||||||
- type: button
|
- type: button
|
||||||
name: Der er mad!
|
name: Der er mad!
|
||||||
icon: mdi:silverware-fork-knife
|
icon: mdi:silverware-fork-knife
|
||||||
@@ -64,69 +71,626 @@ cards:
|
|||||||
action: call-service
|
action: call-service
|
||||||
service: script.mad_announcement
|
service: script.mad_announcement
|
||||||
|
|
||||||
# 📅 Ugens madplan
|
# 📅 Madplan: i dag + 6 dage
|
||||||
|
- type: markdown
|
||||||
|
content: |-
|
||||||
|
{%- set ns = namespace(rows="") -%}
|
||||||
|
{%- set items = state_attr('sensor.mealie_madplan_ugen', 'items') or [] -%}
|
||||||
|
{%- set days = ['Mandag','Tirsdag','Onsdag','Torsdag','Fredag','Lørdag','Søndag'] -%}
|
||||||
|
{%- for offset in range(7) -%}
|
||||||
|
{%- set day = now().date() + timedelta(days=offset) -%}
|
||||||
|
{%- set ms = items | selectattr('date', 'eq', day.isoformat()) | list -%}
|
||||||
|
{%- set m = ms[0] if ms else none -%}
|
||||||
|
{%- set recipe = m.recipe if m else none -%}
|
||||||
|
{%- set name = recipe.name if recipe else '' -%}
|
||||||
|
{%- set slug = recipe.slug if recipe else '' -%}
|
||||||
|
{%- set label = 'I dag' if offset == 0 else days[day.weekday()] -%}
|
||||||
|
{%- if slug -%}
|
||||||
|
{%- set ns.rows = ns.rows + "| **" + label + "** | [" + name + "](https://mealie.anneclaus.dk/g/home/r/" + slug + ") |\n" -%}
|
||||||
|
{%- elif name -%}
|
||||||
|
{%- set ns.rows = ns.rows + "| **" + label + "** | " + name + " |\n" -%}
|
||||||
|
{%- else -%}
|
||||||
|
{%- set ns.rows = ns.rows + "| **" + label + "** | - |\n" -%}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endfor -%}
|
||||||
|
## Næste 7 dage
|
||||||
|
|
||||||
|
| Dag | Ret |
|
||||||
|
| --- | --- |
|
||||||
|
{{ ns.rows }}
|
||||||
|
|
||||||
|
# 🎵 Sonos Køkken
|
||||||
|
- type: media-control
|
||||||
|
entity: media_player.kokken
|
||||||
|
name: Sonos Køkken
|
||||||
|
|
||||||
|
- type: grid
|
||||||
|
columns: 2
|
||||||
|
square: false
|
||||||
|
cards:
|
||||||
- type: custom:button-card
|
- type: custom:button-card
|
||||||
entity: sensor.mealie_madplan_ugen
|
name: Volumen ned
|
||||||
name: Ugens madplan
|
icon: mdi:volume-minus
|
||||||
show_icon: false
|
tap_action:
|
||||||
show_name: true
|
action: perform-action
|
||||||
show_state: false
|
perform_action: media_player.volume_down
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
hold_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.volume_down
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
styles:
|
styles:
|
||||||
card:
|
card:
|
||||||
- padding: 0
|
- height: 72px
|
||||||
- border-radius: 12px
|
|
||||||
name:
|
|
||||||
- font-size: 16px
|
- font-size: 16px
|
||||||
- font-weight: bold
|
- background: var(--primary-color)
|
||||||
- padding: 12px 12px 4px 12px
|
icon:
|
||||||
- justify-self: start
|
- color: white
|
||||||
custom_fields:
|
- width: 36px
|
||||||
week: |
|
name:
|
||||||
[[[
|
- color: white
|
||||||
if (!entity || !entity.attributes || !entity.attributes.items)
|
- font-size: 13px
|
||||||
return '<div>Ingen madplan data</div>';
|
|
||||||
var items = entity.attributes.items;
|
- type: custom:button-card
|
||||||
var dayNames = ['Søndag','Mandag','Tirsdag','Onsdag','Torsdag','Fredag','Lørdag'];
|
name: Volumen op
|
||||||
var today = new Date().toISOString().slice(0,10);
|
icon: mdi:volume-plus
|
||||||
var meals = {};
|
tap_action:
|
||||||
for (var j = 0; j < items.length; j++) {
|
action: perform-action
|
||||||
var it = items[j];
|
perform_action: media_player.volume_up
|
||||||
if (it.recipe && it.recipe.name) {
|
target:
|
||||||
meals[it.date] = { name: it.recipe.name, slug: it.recipe.slug || '' };
|
entity_id: media_player.kokken
|
||||||
}
|
hold_action:
|
||||||
}
|
action: perform-action
|
||||||
var now = new Date();
|
perform_action: media_player.volume_up
|
||||||
var dow = now.getDay();
|
target:
|
||||||
var monday = new Date(now);
|
entity_id: media_player.kokken
|
||||||
monday.setDate(now.getDate() - (dow === 0 ? 6 : dow - 1));
|
styles:
|
||||||
var html = '<div style="width:100%">';
|
card:
|
||||||
for (var i = 0; i < 7; i++) {
|
- height: 72px
|
||||||
var d = new Date(monday);
|
- font-size: 16px
|
||||||
d.setDate(monday.getDate() + i);
|
- background: var(--primary-color)
|
||||||
var dateStr = d.toISOString().slice(0,10);
|
icon:
|
||||||
var dayName = dayNames[d.getDay()];
|
- color: white
|
||||||
var meal = meals[dateStr];
|
- width: 36px
|
||||||
var isToday = dateStr === today;
|
name:
|
||||||
var bg = isToday ? 'var(--primary-color)' : 'transparent';
|
- color: white
|
||||||
var tc = isToday ? 'white' : 'var(--primary-text-color)';
|
- font-size: 13px
|
||||||
var dc = isToday ? 'rgba(255,255,255,0.7)' : 'var(--secondary-text-color)';
|
|
||||||
var fw = isToday ? 'bold' : 'normal';
|
- type: grid
|
||||||
var br = isToday ? '8px' : '0';
|
columns: 3
|
||||||
var bb = isToday ? 'none' : '1px solid var(--divider-color)';
|
square: false
|
||||||
var mn = meal ? meal.name : '-';
|
cards:
|
||||||
var sl = meal ? meal.slug : '';
|
- type: custom:button-card
|
||||||
var lnk = sl ? 'http://anneclaus.dk:9925/g/home/r/' + sl : '';
|
name: DR P3
|
||||||
var cur = sl ? 'pointer' : 'default';
|
icon: mdi:radio
|
||||||
var oc = sl ? ' onclick="window.open(\x27' + lnk + '\x27,\x27_blank\x27)"' : '';
|
tap_action:
|
||||||
html += '<div style="display:flex;align-items:center;padding:10px 12px;background:' + bg + ';border-radius:' + br + ';border-bottom:' + bb + ';cursor:' + cur + '"' + oc + '>';
|
action: perform-action
|
||||||
html += '<div style="width:70px;font-size:12px;color:' + dc + ';text-transform:uppercase;font-weight:600">' + dayName + '</div>';
|
perform_action: media_player.select_source
|
||||||
html += '<div style="flex:1;font-size:15px;color:' + tc + ';font-weight:' + fw + '">' + mn + '</div>';
|
target:
|
||||||
html += '</div>';
|
entity_id: media_player.kokken
|
||||||
}
|
data:
|
||||||
html += '</div>';
|
source: "0 DR P3"
|
||||||
return html;
|
styles:
|
||||||
]]]
|
card:
|
||||||
extra_styles: |
|
- height: 52px
|
||||||
#week {
|
- padding: 6px 8px
|
||||||
width: 100%;
|
icon:
|
||||||
}
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Family Mix
|
||||||
|
icon: mdi:account-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "1 Family Mix"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Danske fav.
|
||||||
|
icon: mdi:music-note
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Danske favoritter"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Rock klassikere
|
||||||
|
icon: mdi:music-note-outline
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Danske rock klassikere"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Anne Mix 1
|
||||||
|
icon: mdi:playlist-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Anne Daily Mix 1"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Anne Mix 2
|
||||||
|
icon: mdi:playlist-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Anne Daily Mix 2"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Anne Mix 3
|
||||||
|
icon: mdi:playlist-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Anne Daily Mix 3"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Anne Mix 4
|
||||||
|
icon: mdi:playlist-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Anne Daily Mix 4"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Anne Mix 5
|
||||||
|
icon: mdi:playlist-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Anne Daily Mix 5"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Anne Mix 6
|
||||||
|
icon: mdi:playlist-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Anne Daily Mix 6"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Claus Mix 1
|
||||||
|
icon: mdi:playlist-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Claus Daily Mix 1"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Claus Mix 2
|
||||||
|
icon: mdi:playlist-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Claus Daily Mix 2"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Claus Mix 3
|
||||||
|
icon: mdi:playlist-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Claus Daily Mix 3"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Claus Mix 4
|
||||||
|
icon: mdi:playlist-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Claus Daily Mix 4"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Claus Mix 5
|
||||||
|
icon: mdi:playlist-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Claus Daily Mix 5"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Claus Mix 6
|
||||||
|
icon: mdi:playlist-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Claus Daily Mix 6"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Andreas Mix 1
|
||||||
|
icon: mdi:playlist-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Andreas Daily Mix 1"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Andreas Mix 2
|
||||||
|
icon: mdi:playlist-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Andreas Daily Mix 2"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Andreas Mix 3
|
||||||
|
icon: mdi:playlist-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Andreas Daily Mix 3"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Andreas Mix 4
|
||||||
|
icon: mdi:playlist-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Andreas Daily Mix 4"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Andreas Mix 5
|
||||||
|
icon: mdi:playlist-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Andreas Daily Mix 5"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Andreas Mix 6
|
||||||
|
icon: mdi:playlist-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Andreas Daily Mix 6"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Daniel Mix 1
|
||||||
|
icon: mdi:playlist-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Daniel Daily Mix 1"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Daniel Mix 2
|
||||||
|
icon: mdi:playlist-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Daniel Daily Mix 2"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Daniel Mix 3
|
||||||
|
icon: mdi:playlist-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Daniel Daily Mix 3"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Daniel Mix 4
|
||||||
|
icon: mdi:playlist-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Daniel Daily Mix 4"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Daniel Mix 5
|
||||||
|
icon: mdi:playlist-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Daniel Daily Mix 5"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Daniel Mix 6
|
||||||
|
icon: mdi:playlist-music
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "Daniel Daily Mix 6"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
icon:
|
||||||
|
- width: 18px
|
||||||
|
name:
|
||||||
|
- font-size: 11px
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,215 @@
|
|||||||
|
title: Vanding
|
||||||
|
path: vanding
|
||||||
|
icon: mdi:sprinkler-variant
|
||||||
|
type: sections
|
||||||
|
|
||||||
|
max_columns: 2
|
||||||
|
|
||||||
|
sections:
|
||||||
|
|
||||||
|
# 💧 Jordfugt – målere
|
||||||
|
- type: grid
|
||||||
|
cards:
|
||||||
|
- type: heading
|
||||||
|
heading: Jordfugt
|
||||||
|
icon: mdi:water-percent
|
||||||
|
|
||||||
|
- type: gauge
|
||||||
|
entity: sensor.annes_havesensor_soil_moisture_1
|
||||||
|
name: Højbed 1 – Ærter
|
||||||
|
min: 0
|
||||||
|
max: 100
|
||||||
|
needle: true
|
||||||
|
severity:
|
||||||
|
green: 40
|
||||||
|
yellow: 20
|
||||||
|
red: 0
|
||||||
|
|
||||||
|
- type: gauge
|
||||||
|
entity: sensor.annes_havesensor_soil_moisture_2
|
||||||
|
name: Højbed 2 – Kartofler
|
||||||
|
min: 0
|
||||||
|
max: 100
|
||||||
|
needle: true
|
||||||
|
severity:
|
||||||
|
green: 40
|
||||||
|
yellow: 20
|
||||||
|
red: 0
|
||||||
|
|
||||||
|
- type: gauge
|
||||||
|
entity: sensor.annes_havesensor_soil_moisture_3
|
||||||
|
name: Højbed 3 – Rabarber
|
||||||
|
min: 0
|
||||||
|
max: 100
|
||||||
|
needle: true
|
||||||
|
severity:
|
||||||
|
green: 40
|
||||||
|
yellow: 20
|
||||||
|
red: 0
|
||||||
|
|
||||||
|
- type: gauge
|
||||||
|
entity: sensor.annes_havesensor_soil_moisture_4
|
||||||
|
name: Drivhus
|
||||||
|
min: 0
|
||||||
|
max: 100
|
||||||
|
needle: true
|
||||||
|
severity:
|
||||||
|
green: 45
|
||||||
|
yellow: 25
|
||||||
|
red: 0
|
||||||
|
|
||||||
|
# 📈 Jordfugt – historik
|
||||||
|
- type: grid
|
||||||
|
cards:
|
||||||
|
- type: heading
|
||||||
|
heading: Jordfugt – 7 dage
|
||||||
|
icon: mdi:chart-line
|
||||||
|
|
||||||
|
- type: history-graph
|
||||||
|
title: Højbede (%)
|
||||||
|
entities:
|
||||||
|
- entity: sensor.annes_havesensor_soil_moisture_1
|
||||||
|
name: HB1 Ærter
|
||||||
|
- entity: sensor.annes_havesensor_soil_moisture_2
|
||||||
|
name: HB2 Kartofler
|
||||||
|
- entity: sensor.annes_havesensor_soil_moisture_3
|
||||||
|
name: HB3 Rabarber
|
||||||
|
hours_to_show: 168
|
||||||
|
refresh_interval: 900
|
||||||
|
|
||||||
|
- type: history-graph
|
||||||
|
title: Drivhus (%)
|
||||||
|
entities:
|
||||||
|
- entity: sensor.annes_havesensor_soil_moisture_4
|
||||||
|
name: Drivhus
|
||||||
|
hours_to_show: 168
|
||||||
|
refresh_interval: 900
|
||||||
|
|
||||||
|
# 🌧️ Regn & vejr
|
||||||
|
- type: grid
|
||||||
|
cards:
|
||||||
|
- type: heading
|
||||||
|
heading: Regn (Netatmo)
|
||||||
|
icon: mdi:weather-rainy
|
||||||
|
|
||||||
|
- type: tile
|
||||||
|
entity: sensor.n22_nedbor
|
||||||
|
name: Nedbør nu
|
||||||
|
|
||||||
|
- type: tile
|
||||||
|
entity: sensor.n22_precipitation_today
|
||||||
|
name: Nedbør i dag
|
||||||
|
|
||||||
|
- type: history-graph
|
||||||
|
title: Nedbør – 7 dage
|
||||||
|
entities:
|
||||||
|
- entity: sensor.n22_precipitation_today
|
||||||
|
name: Nedbør
|
||||||
|
hours_to_show: 168
|
||||||
|
refresh_interval: 1800
|
||||||
|
|
||||||
|
- type: custom:apexcharts-card
|
||||||
|
header:
|
||||||
|
show: true
|
||||||
|
title: Forventet nedbør – næste 7 dage
|
||||||
|
graph_span: 7d
|
||||||
|
span:
|
||||||
|
start: day
|
||||||
|
apex_config:
|
||||||
|
chart:
|
||||||
|
type: bar
|
||||||
|
height: 200
|
||||||
|
dataLabels:
|
||||||
|
enabled: true
|
||||||
|
formatter: |
|
||||||
|
EVAL:function(val) { return val ? val + ' mm' : ''; }
|
||||||
|
xaxis:
|
||||||
|
type: datetime
|
||||||
|
labels:
|
||||||
|
datetimeFormatter:
|
||||||
|
day: "dd/MM"
|
||||||
|
yaxis:
|
||||||
|
min: 0
|
||||||
|
title:
|
||||||
|
text: mm
|
||||||
|
series:
|
||||||
|
- entity: sensor.vejr_daglig_prognose
|
||||||
|
name: Nedbør
|
||||||
|
color: "#4fc3f7"
|
||||||
|
data_generator: |
|
||||||
|
return entity.attributes.forecast.map(f => ({
|
||||||
|
x: new Date(f.datetime).getTime(),
|
||||||
|
y: f.precipitation ?? 0
|
||||||
|
}));
|
||||||
|
|
||||||
|
# ⏸️ Rain Bird RC2
|
||||||
|
- type: grid
|
||||||
|
cards:
|
||||||
|
- type: heading
|
||||||
|
heading: Rain Bird RC2
|
||||||
|
icon: mdi:sprinkler-fire
|
||||||
|
|
||||||
|
- type: tile
|
||||||
|
entity: sensor.annes_vanding_raindelay
|
||||||
|
name: Regn-forsinkelse status
|
||||||
|
|
||||||
|
- type: tile
|
||||||
|
entity: number.annes_vanding_rain_delay
|
||||||
|
name: Sæt forsinkelse (dage)
|
||||||
|
|
||||||
|
- type: tile
|
||||||
|
entity: calendar.annes_vanding
|
||||||
|
name: Vandingsplan
|
||||||
|
|
||||||
|
# 🌿 Zonekontrol
|
||||||
|
- type: grid
|
||||||
|
cards:
|
||||||
|
- type: heading
|
||||||
|
heading: Zoner – manuel styring
|
||||||
|
icon: mdi:water-pump
|
||||||
|
|
||||||
|
- type: tile
|
||||||
|
entity: switch.hojbed_1
|
||||||
|
name: Højbed 1 – Ærter
|
||||||
|
icon: mdi:sprinkler
|
||||||
|
|
||||||
|
- type: tile
|
||||||
|
entity: switch.hojbed_2
|
||||||
|
name: Højbed 2 – Kartofler
|
||||||
|
icon: mdi:sprinkler
|
||||||
|
|
||||||
|
- type: tile
|
||||||
|
entity: switch.hojbed_3
|
||||||
|
name: Højbed 3 – Rabarber
|
||||||
|
icon: mdi:sprinkler
|
||||||
|
|
||||||
|
- type: tile
|
||||||
|
entity: switch.drivhus_drypvanding
|
||||||
|
name: Drivhus
|
||||||
|
icon: mdi:greenhouse
|
||||||
|
|
||||||
|
# 🔋 Sensorbatterier
|
||||||
|
- type: grid
|
||||||
|
cards:
|
||||||
|
- type: heading
|
||||||
|
heading: Sensor batterier
|
||||||
|
icon: mdi:battery
|
||||||
|
|
||||||
|
- type: glance
|
||||||
|
show_name: true
|
||||||
|
show_icon: true
|
||||||
|
show_state: true
|
||||||
|
columns: 4
|
||||||
|
entities:
|
||||||
|
- entity: sensor.annes_havesensor_soil_battery_1
|
||||||
|
name: HB1
|
||||||
|
icon: mdi:battery
|
||||||
|
- entity: sensor.annes_havesensor_soil_battery_2
|
||||||
|
name: HB2
|
||||||
|
icon: mdi:battery
|
||||||
|
- entity: sensor.annes_havesensor_soil_battery_3
|
||||||
|
name: HB3
|
||||||
|
icon: mdi:battery
|
||||||
|
- entity: sensor.annes_havesensor_soil_battery_4
|
||||||
|
name: Drivhus
|
||||||
|
icon: mdi:battery
|
||||||
+261
-28
@@ -37,10 +37,15 @@ sections:
|
|||||||
name: Netatmo
|
name: Netatmo
|
||||||
- entity: sensor.hue_motion_sensor_2_temperature_2
|
- entity: sensor.hue_motion_sensor_2_temperature_2
|
||||||
name: Hue
|
name: Hue
|
||||||
|
- entity: climate.andreas
|
||||||
- type: thermostat
|
attribute: current_temperature
|
||||||
entity: climate.andreas
|
name: Roth aktuelt
|
||||||
name: Andreas
|
- entity: climate.andreas
|
||||||
|
attribute: temperature
|
||||||
|
name: Roth mål
|
||||||
|
stroke_width: 1
|
||||||
|
curve: stepline
|
||||||
|
color: "#ff8800"
|
||||||
|
|
||||||
- type: grid
|
- type: grid
|
||||||
cards:
|
cards:
|
||||||
@@ -72,10 +77,15 @@ sections:
|
|||||||
name: Netatmo
|
name: Netatmo
|
||||||
- entity: sensor.hue_motion_sensor_2_temperature
|
- entity: sensor.hue_motion_sensor_2_temperature
|
||||||
name: Hue
|
name: Hue
|
||||||
|
- entity: climate.daniel
|
||||||
- type: thermostat
|
attribute: current_temperature
|
||||||
entity: climate.daniel
|
name: Roth aktuelt
|
||||||
name: Daniel
|
- entity: climate.daniel
|
||||||
|
attribute: temperature
|
||||||
|
name: Roth mål
|
||||||
|
stroke_width: 1
|
||||||
|
curve: stepline
|
||||||
|
color: "#ff8800"
|
||||||
|
|
||||||
- type: grid
|
- type: grid
|
||||||
cards:
|
cards:
|
||||||
@@ -111,10 +121,15 @@ sections:
|
|||||||
name: Skab Claus
|
name: Skab Claus
|
||||||
- entity: sensor.temperature
|
- entity: sensor.temperature
|
||||||
name: Skab Anne
|
name: Skab Anne
|
||||||
|
- entity: climate.sovev_prelse
|
||||||
- type: thermostat
|
attribute: current_temperature
|
||||||
entity: climate.sovev_prelse
|
name: Roth aktuelt
|
||||||
name: Sovevaerelse
|
- entity: climate.sovev_prelse
|
||||||
|
attribute: temperature
|
||||||
|
name: Roth mål
|
||||||
|
stroke_width: 1
|
||||||
|
curve: stepline
|
||||||
|
color: "#ff8800"
|
||||||
|
|
||||||
- type: grid
|
- type: grid
|
||||||
cards:
|
cards:
|
||||||
@@ -144,10 +159,17 @@ sections:
|
|||||||
series:
|
series:
|
||||||
- entity: sensor.kontor_motion_temperatur
|
- entity: sensor.kontor_motion_temperatur
|
||||||
name: Hue
|
name: Hue
|
||||||
|
- entity: sensor.annes_havesensor_indoor_temperature
|
||||||
- type: thermostat
|
name: Havesensor inde
|
||||||
entity: climate.kontor
|
- entity: climate.kontor
|
||||||
name: Kontor
|
attribute: current_temperature
|
||||||
|
name: Roth aktuelt
|
||||||
|
- entity: climate.kontor
|
||||||
|
attribute: temperature
|
||||||
|
name: Roth mål
|
||||||
|
stroke_width: 1
|
||||||
|
curve: stepline
|
||||||
|
color: "#ff8800"
|
||||||
|
|
||||||
- type: grid
|
- type: grid
|
||||||
cards:
|
cards:
|
||||||
@@ -177,10 +199,15 @@ sections:
|
|||||||
series:
|
series:
|
||||||
- entity: sensor.gang_sensor_temperature
|
- entity: sensor.gang_sensor_temperature
|
||||||
name: Hue
|
name: Hue
|
||||||
|
- entity: climate.fordelingsgang
|
||||||
- type: thermostat
|
attribute: current_temperature
|
||||||
entity: climate.fordelingsgang
|
name: Roth aktuelt
|
||||||
name: Gang
|
- entity: climate.fordelingsgang
|
||||||
|
attribute: temperature
|
||||||
|
name: Roth mål
|
||||||
|
stroke_width: 1
|
||||||
|
curve: stepline
|
||||||
|
color: "#ff8800"
|
||||||
|
|
||||||
- type: grid
|
- type: grid
|
||||||
cards:
|
cards:
|
||||||
@@ -188,7 +215,7 @@ sections:
|
|||||||
graph_span: 24h
|
graph_span: 24h
|
||||||
header:
|
header:
|
||||||
show: true
|
show: true
|
||||||
title: Bad
|
title: Badeværelse
|
||||||
show_states: true
|
show_states: true
|
||||||
colorize_states: true
|
colorize_states: true
|
||||||
now:
|
now:
|
||||||
@@ -210,6 +237,15 @@ sections:
|
|||||||
series:
|
series:
|
||||||
- entity: sensor.bad_motion_sensor_temperature
|
- entity: sensor.bad_motion_sensor_temperature
|
||||||
name: Hue
|
name: Hue
|
||||||
|
- entity: climate.badevarelse
|
||||||
|
attribute: current_temperature
|
||||||
|
name: Ally aktuelt
|
||||||
|
- entity: climate.badevarelse
|
||||||
|
attribute: temperature
|
||||||
|
name: Ally mål
|
||||||
|
stroke_width: 1
|
||||||
|
curve: stepline
|
||||||
|
color: "#ff8800"
|
||||||
|
|
||||||
- type: grid
|
- type: grid
|
||||||
cards:
|
cards:
|
||||||
@@ -239,6 +275,15 @@ sections:
|
|||||||
series:
|
series:
|
||||||
- entity: sensor.stue_motion_temperatur
|
- entity: sensor.stue_motion_temperatur
|
||||||
name: Hue
|
name: Hue
|
||||||
|
- entity: climate.stue
|
||||||
|
attribute: current_temperature
|
||||||
|
name: Ally aktuelt
|
||||||
|
- entity: climate.stue
|
||||||
|
attribute: temperature
|
||||||
|
name: Ally mål
|
||||||
|
stroke_width: 1
|
||||||
|
curve: stepline
|
||||||
|
color: "#ff8800"
|
||||||
|
|
||||||
- type: grid
|
- type: grid
|
||||||
cards:
|
cards:
|
||||||
@@ -268,13 +313,201 @@ sections:
|
|||||||
series:
|
series:
|
||||||
- entity: sensor.forgang_sensor_temperature
|
- entity: sensor.forgang_sensor_temperature
|
||||||
name: Hue
|
name: Hue
|
||||||
|
- entity: climate.forgang
|
||||||
- type: thermostat
|
attribute: current_temperature
|
||||||
entity: climate.forgang
|
name: Roth aktuelt
|
||||||
name: Forgang
|
- entity: climate.forgang
|
||||||
|
attribute: temperature
|
||||||
|
name: Roth mål
|
||||||
|
stroke_width: 1
|
||||||
|
curve: stepline
|
||||||
|
color: "#ff8800"
|
||||||
|
|
||||||
- type: grid
|
- type: grid
|
||||||
cards:
|
cards:
|
||||||
- type: thermostat
|
- type: custom:apexcharts-card
|
||||||
entity: climate.lille_bad
|
graph_span: 24h
|
||||||
name: Lille bad
|
header:
|
||||||
|
show: true
|
||||||
|
title: Lille bad
|
||||||
|
show_states: true
|
||||||
|
colorize_states: true
|
||||||
|
now:
|
||||||
|
show: true
|
||||||
|
label: Nu
|
||||||
|
apex_config:
|
||||||
|
chart:
|
||||||
|
height: 240
|
||||||
|
grid:
|
||||||
|
strokeDashArray: 2
|
||||||
|
xaxis:
|
||||||
|
type: datetime
|
||||||
|
labels:
|
||||||
|
datetimeFormatter:
|
||||||
|
hour: HH:mm
|
||||||
|
yaxis:
|
||||||
|
decimalsInFloat: 1
|
||||||
|
tickAmount: 6
|
||||||
|
series:
|
||||||
|
- entity: climate.lille_bad
|
||||||
|
attribute: current_temperature
|
||||||
|
name: Roth aktuelt
|
||||||
|
- entity: climate.lille_bad
|
||||||
|
attribute: temperature
|
||||||
|
name: Roth mål
|
||||||
|
stroke_width: 1
|
||||||
|
curve: stepline
|
||||||
|
color: "#ff8800"
|
||||||
|
|
||||||
|
- type: grid
|
||||||
|
cards:
|
||||||
|
- type: custom:apexcharts-card
|
||||||
|
graph_span: 24h
|
||||||
|
header:
|
||||||
|
show: true
|
||||||
|
title: Bryggers
|
||||||
|
show_states: true
|
||||||
|
colorize_states: true
|
||||||
|
now:
|
||||||
|
show: true
|
||||||
|
label: Nu
|
||||||
|
apex_config:
|
||||||
|
chart:
|
||||||
|
height: 240
|
||||||
|
grid:
|
||||||
|
strokeDashArray: 2
|
||||||
|
xaxis:
|
||||||
|
type: datetime
|
||||||
|
labels:
|
||||||
|
datetimeFormatter:
|
||||||
|
hour: HH:mm
|
||||||
|
yaxis:
|
||||||
|
decimalsInFloat: 1
|
||||||
|
tickAmount: 6
|
||||||
|
series:
|
||||||
|
- entity: sensor.temp_bryggers_temperatur
|
||||||
|
name: Temperatur
|
||||||
|
# TODO: tilføj climate-entity når tænd/sluk er monteret
|
||||||
|
# - entity: climate.bryggers
|
||||||
|
# attribute: current_temperature
|
||||||
|
# name: Roth aktuelt
|
||||||
|
# - entity: climate.bryggers
|
||||||
|
# attribute: temperature
|
||||||
|
# name: Roth mål
|
||||||
|
# stroke_width: 1
|
||||||
|
# curve: stepline
|
||||||
|
# color: "#ff8800"
|
||||||
|
|
||||||
|
- type: grid
|
||||||
|
cards:
|
||||||
|
- type: custom:apexcharts-card
|
||||||
|
graph_span: 24h
|
||||||
|
header:
|
||||||
|
show: true
|
||||||
|
title: Køkken
|
||||||
|
show_states: true
|
||||||
|
colorize_states: true
|
||||||
|
now:
|
||||||
|
show: true
|
||||||
|
label: Nu
|
||||||
|
apex_config:
|
||||||
|
chart:
|
||||||
|
height: 240
|
||||||
|
grid:
|
||||||
|
strokeDashArray: 2
|
||||||
|
xaxis:
|
||||||
|
type: datetime
|
||||||
|
labels:
|
||||||
|
datetimeFormatter:
|
||||||
|
hour: HH:mm
|
||||||
|
yaxis:
|
||||||
|
decimalsInFloat: 1
|
||||||
|
tickAmount: 6
|
||||||
|
series:
|
||||||
|
- entity: sensor.temp_kokken_temperatur
|
||||||
|
name: Temperatur
|
||||||
|
# TODO: tilføj climate-entity når tænd/sluk er monteret
|
||||||
|
# - entity: climate.kokken
|
||||||
|
# attribute: current_temperature
|
||||||
|
# name: Roth aktuelt
|
||||||
|
# - entity: climate.kokken
|
||||||
|
# attribute: temperature
|
||||||
|
# name: Roth mål
|
||||||
|
# stroke_width: 1
|
||||||
|
# curve: stepline
|
||||||
|
# color: "#ff8800"
|
||||||
|
|
||||||
|
# Indstillinger: Komforttemperaturer og sænkninger
|
||||||
|
- type: grid
|
||||||
|
cards:
|
||||||
|
- type: entities
|
||||||
|
title: Komforttemperaturer
|
||||||
|
entities:
|
||||||
|
- entity: input_number.varme_komfort_andreas
|
||||||
|
- entity: input_number.varme_komfort_daniel
|
||||||
|
- entity: input_number.varme_komfort_sovevaerelse
|
||||||
|
- entity: input_number.varme_komfort_kontor
|
||||||
|
- entity: input_number.varme_komfort_gang
|
||||||
|
- entity: input_number.varme_komfort_forgang
|
||||||
|
- entity: input_number.varme_komfort_lille_bad
|
||||||
|
- entity: input_number.varme_komfort_badevarelse
|
||||||
|
- entity: input_number.varme_komfort_stue
|
||||||
|
# TODO: aktiver når climate-entiteter er oprettet
|
||||||
|
# - entity: input_number.varme_komfort_bryggers
|
||||||
|
# - entity: input_number.varme_komfort_kokken
|
||||||
|
|
||||||
|
- type: entities
|
||||||
|
title: Sænkninger og ferie
|
||||||
|
entities:
|
||||||
|
- entity: input_datetime.varme_morgen_tid
|
||||||
|
- entity: input_datetime.varme_aften_tid
|
||||||
|
- entity: input_number.varme_nat_saenkning
|
||||||
|
- entity: input_number.varme_vaek_saenkning
|
||||||
|
- entity: input_number.varme_ferie_temp
|
||||||
|
|
||||||
|
- type: button
|
||||||
|
name: Genberegn varme nu
|
||||||
|
icon: mdi:refresh
|
||||||
|
tap_action:
|
||||||
|
action: call-service
|
||||||
|
service: script.varme_recalculate
|
||||||
|
|
||||||
|
- type: button
|
||||||
|
name: Gem temperaturer som standard
|
||||||
|
icon: mdi:content-save
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: script.varme_save_defaults
|
||||||
|
|
||||||
|
# Ventilposition
|
||||||
|
- type: grid
|
||||||
|
cards:
|
||||||
|
- type: gauge
|
||||||
|
entity: sensor.fjernvarme_ventil_3_ugers_gennemsnit
|
||||||
|
name: Anbefalet ventilposition – 3 ugers snit (1–5)
|
||||||
|
min: 1
|
||||||
|
max: 5
|
||||||
|
needle: true
|
||||||
|
segments:
|
||||||
|
- from: 1
|
||||||
|
color: "#44aa44" # grøn: lukket/sommer
|
||||||
|
- from: 2
|
||||||
|
color: "#aaaa00" # gul: mildt
|
||||||
|
- from: 3
|
||||||
|
color: "#dd8800" # orange: køligt
|
||||||
|
- from: 4
|
||||||
|
color: "#cc4400" # rød-orange: koldt
|
||||||
|
- from: 4.5
|
||||||
|
color: "#aa0000" # rød: frost
|
||||||
|
|
||||||
|
- type: markdown
|
||||||
|
content: |-
|
||||||
|
**Anbefalet stilling (3 ugers snit): {{ states('sensor.fjernvarme_ventil_3_ugers_gennemsnit') | float(0) | round(1) }}**
|
||||||
|
|
||||||
|
Øjeblikkelig (vejrbaseret): {{ states('sensor.fjernvarme_ventil_anbefalet') }} – {{ state_attr('sensor.fjernvarme_ventil_anbefalet', 'anbefaling') }}
|
||||||
|
|
||||||
|
Udetemperatur nu: {{ state_attr('sensor.fjernvarme_ventil_anbefalet', 'udetemperatur') }}°C
|
||||||
|
|
||||||
|
Gælder for begge manuelle hoveddrejehaner:
|
||||||
|
- Roth-fordeler (sauna)
|
||||||
|
- Fjernvarme indstikning (bryggers)
|
||||||
@@ -0,0 +1,352 @@
|
|||||||
|
title: Sikkerhed
|
||||||
|
path: sikkerhed
|
||||||
|
icon: mdi:shield-home
|
||||||
|
|
||||||
|
cards:
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
# 📷 KAMERAER
|
||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
- type: grid
|
||||||
|
columns: 2
|
||||||
|
square: false
|
||||||
|
cards:
|
||||||
|
|
||||||
|
- type: picture-entity
|
||||||
|
entity: camera.terrasse_sub
|
||||||
|
name: Terasse
|
||||||
|
camera_view: live
|
||||||
|
show_state: false
|
||||||
|
show_name: true
|
||||||
|
tap_action:
|
||||||
|
action: fire-dom-event
|
||||||
|
browser_mod:
|
||||||
|
service: browser_mod.popup
|
||||||
|
data:
|
||||||
|
title: Terasse – Live
|
||||||
|
content:
|
||||||
|
type: vertical-stack
|
||||||
|
cards:
|
||||||
|
- type: picture-entity
|
||||||
|
entity: camera.terrasse_sub
|
||||||
|
camera_view: live
|
||||||
|
show_name: false
|
||||||
|
show_state: false
|
||||||
|
tap_action:
|
||||||
|
action: none
|
||||||
|
- type: tile
|
||||||
|
entity: number.terrasse_focus
|
||||||
|
name: Fokus
|
||||||
|
icon: mdi:focus-field
|
||||||
|
features:
|
||||||
|
- type: numeric-input
|
||||||
|
style: slider
|
||||||
|
- type: tile
|
||||||
|
entity: number.terrasse_zoom
|
||||||
|
name: Zoom
|
||||||
|
icon: mdi:magnify
|
||||||
|
features:
|
||||||
|
- type: numeric-input
|
||||||
|
style: slider
|
||||||
|
|
||||||
|
- type: picture-entity
|
||||||
|
entity: camera.indkoersel_sub
|
||||||
|
name: Indkørsel
|
||||||
|
camera_view: live
|
||||||
|
show_state: false
|
||||||
|
show_name: true
|
||||||
|
tap_action:
|
||||||
|
action: fire-dom-event
|
||||||
|
browser_mod:
|
||||||
|
service: browser_mod.popup
|
||||||
|
data:
|
||||||
|
title: Indkørsel – Live
|
||||||
|
content:
|
||||||
|
type: vertical-stack
|
||||||
|
cards:
|
||||||
|
- type: picture-entity
|
||||||
|
entity: camera.indkoersel_sub
|
||||||
|
camera_view: live
|
||||||
|
show_name: false
|
||||||
|
show_state: false
|
||||||
|
tap_action:
|
||||||
|
action: none
|
||||||
|
- type: tile
|
||||||
|
entity: number.indkoersel_focus
|
||||||
|
name: Fokus
|
||||||
|
icon: mdi:focus-field
|
||||||
|
features:
|
||||||
|
- type: numeric-input
|
||||||
|
style: slider
|
||||||
|
- type: tile
|
||||||
|
entity: number.indkoersel_zoom
|
||||||
|
name: Zoom
|
||||||
|
icon: mdi:magnify
|
||||||
|
features:
|
||||||
|
- type: numeric-input
|
||||||
|
style: slider
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
# 🛡️ SIKKERHEDSSTATUS
|
||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
- type: heading
|
||||||
|
heading: Sikkerhedsstatus
|
||||||
|
heading_style: title
|
||||||
|
|
||||||
|
- type: grid
|
||||||
|
columns: 2
|
||||||
|
square: false
|
||||||
|
cards:
|
||||||
|
|
||||||
|
# 👥 Tilstedeværelse
|
||||||
|
- type: custom:mushroom-template-card
|
||||||
|
entity: binary_sensor.family_presence
|
||||||
|
primary: >
|
||||||
|
{{ 'Nogen hjemme' if is_state('binary_sensor.family_presence', 'on') else 'Ingen hjemme' }}
|
||||||
|
secondary: >
|
||||||
|
{% set persons = [
|
||||||
|
('Claus', 'person.claus_dethlefsen'),
|
||||||
|
('Anne', 'person.anne_schusler_dethlefsen'),
|
||||||
|
('Andreas', 'person.andreas_schusler_dethlefsen'),
|
||||||
|
('Daniel', 'person.daniel_schusler_dethlefsen')
|
||||||
|
] %}
|
||||||
|
{% set ns = namespace(home=[]) %}
|
||||||
|
{% for name, eid in persons %}
|
||||||
|
{% if is_state(eid, 'home') %}{% set ns.home = ns.home + [name] %}{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{{ ns.home | join(', ') if ns.home else 'Alle ude' }}
|
||||||
|
icon: >
|
||||||
|
{{ 'mdi:home-account' if is_state('binary_sensor.family_presence', 'on') else 'mdi:home-outline' }}
|
||||||
|
icon_color: >
|
||||||
|
{{ 'green' if is_state('binary_sensor.family_presence', 'on') else 'blue' }}
|
||||||
|
tap_action:
|
||||||
|
action: none
|
||||||
|
|
||||||
|
# 💡 Lys
|
||||||
|
- type: custom:mushroom-template-card
|
||||||
|
entity: light.alle_lys
|
||||||
|
primary: >
|
||||||
|
{{ 'Lys er tændt' if is_state('light.alle_lys', 'on') else 'Alt lys slukket' }}
|
||||||
|
secondary: ""
|
||||||
|
icon: >
|
||||||
|
{{ 'mdi:lightbulb-on' if is_state('light.alle_lys', 'on') else 'mdi:lightbulb-off' }}
|
||||||
|
icon_color: >
|
||||||
|
{{ 'yellow' if is_state('light.alle_lys', 'on') else 'grey' }}
|
||||||
|
tap_action:
|
||||||
|
action: navigate
|
||||||
|
navigation_path: /lovelace/lys
|
||||||
|
|
||||||
|
# 🪟 Vinduer og terrassedør
|
||||||
|
- type: custom:mushroom-template-card
|
||||||
|
multiline_secondary: true
|
||||||
|
primary: >
|
||||||
|
{% set sensors = [
|
||||||
|
'binary_sensor.andreas_vindue',
|
||||||
|
'binary_sensor.daniel_vindue',
|
||||||
|
'binary_sensor.sovevaerelse_vindue',
|
||||||
|
'binary_sensor.badevaerelse_vindue',
|
||||||
|
'binary_sensor.lille_bad_vindue',
|
||||||
|
'binary_sensor.terrassedor'
|
||||||
|
] %}
|
||||||
|
{% set ns = namespace(open=0) %}
|
||||||
|
{% for s in sensors %}{% if is_state(s, 'on') %}{% set ns.open = ns.open + 1 %}{% endif %}{% endfor %}
|
||||||
|
{{ 'Alle vinduer lukket' if ns.open == 0 else ns.open | string + ' vindue(r) åben' }}
|
||||||
|
secondary: >
|
||||||
|
{% set sensor_map = {
|
||||||
|
'binary_sensor.andreas_vindue': 'Andreas',
|
||||||
|
'binary_sensor.daniel_vindue': 'Daniel',
|
||||||
|
'binary_sensor.sovevaerelse_vindue': 'Soveværelse',
|
||||||
|
'binary_sensor.badevaerelse_vindue': 'Badeværelse',
|
||||||
|
'binary_sensor.lille_bad_vindue': 'Lille bad',
|
||||||
|
'binary_sensor.terrassedor': 'Terrassedør'
|
||||||
|
} %}
|
||||||
|
{% set ns = namespace(aabne=[]) %}
|
||||||
|
{% for s, n in sensor_map.items() %}{% if is_state(s, 'on') %}{% set ns.aabne = ns.aabne + [n] %}{% endif %}{% endfor %}
|
||||||
|
{{ ns.aabne | join(', ') if ns.aabne else '' }}
|
||||||
|
icon: >
|
||||||
|
{% set sensors = [
|
||||||
|
'binary_sensor.andreas_vindue',
|
||||||
|
'binary_sensor.daniel_vindue',
|
||||||
|
'binary_sensor.sovevaerelse_vindue',
|
||||||
|
'binary_sensor.badevaerelse_vindue',
|
||||||
|
'binary_sensor.lille_bad_vindue',
|
||||||
|
'binary_sensor.terrassedor'
|
||||||
|
] %}
|
||||||
|
{% set ns = namespace(open=0) %}
|
||||||
|
{% for s in sensors %}{% if is_state(s, 'on') %}{% set ns.open = ns.open + 1 %}{% endif %}{% endfor %}
|
||||||
|
{{ 'mdi:window-open-variant' if ns.open > 0 else 'mdi:window-closed-variant' }}
|
||||||
|
icon_color: >
|
||||||
|
{% set sensors = [
|
||||||
|
'binary_sensor.andreas_vindue',
|
||||||
|
'binary_sensor.daniel_vindue',
|
||||||
|
'binary_sensor.sovevaerelse_vindue',
|
||||||
|
'binary_sensor.badevaerelse_vindue',
|
||||||
|
'binary_sensor.lille_bad_vindue',
|
||||||
|
'binary_sensor.terrassedor'
|
||||||
|
] %}
|
||||||
|
{% set ns = namespace(open=0) %}
|
||||||
|
{% for s in sensors %}{% if is_state(s, 'on') %}{% set ns.open = ns.open + 1 %}{% endif %}{% endfor %}
|
||||||
|
{{ 'red' if ns.open > 0 else 'green' }}
|
||||||
|
tap_action:
|
||||||
|
action: none
|
||||||
|
|
||||||
|
# 🚗 Garage
|
||||||
|
- type: custom:mushroom-template-card
|
||||||
|
entity: binary_sensor.garageport
|
||||||
|
primary: >
|
||||||
|
{{ 'Garage åben' if is_state('binary_sensor.garageport', 'on') else 'Garage lukket' }}
|
||||||
|
secondary: >
|
||||||
|
Sidst ændret: {{ relative_time(states.binary_sensor.garageport.last_changed) }} siden
|
||||||
|
icon: >
|
||||||
|
{{ 'mdi:garage-open-variant' if is_state('binary_sensor.garageport', 'on') else 'mdi:garage-variant' }}
|
||||||
|
icon_color: >
|
||||||
|
{{ 'orange' if is_state('binary_sensor.garageport', 'on') else 'green' }}
|
||||||
|
tap_action:
|
||||||
|
action: call-service
|
||||||
|
service: cover.toggle
|
||||||
|
target:
|
||||||
|
entity_id: cover.anne
|
||||||
|
|
||||||
|
# 🏖️ Ferietilstand
|
||||||
|
- type: custom:mushroom-template-card
|
||||||
|
entity: input_boolean.vacation_mode
|
||||||
|
primary: >
|
||||||
|
{{ 'Ferie aktiv' if is_state('input_boolean.vacation_mode', 'on') else 'Normal tilstand' }}
|
||||||
|
secondary: >
|
||||||
|
{% if is_state('input_boolean.vacation_mode', 'on') %}
|
||||||
|
{% set end = states('input_datetime.vacation_end') %}
|
||||||
|
{% if end not in ['unknown', 'unavailable', ''] %}Slutter {{ as_datetime(end).strftime('%-d. %b') }}{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
icon: >
|
||||||
|
{{ 'mdi:beach' if is_state('input_boolean.vacation_mode', 'on') else 'mdi:home' }}
|
||||||
|
icon_color: >
|
||||||
|
{{ 'cyan' if is_state('input_boolean.vacation_mode', 'on') else 'grey' }}
|
||||||
|
tap_action:
|
||||||
|
action: more-info
|
||||||
|
|
||||||
|
# 🤖 AI-overvågning (indkørsel)
|
||||||
|
- type: custom:mushroom-template-card
|
||||||
|
primary: >
|
||||||
|
{% set pause = states('input_datetime.ai_indkorsel_ai_pause_until') %}
|
||||||
|
{% set paused = pause not in ['unknown', 'unavailable', ''] and as_timestamp(pause) > as_timestamp(now()) %}
|
||||||
|
{{ 'AI-overvågning pauset' if paused else 'AI-overvågning aktiv' }}
|
||||||
|
secondary: >
|
||||||
|
{% set pause = states('input_datetime.ai_indkorsel_ai_pause_until') %}
|
||||||
|
{% set paused = pause not in ['unknown', 'unavailable', ''] and as_timestamp(pause) > as_timestamp(now()) %}
|
||||||
|
{% if paused %}Genoptages kl. {{ as_datetime(pause).strftime('%H:%M') }}{% else %}Indkørsel overvåges{% endif %}
|
||||||
|
icon: >
|
||||||
|
{% set pause = states('input_datetime.ai_indkorsel_ai_pause_until') %}
|
||||||
|
{% set paused = pause not in ['unknown', 'unavailable', ''] and as_timestamp(pause) > as_timestamp(now()) %}
|
||||||
|
{{ 'mdi:robot-off' if paused else 'mdi:robot' }}
|
||||||
|
icon_color: >
|
||||||
|
{% set pause = states('input_datetime.ai_indkorsel_ai_pause_until') %}
|
||||||
|
{% set paused = pause not in ['unknown', 'unavailable', ''] and as_timestamp(pause) > as_timestamp(now()) %}
|
||||||
|
{{ 'orange' if paused else 'green' }}
|
||||||
|
tap_action:
|
||||||
|
action: more-info
|
||||||
|
entity: input_datetime.ai_indkorsel_ai_pause_until
|
||||||
|
|
||||||
|
# 🔒 Terrassedør (separat overblik)
|
||||||
|
- type: custom:mushroom-template-card
|
||||||
|
entity: binary_sensor.terrassedor
|
||||||
|
primary: >
|
||||||
|
{{ 'Terrassedør åben' if is_state('binary_sensor.terrassedor', 'on') else 'Terrassedør lukket' }}
|
||||||
|
secondary: >
|
||||||
|
Sidst ændret: {{ relative_time(states.binary_sensor.terrassedor.last_changed) }} siden
|
||||||
|
icon: >
|
||||||
|
{{ 'mdi:door-open' if is_state('binary_sensor.terrassedor', 'on') else 'mdi:door-closed' }}
|
||||||
|
icon_color: >
|
||||||
|
{{ 'red' if is_state('binary_sensor.terrassedor', 'on') else 'green' }}
|
||||||
|
tap_action:
|
||||||
|
action: more-info
|
||||||
|
|
||||||
|
# 📡 Bevægelse i indkørslen lige nu
|
||||||
|
- type: custom:mushroom-template-card
|
||||||
|
entity: binary_sensor.indkorsel_sensor_motion
|
||||||
|
primary: >
|
||||||
|
{{ 'Bevægelse registreret!' if is_state('binary_sensor.indkorsel_sensor_motion', 'on') else 'Ingen bevægelse' }}
|
||||||
|
secondary: >
|
||||||
|
Sidst: {{ relative_time(states.binary_sensor.indkorsel_sensor_motion.last_changed) }} siden
|
||||||
|
icon: >
|
||||||
|
{{ 'mdi:motion-sensor' if is_state('binary_sensor.indkorsel_sensor_motion', 'on') else 'mdi:motion-sensor-off' }}
|
||||||
|
icon_color: >
|
||||||
|
{{ 'red' if is_state('binary_sensor.indkorsel_sensor_motion', 'on') else 'grey' }}
|
||||||
|
tap_action:
|
||||||
|
action: none
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
# 📋 SENESTE BEVÆGELSE – INDKØRSEL (AI-log)
|
||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
- type: heading
|
||||||
|
heading: Seneste bevægelse – Indkørsel
|
||||||
|
heading_style: title
|
||||||
|
|
||||||
|
# Seneste snapshot gemt af AI-overvågningsscriptet
|
||||||
|
- type: picture-entity
|
||||||
|
entity: camera.indkorsel_snapshot
|
||||||
|
show_name: false
|
||||||
|
show_state: false
|
||||||
|
tap_action:
|
||||||
|
action: fire-dom-event
|
||||||
|
browser_mod:
|
||||||
|
service: browser_mod.popup
|
||||||
|
data:
|
||||||
|
title: Seneste bevægelse – Indkørsel
|
||||||
|
content:
|
||||||
|
type: picture-entity
|
||||||
|
entity: camera.indkorsel_snapshot
|
||||||
|
show_name: false
|
||||||
|
show_state: false
|
||||||
|
tap_action:
|
||||||
|
action: none
|
||||||
|
|
||||||
|
# Seneste AI-beskrivelse
|
||||||
|
- type: custom:button-card
|
||||||
|
entity: input_text.last_notification_message
|
||||||
|
show_name: false
|
||||||
|
show_icon: true
|
||||||
|
show_state: true
|
||||||
|
icon: mdi:robot
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- padding: 14px 16px
|
||||||
|
- text-align: left
|
||||||
|
grid:
|
||||||
|
- grid-template-areas: '"i s"'
|
||||||
|
- grid-template-columns: 44px 1fr
|
||||||
|
- grid-template-rows: auto
|
||||||
|
icon:
|
||||||
|
- width: 32px
|
||||||
|
- height: 32px
|
||||||
|
- color: var(--primary-color)
|
||||||
|
- align-self: flex-start
|
||||||
|
- margin-top: 2px
|
||||||
|
state:
|
||||||
|
- white-space: normal
|
||||||
|
- word-break: break-word
|
||||||
|
- font-size: 13px
|
||||||
|
- text-align: left
|
||||||
|
- line-height: "1.5"
|
||||||
|
tap_action:
|
||||||
|
action: none
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
# 📸 SENESTE PERSON-SNAPSHOT – INDKØRSEL
|
||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
- type: heading
|
||||||
|
heading: Seneste person i indkørsel
|
||||||
|
heading_style: subtitle
|
||||||
|
|
||||||
|
# Klik åbner galleri med alle tidligere snapshots
|
||||||
|
- type: picture
|
||||||
|
image: /local/snapshots/indkorsel/latest.jpg
|
||||||
|
tap_action:
|
||||||
|
action: navigate
|
||||||
|
navigation_path: /lovelace/indkorsel-snapshots
|
||||||
|
|
||||||
|
# Logbog over bevægelseshændelser (48 timer)
|
||||||
|
- type: logbook
|
||||||
|
entities:
|
||||||
|
- binary_sensor.indkorsel_sensor_motion
|
||||||
|
hours_to_show: 48
|
||||||
|
title: Bevægelseslog (48 timer)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
title: Snapshots Indkørsel
|
||||||
|
path: indkorsel-snapshots
|
||||||
|
icon: mdi:camera-burst
|
||||||
|
panel: true
|
||||||
|
|
||||||
|
cards:
|
||||||
|
- type: iframe
|
||||||
|
url: /local/snapshots/indkorsel_loader.html?v=20260516103651
|
||||||
|
aspect_ratio: 100%
|
||||||
@@ -63,7 +63,128 @@ sections:
|
|||||||
name: Ladehastighed
|
name: Ladehastighed
|
||||||
|
|
||||||
|
|
||||||
# 💡 Automations / kontrol
|
# � Zigbee stik med strømmåling
|
||||||
|
- type: grid
|
||||||
|
cards:
|
||||||
|
- type: heading
|
||||||
|
heading: Zigbee stik
|
||||||
|
|
||||||
|
# Aktuel effekt – alle stik samlet
|
||||||
|
- type: history-graph
|
||||||
|
title: Effekt lige nu (W)
|
||||||
|
entities:
|
||||||
|
- entity: sensor.stik_alrum_effekt
|
||||||
|
name: Alrum
|
||||||
|
- entity: sensor.stik_kontor_effekt
|
||||||
|
name: Kontor
|
||||||
|
- entity: sensor.stik_bad_effekt
|
||||||
|
name: Badeværelse
|
||||||
|
- entity: sensor.stik_lillebad_effekt
|
||||||
|
name: Lille bad
|
||||||
|
- entity: sensor.stik_sonos_stue_effekt
|
||||||
|
name: Sonos stue
|
||||||
|
- entity: sensor.stik_quooker_effekt
|
||||||
|
name: Quooker
|
||||||
|
- entity: sensor.stik_fryser_effekt
|
||||||
|
name: Fryser
|
||||||
|
- entity: sensor.stik_bryggers_effekt
|
||||||
|
name: Bryggers
|
||||||
|
hours_to_show: 6
|
||||||
|
refresh_interval: 30
|
||||||
|
|
||||||
|
# Individuelle stik med tænd/sluk + effekt
|
||||||
|
- type: tile
|
||||||
|
entity: switch.stik_alrum
|
||||||
|
name: Alrum
|
||||||
|
secondary_info: sensor.stik_alrum_effekt
|
||||||
|
icon: mdi:power-plug
|
||||||
|
features:
|
||||||
|
- type: toggle
|
||||||
|
|
||||||
|
- type: tile
|
||||||
|
entity: switch.stik_kontor
|
||||||
|
name: Kontor
|
||||||
|
secondary_info: sensor.stik_kontor_effekt
|
||||||
|
icon: mdi:power-plug
|
||||||
|
features:
|
||||||
|
- type: toggle
|
||||||
|
|
||||||
|
- type: tile
|
||||||
|
entity: switch.stik_bad
|
||||||
|
name: Badeværelse
|
||||||
|
secondary_info: sensor.stik_bad_effekt
|
||||||
|
icon: mdi:power-plug
|
||||||
|
features:
|
||||||
|
- type: toggle
|
||||||
|
|
||||||
|
- type: tile
|
||||||
|
entity: switch.stik_lillebad
|
||||||
|
name: Lille bad
|
||||||
|
secondary_info: sensor.stik_lillebad_effekt
|
||||||
|
icon: mdi:power-plug
|
||||||
|
features:
|
||||||
|
- type: toggle
|
||||||
|
|
||||||
|
- type: tile
|
||||||
|
entity: switch.stik_sonos_stue
|
||||||
|
name: Sonos stue
|
||||||
|
secondary_info: sensor.stik_sonos_stue_effekt
|
||||||
|
icon: mdi:power-plug
|
||||||
|
features:
|
||||||
|
- type: toggle
|
||||||
|
|
||||||
|
- type: tile
|
||||||
|
entity: switch.stik_quooker
|
||||||
|
name: Quooker
|
||||||
|
secondary_info: sensor.stik_quooker_effekt
|
||||||
|
icon: mdi:power-plug
|
||||||
|
features:
|
||||||
|
- type: toggle
|
||||||
|
|
||||||
|
- type: tile
|
||||||
|
entity: switch.stik_fryser
|
||||||
|
name: Fryser
|
||||||
|
secondary_info: sensor.stik_fryser_effekt
|
||||||
|
icon: mdi:power-plug
|
||||||
|
features:
|
||||||
|
- type: toggle
|
||||||
|
|
||||||
|
- type: tile
|
||||||
|
entity: switch.stik_bryggers
|
||||||
|
name: Bryggers
|
||||||
|
secondary_info: sensor.stik_bryggers_effekt
|
||||||
|
icon: mdi:power-plug
|
||||||
|
features:
|
||||||
|
- type: toggle
|
||||||
|
|
||||||
|
# Akkumuleret energiforbrug (kWh) pr. stik
|
||||||
|
- type: statistics-graph
|
||||||
|
title: Energiforbrug (kWh)
|
||||||
|
entities:
|
||||||
|
- entity: sensor.stik_alrum_summation_delivered
|
||||||
|
name: Alrum
|
||||||
|
- entity: sensor.stik_kontor_summation_delivered
|
||||||
|
name: Kontor
|
||||||
|
- entity: sensor.stik_bad_summation_delivered
|
||||||
|
name: Badeværelse
|
||||||
|
- entity: sensor.stik_lillebad_summation_delivered
|
||||||
|
name: Lille bad
|
||||||
|
- entity: sensor.stik_sonos_stue_summation_delivered
|
||||||
|
name: Sonos stue
|
||||||
|
- entity: sensor.stik_quooker_summation_delivered
|
||||||
|
name: Quooker
|
||||||
|
- entity: sensor.stik_fryser_summation_delivered
|
||||||
|
name: Fryser
|
||||||
|
- entity: sensor.stik_bryggers_summation_delivered
|
||||||
|
name: Bryggers
|
||||||
|
period: day
|
||||||
|
days_to_show: 7
|
||||||
|
chart_type: bar
|
||||||
|
stat_types:
|
||||||
|
- change
|
||||||
|
|
||||||
|
|
||||||
|
# �💡 Automations / kontrol
|
||||||
- type: grid
|
- type: grid
|
||||||
cards:
|
cards:
|
||||||
- type: heading
|
- type: heading
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ path: lys
|
|||||||
icon: mdi:lightbulb-multiple
|
icon: mdi:lightbulb-multiple
|
||||||
type: sections
|
type: sections
|
||||||
|
|
||||||
max_columns: 2
|
max_columns: 3
|
||||||
|
|
||||||
sections:
|
sections:
|
||||||
- type: grid
|
- type: grid
|
||||||
@@ -150,7 +150,7 @@ sections:
|
|||||||
data:
|
data:
|
||||||
entity_id: script.raketloop
|
entity_id: script.raketloop
|
||||||
- type: grid
|
- type: grid
|
||||||
columns: 2
|
columns: 3
|
||||||
square: false
|
square: false
|
||||||
cards:
|
cards:
|
||||||
- type: custom:mushroom-light-card
|
- type: custom:mushroom-light-card
|
||||||
@@ -464,7 +464,7 @@ sections:
|
|||||||
- light.spejl2
|
- light.spejl2
|
||||||
- light.badevaerelse_2
|
- light.badevaerelse_2
|
||||||
- type: grid
|
- type: grid
|
||||||
columns: 2
|
columns: 3
|
||||||
square: false
|
square: false
|
||||||
cards:
|
cards:
|
||||||
- type: custom:mushroom-light-card
|
- type: custom:mushroom-light-card
|
||||||
@@ -569,7 +569,7 @@ sections:
|
|||||||
show_color_temp_control: true
|
show_color_temp_control: true
|
||||||
collapsible_controls: true
|
collapsible_controls: true
|
||||||
- type: grid
|
- type: grid
|
||||||
columns: 2
|
columns: 3
|
||||||
square: false
|
square: false
|
||||||
cards:
|
cards:
|
||||||
- type: custom:mushroom-light-card
|
- type: custom:mushroom-light-card
|
||||||
@@ -647,7 +647,7 @@ sections:
|
|||||||
data:
|
data:
|
||||||
entity_id: script.have_color_scene
|
entity_id: script.have_color_scene
|
||||||
- type: grid
|
- type: grid
|
||||||
columns: 2
|
columns: 3
|
||||||
square: false
|
square: false
|
||||||
cards:
|
cards:
|
||||||
- type: custom:mushroom-light-card
|
- type: custom:mushroom-light-card
|
||||||
@@ -711,7 +711,7 @@ sections:
|
|||||||
show_brightness_control: true
|
show_brightness_control: true
|
||||||
collapsible_controls: true
|
collapsible_controls: true
|
||||||
- type: grid
|
- type: grid
|
||||||
columns: 2
|
columns: 3
|
||||||
square: false
|
square: false
|
||||||
cards:
|
cards:
|
||||||
- type: custom:mushroom-light-card
|
- type: custom:mushroom-light-card
|
||||||
@@ -756,7 +756,7 @@ sections:
|
|||||||
- type: heading
|
- type: heading
|
||||||
heading: Ovrige
|
heading: Ovrige
|
||||||
- type: grid
|
- type: grid
|
||||||
columns: 2
|
columns: 3
|
||||||
square: false
|
square: false
|
||||||
cards:
|
cards:
|
||||||
- type: custom:mushroom-light-card
|
- type: custom:mushroom-light-card
|
||||||
@@ -779,3 +779,64 @@ sections:
|
|||||||
use_light_color: true
|
use_light_color: true
|
||||||
show_brightness_control: true
|
show_brightness_control: true
|
||||||
collapsible_controls: true
|
collapsible_controls: true
|
||||||
|
|
||||||
|
- type: grid
|
||||||
|
cards:
|
||||||
|
- type: heading
|
||||||
|
heading: Motion-indstillinger
|
||||||
|
- type: entities
|
||||||
|
title: Gang
|
||||||
|
entities:
|
||||||
|
- entity: input_number.gang_lux_threshold
|
||||||
|
name: Lux grænse
|
||||||
|
- entity: input_number.gang_timeout_day
|
||||||
|
name: Timeout dag
|
||||||
|
- entity: input_number.gang_timeout_night
|
||||||
|
name: Timeout nat
|
||||||
|
- type: entities
|
||||||
|
title: Badeværelse
|
||||||
|
entities:
|
||||||
|
- entity: input_number.badevaerelse_timeout_day
|
||||||
|
name: Timeout dag
|
||||||
|
- entity: input_number.badevaerelse_timeout_night
|
||||||
|
name: Timeout nat
|
||||||
|
- type: entities
|
||||||
|
title: Andreas
|
||||||
|
entities:
|
||||||
|
- entity: input_number.andreas_lux_threshold
|
||||||
|
name: Lux grænse
|
||||||
|
- entity: input_number.andreas_timeout
|
||||||
|
name: Timeout
|
||||||
|
- entity: input_number.andreas_brightness
|
||||||
|
name: Lysstyrke
|
||||||
|
- type: entities
|
||||||
|
title: Daniel
|
||||||
|
entities:
|
||||||
|
- entity: input_number.daniel_lux_threshold
|
||||||
|
name: Lux grænse
|
||||||
|
- entity: input_number.daniel_timeout
|
||||||
|
name: Timeout
|
||||||
|
- entity: input_number.daniel_brightness
|
||||||
|
name: Lysstyrke
|
||||||
|
- type: entities
|
||||||
|
title: Kontor
|
||||||
|
entities:
|
||||||
|
- entity: input_number.kontor_lux_threshold
|
||||||
|
name: Lux grænse
|
||||||
|
- entity: input_number.kontor_timeout_day
|
||||||
|
name: Timeout dag
|
||||||
|
- entity: input_number.kontor_timeout_night
|
||||||
|
name: Timeout nat
|
||||||
|
- type: entities
|
||||||
|
title: Stue
|
||||||
|
entities:
|
||||||
|
- entity: input_number.stue_lux_threshold
|
||||||
|
name: Lux grænse
|
||||||
|
- entity: input_number.stue_timeout_morgen
|
||||||
|
name: Timeout morgen (06-16)
|
||||||
|
- entity: input_number.stue_timeout_eftermiddag
|
||||||
|
name: Timeout eftermiddag (16-19)
|
||||||
|
- entity: input_number.stue_timeout_aften
|
||||||
|
name: Timeout aften/TV off (19-00)
|
||||||
|
- entity: input_number.stue_timeout_nat
|
||||||
|
name: Timeout nat (00-06)
|
||||||
@@ -4,6 +4,7 @@ services:
|
|||||||
image: homeassistant/home-assistant:latest
|
image: homeassistant/home-assistant:latest
|
||||||
volumes:
|
volumes:
|
||||||
- /volume1/homeassistant:/config
|
- /volume1/homeassistant:/config
|
||||||
|
- /volume1/docker/homeassistant/backups:/backups
|
||||||
devices:
|
devices:
|
||||||
- /dev/ttyUSB0:/dev/ttyUSB0
|
- /dev/ttyUSB0:/dev/ttyUSB0
|
||||||
network_mode: host
|
network_mode: host
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
services:
|
||||||
|
home-assistant:
|
||||||
|
container_name: homeassistant
|
||||||
|
image: homeassistant/home-assistant:latest
|
||||||
|
volumes:
|
||||||
|
- /volume1/homeassistant:/config
|
||||||
|
- /volume1/docker/homeassistant/backups:/backups
|
||||||
|
devices:
|
||||||
|
- /dev/ttyUSB0:/dev/ttyUSB0
|
||||||
|
network_mode: host
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
- TZ=Europe/Copenhagen
|
||||||
@@ -37,6 +37,26 @@ services:
|
|||||||
retries: 10
|
retries: 10
|
||||||
start_period: 20s
|
start_period: 20s
|
||||||
|
|
||||||
|
nginx-proxy-manager:
|
||||||
|
container_name: nginx-proxy-manager
|
||||||
|
image: jc21/nginx-proxy-manager:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "10080:80" # HTTP (inkl. Let's Encrypt HTTP-01 challenge) – router forwarder 80→10080
|
||||||
|
- "10443:443" # HTTPS – router forwarder 443→10443
|
||||||
|
- "10.0.0.142:81:81" # Admin UI – kun tilgængeligt fra LAN
|
||||||
|
volumes:
|
||||||
|
- ${DOCKER_ROOT:-/volume1/docker}/nginx-proxy-manager/data:/data
|
||||||
|
- ${DOCKER_ROOT:-/volume1/docker}/nginx-proxy-manager/letsencrypt:/etc/letsencrypt
|
||||||
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway" # Gør det muligt at proxye til HA på host-netværket
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "/usr/bin/check-health"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 3
|
||||||
|
start_period: 20s
|
||||||
|
|
||||||
gitea:
|
gitea:
|
||||||
container_name: gitea
|
container_name: gitea
|
||||||
image: gitea/gitea:${GITEA_IMAGE_TAG:-latest}
|
image: gitea/gitea:${GITEA_IMAGE_TAG:-latest}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
services:
|
||||||
|
unifi:
|
||||||
|
container_name: jacobalberty-unifi-1
|
||||||
|
image: jacobalberty/unifi:latest
|
||||||
|
restart: always
|
||||||
|
network_mode: host
|
||||||
|
volumes:
|
||||||
|
- /volume1/docker/unifi:/var/lib/unifi
|
||||||
+45
-27
@@ -1,30 +1,48 @@
|
|||||||
# TODO - Pending Tasks
|
# TODO - Pending Tasks
|
||||||
|
|
||||||
|
## Home Assistant - Åbne opgaver
|
||||||
|
|
||||||
|
### HA-fejl der skal fikses
|
||||||
|
- [x] **Mealie shopping merge timeout** — udgået, merge med Google Keep droppes. Shopping-liste køres direkte i Mealie hver onsdag morgen.
|
||||||
|
- [x] **aiohttp 400 Bad Request fra ekstern IP** — Løst: Nginx Proxy Manager sat op som HTTPS reverse proxy (`anneclaus.duckdns.org`). Let's Encrypt cert udstedt, Force HTTPS aktiveret. Port 8123 lukket på routeren. HA tilgås nu udelukkende via HTTPS på port 443.
|
||||||
|
- [x] **switch.home_charging mangler** — Bilen oplades fint (16. maj 2026). Ikke et reelt problem.
|
||||||
|
- [x] **climate.badevarelse** — Danfoss Ally TRV monteret og online (7. maj 2026).
|
||||||
|
- [x] **Husqvarna Automower BLE — genopsæt parring** — Kørte fint. Problemet var at telefon-app'en kørte i baggrunden og holdt BLE-forbindelsen, selvom klipperen var slettet fra appen.
|
||||||
|
- [x] **Google AI MAX_TOKENS i AI-indkørsel automation** — Ingen fejl observeret i loggen. Fjernet fra aktiv liste.
|
||||||
|
|
||||||
|
### HA - Kendte ikke-fejl (ingen handling nødvendig)
|
||||||
|
- `husqvarna_automower_ble` BLE fejl — normalt når plæneklipperen klipper (men se auth fail-fejl ovenfor)
|
||||||
|
- `light.spejl1/spejl2` mangler — strøm slukket på kontakt, OK
|
||||||
|
|
||||||
|
|
||||||
|
## Sikring af port forwards via NPM + subdomæner
|
||||||
|
**Baggrund:** Diverse tjenester er direkte eksponeret via port-forwards på routeren uden kryptering. Disse bør flyttes bag NPM med HTTPS og subdomæner under `anneclaus.dk`.
|
||||||
|
|
||||||
|
**Tjenester fra routeren (per 17. maj 2026):**
|
||||||
|
| Tjeneste | WAN-port | Intern port | Forslag til subdomain |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Mealie | 9925 | 9925 | `mealie.anneclaus.dk` |
|
||||||
|
| Plex | 32400 | 32400 | `plex.anneclaus.dk` (Plex har egen HTTPS — lavere prioritet) |
|
||||||
|
| Synology DSM | 5000/5001 | 5000/5001 | `nas.anneclaus.dk` |
|
||||||
|
| Unifi | 8443 | 8443 | (Unifi har egen HTTPS — lavere prioritet) |
|
||||||
|
| FTP | 21 | 21 | FTP er usikkert — overvej om den skal lukkes |
|
||||||
|
| MQTT | 1883 | 1883 | MQTT i klartekst — høj risiko |
|
||||||
|
| SSH | 2222 | 2222 | SSH er krypteret, men tiltrækker brute-force |
|
||||||
|
|
||||||
|
**For hver tjeneste der flyttes:**
|
||||||
|
1. Tilføj CNAME-record hos One.com: `<subdomain>` → `anneclaus.duckdns.org`
|
||||||
|
2. Opret proxy host i NPM med SSL-cert og Force HTTPS
|
||||||
|
3. Luk den direkte port-forward på routeren
|
||||||
|
|
||||||
|
- [x] **MQTT port 1883** — lukket på routeren (17. maj 2026). MQTT bruges kun internt af Aqara-knapper til ringklokke — ingen ekstern adgang nødvendig.
|
||||||
|
- [x] **FTP port 21** — lukket på routeren (17. maj 2026).
|
||||||
|
- [x] **Mealie** — `mealie.anneclaus.dk` via NPM med HTTPS (18. maj 2026). Port 9925 lukket på routeren.
|
||||||
|
- [x] **Synology DSM** — `nas.anneclaus.dk` via NPM med HTTPS (19. maj 2026). Port 5000/5001 lukket på routeren.
|
||||||
|
- [ ] **SSH port 2222** — overvej at begrænse til nøgle-login og deaktivere password-login
|
||||||
|
- [~] **Plex** — springer over. Plex krypterer selv sin trafik, og alle klientenheder (telefoner, iPad, Apple TV) skulle opdateres manuelt. Ikke umagen værd.
|
||||||
|
- [ ] **Unifi** — lavere prioritet, Unifi har allerede HTTPS
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Gitea External HTTPS Access
|
## Gitea External HTTPS Access
|
||||||
**Status:** Partial - content accessible but SSL certificate warning
|
- [x] **gitea.anneclaus.dk via NPM** — CNAME oprettet hos One.com, NPM proxy host med Let's Encrypt + Force SSL, `.env.infrastructure` opdateret, container genstartet. HTTPS 200 OK (20. maj 2026).
|
||||||
|
|
||||||
### Task 1: SSL Certificate Binding
|
|
||||||
- [ ] Access Synology DSM Control Panel
|
|
||||||
- [ ] Navigate to Security → Certificate
|
|
||||||
- [ ] Bind the SSL certificate to `gitea.anneclaus.synology.me` in the reverse proxy configuration
|
|
||||||
- [ ] If certificate missing: Obtain new Let's Encrypt certificate for the hostname
|
|
||||||
- [ ] Test: Verify no SSL warning when accessing https://gitea.anneclaus.synology.me/
|
|
||||||
|
|
||||||
### Task 2: Update Gitea Configuration for External URL
|
|
||||||
- [ ] Edit `.env.infrastructure` file
|
|
||||||
- [ ] Update the following variables:
|
|
||||||
```
|
|
||||||
GITEA_DOMAIN=gitea.anneclaus.synology.me
|
|
||||||
GITEA_ROOT_URL=https://gitea.anneclaus.synology.me/
|
|
||||||
GITEA_SSH_DOMAIN=gitea.anneclaus.synology.me
|
|
||||||
```
|
|
||||||
- [ ] Restart Gitea container:
|
|
||||||
```bash
|
|
||||||
docker compose --env-file .env.infrastructure -f docker-compose.infrastructure.yml up -d gitea
|
|
||||||
```
|
|
||||||
- [ ] Test: Verify Git clone links show external URL
|
|
||||||
|
|
||||||
### Notes
|
|
||||||
- DNS routing and reverse proxy already working (content is accessible)
|
|
||||||
- Only certificate binding and Gitea configuration update remaining
|
|
||||||
- These changes will enable full external functionality for Git operations
|
|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1,6 @@
|
|||||||
|
# Google Keep basis-indkøbsliste
|
||||||
|
# En vare per linje. Linjer der starter med # ignoreres.
|
||||||
|
# Eksempel:
|
||||||
|
# Mælk
|
||||||
|
# Toiletpapir
|
||||||
|
# Bananer
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
# Procedure for fuld netværksgenstart
|
||||||
|
|
||||||
|
Brug denne procedure ved fejlfinding, strømafbrydelse eller planlagt vedligehold.
|
||||||
|
Formål: sikre at alle enheder starter i korrekt rækkefølge og at Home Assistant
|
||||||
|
kan forbinde til alle integrationer ved opstart.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## NEDLUKNING
|
||||||
|
|
||||||
|
Luk i denne rækkefølge — afhængige enheder lukkes **før** infrastruktur.
|
||||||
|
|
||||||
|
1. Luk NAS (Synology) ned via DSM eller SSH
|
||||||
|
2. Sluk Sonos-enheder i alle rum og tag stikket ud
|
||||||
|
3. Sluk PlayStation 5
|
||||||
|
4. Sluk printer og Sonos S1 på kontakten
|
||||||
|
5. Luk Mac mini og gamer-PC ned
|
||||||
|
6. Sluk Ubiquiti Access Points (kontor og stue)
|
||||||
|
7. Sluk Roth Touchline controller
|
||||||
|
8. Sluk gardin-controller (Hunter Douglas hub)
|
||||||
|
9. Sluk Denon forstærker
|
||||||
|
10. Sluk Google Nest Mini
|
||||||
|
11. Sluk Netatmo central enhed
|
||||||
|
12. Sluk Hue bridge
|
||||||
|
13. Sluk Ubiquiti switch
|
||||||
|
14. Sluk router
|
||||||
|
15. Sluk bredbåndsmodem (fiber)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## OPSTART
|
||||||
|
|
||||||
|
Start i denne rækkefølge — infrastruktur **før** afhængige enheder.
|
||||||
|
|
||||||
|
### Netværk (fundament)
|
||||||
|
1. **Bredbåndsmodem** — vent **5 min** til synkronisering
|
||||||
|
2. **Router** — vent **5 min**
|
||||||
|
3. **Ubiquiti switch** — vent **2 min**
|
||||||
|
4. **Ubiquiti Access Points** (stue og kontor) — vent **2 min**
|
||||||
|
|
||||||
|
### Enheder HA afhænger af ved opstart — tænd ALLE før NAS
|
||||||
|
5. **Hue bridge**
|
||||||
|
6. **Roth Touchline controller**
|
||||||
|
7. **Gardin-controller** (Hunter Douglas hub)
|
||||||
|
8. **Netatmo central enhed**
|
||||||
|
9. **Sonos Port** (central Sonos-enhed i stuen) — vent til den er online
|
||||||
|
10. **Sonos-enheder** — tænd én ad gangen, startende tættest på Sonos Port
|
||||||
|
11. **Denon receiver**
|
||||||
|
12. **Google Nest Mini** *(smart speaker/Google Assistant)*
|
||||||
|
13. **Printer og Sonos S1**
|
||||||
|
|
||||||
|
### NAS og Home Assistant
|
||||||
|
14. **NAS (Synology)** — tænd og vent **10 min**
|
||||||
|
- Home Assistant Docker-container starter automatisk
|
||||||
|
- HA bruger 3–5 min på at initialisere alle integrationer
|
||||||
|
- Tjek at HA er oppe: åbn `http://homeassistant.local:8123`
|
||||||
|
|
||||||
|
### Verificer Home Assistant
|
||||||
|
15. Tjek HA-loggen for fejl:
|
||||||
|
- Gå til **Indstillinger → System → Log** i HA
|
||||||
|
- Forventede (acceptable) fejl ved opstart:
|
||||||
|
- `husqvarna_automower_ble` — plæneklipper ikke i paringstilstand (normalt)
|
||||||
|
- `cover.terrasse_dor` / `cover.hojre` — Hunter Douglas timing (forsvinder efter et par min)
|
||||||
|
- `light.spejl1`, `light.spejl2` — kun tilgængelige når manuel kontakt er tændt
|
||||||
|
- Fejl der **kræver handling**:
|
||||||
|
- `Can not write request body for https://10.0.0.154` → Hue bridge reagerer ikke, genstartden igen
|
||||||
|
- `touchline` timeout hvert minut → Touchline CGI API stadig nede, prøv genstart af Touchline
|
||||||
|
|
||||||
|
### Øvrige enheder (ikke styret af HA)
|
||||||
|
16. **Mac mini, PS5, gamer-PC**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## KENDTE PROBLEMER OG STATUS (maj 2026)
|
||||||
|
|
||||||
|
| Integration | Problem | Løsning |
|
||||||
|
|---|---|---|
|
||||||
|
| Roth Touchline | CGI API (`/cgi-bin/ILRReadValues.cgi`) returnerer HTTP 000 | Netværksgenstart hjælper typisk |
|
||||||
|
| Hue bridge (10.0.0.154) | "Can not write request body" ved RAM-udtømning | HA-restart eller Hue-genstart |
|
||||||
|
| Zigbee USB | Periodisk `NcpFailure: ERROR_EXCEEDED_MAXIMUM_ACK_TIMEOUT_COUNT` | Acceptabelt, genoprettes automatisk |
|
||||||
|
| RAM | 3.7 GiB total — RAM-opgradering bestilt | Installer ny RAM når den ankommer |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Opdateret: maj 2026*
|
||||||
+97
-32
@@ -1,44 +1,35 @@
|
|||||||
# Ønskeliste – Nyt udstyr til Home Assistant
|
# Ønskeliste – Nyt udstyr til Home Assistant
|
||||||
|
|
||||||
*Sidst opdateret: april 2026*
|
*Sidst opdateret: maj 2026*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Netværk / UniFi
|
||||||
|
|
||||||
|
### IoT VLAN-segmentering (trin 4 fra UniFi check 10. maj 2026)
|
||||||
|
|
||||||
|
**Formål:** Isolere IoT-enheder fra primært hjemmenetværk af sikkerhedshensyn.
|
||||||
|
|
||||||
|
**Plan:**
|
||||||
|
- Nyt netværk: `IoT` med VLAN 20, subnet `192.168.20.0/24`
|
||||||
|
- Genaktivér eksisterende `sonoff`-SSID og tilknyt til IoT-netværk
|
||||||
|
- Firewall-regler:
|
||||||
|
- IoT → LAN: **BLOKERET**
|
||||||
|
- Home Assistant → IoT: **TILLADT** (nødvendigt for styring)
|
||||||
|
- IoT → Internet: **TILLADT**
|
||||||
|
- Enheder der skal flyttes til IoT-netværk: Sonoff, Zaptec, Tesla-lader, Wavin, øvrige IoT
|
||||||
|
|
||||||
|
**Kræver:** Manuel re-tilslutning af WiFi-enheder til nyt SSID. Kablede enheder tildeles VLAN via switch-port.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Høj prioritet
|
## Høj prioritet
|
||||||
|
|
||||||
### Zigbee Smart Plugs (med energimåling)
|
### Zigbee Termostater – 1× Danfoss Ally TRV
|
||||||
|
|
||||||
| Antal | Beskrivelse | Anvendelse | Status |
|
|
||||||
|---|---|---|---|
|
|
||||||
| ~8-10 | Zigbee smart plugs med energimåling (fx Nous A1Z, IKEA TRETAKT, Innr SP 242) | Sonos-styring, mesh-udvidelse, strømmåling | ⬜ Ønsket |
|
|
||||||
|
|
||||||
**Eksisterende:** 4× Nous A1Z Smart Plug (Zigbee 3.0) + 2× Hue On/Off Plug (udetræer)
|
|
||||||
|
|
||||||
**Formål 1 – Mesh-udvidelse:**
|
|
||||||
- Zigbee-plugs fungerer som routere og forstærker mesh-netværket
|
|
||||||
- Mål: dækning helt ud til postkasse, garage og drivhus
|
|
||||||
- Placér plugs strategisk som "stepping stones" fra huset og ud
|
|
||||||
- Sonoff-enhederne (WiFi) hjælper ikke på Zigbee-mesh – erstat evt. med Zigbee-plugs over tid
|
|
||||||
|
|
||||||
**Formål 2 – Sonos genstart-automation:**
|
|
||||||
- 8 Sonos-højttalere tilsluttes Zigbee smart plugs
|
|
||||||
- Køkken og soveværelse kræver sandsynligvis indbygget mini-relæ i stikkontakt i stedet for smart plug
|
|
||||||
- Muliggør automatisk power-cycle i korrekt rækkefølge (sluk alle → vent → tænd én ad gangen)
|
|
||||||
- Løser det kendte problem med Sonos der "hænger" og kræver strømgenstart
|
|
||||||
- Automation: script der slukker alle plugs, venter 10 sek, tænder dem sekventielt med delay
|
|
||||||
|
|
||||||
**Formål 3 – Energimåling:**
|
|
||||||
- Spor strømforbrug på individuelle enheder
|
|
||||||
- Nous A1Z understøtter energimåling – køb samme model for ensartethed
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Zigbee Termostater – 2× Danfoss Ally TRV
|
|
||||||
|
|
||||||
| Antal | Rum | Beskrivelse | Status |
|
| Antal | Rum | Beskrivelse | Status |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| 1 | Stue | Danfoss Ally TRV (Zigbee) | ⬜ Ønsket |
|
| 1 | Badeværelse | Danfoss Ally TRV (Zigbee) | ✅ Monteret og online (7. maj 2026) |
|
||||||
| 1 | Badeværelse | Danfoss Ally TRV (Zigbee) | ⬜ Ønsket |
|
|
||||||
|
|
||||||
**Bekræftet ventiltype:** Danfoss RA (snap-on clips) – Ally passer direkte med medfølgende RA-adapter.
|
**Bekræftet ventiltype:** Danfoss RA (snap-on clips) – Ally passer direkte med medfølgende RA-adapter.
|
||||||
|
|
||||||
@@ -74,8 +65,57 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### Gulvvarme: Wavin bryggers + køkken – Sonoff ZBMINI-L2 + temp-sensorer
|
||||||
|
|
||||||
|
| Antal | Enhed | Beskrivelse | Status |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 2 | Sonoff ZBMINI-L2 | Zigbee relæ, erstatter Wavin RF-modtager | ⬜ Ønsket |
|
||||||
|
| 2 | SONOFF SNZB-02D | Zigbee temperatur/fugt sensor | ⬜ Ønsket |
|
||||||
|
|
||||||
|
**Baggrund:** Bryggers og køkken har i dag en dumb Wavin RF-modtager (JT6/3003-boksen) med to relækanaler (X = bryggers, Y = køkken) der styres af simple trådløse Wavin-termostater. Ingen smart protokol.
|
||||||
|
|
||||||
|
**Plan:** ZBMINI-L2 sættes i serie med fasen ud til aktuatoren (kanalernes brune ledning ud) inde i Wavin-boksen. Temp-sensor per rum. HA `generic_thermostat` samler dem til climate-entiteter der integreres med `script.varme_recalculate` som de øvrige rum.
|
||||||
|
|
||||||
|
**Se:** `dokumenter/wavin_sonoff_installation.md` for komplet installationsguide.
|
||||||
|
|
||||||
|
**Pris:** ~260 kr (ZBMINI) + ~200 kr (temp-sensorer) = **~460 kr total**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Normal prioritet
|
## Normal prioritet
|
||||||
|
|
||||||
|
### Vindues-/dørsensorer – Aqara
|
||||||
|
|
||||||
|
| Antal | Placering | Beskrivelse | Status |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 4 | Køkken | Aqara kontaktsensor (vindue) | ⬜ Ønsket |
|
||||||
|
| 2 | Bryggers | Aqara kontaktsensor (vindue) | ⬜ Ønsket |
|
||||||
|
| 4 | Forgang | Aqara kontaktsensor (vindue/dør) | ⬜ Ønsket |
|
||||||
|
| 2 | Stue | Aqara kontaktsensor (vindue) | ⬜ Ønsket |
|
||||||
|
| 1 | Fordør | Aqara kontaktsensor (dør) | ⬜ Ønsket |
|
||||||
|
| 1 | Bagdør | Aqara kontaktsensor (dør) | ⬜ Ønsket |
|
||||||
|
| 1 | Kontor | Aqara kontaktsensor (vindue) | ⬜ Ønsket |
|
||||||
|
|
||||||
|
**Total:** 15 stk. Aqara Door & Window Sensor (Zigbee) — 6 eksisterende + 9 nye
|
||||||
|
|
||||||
|
**Formål:** Automatisk stop af gulvvarme i rum med åbent vindue — samme logik som eksisterende sensorer i Andreas, Daniel, Soveværelse og Lille bad. Kobles direkte ind i `script.varme_recalculate` med if-betingelse per rum.
|
||||||
|
|
||||||
|
**Bemærk:** Afklar præcis placering (vinduesramme vs. dørfals) inden køb, særligt forgang og fordør/bagdør.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Mere RAM til Synology NAS
|
||||||
|
|
||||||
|
| Antal | Beskrivelse | Status |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 stk | Crucial 16GB DDR4-2666 SODIMM — CT16G4SFD8266 | ✅ Installeret 13. maj 2026 |
|
||||||
|
|
||||||
|
**Baggrund:** OOM-kill (exit 137) 23. april 2026 — Synology løb tør for RAM og dræbte mosquitto, gitea, gitea-db og DokuWiki på samme tid. HA selv overlevede men crashede urent.
|
||||||
|
|
||||||
|
**Resultat:** DS920+ kører nu med 4GB (loddet) + 16GB SODIMM = **20GB total RAM**. Bekræftet i DSM Info Center. Sundhedscheck 13. maj 2026: 3,4GB brugt af 19GB tilgængeligt, swap-brug 0B — ingen memory-pressure. Alle 8 Docker containere kører healthy.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### Idéer til fremtidige udvidelser
|
### Idéer til fremtidige udvidelser
|
||||||
|
|
||||||
| Enhed | Beskrivelse | Bemærkninger |
|
| Enhed | Beskrivelse | Bemærkninger |
|
||||||
@@ -97,6 +137,30 @@
|
|||||||
| Luftkvalitetssensor | VOC / PM2.5 | Udvidelse af eksisterende CO₂-måling |
|
| Luftkvalitetssensor | VOC / PM2.5 | Udvidelse af eksisterende CO₂-måling |
|
||||||
| Energimåler (CT-clamp) | Realtids strømmåling pr. kredsløb | Supplement til Eloverblik |
|
| Energimåler (CT-clamp) | Realtids strømmåling pr. kredsløb | Supplement til Eloverblik |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Vildtkamera
|
||||||
|
|
||||||
|
**Krav:**
|
||||||
|
- Batteri-drevet (ingen strøm på placeringen)
|
||||||
|
- WiFi-upload — ingen SD-kortafhentning
|
||||||
|
- Integration med Home Assistant og/eller Synology NAS
|
||||||
|
|
||||||
|
**Valgt: Reolink Argus 4 Pro + Reolink solpanel**
|
||||||
|
|
||||||
|
- Batteri + solpanel → aldrig behov for genopladning
|
||||||
|
- WiFi 6 — upload direkte til NAS/cloud uden SD-kort
|
||||||
|
- Officiel HA-integration (bevægelses-`binary_sensor` + `camera`-entitet med live stream og snapshot)
|
||||||
|
- Virker med Synology Surveillance Station via RTSP
|
||||||
|
- Farve-natvisning (spotlight)
|
||||||
|
|
||||||
|
| Enhed | Antal | Status |
|
||||||
|
|---|---|---|
|
||||||
|
| Reolink Argus 4 Pro | 1 | ⬜ Ønsket |
|
||||||
|
| Reolink solpanel (kompatibelt) | 1 | ⬜ Ønsket |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Indkøbt ✅
|
## Indkøbt ✅
|
||||||
@@ -105,4 +169,5 @@
|
|||||||
|
|
||||||
| Dato | Enhed | Antal | Bemærkninger |
|
| Dato | Enhed | Antal | Bemærkninger |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| — | — | — | — |
|
| April 2026 | Nous A1Z Smart Plug (Zigbee, energimåling) | 8 | Til Sonos + mesh-udvidelse |
|
||||||
|
| April 2026 | Danfoss Ally TRV (Zigbee) | 1 | Stue – installeret |
|
||||||
|
|||||||
@@ -0,0 +1,252 @@
|
|||||||
|
# Gulvvarme: Wavin bryggers + køkken → HA styring
|
||||||
|
## Idiot-sikker installationsguide
|
||||||
|
|
||||||
|
**Formål:** Erstatte den dumme RF-modtager (Wavin JT6/3003-boksen) med to Sonoff ZBMINI Zigbee-relæer,
|
||||||
|
så Home Assistant kan styre bryggers og køkken-gulvvarme præcis som de andre rum.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Del 1: Indkøb
|
||||||
|
|
||||||
|
| Vare | Antal | Pris ca. | Link/søg |
|
||||||
|
|------|-------|----------|----------|
|
||||||
|
| **Sonoff ZBMINI-L2** (Zigbee relæ, ingen nul-ledning) | 2 | ~130 kr/stk | Aliexpress, Elgiganten |
|
||||||
|
| **SONOFF SNZB-02D** Zigbee temp/fugt sensor | 2 | ~100 kr/stk | Aliexpress |
|
||||||
|
|
||||||
|
> **Vigtigt:** Vælg ZBMINI-**L2** (eller ZBMINI Extreme) – den kræver **ikke** en nuleder (N).
|
||||||
|
> Wavin-boksen har måske ikke nuleder fremme til brug for et relæ.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Del 2: Forståelse af Wavin-boksen
|
||||||
|
|
||||||
|
Når du kigger på det grønne printplade med låget af:
|
||||||
|
|
||||||
|
```
|
||||||
|
MAINS IND (fra stikkontakt i væggen):
|
||||||
|
Brun = FASE (L) – "det farlige"
|
||||||
|
Blå = NUL (N)
|
||||||
|
|
||||||
|
KANAL X (til aktuator 1, fx bryggers):
|
||||||
|
Brun = FASE UD til aktuator
|
||||||
|
|
||||||
|
KANAL Y (til aktuator 2, fx køkken):
|
||||||
|
Brun = FASE UD til aktuator
|
||||||
|
|
||||||
|
Aktuatorerne får NUL fra boksen via blå ledning.
|
||||||
|
```
|
||||||
|
|
||||||
|
Boksen virker som et simpelt on/off relæ per kanal:
|
||||||
|
- Når termostaten sender "varm op" → relæet lukker → 230V fase sendes ud til aktuatoren → ventil åbner
|
||||||
|
- Sonoff ZBMINI erstatter præcis dette relæ
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Del 3: Installation trin for trin
|
||||||
|
|
||||||
|
### ⚠️ STOP – Sluk strøm FØR du rører noget
|
||||||
|
|
||||||
|
1. Find den sikring eller kontakt der forsyner Wavin-boksen
|
||||||
|
2. Sluk den
|
||||||
|
3. Brug en spændingsprøver/-tester på de brune ledninger inde i boksen – bekræft at der er 0V
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Trin 1: Fotografér ledningerne i boksen FØR du piller noget
|
||||||
|
|
||||||
|
Tag et billede med din telefon. Du vil gerne huske hvad der sidder hvor.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Trin 2: Identificér de 4 relevante ledninger
|
||||||
|
|
||||||
|
I Wavin-boksen sidder:
|
||||||
|
- **Brun ind** = Fase fra væggen (fælles for begge kanaler)
|
||||||
|
- **Blå ind** = Nul fra væggen (fælles)
|
||||||
|
- **Brun ud X** = Fase ud til aktuator bryggers
|
||||||
|
- **Brun ud Y** = Fase ud til aktuator køkken
|
||||||
|
|
||||||
|
(De blå ledninger der går ud er nuleder direkte til aktuatorerne – de ændres ikke)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Trin 3: Monter Sonoff ZBMINI-L2 nr. 1 (bryggers)
|
||||||
|
|
||||||
|
ZBMINI-L2 har disse klemmer:
|
||||||
|
|
||||||
|
```
|
||||||
|
[ L in ] [ L out ] [ S1 ] [ S2 ]
|
||||||
|
```
|
||||||
|
|
||||||
|
Tilslut:
|
||||||
|
- **L in** ← Brun fase ind fra væggen (eller tag en aftapning fra eksisterende brun)
|
||||||
|
- **L out** → Brun fase ud til bryggers-aktuatoren (den ledning der tidligere sad i X-relæet)
|
||||||
|
- **S1/S2** = bruges kun hvis du vil have en fysisk kontakt – lad dem sidde tomme
|
||||||
|
|
||||||
|
Sonoff ZBMINI-L2 kræver ikke N (nuleder) – det er pointen med L2-modellen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Trin 4: Monter Sonoff ZBMINI-L2 nr. 2 (køkken)
|
||||||
|
|
||||||
|
Identisk som trin 3, men brug Y-kanalens udgang:
|
||||||
|
- **L in** ← Brun fase ind (kan sidde på samme aftapning som nr. 1)
|
||||||
|
- **L out** → Brun fase ud til køkken-aktuatoren
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Trin 5: Wavin RF-modtagerboksen
|
||||||
|
|
||||||
|
Den eksisterende boks kobles nu **forbi** – dens relæer bruges ikke længere.
|
||||||
|
Du kan enten:
|
||||||
|
- Efterlade den hængende (ufarlig, bare strøm ind og tomme udgange)
|
||||||
|
- Klippe strømmen til den (tag brun og blå ind ud af klemmerne og tape enderne)
|
||||||
|
|
||||||
|
Den gamle Wavin termostat på væggen virker stadig men gør intet – du kan efterlade den eller tage den ned.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Trin 6: Gendan strøm og test
|
||||||
|
|
||||||
|
1. Sæt strøm til igen
|
||||||
|
2. Begge Sonoff-enheder bør lyse rødt (venter på pairing)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Del 4: Zigbee-pairing i Home Assistant
|
||||||
|
|
||||||
|
1. Gå til **Indstillinger → Enheder → Zigbee2MQTT** (eller ZHA hvis du bruger det)
|
||||||
|
2. Klik **Tillad tilslutning / Permit join** (60 sekunder)
|
||||||
|
3. Hold knappen på Sonoff ZBMINI nede i 5 sekunder til LED blinker hurtigt
|
||||||
|
4. Enheden dukker op – navngiv den `bryggers_relæ` og `kokken_relæ`
|
||||||
|
5. Gentag for temp-sensorerne (tryk lille knap på siden for at parre)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Del 5: Home Assistant konfiguration
|
||||||
|
|
||||||
|
### 5a: generic_thermostat (climate entity)
|
||||||
|
|
||||||
|
Tilføj til `configuration.yaml` (eller en inkluderet fil):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
climate:
|
||||||
|
- platform: generic_thermostat
|
||||||
|
name: Bryggers
|
||||||
|
unique_id: generic_thermostat_bryggers
|
||||||
|
heater: switch.bryggers_relae # Sonoff enhedens switch entity
|
||||||
|
target_sensor: sensor.bryggers_temp_sensor_temperature
|
||||||
|
min_temp: 15
|
||||||
|
max_temp: 28
|
||||||
|
target_temp: 20
|
||||||
|
cold_tolerance: 0.3
|
||||||
|
hot_tolerance: 0.3
|
||||||
|
min_cycle_duration:
|
||||||
|
minutes: 5
|
||||||
|
ac_mode: false
|
||||||
|
|
||||||
|
- platform: generic_thermostat
|
||||||
|
name: Køkken
|
||||||
|
unique_id: generic_thermostat_kokken
|
||||||
|
heater: switch.kokken_relae
|
||||||
|
target_sensor: sensor.kokken_temp_sensor_temperature
|
||||||
|
min_temp: 15
|
||||||
|
max_temp: 28
|
||||||
|
target_temp: 20
|
||||||
|
cold_tolerance: 0.3
|
||||||
|
hot_tolerance: 0.3
|
||||||
|
min_cycle_duration:
|
||||||
|
minutes: 5
|
||||||
|
ac_mode: false
|
||||||
|
```
|
||||||
|
|
||||||
|
> Tilpas entity-navnene til hvad Zigbee2MQTT faktisk kalder dem efter pairing.
|
||||||
|
|
||||||
|
### 5b: input_number til komforttemperaturer
|
||||||
|
|
||||||
|
Tilføj til `include/input/number/varme.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
varme_komfort_bryggers:
|
||||||
|
name: Komfort - Bryggers
|
||||||
|
min: 15
|
||||||
|
max: 28
|
||||||
|
step: 0.5
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
initial: 20
|
||||||
|
icon: mdi:thermometer
|
||||||
|
|
||||||
|
varme_komfort_kokken:
|
||||||
|
name: Komfort - Køkken
|
||||||
|
min: 15
|
||||||
|
max: 28
|
||||||
|
step: 0.5
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
initial: 20
|
||||||
|
icon: mdi:thermometer
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5c: Tilføj til varme_recalculate scriptet
|
||||||
|
|
||||||
|
De to nye rum skal med i `include/scripts/varme_styring.yaml` → `varme_recalculate`
|
||||||
|
på samme måde som badeværelse og stue (Danfoss Ally-mønsteret):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# ---- Bryggers – generic_thermostat ----
|
||||||
|
- if:
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ true }}" # ingen vinduessensor endnu
|
||||||
|
then:
|
||||||
|
- service: climate.set_temperature
|
||||||
|
target:
|
||||||
|
entity_id: climate.bryggers
|
||||||
|
data:
|
||||||
|
hvac_mode: heat
|
||||||
|
temperature: >
|
||||||
|
{% set k = states('input_number.varme_komfort_bryggers') | float(20) %}
|
||||||
|
{% if vacation %} {{ ferie_temp }}
|
||||||
|
{% elif night %} {{ [k - nat_sænk, 15] | max }}
|
||||||
|
{% elif not home %} {{ [k - vaek_sænk, 15] | max }}
|
||||||
|
{% else %} {{ k }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# ---- Køkken – generic_thermostat ----
|
||||||
|
- if:
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ true }}"
|
||||||
|
then:
|
||||||
|
- service: climate.set_temperature
|
||||||
|
target:
|
||||||
|
entity_id: climate.kokken
|
||||||
|
data:
|
||||||
|
hvac_mode: heat
|
||||||
|
temperature: >
|
||||||
|
{% set k = states('input_number.varme_komfort_kokken') | float(20) %}
|
||||||
|
{% if vacation %} {{ ferie_temp }}
|
||||||
|
{% elif night %} {{ [k - nat_sænk, 15] | max }}
|
||||||
|
{% elif not home %} {{ [k - vaek_sænk, 15] | max }}
|
||||||
|
{% else %} {{ k }}
|
||||||
|
{% endif %}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Del 6: Verificering
|
||||||
|
|
||||||
|
Når alt er sat op:
|
||||||
|
1. Gå til **Udviklerværktøjer → Tjenester**
|
||||||
|
2. Kald `climate.set_temperature` på `climate.bryggers` med `temperature: 25`
|
||||||
|
3. Lyt efter at aktuatoren klikker (kan høres eller mærkes) inden for 1-2 minutter
|
||||||
|
4. Sæt tilbage til normal komforttemperatur
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Resumé: Hvad du køber
|
||||||
|
|
||||||
|
| | |
|
||||||
|
|---|---|
|
||||||
|
| 2× Sonoff ZBMINI-L2 | ~260 kr |
|
||||||
|
| 2× Sonoff SNZB-02D temp-sensor | ~200 kr |
|
||||||
|
| **Total** | **~460 kr** |
|
||||||
|
|
||||||
|
Ingen elektriker, ingen nye kabler til aktuatorerne, ingen cloud-afhængighed.
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
# Zigbee husplan og netværksnoter
|
||||||
|
|
||||||
|
## Fysisk layout
|
||||||
|
|
||||||
|
```
|
||||||
|
NORD/INDKØRSEL-SIDE
|
||||||
|
══════════════════════════════════════════════════════════════════
|
||||||
|
GARAGE
|
||||||
|
[stik_fryser] ←── fjern ende (mod nord)
|
||||||
|
|
||||||
|
[garageport sensor] [stik_indkørsel] ←── tæt ende (mod kontor/syd)
|
||||||
|
══════════════ GARAGEMUR (beton - dæmper signal markant) ══════════
|
||||||
|
[stik_bryggers] [køkken] [forgang] [stik_lillebad] [stik_kontor]
|
||||||
|
↑ indkørslen løber langs denne side (bryggers → køkken → forgang → lille bad → badeværelse → kontor)
|
||||||
|
|
||||||
|
══════════════════════════════════════════════════════════════════
|
||||||
|
[badeværelse] [stik_bad]
|
||||||
|
[stik_sonos_stue] [stik_quooker] [stue]
|
||||||
|
[stik_daniel] (Daniels værelse - nabo til Andreas og over for bad)
|
||||||
|
[stik_soveværelse] (nabo til Daniel og kontor)
|
||||||
|
[stik_andreas] (Andreas værelse)
|
||||||
|
[stik_alrum]
|
||||||
|
SYD/STUE-SIDE
|
||||||
|
|
||||||
|
KOORDINATOR: Sonoff ZBDongle-E sidder på loftet over stue/Andreas-siden
|
||||||
|
```
|
||||||
|
|
||||||
|
## Zigbee enheder
|
||||||
|
|
||||||
|
| Enhed | Type | Placering |
|
||||||
|
|-------|------|-----------|
|
||||||
|
| SONOFF ZBDongle-E | Coordinator | Loft over stue/Andreas |
|
||||||
|
| stik_indkørsel | Router (TS011F) | Garage, tæt ende mod kontor |
|
||||||
|
| stik_fryser | Router (TS011F) | Garage, fjern ende mod nord |
|
||||||
|
| stik_kontor | Router (TS011F) | Kontor |
|
||||||
|
| stik_bryggers | Router (TS011F) | Bryggers |
|
||||||
|
| stik_lillebad | Router (TS011F) | Lille bad |
|
||||||
|
| stik_bad | Router (TS011F) | Badeværelse |
|
||||||
|
| stik_daniel | Router (TS011F) | Daniels værelse |
|
||||||
|
| stik_soveværelse | Router (TS011F) | Soveværelse |
|
||||||
|
| stik_andreas | Router (TS011F) | Andreas' værelse |
|
||||||
|
| stik_alrum | Router (TS011F) | Alrum |
|
||||||
|
| stik_sonos_stue | Router (TS011F) | Stue (Sonos) |
|
||||||
|
| stik_quooker | Router (TS011F) | Køkken (Quooker) |
|
||||||
|
| garageport | EndDevice (3RDTS01056Z) | Garage, tæt ende - tiltssensor på garageport |
|
||||||
|
| badevarelse | EndDevice | Badeværelse |
|
||||||
|
| stue | EndDevice | Stue |
|
||||||
|
| temp_bryggers | EndDevice | Bryggers (temperatursensor) |
|
||||||
|
| temp_køkken | EndDevice | Køkken (temperatursensor) |
|
||||||
|
| LUMI magnetsensorer (×6) | EndDevice | Spredt i huset |
|
||||||
|
|
||||||
|
## LQI-målinger over tid
|
||||||
|
|
||||||
|
| Enhed | 18/5 (morgen) | 18/5 (aften) | 20/5 | Bemærkning |
|
||||||
|
|-------|--------------|-------------|------|------------|
|
||||||
|
| stik_alrum | 184–192 | 188–192 | 184 | Stærk, tæt på koordinator |
|
||||||
|
| stik_andreas | 184–192 | 188–192 | 184 | Stærk, tæt på koordinator |
|
||||||
|
| stik_soveværelse | 76–192 | 76–188 | 84 | Svingende — route-afhængig |
|
||||||
|
| stik_quooker | 144–152 | 124–148 | 144 | God |
|
||||||
|
| stik_sonos_stue | 108–152 | 108–152 | 148 | God |
|
||||||
|
| stik_lillebad | 112–136 | 112–136 | 144 | OK–God |
|
||||||
|
| stik_daniel | — | 100–120 | 92 | OK |
|
||||||
|
| stik_bryggers | 100–132 | 100–132 | 108 | OK |
|
||||||
|
| stik_bad | 40–104 | 96–144 | 144 | Forbedret |
|
||||||
|
| stik_kontor | 52–96 | 72–96 | 96 | Forbedret |
|
||||||
|
| stik_fryser | 56–84 | 56–84 | 84 | Forbedret |
|
||||||
|
| stik_indkørsel | 44–80 | 56–124 | 84 | OK efter genstart |
|
||||||
|
| garageport sensor | 40–92 | 52–92 | 84 | Bedste måling! |
|
||||||
|
| badevarelse | 108–136 | 108–136 | 144 | God |
|
||||||
|
| stue | 84–116 | 84–116 | 112 | OK |
|
||||||
|
| temp_bryggers | — | — | 148 | Ny 20/5 |
|
||||||
|
| temp_køkken | — | — | 152 | Ny 20/5 |
|
||||||
|
|
||||||
|
## Kendte problemer
|
||||||
|
|
||||||
|
- **Garagemuren** dæmper signalet markant — alle enheder bag muren har LQI 40–80
|
||||||
|
- **Garageport-sensoren** er tæt på grænsen og har tidligere været unavailable
|
||||||
|
- Koordinatoren sidder i den modsatte ende af huset fra garagen
|
||||||
|
|
||||||
|
## Optimeringforslag
|
||||||
|
|
||||||
|
1. **Flyt koordinatoren til midten** (forgang/køkken-loftet) — størst effekt, kræver blot USB-forlænger
|
||||||
|
2. **Tilsæt router i forgang/gang** tæt på garagemur — bedre mellemled til garagen
|
||||||
|
3. stik_fryser er acceptabelt svag hvis fryseren blot er til strømmåling
|
||||||
|
|
||||||
|
## Entiteter der styrer indkørselslys
|
||||||
|
|
||||||
|
- `switch.stik_indkorsel` — Zigbee plug (erstattede `light.indkorsel_plug` fra Hue, maj 2026)
|
||||||
|
- `light.indkorsel_2` — Hue gruppe (garage venstre + højre + bryggersdør)
|
||||||
|
- `scene.indkorsel_bright` / `scene.indkorsel_dimmed` — Hue scener
|
||||||
|
- Automationer: `lysindkorsel.yaml`, `presence_simulation.yaml`, `andreas_kommer_hjem_taend_lys.yaml`
|
||||||
@@ -7,6 +7,10 @@
|
|||||||
- condition: state
|
- condition: state
|
||||||
entity_id: binary_sensor.arbejdsdagimorgen
|
entity_id: binary_sensor.arbejdsdagimorgen
|
||||||
state: 'on'
|
state: 'on'
|
||||||
|
- condition: template
|
||||||
|
value_template: >-
|
||||||
|
{{ not is_state('input_select.anne_status', 'syg') and
|
||||||
|
not is_state('input_select.claus_status', 'syg') }}
|
||||||
action:
|
action:
|
||||||
- service: homeassistant.turn_on
|
- service: homeassistant.turn_on
|
||||||
entity_id: switch.sonos_alarm_1782
|
entity_id: switch.sonos_alarm_1782
|
||||||
@@ -31,6 +35,8 @@
|
|||||||
- condition: state
|
- condition: state
|
||||||
entity_id: binary_sensor.arbejdsdagimorgen
|
entity_id: binary_sensor.arbejdsdagimorgen
|
||||||
state: 'on'
|
state: 'on'
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ not is_state('input_select.andreas_status', 'syg') }}"
|
||||||
action:
|
action:
|
||||||
- service: homeassistant.turn_on
|
- service: homeassistant.turn_on
|
||||||
entity_id: switch.sonos_alarm_445
|
entity_id: switch.sonos_alarm_445
|
||||||
@@ -58,6 +64,8 @@
|
|||||||
value_template: '{{ now().date() | string != "2022-12-24" }}'
|
value_template: '{{ now().date() | string != "2022-12-24" }}'
|
||||||
- condition: template
|
- condition: template
|
||||||
value_template: '{{ now().date() | string != "2022-12-31" }}'
|
value_template: '{{ now().date() | string != "2022-12-31" }}'
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ not is_state('input_select.andreas_status', 'syg') }}"
|
||||||
action:
|
action:
|
||||||
- service: homeassistant.turn_on
|
- service: homeassistant.turn_on
|
||||||
entity_id: switch.sonos_alarm_1874
|
entity_id: switch.sonos_alarm_1874
|
||||||
@@ -78,6 +86,8 @@
|
|||||||
- condition: state
|
- condition: state
|
||||||
entity_id: binary_sensor.arbejdsdagimorgen
|
entity_id: binary_sensor.arbejdsdagimorgen
|
||||||
state: 'on'
|
state: 'on'
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ not is_state('input_select.daniel_status', 'syg') }}"
|
||||||
action:
|
action:
|
||||||
- service: homeassistant.turn_on
|
- service: homeassistant.turn_on
|
||||||
entity_id: switch.sonos_alarm_377
|
entity_id: switch.sonos_alarm_377
|
||||||
@@ -103,6 +113,8 @@
|
|||||||
- condition: state
|
- condition: state
|
||||||
entity_id: binary_sensor.arbejdsdagimorgen
|
entity_id: binary_sensor.arbejdsdagimorgen
|
||||||
state: 'on'
|
state: 'on'
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ not is_state('input_select.daniel_status', 'syg') }}"
|
||||||
action:
|
action:
|
||||||
- service: homeassistant.turn_on
|
- service: homeassistant.turn_on
|
||||||
entity_id: switch.sonos_alarm_1894
|
entity_id: switch.sonos_alarm_1894
|
||||||
@@ -128,6 +140,8 @@
|
|||||||
- condition: state
|
- condition: state
|
||||||
entity_id: binary_sensor.arbejdsdagimorgen
|
entity_id: binary_sensor.arbejdsdagimorgen
|
||||||
state: 'on'
|
state: 'on'
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ not is_state('input_select.daniel_status', 'syg') }}"
|
||||||
action:
|
action:
|
||||||
- service: homeassistant.turn_on
|
- service: homeassistant.turn_on
|
||||||
entity_id: switch.sonos_alarm_2273
|
entity_id: switch.sonos_alarm_2273
|
||||||
@@ -155,6 +169,8 @@
|
|||||||
value_template: '{{ now().date() | string != "2022-12-24" }}'
|
value_template: '{{ now().date() | string != "2022-12-24" }}'
|
||||||
- condition: template
|
- condition: template
|
||||||
value_template: '{{ now().date() | string != "2022-12-31" }}'
|
value_template: '{{ now().date() | string != "2022-12-31" }}'
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ not is_state('input_select.daniel_status', 'syg') }}"
|
||||||
action:
|
action:
|
||||||
- service: homeassistant.turn_on
|
- service: homeassistant.turn_on
|
||||||
entity_id: switch.sonos_alarm_3471
|
entity_id: switch.sonos_alarm_3471
|
||||||
@@ -177,6 +193,10 @@
|
|||||||
- condition: state
|
- condition: state
|
||||||
entity_id: binary_sensor.arbejdsdagimorgen
|
entity_id: binary_sensor.arbejdsdagimorgen
|
||||||
state: 'on'
|
state: 'on'
|
||||||
|
- condition: template
|
||||||
|
value_template: >-
|
||||||
|
{{ not is_state('input_select.anne_status', 'syg') and
|
||||||
|
not is_state('input_select.claus_status', 'syg') }}
|
||||||
action:
|
action:
|
||||||
- service: homeassistant.turn_on
|
- service: homeassistant.turn_on
|
||||||
entity_id: switch.sonos_alarm_298
|
entity_id: switch.sonos_alarm_298
|
||||||
@@ -194,27 +214,3 @@
|
|||||||
- service: homeassistant.turn_off
|
- service: homeassistant.turn_off
|
||||||
entity_id: switch.sonos_alarm_298
|
entity_id: switch.sonos_alarm_298
|
||||||
|
|
||||||
- alias: 'Turn on alarms Badeværelse Afsted'
|
|
||||||
trigger:
|
|
||||||
platform: time
|
|
||||||
at: '20:07:10'
|
|
||||||
condition:
|
|
||||||
- condition: state
|
|
||||||
entity_id: binary_sensor.arbejdsdagimorgen
|
|
||||||
state: 'on'
|
|
||||||
action:
|
|
||||||
- service: homeassistant.turn_on
|
|
||||||
entity_id: switch.sonos_alarm_1899
|
|
||||||
|
|
||||||
|
|
||||||
- alias: 'Turn off alarms Badeværelse Afsted'
|
|
||||||
trigger:
|
|
||||||
platform: time
|
|
||||||
at: '20:06:20'
|
|
||||||
condition:
|
|
||||||
- condition: state
|
|
||||||
entity_id: binary_sensor.arbejdsdagimorgen
|
|
||||||
state: 'off'
|
|
||||||
action:
|
|
||||||
- service: homeassistant.turn_off
|
|
||||||
entity_id: switch.sonos_alarm_1899
|
|
||||||
|
|||||||
@@ -29,10 +29,13 @@
|
|||||||
|
|
||||||
action:
|
action:
|
||||||
- variables:
|
- variables:
|
||||||
lights:
|
lights: >
|
||||||
- light.indkorsel_2
|
{% set base = ['light.indkorsel_2', 'light.garage'] %}
|
||||||
- light.extended_color_light_1
|
{% if now().isocalendar()[1] >= 42 or now().isocalendar()[1] <= 8 %}
|
||||||
- light.garage
|
{{ (base + ['light.extended_color_light_1']) | list }}
|
||||||
|
{% else %}
|
||||||
|
{{ base }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
lights_to_turn_on: >
|
lights_to_turn_on: >
|
||||||
{{ lights | select('is_state','off') | list }}
|
{{ lights | select('is_state','off') | list }}
|
||||||
@@ -45,9 +48,17 @@
|
|||||||
target:
|
target:
|
||||||
entity_id: "{{ lights_to_turn_on }}"
|
entity_id: "{{ lights_to_turn_on }}"
|
||||||
|
|
||||||
|
- service: switch.turn_on
|
||||||
|
data:
|
||||||
|
entity_id: switch.stik_indkorsel
|
||||||
|
|
||||||
- delay: "00:10:00"
|
- delay: "00:10:00"
|
||||||
|
|
||||||
- service: light.turn_off
|
- service: light.turn_off
|
||||||
target:
|
target:
|
||||||
entity_id: "{{ lights_to_turn_on }}"
|
entity_id: "{{ lights_to_turn_on }}"
|
||||||
|
|
||||||
|
- service: switch.turn_off
|
||||||
|
data:
|
||||||
|
entity_id: switch.stik_indkorsel
|
||||||
|
|
||||||
|
|||||||
@@ -55,53 +55,12 @@
|
|||||||
sequence:
|
sequence:
|
||||||
- parallel:
|
- parallel:
|
||||||
- sequence:
|
- sequence:
|
||||||
- choose:
|
- variables:
|
||||||
- conditions:
|
lille_bad_volumen: "{{ 0.5 if (now().hour >= 20 or now().hour < 6) else 0.8 }}"
|
||||||
- condition: time
|
- service: script.spil_paa_lille_bad
|
||||||
after: '20:00:00'
|
|
||||||
before: '06:00:00'
|
|
||||||
sequence:
|
|
||||||
- service: media_player.volume_set
|
|
||||||
data:
|
data:
|
||||||
volume_level: 0.5
|
lydfil: german-shephard.mp3
|
||||||
target:
|
volumen: "{{ lille_bad_volumen }}"
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
- service: media_player.play_media
|
|
||||||
target:
|
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
data:
|
|
||||||
media_content_id: media-source://media_source/local/german-shephard.mp3
|
|
||||||
media_content_type: audio/mpeg
|
|
||||||
metadata:
|
|
||||||
title: german-shephard.mp3
|
|
||||||
thumbnail:
|
|
||||||
media_class: music
|
|
||||||
children_media_class:
|
|
||||||
navigateIds:
|
|
||||||
- {}
|
|
||||||
- media_content_type: app
|
|
||||||
media_content_id: media-source://media_source
|
|
||||||
default:
|
|
||||||
- service: media_player.volume_set
|
|
||||||
data:
|
|
||||||
volume_level: 0.8
|
|
||||||
target:
|
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
- service: media_player.play_media
|
|
||||||
target:
|
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
data:
|
|
||||||
media_content_id: media-source://media_source/local/german-shephard.mp3
|
|
||||||
media_content_type: audio/mpeg
|
|
||||||
metadata:
|
|
||||||
title: german-shephard.mp3
|
|
||||||
thumbnail:
|
|
||||||
media_class: music
|
|
||||||
children_media_class:
|
|
||||||
navigateIds:
|
|
||||||
- {}
|
|
||||||
- media_content_type: app
|
|
||||||
media_content_id: media-source://media_source
|
|
||||||
- sequence:
|
- sequence:
|
||||||
- service: media_player.volume_set
|
- service: media_player.volume_set
|
||||||
data:
|
data:
|
||||||
@@ -165,53 +124,12 @@
|
|||||||
sequence:
|
sequence:
|
||||||
- parallel:
|
- parallel:
|
||||||
- sequence:
|
- sequence:
|
||||||
- choose:
|
- variables:
|
||||||
- conditions:
|
lille_bad_volumen: "{{ 0.5 if (now().hour >= 20 or now().hour < 6) else 0.8 }}"
|
||||||
- condition: time
|
- service: script.spil_paa_lille_bad
|
||||||
after: '20:00:00'
|
|
||||||
before: '06:00:00'
|
|
||||||
sequence:
|
|
||||||
- service: media_player.volume_set
|
|
||||||
data:
|
data:
|
||||||
volume_level: 0.5
|
lydfil: dog-barking-2-bullmastiff.mp3
|
||||||
target:
|
volumen: "{{ lille_bad_volumen }}"
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
- service: media_player.play_media
|
|
||||||
target:
|
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
data:
|
|
||||||
media_content_id: media-source://media_source/local/dog-barking-2-bullmastiff.mp3
|
|
||||||
media_content_type: audio/mpeg
|
|
||||||
metadata:
|
|
||||||
title: dog-barking-2-bullmastiff.mp3
|
|
||||||
thumbnail:
|
|
||||||
media_class: music
|
|
||||||
children_media_class:
|
|
||||||
navigateIds:
|
|
||||||
- {}
|
|
||||||
- media_content_type: app
|
|
||||||
media_content_id: media-source://media_source
|
|
||||||
default:
|
|
||||||
- service: media_player.volume_set
|
|
||||||
data:
|
|
||||||
volume_level: 0.8
|
|
||||||
target:
|
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
- service: media_player.play_media
|
|
||||||
target:
|
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
data:
|
|
||||||
media_content_id: media-source://media_source/local/dog-barking-2-bullmastiff.mp3
|
|
||||||
media_content_type: audio/mpeg
|
|
||||||
metadata:
|
|
||||||
title: dog-barking-2-bullmastiff.mp3
|
|
||||||
thumbnail:
|
|
||||||
media_class: music
|
|
||||||
children_media_class:
|
|
||||||
navigateIds:
|
|
||||||
- {}
|
|
||||||
- media_content_type: app
|
|
||||||
media_content_id: media-source://media_source
|
|
||||||
- sequence:
|
- sequence:
|
||||||
- service: tts.speak
|
- service: tts.speak
|
||||||
target:
|
target:
|
||||||
@@ -233,53 +151,12 @@
|
|||||||
sequence:
|
sequence:
|
||||||
- parallel:
|
- parallel:
|
||||||
- sequence:
|
- sequence:
|
||||||
- choose:
|
- variables:
|
||||||
- conditions:
|
lille_bad_volumen: "{{ 0.5 if (now().hour >= 20 or now().hour < 6) else 0.8 }}"
|
||||||
- condition: time
|
- service: script.spil_paa_lille_bad
|
||||||
after: '20:00:00'
|
|
||||||
before: '06:00:00'
|
|
||||||
sequence:
|
|
||||||
- service: media_player.volume_set
|
|
||||||
data:
|
data:
|
||||||
volume_level: 0.5
|
lydfil: two-tone-chime.mp3
|
||||||
target:
|
volumen: "{{ lille_bad_volumen }}"
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
- service: media_player.play_media
|
|
||||||
target:
|
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
data:
|
|
||||||
media_content_id: media-source://media_source/local/two-tone-chime.mp3
|
|
||||||
media_content_type: audio/mpeg
|
|
||||||
metadata:
|
|
||||||
title: two-tone-chime.mp3
|
|
||||||
thumbnail:
|
|
||||||
media_class: music
|
|
||||||
children_media_class:
|
|
||||||
navigateIds:
|
|
||||||
- {}
|
|
||||||
- media_content_type: app
|
|
||||||
media_content_id: media-source://media_source
|
|
||||||
default:
|
|
||||||
- service: media_player.volume_set
|
|
||||||
data:
|
|
||||||
volume_level: 0.8
|
|
||||||
target:
|
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
- service: media_player.play_media
|
|
||||||
target:
|
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
data:
|
|
||||||
media_content_id: media-source://media_source/local/two-tone-chime.mp3
|
|
||||||
media_content_type: audio/mpeg
|
|
||||||
metadata:
|
|
||||||
title: two-tone-chime.mp3
|
|
||||||
thumbnail:
|
|
||||||
media_class: music
|
|
||||||
children_media_class:
|
|
||||||
navigateIds:
|
|
||||||
- {}
|
|
||||||
- media_content_type: app
|
|
||||||
media_content_id: media-source://media_source
|
|
||||||
- sequence:
|
- sequence:
|
||||||
- service: tts.speak
|
- service: tts.speak
|
||||||
target:
|
target:
|
||||||
|
|||||||
@@ -44,53 +44,13 @@
|
|||||||
sequence:
|
sequence:
|
||||||
- parallel:
|
- parallel:
|
||||||
- sequence:
|
- sequence:
|
||||||
- choose:
|
- variables:
|
||||||
- conditions:
|
lille_bad_volumen: "{{ 0.5 if (now().hour >= 20 or now().hour < 6) else 0.8 }}"
|
||||||
- condition: time
|
lille_bad_lydfil: "{{ 'Halloween-doorbell.mp3' if (now().hour >= 20 or now().hour < 6) else 'doorbell.mp3' }}"
|
||||||
after: '20:00:00'
|
- service: script.spil_paa_lille_bad
|
||||||
before: '06:00:00'
|
|
||||||
sequence:
|
|
||||||
- service: media_player.volume_set
|
|
||||||
data:
|
data:
|
||||||
volume_level: 0.5
|
lydfil: "{{ lille_bad_lydfil }}"
|
||||||
target:
|
volumen: "{{ lille_bad_volumen }}"
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
- service: media_player.play_media
|
|
||||||
target:
|
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
data:
|
|
||||||
media_content_id: media-source://media_source/local/Halloween-doorbell.mp3
|
|
||||||
media_content_type: audio/mpeg
|
|
||||||
metadata:
|
|
||||||
title: Halloween-doorbell.mp3
|
|
||||||
thumbnail:
|
|
||||||
media_class: music
|
|
||||||
children_media_class:
|
|
||||||
navigateIds:
|
|
||||||
- {}
|
|
||||||
- media_content_type: app
|
|
||||||
media_content_id: media-source://media_source
|
|
||||||
default:
|
|
||||||
- service: media_player.volume_set
|
|
||||||
data:
|
|
||||||
volume_level: 0.8
|
|
||||||
target:
|
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
- service: media_player.play_media
|
|
||||||
target:
|
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
data:
|
|
||||||
media_content_id: media-source://media_source/local/doorbell.mp3
|
|
||||||
media_content_type: audio/mpeg
|
|
||||||
metadata:
|
|
||||||
title: doorbell.mp3
|
|
||||||
thumbnail:
|
|
||||||
media_class: music
|
|
||||||
children_media_class:
|
|
||||||
navigateIds:
|
|
||||||
- {}
|
|
||||||
- media_content_type: app
|
|
||||||
media_content_id: media-source://media_source
|
|
||||||
- sequence:
|
- sequence:
|
||||||
- service: tts.speak
|
- service: tts.speak
|
||||||
target:
|
target:
|
||||||
@@ -120,6 +80,23 @@
|
|||||||
- delay: 2
|
- delay: 2
|
||||||
- service: scene.turn_on
|
- service: scene.turn_on
|
||||||
entity_id: scene.before
|
entity_id: scene.before
|
||||||
|
- sequence:
|
||||||
|
- condition: template
|
||||||
|
value_template: >
|
||||||
|
{{ states('sensor.forgang_sensor_illuminance') | int < 60 }}
|
||||||
|
- choose:
|
||||||
|
- conditions:
|
||||||
|
- condition: time
|
||||||
|
after: "06:00:00"
|
||||||
|
before: "23:59:00"
|
||||||
|
sequence:
|
||||||
|
- service: scene.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: scene.forgang_bright
|
||||||
|
default:
|
||||||
|
- service: scene.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: scene.forgang_dimmed
|
||||||
|
|
||||||
- conditions:
|
- conditions:
|
||||||
- condition: template
|
- condition: template
|
||||||
@@ -127,53 +104,12 @@
|
|||||||
sequence:
|
sequence:
|
||||||
- parallel:
|
- parallel:
|
||||||
- sequence:
|
- sequence:
|
||||||
- choose:
|
- variables:
|
||||||
- conditions:
|
lille_bad_volumen: "{{ 0.5 if (now().hour >= 20 or now().hour < 6) else 0.8 }}"
|
||||||
- condition: time
|
- service: script.spil_paa_lille_bad
|
||||||
after: '20:00:00'
|
|
||||||
before: '06:00:00'
|
|
||||||
sequence:
|
|
||||||
- service: media_player.volume_set
|
|
||||||
data:
|
data:
|
||||||
volume_level: 0.5
|
lydfil: german-shephard.mp3
|
||||||
target:
|
volumen: "{{ lille_bad_volumen }}"
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
- service: media_player.play_media
|
|
||||||
target:
|
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
data:
|
|
||||||
media_content_id: media-source://media_source/local/german-shephard.mp3
|
|
||||||
media_content_type: audio/mpeg
|
|
||||||
metadata:
|
|
||||||
title: german-shephard.mp3
|
|
||||||
thumbnail:
|
|
||||||
media_class: music
|
|
||||||
children_media_class:
|
|
||||||
navigateIds:
|
|
||||||
- {}
|
|
||||||
- media_content_type: app
|
|
||||||
media_content_id: media-source://media_source
|
|
||||||
default:
|
|
||||||
- service: media_player.volume_set
|
|
||||||
data:
|
|
||||||
volume_level: 0.8
|
|
||||||
target:
|
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
- service: media_player.play_media
|
|
||||||
target:
|
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
data:
|
|
||||||
media_content_id: media-source://media_source/local/german-shephard.mp3
|
|
||||||
media_content_type: audio/mpeg
|
|
||||||
metadata:
|
|
||||||
title: german-shephard.mp3
|
|
||||||
thumbnail:
|
|
||||||
media_class: music
|
|
||||||
children_media_class:
|
|
||||||
navigateIds:
|
|
||||||
- {}
|
|
||||||
- media_content_type: app
|
|
||||||
media_content_id: media-source://media_source
|
|
||||||
- sequence:
|
- sequence:
|
||||||
- service: tts.speak
|
- service: tts.speak
|
||||||
target:
|
target:
|
||||||
@@ -195,53 +131,12 @@
|
|||||||
sequence:
|
sequence:
|
||||||
- parallel:
|
- parallel:
|
||||||
- sequence:
|
- sequence:
|
||||||
- choose:
|
- variables:
|
||||||
- conditions:
|
lille_bad_volumen: "{{ 0.5 if (now().hour >= 20 or now().hour < 6) else 0.8 }}"
|
||||||
- condition: time
|
- service: script.spil_paa_lille_bad
|
||||||
after: '20:00:00'
|
|
||||||
before: '06:00:00'
|
|
||||||
sequence:
|
|
||||||
- service: media_player.volume_set
|
|
||||||
data:
|
data:
|
||||||
volume_level: 0.5
|
lydfil: dog-barking-2-bullmastiff.mp3
|
||||||
target:
|
volumen: "{{ lille_bad_volumen }}"
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
- service: media_player.play_media
|
|
||||||
target:
|
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
data:
|
|
||||||
media_content_id: media-source://media_source/local/dog-barking-2-bullmastiff.mp3
|
|
||||||
media_content_type: audio/mpeg
|
|
||||||
metadata:
|
|
||||||
title: dog-barking-2-bullmastiff.mp3
|
|
||||||
thumbnail:
|
|
||||||
media_class: music
|
|
||||||
children_media_class:
|
|
||||||
navigateIds:
|
|
||||||
- {}
|
|
||||||
- media_content_type: app
|
|
||||||
media_content_id: media-source://media_source
|
|
||||||
default:
|
|
||||||
- service: media_player.volume_set
|
|
||||||
data:
|
|
||||||
volume_level: 0.8
|
|
||||||
target:
|
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
- service: media_player.play_media
|
|
||||||
target:
|
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
data:
|
|
||||||
media_content_id: media-source://media_source/local/dog-barking-2-bullmastiff.mp3
|
|
||||||
media_content_type: audio/mpeg
|
|
||||||
metadata:
|
|
||||||
title: dog-barking-2-bullmastiff.mp3
|
|
||||||
thumbnail:
|
|
||||||
media_class: music
|
|
||||||
children_media_class:
|
|
||||||
navigateIds:
|
|
||||||
- {}
|
|
||||||
- media_content_type: app
|
|
||||||
media_content_id: media-source://media_source
|
|
||||||
- sequence:
|
- sequence:
|
||||||
- service: tts.speak
|
- service: tts.speak
|
||||||
target:
|
target:
|
||||||
@@ -263,53 +158,12 @@
|
|||||||
sequence:
|
sequence:
|
||||||
- parallel:
|
- parallel:
|
||||||
- sequence:
|
- sequence:
|
||||||
- choose:
|
- variables:
|
||||||
- conditions:
|
lille_bad_volumen: "{{ 0.5 if (now().hour >= 20 or now().hour < 6) else 0.8 }}"
|
||||||
- condition: time
|
- service: script.spil_paa_lille_bad
|
||||||
after: '20:00:00'
|
|
||||||
before: '06:00:00'
|
|
||||||
sequence:
|
|
||||||
- service: media_player.volume_set
|
|
||||||
data:
|
data:
|
||||||
volume_level: 0.5
|
lydfil: two-tone-chime.mp3
|
||||||
target:
|
volumen: "{{ lille_bad_volumen }}"
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
- service: media_player.play_media
|
|
||||||
target:
|
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
data:
|
|
||||||
media_content_id: media-source://media_source/local/two-tone-chime.mp3
|
|
||||||
media_content_type: audio/mpeg
|
|
||||||
metadata:
|
|
||||||
title: two-tone-chime.mp3
|
|
||||||
thumbnail:
|
|
||||||
media_class: music
|
|
||||||
children_media_class:
|
|
||||||
navigateIds:
|
|
||||||
- {}
|
|
||||||
- media_content_type: app
|
|
||||||
media_content_id: media-source://media_source
|
|
||||||
default:
|
|
||||||
- service: media_player.volume_set
|
|
||||||
data:
|
|
||||||
volume_level: 0.8
|
|
||||||
target:
|
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
- service: media_player.play_media
|
|
||||||
target:
|
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
data:
|
|
||||||
media_content_id: media-source://media_source/local/two-tone-chime.mp3
|
|
||||||
media_content_type: audio/mpeg
|
|
||||||
metadata:
|
|
||||||
title: two-tone-chime.mp3
|
|
||||||
thumbnail:
|
|
||||||
media_class: music
|
|
||||||
children_media_class:
|
|
||||||
navigateIds:
|
|
||||||
- {}
|
|
||||||
- media_content_type: app
|
|
||||||
media_content_id: media-source://media_source
|
|
||||||
- sequence:
|
- sequence:
|
||||||
- service: tts.speak
|
- service: tts.speak
|
||||||
target:
|
target:
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
data:
|
data:
|
||||||
group_members:
|
group_members:
|
||||||
- media_player.badevaerelse
|
- media_player.badevaerelse
|
||||||
|
- media_player.lille_badevaerelse
|
||||||
- media_player.sovevaerelse
|
- media_player.sovevaerelse
|
||||||
- media_player.stue
|
- media_player.stue
|
||||||
- media_player.alrum
|
- media_player.alrum
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
- condition: state # from sunset until sunrise
|
- condition: state # from sunset until sunrise
|
||||||
entity_id: sun.sun
|
entity_id: sun.sun
|
||||||
state: 'below_horizon'
|
state: 'below_horizon'
|
||||||
|
- condition: template # Vintersæson uge 42-8
|
||||||
|
value_template: "{{ now().isocalendar()[1] >= 42 or now().isocalendar()[1] <= 8 }}"
|
||||||
action:
|
action:
|
||||||
- service: light.turn_on
|
- service: light.turn_on
|
||||||
data:
|
data:
|
||||||
@@ -19,6 +21,9 @@
|
|||||||
trigger:
|
trigger:
|
||||||
platform: sun
|
platform: sun
|
||||||
event: sunrise
|
event: sunrise
|
||||||
|
condition:
|
||||||
|
- condition: template # Vintersæson uge 42-8
|
||||||
|
value_template: "{{ now().isocalendar()[1] >= 42 or now().isocalendar()[1] <= 8 }}"
|
||||||
action:
|
action:
|
||||||
- service: light.turn_off
|
- service: light.turn_off
|
||||||
data:
|
data:
|
||||||
@@ -31,6 +36,8 @@
|
|||||||
condition:
|
condition:
|
||||||
- condition: time
|
- condition: time
|
||||||
before: '21:30:00'
|
before: '21:30:00'
|
||||||
|
- condition: template # Vintersæson uge 42-8
|
||||||
|
value_template: "{{ now().isocalendar()[1] >= 42 or now().isocalendar()[1] <= 8 }}"
|
||||||
action:
|
action:
|
||||||
- service: light.turn_on
|
- service: light.turn_on
|
||||||
data:
|
data:
|
||||||
@@ -40,6 +47,9 @@
|
|||||||
trigger:
|
trigger:
|
||||||
platform: time
|
platform: time
|
||||||
at: "22:00:00"
|
at: "22:00:00"
|
||||||
|
condition:
|
||||||
|
- condition: template # Vintersæson uge 42-8
|
||||||
|
value_template: "{{ now().isocalendar()[1] >= 42 or now().isocalendar()[1] <= 8 }}"
|
||||||
action:
|
action:
|
||||||
- service: light.turn_off
|
- service: light.turn_off
|
||||||
data:
|
data:
|
||||||
|
|||||||
@@ -40,6 +40,10 @@
|
|||||||
{{ 1000 <= t <= 2030 }}
|
{{ 1000 <= t <= 2030 }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
# Ikke syg
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ not is_state('input_select.andreas_status', 'syg') }}"
|
||||||
|
|
||||||
sequence:
|
sequence:
|
||||||
|
|
||||||
- service: light.turn_on
|
- service: light.turn_on
|
||||||
|
|||||||
@@ -1,3 +1,27 @@
|
|||||||
|
- id: badevaerelse_startup_sluk
|
||||||
|
alias: Badeværelse lys sluk ved HA opstart
|
||||||
|
description: >
|
||||||
|
Slukker badeværelsets lys ved genstart hvis bevægelsessensoren er inaktiv.
|
||||||
|
Sikrer mod lys der sidder tændt efter strømudfald eller HA-genstart.
|
||||||
|
mode: single
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
- platform: homeassistant
|
||||||
|
event: start
|
||||||
|
|
||||||
|
action:
|
||||||
|
- delay:
|
||||||
|
seconds: 30
|
||||||
|
- condition: state
|
||||||
|
entity_id: binary_sensor.badevaerelse_bevaegelse
|
||||||
|
state: "off"
|
||||||
|
- service: light.turn_off
|
||||||
|
target:
|
||||||
|
area_id: badevaerelse
|
||||||
|
- service: input_boolean.turn_off
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.badevaerelse_manuel_tilstand
|
||||||
|
|
||||||
- id: badevaerelse_motion_lys
|
- id: badevaerelse_motion_lys
|
||||||
alias: Badeværelse lys via bevægelse
|
alias: Badeværelse lys via bevægelse
|
||||||
mode: restart
|
mode: restart
|
||||||
@@ -7,6 +31,12 @@
|
|||||||
entity_id: binary_sensor.badevaerelse_bevaegelse
|
entity_id: binary_sensor.badevaerelse_bevaegelse
|
||||||
to: "on"
|
to: "on"
|
||||||
|
|
||||||
|
condition:
|
||||||
|
# Spring over hvis manuel tilstand er aktiv – Hue-knap styrer lyset
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.badevaerelse_manuel_tilstand
|
||||||
|
state: "off"
|
||||||
|
|
||||||
action:
|
action:
|
||||||
- choose:
|
- choose:
|
||||||
# Arbejdsdag dagtid (06:00–22:00)
|
# Arbejdsdag dagtid (06:00–22:00)
|
||||||
@@ -49,10 +79,104 @@
|
|||||||
- platform: state
|
- platform: state
|
||||||
entity_id: binary_sensor.badevaerelse_bevaegelse
|
entity_id: binary_sensor.badevaerelse_bevaegelse
|
||||||
to: "off"
|
to: "off"
|
||||||
for:
|
|
||||||
minutes: 3 # Standard nat-timeout, kan ændres til 10 for dag, osv.
|
variables:
|
||||||
|
is_dag: >
|
||||||
|
{% set t = now().strftime('%H%M') | int %}
|
||||||
|
{% if is_state('binary_sensor.arbejdsdag', 'on') %}
|
||||||
|
{{ 600 <= t < 2200 }}
|
||||||
|
{% else %}
|
||||||
|
{{ 800 <= t < 2200 }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
action:
|
action:
|
||||||
|
- delay:
|
||||||
|
minutes: >
|
||||||
|
{% if is_state('input_boolean.badevaerelse_manuel_tilstand', 'on') %}
|
||||||
|
10
|
||||||
|
{% elif is_dag %}
|
||||||
|
{{ states('input_number.badevaerelse_timeout_day') | int }}
|
||||||
|
{% else %}
|
||||||
|
{{ states('input_number.badevaerelse_timeout_night') | int }}
|
||||||
|
{% endif %}
|
||||||
|
- condition: state
|
||||||
|
entity_id: binary_sensor.badevaerelse_bevaegelse
|
||||||
|
state: "off"
|
||||||
|
- service: light.turn_off
|
||||||
|
target:
|
||||||
|
area_id: badevaerelse
|
||||||
|
- service: input_boolean.turn_off
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.badevaerelse_manuel_tilstand
|
||||||
|
|
||||||
|
- id: badevaerelse_hue_knap
|
||||||
|
alias: Badeværelse Hue knap
|
||||||
|
description: >
|
||||||
|
Hue Tap Switch sætter manuel tilstand og tænder valgt scene.
|
||||||
|
Bevægelses-automatik springes over så længe manuel tilstand er aktiv.
|
||||||
|
Knap 4 slukker lyset og nulstiller til automatisk styring.
|
||||||
|
mode: restart
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: event.hue_tap_switch_1_button_1
|
||||||
|
id: knap_1
|
||||||
|
- platform: state
|
||||||
|
entity_id: event.hue_tap_switch_1_button_2
|
||||||
|
id: knap_2
|
||||||
|
- platform: state
|
||||||
|
entity_id: event.hue_tap_switch_1_button_3
|
||||||
|
id: knap_3
|
||||||
|
- platform: state
|
||||||
|
entity_id: event.hue_tap_switch_1_button_4
|
||||||
|
id: knap_4
|
||||||
|
|
||||||
|
action:
|
||||||
|
- choose:
|
||||||
|
# Knap 1 – Nat/dæmpet lys
|
||||||
|
- conditions:
|
||||||
|
- condition: trigger
|
||||||
|
id: knap_1
|
||||||
|
sequence:
|
||||||
|
- service: input_boolean.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.badevaerelse_manuel_tilstand
|
||||||
|
- service: scene.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: scene.badevaerelse_nat_2_lys
|
||||||
|
|
||||||
|
# Knap 2 – Fuld lys (klar til brug)
|
||||||
|
- conditions:
|
||||||
|
- condition: trigger
|
||||||
|
id: knap_2
|
||||||
|
sequence:
|
||||||
|
- service: input_boolean.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.badevaerelse_manuel_tilstand
|
||||||
|
- service: scene.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: scene.badevaerelse_klar
|
||||||
|
|
||||||
|
# Knap 3 – Blomstrende forår (medium)
|
||||||
|
- conditions:
|
||||||
|
- condition: trigger
|
||||||
|
id: knap_3
|
||||||
|
sequence:
|
||||||
|
- service: input_boolean.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.badevaerelse_manuel_tilstand
|
||||||
|
- service: scene.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: scene.badevaerelse_blomstrende_forar
|
||||||
|
|
||||||
|
# Knap 4 – Sluk lys + nulstil til automatisk
|
||||||
|
- conditions:
|
||||||
|
- condition: trigger
|
||||||
|
id: knap_4
|
||||||
|
sequence:
|
||||||
|
- service: input_boolean.turn_off
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.badevaerelse_manuel_tilstand
|
||||||
- service: light.turn_off
|
- service: light.turn_off
|
||||||
target:
|
target:
|
||||||
area_id: badevaerelse
|
area_id: badevaerelse
|
||||||
|
|||||||
@@ -1,94 +1,60 @@
|
|||||||
# - alias: 'Lys Daniel dag - arbejdsdag - sunrise'
|
- id: daniel_motion_lys
|
||||||
# trigger:
|
alias: Daniel lys via bevaegelse
|
||||||
# platform: time
|
mode: restart
|
||||||
# at: '06:00:00'
|
|
||||||
# condition:
|
|
||||||
# - condition: state
|
|
||||||
# entity_id: binary_sensor.arbejdsdag
|
|
||||||
# state: 'on'
|
|
||||||
# action:
|
|
||||||
# - service: script.sunrise
|
|
||||||
|
|
||||||
|
|
||||||
- alias: 'Lys Daniel dag - arbejdsdag'
|
|
||||||
trigger:
|
trigger:
|
||||||
platform: state
|
- platform: state
|
||||||
entity_id: binary_sensor.hue_motion_sensor_2_motion
|
entity_id: binary_sensor.hue_motion_sensor_2_motion
|
||||||
to: 'on'
|
to: "on"
|
||||||
condition:
|
id: motion_on
|
||||||
- condition: numeric_state
|
|
||||||
entity_id: sensor.hue_motion_sensor_2_illuminance
|
- platform: state
|
||||||
below: 90
|
entity_id: binary_sensor.hue_motion_sensor_2_motion
|
||||||
- condition: state
|
to: "off"
|
||||||
entity_id: binary_sensor.arbejdsdag
|
id: motion_off
|
||||||
state: 'on'
|
|
||||||
- condition: time
|
variables:
|
||||||
after: '06:30:00'
|
lux_limit: "{{ states('input_number.daniel_lux_threshold') | int }}"
|
||||||
before: '19:30:00'
|
brightness: "{{ states('input_number.daniel_brightness') | int }}"
|
||||||
|
timeout: "{{ states('input_number.daniel_timeout') | int }}"
|
||||||
|
|
||||||
action:
|
action:
|
||||||
- service: homeassistant.turn_on
|
- choose:
|
||||||
|
|
||||||
|
# Motion on: taend lys hvis lux lavt og indenfor tidsvindue
|
||||||
|
- conditions:
|
||||||
|
- condition: trigger
|
||||||
|
id: motion_on
|
||||||
|
- condition: template
|
||||||
|
value_template: >
|
||||||
|
{{ states('sensor.hue_motion_sensor_2_illuminance') | int < lux_limit }}
|
||||||
|
- condition: template
|
||||||
|
value_template: >
|
||||||
|
{% set t = now().strftime('%H%M') | int %}
|
||||||
|
{% if is_state('binary_sensor.arbejdsdag', 'on') %}
|
||||||
|
{{ 630 <= t < 1930 }}
|
||||||
|
{% else %}
|
||||||
|
{{ 1000 <= t < 1945 }}
|
||||||
|
{% endif %}
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ not is_state('input_select.daniel_status', 'syg') }}"
|
||||||
|
sequence:
|
||||||
|
- service: light.turn_on
|
||||||
target:
|
target:
|
||||||
entity_id: light.daniels_vaerelse
|
entity_id: light.daniels_vaerelse
|
||||||
data:
|
data:
|
||||||
brightness_pct: 100
|
brightness_pct: "{{ brightness }}"
|
||||||
|
|
||||||
- alias: 'Lys Daniel dag - ikke arbejdsdag'
|
# Motion off: vent timeout, sluk hvis stadig ingen bevaegelse
|
||||||
trigger:
|
- conditions:
|
||||||
platform: state
|
- condition: trigger
|
||||||
entity_id: binary_sensor.hue_motion_sensor_2_motion
|
id: motion_off
|
||||||
to: 'on'
|
sequence:
|
||||||
condition:
|
- delay:
|
||||||
- condition: numeric_state
|
minutes: "{{ timeout }}"
|
||||||
entity_id: sensor.hue_motion_sensor_2_illuminance
|
|
||||||
below: 90
|
|
||||||
- condition: state
|
- condition: state
|
||||||
entity_id: binary_sensor.arbejdsdag
|
entity_id: binary_sensor.hue_motion_sensor_2_motion
|
||||||
state: 'off'
|
state: "off"
|
||||||
- condition: time
|
- service: light.turn_off
|
||||||
after: '10:00:00'
|
|
||||||
before: '19:45:00'
|
|
||||||
action:
|
|
||||||
- service: homeassistant.turn_on
|
|
||||||
target:
|
target:
|
||||||
entity_id: light.daniels_vaerelse
|
entity_id: light.daniels_vaerelse
|
||||||
data:
|
|
||||||
brightness_pct: 100
|
|
||||||
|
|
||||||
# - alias: 'Lys Daniel aften'
|
|
||||||
# trigger:
|
|
||||||
# platform: state
|
|
||||||
# entity_id: binary_sensor.hue_motion_sensor_2_motion
|
|
||||||
# to: 'on'
|
|
||||||
# condition:
|
|
||||||
# - condition: numeric_state
|
|
||||||
# entity_id: sensor.hue_motion_sensor_2_illuminance
|
|
||||||
# below: 90
|
|
||||||
# - condition: time
|
|
||||||
# after: '20:00:01'
|
|
||||||
# before: '21:00:00'
|
|
||||||
# action:
|
|
||||||
# - service: homeassistant.turn_on
|
|
||||||
# target:
|
|
||||||
# entity_id: light.daniels_vaerelse
|
|
||||||
# data:
|
|
||||||
# brightness_pct: 50
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- alias: 'Sluk lys i Daniel'
|
|
||||||
trigger:
|
|
||||||
platform: state
|
|
||||||
entity_id: binary_sensor.hue_motion_sensor_2_motion
|
|
||||||
to: 'off'
|
|
||||||
for:
|
|
||||||
minutes: 10
|
|
||||||
condition:
|
|
||||||
- condition: time
|
|
||||||
after: '05:00:00'
|
|
||||||
before: '22:00:00'
|
|
||||||
action:
|
|
||||||
- service: homeassistant.turn_off
|
|
||||||
data:
|
|
||||||
entity_id:
|
|
||||||
- light.daniels_vaerelse
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,171 +1,68 @@
|
|||||||
- alias: 'Lys i gang - arbejdsdag - dag'
|
- id: gang_motion_lys
|
||||||
|
alias: Gang lys via bevægelse
|
||||||
|
mode: restart
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
platform: state
|
- platform: state
|
||||||
entity_id: binary_sensor.gang_sensor_motion
|
entity_id: binary_sensor.gang_sensor_motion
|
||||||
to: 'on'
|
to: "on"
|
||||||
condition:
|
id: motion_on
|
||||||
- condition: state
|
|
||||||
entity_id: binary_sensor.arbejdsdag
|
- platform: state
|
||||||
state: 'on'
|
entity_id: binary_sensor.gang_sensor_motion
|
||||||
- condition: numeric_state
|
to: "off"
|
||||||
entity_id: sensor.gang_sensor_illuminance
|
id: motion_off
|
||||||
below: '70'
|
|
||||||
- condition: time
|
variables:
|
||||||
before: '21:30:00'
|
lux_limit: "{{ states('input_number.gang_lux_threshold') | int }}"
|
||||||
- condition: time
|
is_dag: >
|
||||||
after: '06:30:00'
|
{% set t = now().strftime('%H%M') | int %}
|
||||||
|
{% if is_state('binary_sensor.arbejdsdag', 'on') %}
|
||||||
|
{{ 630 <= t < 2130 }}
|
||||||
|
{% else %}
|
||||||
|
{{ 800 <= t < 2200 }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
action:
|
action:
|
||||||
|
- choose:
|
||||||
|
|
||||||
|
# Motion on - dag: taend bright scene hvis lux er lavt nok
|
||||||
|
- conditions:
|
||||||
|
- condition: trigger
|
||||||
|
id: motion_on
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ is_dag }}"
|
||||||
|
- condition: template
|
||||||
|
value_template: >
|
||||||
|
{{ states('sensor.gang_sensor_illuminance') | int < lux_limit }}
|
||||||
|
sequence:
|
||||||
- service: scene.turn_on
|
- service: scene.turn_on
|
||||||
data:
|
|
||||||
entity_id: scene.gang_bright
|
|
||||||
# brightness: 255
|
|
||||||
# color_temp: 396
|
|
||||||
|
|
||||||
- alias: 'Sluk Lys i gang - arbejdsdag - dag'
|
|
||||||
trigger:
|
|
||||||
platform: state
|
|
||||||
entity_id: binary_sensor.gang_sensor_motion
|
|
||||||
to: 'off'
|
|
||||||
for:
|
|
||||||
minutes: 3
|
|
||||||
condition:
|
|
||||||
- condition: state
|
|
||||||
entity_id: binary_sensor.arbejdsdag
|
|
||||||
state: 'on'
|
|
||||||
- condition: time
|
|
||||||
after: '06:30:10'
|
|
||||||
- condition: time
|
|
||||||
before: '21:30:00'
|
|
||||||
action:
|
|
||||||
service: light.turn_off
|
|
||||||
data:
|
|
||||||
entity_id: light.gang
|
|
||||||
|
|
||||||
- alias: 'Lys i gang - ikke arbejdsdag - dag'
|
|
||||||
trigger:
|
|
||||||
platform: state
|
|
||||||
entity_id: binary_sensor.gang_sensor_motion
|
|
||||||
to: 'on'
|
|
||||||
condition:
|
|
||||||
- condition: state
|
|
||||||
entity_id: binary_sensor.arbejdsdag
|
|
||||||
state: 'off'
|
|
||||||
- condition: numeric_state
|
|
||||||
entity_id: sensor.gang_sensor_illuminance
|
|
||||||
below: 70
|
|
||||||
- condition: time
|
|
||||||
after: '08:00:00'
|
|
||||||
- condition: time
|
|
||||||
before: '22:00:00'
|
|
||||||
action:
|
|
||||||
- service: scene.turn_on
|
|
||||||
data:
|
|
||||||
entity_id: scene.gang_bright
|
|
||||||
# brightness: 255
|
|
||||||
# color_temp: 396
|
|
||||||
|
|
||||||
- alias: 'Sluk Lys i gang - ikke arbejdsdag - dag'
|
|
||||||
trigger:
|
|
||||||
platform: state
|
|
||||||
entity_id: binary_sensor.gang_sensor_motion
|
|
||||||
to: 'off'
|
|
||||||
for:
|
|
||||||
minutes: 3
|
|
||||||
condition:
|
|
||||||
- condition: state
|
|
||||||
entity_id: binary_sensor.arbejdsdag
|
|
||||||
state: 'off'
|
|
||||||
- condition: time
|
|
||||||
after: '08:00:10'
|
|
||||||
- condition: time
|
|
||||||
before: '22:00:00'
|
|
||||||
action:
|
|
||||||
service: light.turn_off
|
|
||||||
target:
|
target:
|
||||||
entity_id: light.gang
|
entity_id: scene.gang_bright
|
||||||
|
|
||||||
########## NIGHT
|
# Motion on - nat: taend daempet scene (ingen lux-check)
|
||||||
|
- conditions:
|
||||||
- alias: 'Lys i gang - arbejdsdag - nat'
|
- condition: trigger
|
||||||
trigger:
|
id: motion_on
|
||||||
platform: state
|
- condition: template
|
||||||
entity_id: binary_sensor.gang_sensor_motion
|
value_template: "{{ not is_dag }}"
|
||||||
to: 'on'
|
sequence:
|
||||||
condition:
|
|
||||||
- condition: state
|
|
||||||
entity_id: binary_sensor.arbejdsdag
|
|
||||||
state: 'on'
|
|
||||||
- condition: numeric_state
|
|
||||||
entity_id: sensor.gang_sensor_illuminance
|
|
||||||
below: '70'
|
|
||||||
- condition: time
|
|
||||||
before: '06:30:00'
|
|
||||||
after: '21:30:00'
|
|
||||||
action:
|
|
||||||
- service: scene.turn_on
|
- service: scene.turn_on
|
||||||
data:
|
|
||||||
entity_id: scene.gang_daempet_nat
|
|
||||||
# brightness_pct: 1
|
|
||||||
# color_temp: 396
|
|
||||||
|
|
||||||
- alias: 'Sluk Lys i gang - arbejdsdag - nat'
|
|
||||||
trigger:
|
|
||||||
platform: state
|
|
||||||
entity_id: binary_sensor.gang_sensor_motion
|
|
||||||
to: 'off'
|
|
||||||
for:
|
|
||||||
minutes: 1
|
|
||||||
condition:
|
|
||||||
- condition: state
|
|
||||||
entity_id: binary_sensor.arbejdsdag
|
|
||||||
state: 'on'
|
|
||||||
- condition: time
|
|
||||||
after: '21:30:10'
|
|
||||||
before: '06:30:00'
|
|
||||||
action:
|
|
||||||
service: light.turn_off
|
|
||||||
target:
|
target:
|
||||||
entity_id: light.gang
|
|
||||||
|
|
||||||
- alias: 'Lys i gang - ikke arbejdsdag - nat'
|
|
||||||
trigger:
|
|
||||||
platform: state
|
|
||||||
entity_id: binary_sensor.gang_sensor_motion
|
|
||||||
to: 'on'
|
|
||||||
condition:
|
|
||||||
- condition: state
|
|
||||||
entity_id: binary_sensor.arbejdsdag
|
|
||||||
state: 'off'
|
|
||||||
# - condition: numeric_state
|
|
||||||
# entity_id: sensor.gang_sensor_light_level
|
|
||||||
# below: '70'
|
|
||||||
- condition: time
|
|
||||||
after: '22:00:00'
|
|
||||||
before: '08:00:00'
|
|
||||||
action:
|
|
||||||
- service: scene.turn_on
|
|
||||||
data:
|
|
||||||
entity_id: scene.gang_daempet_nat
|
entity_id: scene.gang_daempet_nat
|
||||||
# brightness_pct: 1
|
|
||||||
# color_temp: 396
|
|
||||||
|
|
||||||
|
# Motion off: vent timeout, sluk hvis stadig ingen bevaegelse
|
||||||
- alias: 'Sluk Lys i gang - ikke arbejdsdag - nat'
|
- conditions:
|
||||||
trigger:
|
- condition: trigger
|
||||||
platform: state
|
id: motion_off
|
||||||
entity_id: binary_sensor.gang_sensor_motion
|
sequence:
|
||||||
to: 'off'
|
- delay:
|
||||||
for:
|
minutes: >
|
||||||
minutes: 1
|
{{ states('input_number.gang_timeout_day') | int if is_dag
|
||||||
condition:
|
else states('input_number.gang_timeout_night') | int }}
|
||||||
- condition: state
|
- condition: state
|
||||||
entity_id: binary_sensor.arbejdsdag
|
entity_id: binary_sensor.gang_sensor_motion
|
||||||
state: 'off'
|
state: "off"
|
||||||
- condition: time
|
|
||||||
after: '22:00:10'
|
|
||||||
before: '08:00:00'
|
|
||||||
action:
|
|
||||||
- service: light.turn_off
|
- service: light.turn_off
|
||||||
target:
|
target:
|
||||||
entity_id: light.gang
|
entity_id: light.gang
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,10 @@
|
|||||||
to: "on"
|
to: "on"
|
||||||
|
|
||||||
condition:
|
condition:
|
||||||
- condition: numeric_state
|
- condition: template
|
||||||
entity_id: sensor.kontor_belysningsstyrke
|
value_template: >
|
||||||
below: 60
|
{{ states('sensor.kontor_belysningsstyrke') | int <
|
||||||
|
states('input_number.kontor_lux_threshold') | int }}
|
||||||
|
|
||||||
action:
|
action:
|
||||||
- service: scene.turn_on
|
- service: scene.turn_on
|
||||||
@@ -36,7 +37,8 @@
|
|||||||
- platform: state
|
- platform: state
|
||||||
entity_id: binary_sensor.kontor_motion_bevaegelse
|
entity_id: binary_sensor.kontor_motion_bevaegelse
|
||||||
to: "on"
|
to: "on"
|
||||||
timeout: "00:10:00"
|
timeout:
|
||||||
|
minutes: "{{ states('input_number.kontor_timeout_day') | int }}"
|
||||||
continue_on_timeout: true
|
continue_on_timeout: true
|
||||||
- condition: state
|
- condition: state
|
||||||
entity_id: binary_sensor.kontor_motion_bevaegelse
|
entity_id: binary_sensor.kontor_motion_bevaegelse
|
||||||
@@ -67,7 +69,8 @@
|
|||||||
- platform: state
|
- platform: state
|
||||||
entity_id: binary_sensor.kontor_motion_bevaegelse
|
entity_id: binary_sensor.kontor_motion_bevaegelse
|
||||||
to: "on"
|
to: "on"
|
||||||
timeout: "00:05:00"
|
timeout:
|
||||||
|
minutes: "{{ states('input_number.kontor_timeout_night') | int }}"
|
||||||
continue_on_timeout: true
|
continue_on_timeout: true
|
||||||
- condition: state
|
- condition: state
|
||||||
entity_id: binary_sensor.kontor_motion_bevaegelse
|
entity_id: binary_sensor.kontor_motion_bevaegelse
|
||||||
|
|||||||
@@ -6,6 +6,10 @@
|
|||||||
- condition: state
|
- condition: state
|
||||||
entity_id: binary_sensor.arbejdsdag
|
entity_id: binary_sensor.arbejdsdag
|
||||||
state: 'on'
|
state: 'on'
|
||||||
|
- condition: template
|
||||||
|
value_template: >-
|
||||||
|
{{ not is_state('input_select.anne_status', 'syg') and
|
||||||
|
not is_state('input_select.claus_status', 'syg') }}
|
||||||
action:
|
action:
|
||||||
- service: script.sunrise
|
- service: script.sunrise
|
||||||
|
|
||||||
@@ -25,6 +29,10 @@
|
|||||||
- condition: time
|
- condition: time
|
||||||
after: '06:30:00'
|
after: '06:30:00'
|
||||||
before: '20:00:00'
|
before: '20:00:00'
|
||||||
|
- condition: template
|
||||||
|
value_template: >-
|
||||||
|
{{ not is_state('input_select.anne_status', 'syg') and
|
||||||
|
not is_state('input_select.claus_status', 'syg') }}
|
||||||
action:
|
action:
|
||||||
- service: homeassistant.turn_on
|
- service: homeassistant.turn_on
|
||||||
target:
|
target:
|
||||||
@@ -47,6 +55,10 @@
|
|||||||
- condition: time
|
- condition: time
|
||||||
after: '10:00:00'
|
after: '10:00:00'
|
||||||
before: '20:00:00'
|
before: '20:00:00'
|
||||||
|
- condition: template
|
||||||
|
value_template: >-
|
||||||
|
{{ not is_state('input_select.anne_status', 'syg') and
|
||||||
|
not is_state('input_select.claus_status', 'syg') }}
|
||||||
action:
|
action:
|
||||||
- service: homeassistant.turn_on
|
- service: homeassistant.turn_on
|
||||||
target:
|
target:
|
||||||
@@ -161,6 +173,9 @@
|
|||||||
- condition: time
|
- condition: time
|
||||||
after: '06:30:00'
|
after: '06:30:00'
|
||||||
before: '22:00:00'
|
before: '22:00:00'
|
||||||
|
- condition: state
|
||||||
|
entity_id: script.godnat_sovevaerelse
|
||||||
|
state: 'off'
|
||||||
action:
|
action:
|
||||||
- service: homeassistant.turn_off
|
- service: homeassistant.turn_off
|
||||||
data:
|
data:
|
||||||
|
|||||||
@@ -1,31 +1,91 @@
|
|||||||
- id: stue_motion_lys
|
- id: stue_motion_lys
|
||||||
alias: Stue lys via bevægelse
|
alias: Stue lys via bevaegelse
|
||||||
mode: restart
|
mode: restart
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
- platform: state
|
- platform: state
|
||||||
entity_id: binary_sensor.stue_bevaegelse
|
entity_id: binary_sensor.stue_bevaegelse
|
||||||
to: "on"
|
to: "on"
|
||||||
|
id: motion_on
|
||||||
|
|
||||||
condition:
|
- platform: state
|
||||||
- condition: numeric_state
|
entity_id: binary_sensor.stue_bevaegelse
|
||||||
entity_id: sensor.stue_belysningsstyrke
|
to: "off"
|
||||||
below: 60
|
id: motion_off
|
||||||
|
|
||||||
|
- platform: state
|
||||||
|
entity_id: media_player.samsung_s95ca_55_3
|
||||||
|
to: "off"
|
||||||
|
id: tv_off
|
||||||
|
|
||||||
|
variables:
|
||||||
|
lux_limit: "{{ states('input_number.stue_lux_threshold') | int }}"
|
||||||
|
dagperiode: >
|
||||||
|
{% set t = now().strftime('%H%M') | int %}
|
||||||
|
{% if 600 <= t < 1600 %}morgen
|
||||||
|
{% elif 1600 <= t < 1900 %}eftermiddag
|
||||||
|
{% elif 1900 <= t < 2100 %}aften_lys
|
||||||
|
{% elif 2100 <= t %}aften
|
||||||
|
{% else %}nat{% endif %}
|
||||||
|
timeout_min: >
|
||||||
|
{% set t = now().strftime('%H%M') | int %}
|
||||||
|
{% if 600 <= t < 1600 %}
|
||||||
|
{{ states('input_number.stue_timeout_morgen') | int }}
|
||||||
|
{% elif 1600 <= t < 1900 %}
|
||||||
|
{{ states('input_number.stue_timeout_eftermiddag') | int }}
|
||||||
|
{% elif 1900 <= t < 2100 %}
|
||||||
|
{{ states('input_number.stue_timeout_aften') | int }}
|
||||||
|
{% elif 2100 <= t %}
|
||||||
|
{{ states('input_number.stue_timeout_aften') | int }}
|
||||||
|
{% else %}
|
||||||
|
{{ states('input_number.stue_timeout_nat') | int }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
action:
|
action:
|
||||||
- choose:
|
- choose:
|
||||||
|
|
||||||
|
# Motion on: taend lys hvis lux lavt
|
||||||
- conditions:
|
- conditions:
|
||||||
- condition: time
|
- condition: trigger
|
||||||
after: "06:00:00"
|
id: motion_on
|
||||||
before: "16:00:00"
|
- condition: template
|
||||||
|
value_template: >
|
||||||
|
{{ states('sensor.stue_belysningsstyrke') | int < lux_limit }}
|
||||||
|
sequence:
|
||||||
|
- choose:
|
||||||
|
# Gæster: altid Annes favorit uanset tidspunkt
|
||||||
|
- conditions:
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.gaester
|
||||||
|
state: "on"
|
||||||
|
sequence:
|
||||||
|
- service: scene.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: scene.stue_annes_favorit
|
||||||
|
- conditions:
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ dagperiode == 'morgen' }}"
|
||||||
sequence:
|
sequence:
|
||||||
- service: scene.turn_on
|
- service: scene.turn_on
|
||||||
target:
|
target:
|
||||||
entity_id: scene.stue_bright
|
entity_id: scene.stue_bright
|
||||||
- conditions:
|
- conditions:
|
||||||
- condition: time
|
- condition: template
|
||||||
after: "16:00:00"
|
value_template: "{{ dagperiode == 'eftermiddag' }}"
|
||||||
before: "22:00:00"
|
sequence:
|
||||||
|
- service: scene.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: scene.stue_annes_favorit
|
||||||
|
- conditions:
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ dagperiode == 'aften_lys' }}"
|
||||||
|
sequence:
|
||||||
|
- service: scene.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: scene.stue_annes_favorit
|
||||||
|
- conditions:
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ dagperiode == 'aften' }}"
|
||||||
sequence:
|
sequence:
|
||||||
- service: scene.turn_on
|
- service: scene.turn_on
|
||||||
target:
|
target:
|
||||||
@@ -35,89 +95,38 @@
|
|||||||
target:
|
target:
|
||||||
entity_id: scene.stue_relax_minus_syd
|
entity_id: scene.stue_relax_minus_syd
|
||||||
|
|
||||||
- id: stue_motion_sluk_morgen
|
# Motion off: vent timeout, sluk hvis stadig ingen bevaegelse
|
||||||
alias: Sluk stue lys efter 60 min uden bevægelse om morgenen
|
# Aften: springer over hvis TV er taendt (TV-off trigger haandterer det)
|
||||||
mode: restart
|
- conditions:
|
||||||
|
- condition: trigger
|
||||||
trigger:
|
id: motion_off
|
||||||
- platform: state
|
- condition: template
|
||||||
entity_id: binary_sensor.stue_bevaegelse
|
value_template: >
|
||||||
to: "off"
|
{{ dagperiode not in ('aften','aften_lys') or
|
||||||
|
is_state('media_player.samsung_s95ca_55_3', 'off') }}
|
||||||
condition:
|
sequence:
|
||||||
- condition: time
|
- delay:
|
||||||
after: "06:00:00"
|
minutes: "{{ timeout_min }}"
|
||||||
before: "16:00:00"
|
|
||||||
|
|
||||||
action:
|
|
||||||
- wait_for_trigger:
|
|
||||||
- platform: state
|
|
||||||
entity_id: binary_sensor.stue_bevaegelse
|
|
||||||
to: "on"
|
|
||||||
timeout: "01:00:00"
|
|
||||||
continue_on_timeout: true
|
|
||||||
- condition: state
|
- condition: state
|
||||||
entity_id: binary_sensor.stue_bevaegelse
|
entity_id: binary_sensor.stue_bevaegelse
|
||||||
state: "off"
|
state: "off"
|
||||||
|
- condition: template
|
||||||
|
value_template: >
|
||||||
|
{{ dagperiode not in ('aften','aften_lys') or
|
||||||
|
is_state('media_player.samsung_s95ca_55_3', 'off') }}
|
||||||
- service: light.turn_off
|
- service: light.turn_off
|
||||||
target:
|
target:
|
||||||
entity_id: light.livingroom
|
entity_id: light.livingroom
|
||||||
|
|
||||||
- id: stue_motion_sluk_eftermiddag_aften
|
# TV slukket om aftenen: vent 10 min, sluk hvis ingen bevaegelse
|
||||||
alias: Sluk stue lys efter 120 min uden bevægelse mellem 16 og 19
|
- conditions:
|
||||||
mode: restart
|
- condition: trigger
|
||||||
|
id: tv_off
|
||||||
trigger:
|
- condition: template
|
||||||
- platform: state
|
value_template: "{{ dagperiode in ('aften','aften_lys') }}"
|
||||||
entity_id: binary_sensor.stue_bevaegelse
|
sequence:
|
||||||
to: "off"
|
- delay:
|
||||||
|
minutes: "{{ timeout_min }}"
|
||||||
condition:
|
|
||||||
- condition: time
|
|
||||||
after: "16:00:00"
|
|
||||||
before: "19:00:00"
|
|
||||||
|
|
||||||
action:
|
|
||||||
- wait_for_trigger:
|
|
||||||
- platform: state
|
|
||||||
entity_id: binary_sensor.stue_bevaegelse
|
|
||||||
to: "on"
|
|
||||||
timeout: "02:00:00"
|
|
||||||
continue_on_timeout: true
|
|
||||||
- condition: state
|
|
||||||
entity_id: binary_sensor.stue_bevaegelse
|
|
||||||
state: "off"
|
|
||||||
- service: light.turn_off
|
|
||||||
target:
|
|
||||||
entity_id: light.livingroom
|
|
||||||
|
|
||||||
- id: stue_motion_sluk_aften_tv
|
|
||||||
alias: Sluk stue lys efter 10 min uden bevægelse når TV er slukket mellem 19 og 00
|
|
||||||
mode: restart
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
- platform: state
|
|
||||||
entity_id: binary_sensor.stue_bevaegelse
|
|
||||||
to: "off"
|
|
||||||
- platform: state
|
|
||||||
entity_id: media_player.samsung_s95ca_55_3
|
|
||||||
to: "off"
|
|
||||||
|
|
||||||
condition:
|
|
||||||
- condition: time
|
|
||||||
after: "19:00:00"
|
|
||||||
before: "00:00:00"
|
|
||||||
|
|
||||||
action:
|
|
||||||
- condition: state
|
|
||||||
entity_id: media_player.samsung_s95ca_55_3
|
|
||||||
state: "off"
|
|
||||||
- wait_for_trigger:
|
|
||||||
- platform: state
|
|
||||||
entity_id: binary_sensor.stue_bevaegelse
|
|
||||||
to: "on"
|
|
||||||
timeout: "00:10:00"
|
|
||||||
continue_on_timeout: true
|
|
||||||
- condition: state
|
- condition: state
|
||||||
entity_id: binary_sensor.stue_bevaegelse
|
entity_id: binary_sensor.stue_bevaegelse
|
||||||
state: "off"
|
state: "off"
|
||||||
@@ -127,31 +136,3 @@
|
|||||||
- service: light.turn_off
|
- service: light.turn_off
|
||||||
target:
|
target:
|
||||||
entity_id: light.livingroom
|
entity_id: light.livingroom
|
||||||
|
|
||||||
- id: stue_motion_sluk_nat
|
|
||||||
alias: Sluk stue lys efter 30 min uden bevægelse om natten
|
|
||||||
mode: restart
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
- platform: state
|
|
||||||
entity_id: binary_sensor.stue_bevaegelse
|
|
||||||
to: "off"
|
|
||||||
|
|
||||||
condition:
|
|
||||||
- condition: time
|
|
||||||
after: "00:00:00"
|
|
||||||
before: "06:00:00"
|
|
||||||
|
|
||||||
action:
|
|
||||||
- wait_for_trigger:
|
|
||||||
- platform: state
|
|
||||||
entity_id: binary_sensor.stue_bevaegelse
|
|
||||||
to: "on"
|
|
||||||
timeout: "00:30:00"
|
|
||||||
continue_on_timeout: true
|
|
||||||
- condition: state
|
|
||||||
entity_id: binary_sensor.stue_bevaegelse
|
|
||||||
state: "off"
|
|
||||||
- service: light.turn_off
|
|
||||||
target:
|
|
||||||
entity_id: light.livingroom
|
|
||||||
@@ -16,6 +16,9 @@
|
|||||||
- service: scene.turn_on
|
- service: scene.turn_on
|
||||||
data:
|
data:
|
||||||
entity_id: scene.indkorsel_bright
|
entity_id: scene.indkorsel_bright
|
||||||
|
- service: switch.turn_on
|
||||||
|
data:
|
||||||
|
entity_id: switch.stik_indkorsel
|
||||||
|
|
||||||
- alias: 'Sluk lys indkørsel når der er lys nok'
|
- alias: 'Sluk lys indkørsel når der er lys nok'
|
||||||
trigger:
|
trigger:
|
||||||
@@ -30,6 +33,9 @@
|
|||||||
- service: light.turn_off
|
- service: light.turn_off
|
||||||
data:
|
data:
|
||||||
entity_id: light.indkorsel_2
|
entity_id: light.indkorsel_2
|
||||||
|
- service: switch.turn_off
|
||||||
|
data:
|
||||||
|
entity_id: switch.stik_indkorsel
|
||||||
|
|
||||||
- alias: 'Tænd lys indkørsel aften'
|
- alias: 'Tænd lys indkørsel aften'
|
||||||
trigger:
|
trigger:
|
||||||
@@ -43,6 +49,9 @@
|
|||||||
- service: scene.turn_on
|
- service: scene.turn_on
|
||||||
data:
|
data:
|
||||||
entity_id: scene.indkorsel_bright
|
entity_id: scene.indkorsel_bright
|
||||||
|
- service: switch.turn_on
|
||||||
|
data:
|
||||||
|
entity_id: switch.stik_indkorsel
|
||||||
|
|
||||||
- alias: 'Sluk lys indkørsel aften'
|
- alias: 'Sluk lys indkørsel aften'
|
||||||
trigger:
|
trigger:
|
||||||
@@ -52,6 +61,9 @@
|
|||||||
- service: light.turn_off
|
- service: light.turn_off
|
||||||
data:
|
data:
|
||||||
entity_id: light.indkorsel_2
|
entity_id: light.indkorsel_2
|
||||||
|
- service: switch.turn_off
|
||||||
|
data:
|
||||||
|
entity_id: switch.stik_indkorsel
|
||||||
|
|
||||||
|
|
||||||
- alias: 'Tænd lys indkørsel ved bevægelse'
|
- alias: 'Tænd lys indkørsel ved bevægelse'
|
||||||
@@ -78,6 +90,9 @@
|
|||||||
- service: scene.turn_on
|
- service: scene.turn_on
|
||||||
data:
|
data:
|
||||||
entity_id: scene.indkorsel_bright
|
entity_id: scene.indkorsel_bright
|
||||||
|
- service: switch.turn_on
|
||||||
|
data:
|
||||||
|
entity_id: switch.stik_indkorsel
|
||||||
|
|
||||||
- alias: 'Sluk lys indkørsel 15 min efter bevægelse'
|
- alias: 'Sluk lys indkørsel 15 min efter bevægelse'
|
||||||
trigger:
|
trigger:
|
||||||
@@ -97,8 +112,11 @@
|
|||||||
# entity_id: sun.sun
|
# entity_id: sun.sun
|
||||||
# state: below_horizon
|
# state: below_horizon
|
||||||
action:
|
action:
|
||||||
service: light.turn_off
|
- service: light.turn_off
|
||||||
data:
|
data:
|
||||||
entity_id: light.indkorsel_2
|
entity_id: light.indkorsel_2
|
||||||
|
- service: switch.turn_off
|
||||||
|
data:
|
||||||
|
entity_id: switch.stik_indkorsel
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,10 @@
|
|||||||
entity_id:
|
entity_id:
|
||||||
- light.drivhus
|
- light.drivhus
|
||||||
- light.paradis
|
- light.paradis
|
||||||
- light.extended_color_light_1
|
- condition: template
|
||||||
|
value_template: "{{ now().isocalendar()[1] >= 42 or now().isocalendar()[1] <= 8 }}"
|
||||||
|
- service: homeassistant.turn_on
|
||||||
|
entity_id: light.extended_color_light_1
|
||||||
|
|
||||||
- alias: 'Sluk lys ved garage'
|
- alias: 'Sluk lys ved garage'
|
||||||
trigger:
|
trigger:
|
||||||
@@ -29,12 +32,15 @@
|
|||||||
for:
|
for:
|
||||||
minutes: 10
|
minutes: 10
|
||||||
action:
|
action:
|
||||||
service: homeassistant.turn_off
|
- service: homeassistant.turn_off
|
||||||
data:
|
data:
|
||||||
entity_id:
|
entity_id:
|
||||||
- light.drivhus
|
- light.drivhus
|
||||||
- light.paradis
|
- light.paradis
|
||||||
- light.extended_color_light_1
|
- condition: template
|
||||||
|
value_template: "{{ now().isocalendar()[1] >= 42 or now().isocalendar()[1] <= 8 }}"
|
||||||
|
- service: homeassistant.turn_off
|
||||||
|
entity_id: light.extended_color_light_1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,15 @@
|
|||||||
|
- id: mealie_generate_bilka_checklist_wednesday
|
||||||
|
alias: "Mealie indkøbsliste - onsdag morgen"
|
||||||
|
trigger:
|
||||||
|
- platform: time
|
||||||
|
at: "06:30:00"
|
||||||
|
condition:
|
||||||
|
- condition: time
|
||||||
|
weekday:
|
||||||
|
- wed
|
||||||
|
action:
|
||||||
|
- service: script.mealie_shopping_refresh
|
||||||
|
|
||||||
- id: mealie_update_mealplan
|
- id: mealie_update_mealplan
|
||||||
alias: "Mealie opdater madplan"
|
alias: "Mealie opdater madplan"
|
||||||
trigger:
|
trigger:
|
||||||
@@ -7,3 +19,4 @@
|
|||||||
minutes: "/30"
|
minutes: "/30"
|
||||||
action:
|
action:
|
||||||
- service: shell_command.mealie_update
|
- service: shell_command.mealie_update
|
||||||
|
|
||||||
|
|||||||
@@ -94,11 +94,14 @@
|
|||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
- platform: time_pattern
|
- platform: time_pattern
|
||||||
minutes: "0"
|
minutes: "/15"
|
||||||
|
|
||||||
condition:
|
condition:
|
||||||
- condition: template
|
- condition: template
|
||||||
value_template: "{{ now().hour >= 19 and now().hour <= 21 }}"
|
value_template: "{{ now().hour >= 19 and now().hour <= 21 }}"
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.dishwasher_reminder_snoozed
|
||||||
|
state: "off"
|
||||||
- condition: template
|
- condition: template
|
||||||
value_template: "{{ is_state('sensor.dishwasher_status_2', 'Off') }}"
|
value_template: "{{ is_state('sensor.dishwasher_status_2', 'Off') }}"
|
||||||
- condition: or
|
- condition: or
|
||||||
@@ -108,22 +111,68 @@
|
|||||||
state: "off"
|
state: "off"
|
||||||
- condition: template
|
- condition: template
|
||||||
value_template: "{{ not is_state('binary_sensor.dishwasher_dor', 'off') }}"
|
value_template: "{{ not is_state('binary_sensor.dishwasher_dor', 'off') }}"
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ is_state('binary_sensor.dishwasher_info_2', 'on') }}"
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ is_state('binary_sensor.dishwasher_svigt', 'on') }}"
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ states('sensor.dishwasher_salt_level') | float(999) < 20 }}"
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ states('sensor.dishwasher_rinse_aid_level') | float(999) < 20 }}"
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ states('sensor.dishwasher_powerdisk_level') | float(999) < 20 }}"
|
||||||
|
|
||||||
action:
|
action:
|
||||||
- variables:
|
- variables:
|
||||||
reminder_time: "{{ now().strftime('%d-%m %H:%M') }}"
|
reminder_time: "{{ now().strftime('%H:%M') }}"
|
||||||
remote_off: "{{ is_state('binary_sensor.dishwasher_fjernbetjening', 'off') }}"
|
remote_off: "{{ is_state('binary_sensor.dishwasher_fjernbetjening', 'off') }}"
|
||||||
door_not_closed: "{{ not is_state('binary_sensor.dishwasher_dor', 'off') }}"
|
door_not_closed: "{{ not is_state('binary_sensor.dishwasher_dor', 'off') }}"
|
||||||
|
low_salt: "{{ states('sensor.dishwasher_salt_level') | float(999) < 20 }}"
|
||||||
|
low_rinse: "{{ states('sensor.dishwasher_rinse_aid_level') | float(999) < 20 }}"
|
||||||
|
low_powerdisk: "{{ states('sensor.dishwasher_powerdisk_level') | float(999) < 20 }}"
|
||||||
|
info_on: "{{ is_state('binary_sensor.dishwasher_info_2', 'on') }}"
|
||||||
|
svigt: "{{ is_state('binary_sensor.dishwasher_svigt', 'on') }}"
|
||||||
issue_text: >
|
issue_text: >
|
||||||
{% set issues = [] %}
|
{% set issues = [] %}
|
||||||
{% if remote_off %}
|
{% if remote_off %}{% set issues = issues + ['remote control er ikke slået til'] %}{% endif %}
|
||||||
{% set issues = issues + ['remote control er ikke slået til'] %}
|
{% if door_not_closed %}{% set issues = issues + ['døren er ikke lukket'] %}{% endif %}
|
||||||
{% endif %}
|
{% if low_salt %}{% set issues = issues + ['salt er lavt (' ~ states('sensor.dishwasher_salt_level') ~ '%)'] %}{% endif %}
|
||||||
{% if door_not_closed %}
|
{% if low_rinse %}{% set issues = issues + ['afspændingsmiddel er lavt (' ~ states('sensor.dishwasher_rinse_aid_level') ~ '%)'] %}{% endif %}
|
||||||
{% set issues = issues + ['døren er ikke lukket'] %}
|
{% if low_powerdisk %}{% set issues = issues + ['powerdisk er lav (' ~ states('sensor.dishwasher_powerdisk_level') ~ '%)'] %}{% endif %}
|
||||||
{% endif %}
|
{% if info_on %}{% set issues = issues + ['info-advarsel aktiv'] %}{% endif %}
|
||||||
{{ issues | join(' og ') }}
|
{% if svigt %}{% set issues = issues + ['maskinsvigt'] %}{% endif %}
|
||||||
|
{{ issues | join(', ') }}
|
||||||
- service: notify.mobile_app_claus_iphone_15pro
|
- service: notify.mobile_app_claus_iphone_15pro
|
||||||
data:
|
data:
|
||||||
title: "Slå fjernbetjening til på opvaskemaskinen"
|
title: "⚠️ Opvaskemaskine - tjek inden natkørsel"
|
||||||
message: "[{{ reminder_time }}] Opvaskemaskinen er planlagt til natkørsel, men {{ issue_text }}."
|
message: "[{{ reminder_time }}] {{ issue_text | capitalize }}."
|
||||||
|
data:
|
||||||
|
actions:
|
||||||
|
- action: "DISHWASHER_DONE"
|
||||||
|
title: "✅ Gjort det"
|
||||||
|
- action: "DISHWASHER_IGNORE"
|
||||||
|
title: "🔕 Ignorer i aften"
|
||||||
|
|
||||||
|
- id: dishwasher_reminder_action_ignore
|
||||||
|
alias: Opvaskemaskine - ignorer påmindelser i aften
|
||||||
|
mode: single
|
||||||
|
trigger:
|
||||||
|
- platform: event
|
||||||
|
event_type: mobile_app_notification_action
|
||||||
|
event_data:
|
||||||
|
action: DISHWASHER_IGNORE
|
||||||
|
action:
|
||||||
|
- service: input_boolean.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.dishwasher_reminder_snoozed
|
||||||
|
|
||||||
|
- id: dishwasher_reminder_snooze_reset
|
||||||
|
alias: Opvaskemaskine - nulstil snooze ved midnat
|
||||||
|
mode: single
|
||||||
|
trigger:
|
||||||
|
- platform: time
|
||||||
|
at: "00:00:00"
|
||||||
|
action:
|
||||||
|
- service: input_boolean.turn_off
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.dishwasher_reminder_snoozed
|
||||||
@@ -1,3 +1,16 @@
|
|||||||
|
- alias: 'Plæneklipper - opdater sidst klippet'
|
||||||
|
description: 'Opdater input_datetime når klipperen starter (uanset hvem der startede den)'
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: lawn_mower.husqvarna_automower
|
||||||
|
to: mowing
|
||||||
|
action:
|
||||||
|
- service: input_datetime.set_datetime
|
||||||
|
target:
|
||||||
|
entity_id: input_datetime.ploeneklipper_sidst_koert
|
||||||
|
data:
|
||||||
|
datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"
|
||||||
|
|
||||||
- alias: 'Plæneklipper - start arbejdsdag'
|
- alias: 'Plæneklipper - start arbejdsdag'
|
||||||
description: 'Start plæneklipper kl 9 på arbejdsdage hvis det ikke regner og ingen er hjemme'
|
description: 'Start plæneklipper kl 9 på arbejdsdage hvis det ikke regner og ingen er hjemme'
|
||||||
trigger:
|
trigger:
|
||||||
@@ -38,11 +51,6 @@
|
|||||||
- service: lawn_mower.start_mowing
|
- service: lawn_mower.start_mowing
|
||||||
target:
|
target:
|
||||||
entity_id: lawn_mower.husqvarna_automower
|
entity_id: lawn_mower.husqvarna_automower
|
||||||
- service: input_datetime.set_datetime
|
|
||||||
target:
|
|
||||||
entity_id: input_datetime.ploeneklipper_sidst_koert
|
|
||||||
data:
|
|
||||||
datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"
|
|
||||||
- service: notify.mobile_app_claus_iphone_15pro
|
- service: notify.mobile_app_claus_iphone_15pro
|
||||||
data:
|
data:
|
||||||
title: "Plæneklipper"
|
title: "Plæneklipper"
|
||||||
@@ -103,6 +111,9 @@
|
|||||||
- condition: state
|
- condition: state
|
||||||
entity_id: lawn_mower.husqvarna_automower
|
entity_id: lawn_mower.husqvarna_automower
|
||||||
state: 'mowing'
|
state: 'mowing'
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.ploeneklipper_manuelt_startet
|
||||||
|
state: 'off'
|
||||||
action:
|
action:
|
||||||
- service: lawn_mower.dock
|
- service: lawn_mower.dock
|
||||||
target:
|
target:
|
||||||
@@ -111,3 +122,74 @@
|
|||||||
data:
|
data:
|
||||||
title: "Plæneklipper"
|
title: "Plæneklipper"
|
||||||
message: "Klipperen er sendt hjem - {{ trigger.to_state.attributes.friendly_name }} kom hjem."
|
message: "Klipperen er sendt hjem - {{ trigger.to_state.attributes.friendly_name }} kom hjem."
|
||||||
|
|
||||||
|
- alias: 'Plæneklipper - reset manuelt startet flag'
|
||||||
|
description: 'Nulstil manuelt-startet flag når klipperen dokker efter kl. 20 (i dagtimerne håndteres genstarten af genstart-automation)'
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: lawn_mower.husqvarna_automower
|
||||||
|
to: 'docked'
|
||||||
|
condition:
|
||||||
|
- condition: time
|
||||||
|
after: '20:00:00'
|
||||||
|
action:
|
||||||
|
- service: input_boolean.turn_off
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.ploeneklipper_manuelt_startet
|
||||||
|
|
||||||
|
- alias: 'Plæneklipper - genstart efter opladning (manuelt startet)'
|
||||||
|
description: 'Genstart klipperen 75 min efter den er dokket, hvis den er manuelt startet og det er før kl. 20'
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: lawn_mower.husqvarna_automower
|
||||||
|
to: 'docked'
|
||||||
|
condition:
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.ploeneklipper_manuelt_startet
|
||||||
|
state: 'on'
|
||||||
|
- condition: time
|
||||||
|
before: '20:00:00'
|
||||||
|
action:
|
||||||
|
- delay: '01:15:00'
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.ploeneklipper_manuelt_startet
|
||||||
|
state: 'on'
|
||||||
|
- condition: time
|
||||||
|
before: '20:00:00'
|
||||||
|
- condition: state
|
||||||
|
entity_id: lawn_mower.husqvarna_automower
|
||||||
|
state: 'docked'
|
||||||
|
- service: lawn_mower.start_mowing
|
||||||
|
target:
|
||||||
|
entity_id: lawn_mower.husqvarna_automower
|
||||||
|
- service: notify.mobile_app_claus_iphone_15pro
|
||||||
|
data:
|
||||||
|
title: "Plæneklipper"
|
||||||
|
message: "Klipperen er genstartet efter opladning."
|
||||||
|
|
||||||
|
- alias: 'Plæneklipper - stop kl. 20 ved manuel start'
|
||||||
|
description: 'Stop manuelt startet klipper kl. 20 og nulstil flag'
|
||||||
|
trigger:
|
||||||
|
- platform: time
|
||||||
|
at: '20:00:00'
|
||||||
|
condition:
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.ploeneklipper_manuelt_startet
|
||||||
|
state: 'on'
|
||||||
|
action:
|
||||||
|
- choose:
|
||||||
|
- conditions:
|
||||||
|
- condition: state
|
||||||
|
entity_id: lawn_mower.husqvarna_automower
|
||||||
|
state: 'mowing'
|
||||||
|
sequence:
|
||||||
|
- service: lawn_mower.dock
|
||||||
|
target:
|
||||||
|
entity_id: lawn_mower.husqvarna_automower
|
||||||
|
- service: input_boolean.turn_off
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.ploeneklipper_manuelt_startet
|
||||||
|
- service: notify.mobile_app_claus_iphone_15pro
|
||||||
|
data:
|
||||||
|
title: "Plæneklipper"
|
||||||
|
message: "Klipperen er stoppet - kl. 20 grænse nået."
|
||||||
|
|||||||
@@ -54,6 +54,10 @@
|
|||||||
- delay:
|
- delay:
|
||||||
seconds: "{{ range(30,180) | random }}"
|
seconds: "{{ range(30,180) | random }}"
|
||||||
|
|
||||||
|
- service: switch.turn_on
|
||||||
|
data:
|
||||||
|
entity_id: switch.stik_indkorsel
|
||||||
|
|
||||||
- delay:
|
- delay:
|
||||||
minutes: "{{ range(10,30) | random }}"
|
minutes: "{{ range(10,30) | random }}"
|
||||||
|
|
||||||
@@ -66,6 +70,10 @@
|
|||||||
- delay:
|
- delay:
|
||||||
seconds: "{{ range(20,120) | random }}"
|
seconds: "{{ range(20,120) | random }}"
|
||||||
|
|
||||||
|
- service: switch.turn_off
|
||||||
|
data:
|
||||||
|
entity_id: switch.stik_indkorsel
|
||||||
|
|
||||||
- choose:
|
- choose:
|
||||||
- conditions:
|
- conditions:
|
||||||
- condition: template
|
- condition: template
|
||||||
@@ -146,6 +154,10 @@
|
|||||||
- delay:
|
- delay:
|
||||||
seconds: "{{ range(20,120) | random }}"
|
seconds: "{{ range(20,120) | random }}"
|
||||||
|
|
||||||
|
- service: switch.turn_on
|
||||||
|
data:
|
||||||
|
entity_id: switch.stik_indkorsel
|
||||||
|
|
||||||
- delay:
|
- delay:
|
||||||
minutes: "{{ range(15,60) | random }}"
|
minutes: "{{ range(15,60) | random }}"
|
||||||
|
|
||||||
@@ -158,6 +170,10 @@
|
|||||||
- delay:
|
- delay:
|
||||||
seconds: "{{ range(20,120) | random }}"
|
seconds: "{{ range(20,120) | random }}"
|
||||||
|
|
||||||
|
- service: switch.turn_off
|
||||||
|
data:
|
||||||
|
entity_id: switch.stik_indkorsel
|
||||||
|
|
||||||
- choose:
|
- choose:
|
||||||
- conditions:
|
- conditions:
|
||||||
- condition: template
|
- condition: template
|
||||||
|
|||||||
@@ -88,13 +88,13 @@
|
|||||||
target:
|
target:
|
||||||
entity_id: button.roborock_s8_pro_ultra_kokken_bryggers
|
entity_id: button.roborock_s8_pro_ultra_kokken_bryggers
|
||||||
|
|
||||||
- delay: "00:00:20"
|
- wait_template: "{{ is_state('vacuum.roborock_s8_pro_ultra', 'cleaning') }}"
|
||||||
|
timeout: "00:02:00"
|
||||||
|
|
||||||
- choose:
|
- choose:
|
||||||
- conditions:
|
- conditions:
|
||||||
- condition: state
|
- condition: template
|
||||||
entity_id: vacuum.roborock_s8_pro_ultra
|
value_template: "{{ wait.completed }}"
|
||||||
state: "cleaning"
|
|
||||||
sequence:
|
sequence:
|
||||||
- service: input_number.increment
|
- service: input_number.increment
|
||||||
target:
|
target:
|
||||||
@@ -109,17 +109,14 @@
|
|||||||
}} min.
|
}} min.
|
||||||
|
|
||||||
- conditions:
|
- conditions:
|
||||||
- condition: not
|
- condition: template
|
||||||
conditions:
|
value_template: "{{ not wait.completed }}"
|
||||||
- condition: state
|
|
||||||
entity_id: vacuum.roborock_s8_pro_ultra
|
|
||||||
state: "cleaning"
|
|
||||||
sequence:
|
sequence:
|
||||||
- service: notify.mobile_app_claus_iphone_15pro
|
- service: notify.mobile_app_claus_iphone_15pro
|
||||||
data:
|
data:
|
||||||
title: "⚠️ Roborock start fejlede"
|
title: "⚠️ Roborock start fejlede"
|
||||||
message: >
|
message: >
|
||||||
Startkommando sendt, men den begyndte ikke at køre.
|
Startkommando sendt, men den begyndte ikke at køre inden for 2 min.
|
||||||
State: {{ states('vacuum.roborock_s8_pro_ultra') }}.
|
State: {{ states('vacuum.roborock_s8_pro_ultra') }}.
|
||||||
Status: {{ state_attr('vacuum.roborock_s8_pro_ultra', 'status') | default('ukendt', true) }}.
|
Status: {{ state_attr('vacuum.roborock_s8_pro_ultra', 'status') | default('ukendt', true) }}.
|
||||||
Error: {{ state_attr('vacuum.roborock_s8_pro_ultra', 'error') | default('ingen', true) }}.
|
Error: {{ state_attr('vacuum.roborock_s8_pro_ultra', 'error') | default('ingen', true) }}.
|
||||||
@@ -153,6 +150,9 @@
|
|||||||
- condition: state
|
- condition: state
|
||||||
entity_id: vacuum.roborock_s8_pro_ultra
|
entity_id: vacuum.roborock_s8_pro_ultra
|
||||||
state: "cleaning"
|
state: "cleaning"
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.roborock_manuelt_startet
|
||||||
|
state: "off"
|
||||||
|
|
||||||
action:
|
action:
|
||||||
- service: vacuum.return_to_base
|
- service: vacuum.return_to_base
|
||||||
@@ -165,6 +165,21 @@
|
|||||||
message: "Rengøring stoppet fordi nogen er kommet hjem."
|
message: "Rengøring stoppet fordi nogen er kommet hjem."
|
||||||
|
|
||||||
|
|
||||||
|
# 🔄 Reset manuelt-startet flag når Roborock dokker
|
||||||
|
- id: roborock_reset_manuelt_flag
|
||||||
|
alias: Roborock - Reset manuelt startet flag
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: vacuum.roborock_s8_pro_ultra
|
||||||
|
to: "docked"
|
||||||
|
|
||||||
|
action:
|
||||||
|
- service: input_boolean.turn_off
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.roborock_manuelt_startet
|
||||||
|
|
||||||
|
|
||||||
# 🧹 Syd på arbejdsdage
|
# 🧹 Syd på arbejdsdage
|
||||||
- id: roborock_syd_workday_vacuum
|
- id: roborock_syd_workday_vacuum
|
||||||
alias: Roborock støvsug syd på arbejdsdage
|
alias: Roborock støvsug syd på arbejdsdage
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
- alias: 'Indkorsel: Slet gamle snapshots (behold 100)'
|
||||||
|
description: Køres via webhook fra galleriet – sletter alle undtagen de 100 nyeste snapshots og regenererer galleriet.
|
||||||
|
trigger:
|
||||||
|
- platform: webhook
|
||||||
|
webhook_id: indkorsel_prune_100
|
||||||
|
allowed_methods: [POST]
|
||||||
|
local_only: true
|
||||||
|
action:
|
||||||
|
- action: shell_command.indkorsel_prune_keep_100
|
||||||
|
- delay: '00:00:02'
|
||||||
|
- action: shell_command.indkorsel_generate_gallery
|
||||||
|
mode: single
|
||||||
|
|
||||||
|
- alias: 'Snapshot ved person i indkorsel'
|
||||||
|
description: >
|
||||||
|
Gemmer et tidsstemplet snapshot + opdaterer latest.jpg + regenererer HTML-galleri,
|
||||||
|
hver gang binary_sensor.indkoersel_person skifter til 'on'.
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: binary_sensor.indkoersel_person
|
||||||
|
to: 'on'
|
||||||
|
condition: []
|
||||||
|
action:
|
||||||
|
- variables:
|
||||||
|
ts: "{{ now().strftime('%Y-%m-%d_%H-%M-%S') }}"
|
||||||
|
# Gem tidsstemplet kopi
|
||||||
|
- action: camera.snapshot
|
||||||
|
data:
|
||||||
|
entity_id: camera.indkoersel_sub
|
||||||
|
filename: "/config/www/snapshots/indkorsel/{{ ts }}.jpg"
|
||||||
|
# Overskriv latest.jpg (bruges af local_file-kamera i dashboardet)
|
||||||
|
- action: camera.snapshot
|
||||||
|
data:
|
||||||
|
entity_id: camera.indkoersel_sub
|
||||||
|
filename: "/config/www/snapshots/indkorsel/latest.jpg"
|
||||||
|
# Regenerer HTML-galleriet
|
||||||
|
- action: shell_command.indkorsel_generate_gallery
|
||||||
|
mode: queued
|
||||||
|
max: 5
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
##################################################
|
||||||
|
# Syg-status: sluk alarmer + motion-lys ved sygdom
|
||||||
|
#
|
||||||
|
# Triggeres af input_select.PERSON_status → "syg"
|
||||||
|
# Genaktiverer alarmer når status ikke længere er "syg"
|
||||||
|
# Motion-lys håndteres via conditions i lys_*.yaml
|
||||||
|
##################################################
|
||||||
|
|
||||||
|
# ---- Andreas ----
|
||||||
|
# Andreas har ingen Sonos-alarmer, så der er intet at slå fra/til ved sygdom.
|
||||||
|
# Motion-lys håndteres via condition i lys_andreas.yaml (tjekker andreas_status != syg).
|
||||||
|
|
||||||
|
# ---- Daniel ----
|
||||||
|
|
||||||
|
- alias: "Syg - Daniel - sluk alarmer"
|
||||||
|
id: syg_daniel_sluk_alarmer
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: input_select.daniel_status
|
||||||
|
to: "syg"
|
||||||
|
action:
|
||||||
|
- service: homeassistant.turn_off
|
||||||
|
target:
|
||||||
|
entity_id:
|
||||||
|
- switch.sonos_alarm_377 # Daniel hverdagsalarm
|
||||||
|
- switch.sonos_alarm_1894 # Daniel afsted
|
||||||
|
- switch.sonos_alarm_2273 # Daniel man/fre
|
||||||
|
- switch.sonos_alarm_3471 # Daniel lørdag
|
||||||
|
|
||||||
|
- alias: "Syg - Daniel - genaktiver alarmer"
|
||||||
|
id: syg_daniel_genaktiver_alarmer
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: input_select.daniel_status
|
||||||
|
not_to: "syg"
|
||||||
|
condition:
|
||||||
|
- condition: state
|
||||||
|
entity_id: binary_sensor.arbejdsdagimorgen
|
||||||
|
state: "on"
|
||||||
|
action:
|
||||||
|
- service: homeassistant.turn_on
|
||||||
|
target:
|
||||||
|
entity_id:
|
||||||
|
- switch.sonos_alarm_377
|
||||||
|
- switch.sonos_alarm_3471
|
||||||
|
|
||||||
|
# ---- Anne / Claus (soveværelse) ----
|
||||||
|
|
||||||
|
- alias: "Syg - Soveværelse - sluk alarmer"
|
||||||
|
id: syg_sovevaerelse_sluk_alarmer
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: input_select.anne_status
|
||||||
|
to: "syg"
|
||||||
|
- platform: state
|
||||||
|
entity_id: input_select.claus_status
|
||||||
|
to: "syg"
|
||||||
|
action:
|
||||||
|
- service: homeassistant.turn_off
|
||||||
|
target:
|
||||||
|
entity_id:
|
||||||
|
- switch.sonos_alarm_1782 # Soft wakeup
|
||||||
|
- switch.sonos_alarm_298 # Badeværelse
|
||||||
|
|
||||||
|
- alias: "Syg - Soveværelse - genaktiver alarmer"
|
||||||
|
id: syg_sovevaerelse_genaktiver_alarmer
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: input_select.anne_status
|
||||||
|
not_to: "syg"
|
||||||
|
- platform: state
|
||||||
|
entity_id: input_select.claus_status
|
||||||
|
not_to: "syg"
|
||||||
|
condition:
|
||||||
|
# Kun genaktiver hvis BEGGE ikke er syge
|
||||||
|
- condition: template
|
||||||
|
value_template: >
|
||||||
|
{{ not is_state('input_select.anne_status', 'syg') and
|
||||||
|
not is_state('input_select.claus_status', 'syg') }}
|
||||||
|
- condition: state
|
||||||
|
entity_id: binary_sensor.arbejdsdagimorgen
|
||||||
|
state: "on"
|
||||||
|
action:
|
||||||
|
- service: homeassistant.turn_on
|
||||||
|
target:
|
||||||
|
entity_id:
|
||||||
|
- switch.sonos_alarm_1782
|
||||||
|
- switch.sonos_alarm_298
|
||||||
|
|
||||||
@@ -10,6 +10,8 @@
|
|||||||
action:
|
action:
|
||||||
- service: media_player.unjoin
|
- service: media_player.unjoin
|
||||||
entity_id: media_player.badevaerelse
|
entity_id: media_player.badevaerelse
|
||||||
|
- service: media_player.unjoin
|
||||||
|
entity_id: media_player.lille_badevaerelse
|
||||||
- service: media_player.unjoin
|
- service: media_player.unjoin
|
||||||
entity_id: media_player.sovevaerelse
|
entity_id: media_player.sovevaerelse
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,2 @@
|
|||||||
### Set temperature to 24 in heat mode
|
### Kontor-varme styres nu af script.varme_recalculate (include/scripts/varme_styring.yaml)
|
||||||
- alias: "Tænd varme i kontor"
|
### Denne fil er beholdt tom for fremtidige manuelle overrides
|
||||||
trigger:
|
|
||||||
platform: time
|
|
||||||
at: "19:30:00"
|
|
||||||
action:
|
|
||||||
- service: climate.set_temperature
|
|
||||||
target:
|
|
||||||
entity_id: climate.kontor
|
|
||||||
data:
|
|
||||||
temperature: 24
|
|
||||||
hvac_mode: heat
|
|
||||||
|
|||||||
@@ -0,0 +1,112 @@
|
|||||||
|
##################################################
|
||||||
|
# Varme: Triggers der kalder script.varme_recalculate
|
||||||
|
# Scenarierne der kræver genberegning:
|
||||||
|
# - Morgen (input_datetime.varme_morgen_tid) - nat slut → komforttemp
|
||||||
|
# - Aften (input_datetime.varme_aften_tid) - nat start → natsænkning
|
||||||
|
# - Tilstedeværelse - hjemme/væk skifter
|
||||||
|
# - Ferietilstand - ferie til/fra
|
||||||
|
# - HA genstart - genopret korrekt temp
|
||||||
|
##################################################
|
||||||
|
|
||||||
|
- alias: "Varme - Genberegn: Morgen"
|
||||||
|
id: varme_recalc_morgen
|
||||||
|
description: "Slutter natsænkning - tidspunkt styret af input_datetime.varme_morgen_tid"
|
||||||
|
trigger:
|
||||||
|
- platform: time
|
||||||
|
at: input_datetime.varme_morgen_tid
|
||||||
|
action:
|
||||||
|
- service: script.varme_recalculate
|
||||||
|
|
||||||
|
- alias: "Varme - Genberegn: Nat"
|
||||||
|
id: varme_recalc_nat
|
||||||
|
description: "Starter natsænkning - tidspunkt styret af input_datetime.varme_aften_tid"
|
||||||
|
trigger:
|
||||||
|
- platform: time
|
||||||
|
at: input_datetime.varme_aften_tid
|
||||||
|
action:
|
||||||
|
- service: script.varme_recalculate
|
||||||
|
|
||||||
|
- alias: "Varme - Genberegn: Tilstedeværelse"
|
||||||
|
id: varme_recalc_presence
|
||||||
|
description: "Justerer temperaturer når family_presence ændres (hjemme/væk)"
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: binary_sensor.family_presence
|
||||||
|
action:
|
||||||
|
# Kort forsinkelse så person-sensorer er stabile
|
||||||
|
- delay: "00:01:00"
|
||||||
|
- service: script.varme_recalculate
|
||||||
|
|
||||||
|
- alias: "Varme - Genberegn: Ferie"
|
||||||
|
id: varme_recalc_vacation
|
||||||
|
description: "Skifter til ferie-/frosttemperatur når vacation_mode ændres"
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: input_boolean.vacation_mode
|
||||||
|
action:
|
||||||
|
- service: script.varme_recalculate
|
||||||
|
|
||||||
|
- alias: "Varme - Genberegn: HA genstart"
|
||||||
|
id: varme_recalc_ha_start
|
||||||
|
description: "Genopret korrekte temperaturer efter HA genstart"
|
||||||
|
trigger:
|
||||||
|
- platform: homeassistant
|
||||||
|
event: start
|
||||||
|
action:
|
||||||
|
# Vent til integrationer er loaded
|
||||||
|
- delay: "00:01:00"
|
||||||
|
- service: script.varme_recalculate
|
||||||
|
|
||||||
|
- alias: "Varme - Genberegn: Komforttemperatur ændret"
|
||||||
|
id: varme_recalc_komfort_changed
|
||||||
|
description: "Opdater varmeanlægget straks når en komforttemperatur justeres"
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id:
|
||||||
|
- input_number.varme_komfort_andreas
|
||||||
|
- input_number.varme_komfort_daniel
|
||||||
|
- input_number.varme_komfort_sovevaerelse
|
||||||
|
- input_number.varme_komfort_kontor
|
||||||
|
- input_number.varme_komfort_gang
|
||||||
|
- input_number.varme_komfort_forgang
|
||||||
|
- input_number.varme_komfort_lille_bad
|
||||||
|
- input_number.varme_komfort_badevarelse
|
||||||
|
- input_number.varme_komfort_stue
|
||||||
|
action:
|
||||||
|
- service: script.varme_recalculate
|
||||||
|
|
||||||
|
- alias: "Varme - Ferietilstand: Aktiver ved afrejse"
|
||||||
|
id: varme_ferie_aktiver
|
||||||
|
description: "Slår vacation_mode til automatisk på afrejsetidspunktet (vacation_start)"
|
||||||
|
trigger:
|
||||||
|
- platform: time
|
||||||
|
at: input_datetime.vacation_start
|
||||||
|
action:
|
||||||
|
- service: input_boolean.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.vacation_mode
|
||||||
|
|
||||||
|
- alias: "Varme - Ferieopvarmning: Start 2 dage før hjemkomst"
|
||||||
|
id: varme_ferie_forvarm
|
||||||
|
description: >
|
||||||
|
Slår vacation_mode fra 2 dage inden vacation_end så huset er
|
||||||
|
varmt ved hjemkomst. Kører dagligt ved morgen-tidspunktet.
|
||||||
|
trigger:
|
||||||
|
- platform: time
|
||||||
|
at: input_datetime.varme_morgen_tid
|
||||||
|
condition:
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.vacation_mode
|
||||||
|
state: "on"
|
||||||
|
- condition: template
|
||||||
|
value_template: >
|
||||||
|
{% set end = states('input_datetime.vacation_end') %}
|
||||||
|
{% if end not in ['unknown', 'unavailable', ''] %}
|
||||||
|
{{ 0 < (as_timestamp(end) - as_timestamp(now())) < (2 * 86400) }}
|
||||||
|
{% else %}
|
||||||
|
false
|
||||||
|
{% endif %}
|
||||||
|
action:
|
||||||
|
- service: input_boolean.turn_off
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.vacation_mode
|
||||||
@@ -2,14 +2,14 @@
|
|||||||
# Vindue-automationer (Aqara vinduesensorer)
|
# Vindue-automationer (Aqara vinduesensorer)
|
||||||
##################################################
|
##################################################
|
||||||
|
|
||||||
### Varme: Sluk varme når vindue åbnes, tænd igen når det lukkes
|
### Varme: Genberegn ved vindue-ændring (åbner og lukker)
|
||||||
### Rum-mapping: andreas, daniel, sovevaerelse, lille_bad
|
### Script slukker klimaenhed hvis vindue er åbent, tænder igen ved lukning
|
||||||
|
|
||||||
- alias: "Varme - sluk ved åbent vindue"
|
- alias: "Varme - vindue åbner eller lukker"
|
||||||
id: varme_sluk_ved_aabent_vindue
|
id: varme_vindue_trigger
|
||||||
description: "Slukker varme i rummet når vinduet åbnes og genstarter når det lukkes"
|
description: "Kalder varme_recalculate når et vindue eller terrassedøren skifter tilstand"
|
||||||
mode: parallel
|
mode: queued
|
||||||
max: 4
|
max: 10
|
||||||
trigger:
|
trigger:
|
||||||
- platform: state
|
- platform: state
|
||||||
entity_id:
|
entity_id:
|
||||||
@@ -17,33 +17,14 @@
|
|||||||
- binary_sensor.daniel_vindue
|
- binary_sensor.daniel_vindue
|
||||||
- binary_sensor.sovevaerelse_vindue
|
- binary_sensor.sovevaerelse_vindue
|
||||||
- binary_sensor.lille_bad_vindue
|
- binary_sensor.lille_bad_vindue
|
||||||
to: "on"
|
- binary_sensor.badevaerelse_vindue
|
||||||
|
- binary_sensor.terrassedor
|
||||||
|
to:
|
||||||
|
- "on"
|
||||||
|
- "off"
|
||||||
|
for: "00:00:05"
|
||||||
action:
|
action:
|
||||||
- variables:
|
- service: script.varme_recalculate
|
||||||
room_map:
|
|
||||||
binary_sensor.andreas_vindue: climate.andreas
|
|
||||||
binary_sensor.daniel_vindue: climate.daniel
|
|
||||||
binary_sensor.sovevaerelse_vindue: climate.sovev_prelse
|
|
||||||
binary_sensor.lille_bad_vindue: climate.lille_bad
|
|
||||||
climate_entity: "{{ room_map[trigger.entity_id] }}"
|
|
||||||
window_entity: "{{ trigger.entity_id }}"
|
|
||||||
scene_name: "vindue_varme_{{ trigger.entity_id.split('.')[1] }}"
|
|
||||||
- service: scene.create
|
|
||||||
data:
|
|
||||||
scene_id: "{{ scene_name }}"
|
|
||||||
snapshot_entities:
|
|
||||||
- "{{ climate_entity }}"
|
|
||||||
- service: climate.set_hvac_mode
|
|
||||||
target:
|
|
||||||
entity_id: "{{ climate_entity }}"
|
|
||||||
data:
|
|
||||||
hvac_mode: "off"
|
|
||||||
- wait_template: "{{ is_state(window_entity, 'off') }}"
|
|
||||||
timeout: "04:00:00"
|
|
||||||
continue_on_timeout: true
|
|
||||||
- service: scene.turn_on
|
|
||||||
target:
|
|
||||||
entity_id: "scene.{{ scene_name }}"
|
|
||||||
|
|
||||||
### Notifikation: Vindue åbner og ingen er hjemme
|
### Notifikation: Vindue åbner og ingen er hjemme
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
badevaerelse_manuel_tilstand:
|
||||||
|
name: Badeværelse manuel tilstand
|
||||||
|
initial: off
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
gaester:
|
||||||
|
name: "Gæster hjemme"
|
||||||
|
icon: mdi:account-group
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
vis_alle_vedligehold:
|
vis_alle_vedligehold:
|
||||||
name: Vis alle vedligehold
|
name: Vis alle vedligehold
|
||||||
icon: mdi:eye-outline
|
icon: mdi:eye-outline
|
||||||
|
|
||||||
|
dishwasher_reminder_snoozed:
|
||||||
|
name: Opvaskemaskine - påmindelser snoozed
|
||||||
|
icon: mdi:bell-sleep
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
roborock_manuelt_startet:
|
||||||
|
name: Roborock manuelt startet
|
||||||
|
icon: mdi:robot-vacuum
|
||||||
|
|
||||||
|
ploeneklipper_manuelt_startet:
|
||||||
|
name: Plæneklipper manuelt startet
|
||||||
|
icon: mdi:robot-mower
|
||||||
@@ -1,5 +1,12 @@
|
|||||||
|
vacation_start:
|
||||||
|
name: Vacation Start
|
||||||
|
has_date: true
|
||||||
|
has_time: true
|
||||||
|
initial: "2026-07-13 10:00:00"
|
||||||
|
|
||||||
vacation_end:
|
vacation_end:
|
||||||
name: Vacation End
|
name: Vacation End
|
||||||
has_date: true
|
has_date: true
|
||||||
has_time: true
|
has_time: true
|
||||||
|
initial: "2026-07-27 12:00:00"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
varme_morgen_tid:
|
||||||
|
name: Varme - Morgen (nat slut)
|
||||||
|
has_date: false
|
||||||
|
has_time: true
|
||||||
|
icon: mdi:weather-sunny
|
||||||
|
|
||||||
|
varme_aften_tid:
|
||||||
|
name: Varme - Aften (nat start)
|
||||||
|
has_date: false
|
||||||
|
has_time: true
|
||||||
|
icon: mdi:weather-night
|
||||||
@@ -4,7 +4,7 @@ badevaerelse_timeout_day:
|
|||||||
max: 30
|
max: 30
|
||||||
step: 1
|
step: 1
|
||||||
unit_of_measurement: min
|
unit_of_measurement: min
|
||||||
initial: 10
|
initial: 5
|
||||||
|
|
||||||
badevaerelse_timeout_night:
|
badevaerelse_timeout_night:
|
||||||
name: Badeværelse lys timeout nat
|
name: Badeværelse lys timeout nat
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
daniel_lux_threshold:
|
||||||
|
name: Daniel lux grænse
|
||||||
|
min: 0
|
||||||
|
max: 500
|
||||||
|
step: 5
|
||||||
|
unit_of_measurement: lux
|
||||||
|
initial: 90
|
||||||
|
|
||||||
|
daniel_timeout:
|
||||||
|
name: Daniel lys timeout
|
||||||
|
min: 1
|
||||||
|
max: 60
|
||||||
|
step: 1
|
||||||
|
unit_of_measurement: min
|
||||||
|
initial: 10
|
||||||
|
|
||||||
|
daniel_brightness:
|
||||||
|
name: Daniel lysstyrke
|
||||||
|
min: 1
|
||||||
|
max: 100
|
||||||
|
step: 1
|
||||||
|
unit_of_measurement: "%"
|
||||||
|
initial: 100
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
gang_lux_threshold:
|
||||||
|
name: Gang lux grænse
|
||||||
|
min: 0
|
||||||
|
max: 300
|
||||||
|
step: 5
|
||||||
|
unit_of_measurement: lux
|
||||||
|
initial: 70
|
||||||
|
|
||||||
|
gang_timeout_day:
|
||||||
|
name: Gang timeout dag
|
||||||
|
min: 1
|
||||||
|
max: 20
|
||||||
|
step: 1
|
||||||
|
unit_of_measurement: min
|
||||||
|
initial: 3
|
||||||
|
|
||||||
|
gang_timeout_night:
|
||||||
|
name: Gang timeout nat
|
||||||
|
min: 1
|
||||||
|
max: 10
|
||||||
|
step: 1
|
||||||
|
unit_of_measurement: min
|
||||||
|
initial: 1
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
kontor_lux_threshold:
|
||||||
|
name: Kontor lux grænse
|
||||||
|
min: 0
|
||||||
|
max: 500
|
||||||
|
step: 5
|
||||||
|
unit_of_measurement: lux
|
||||||
|
initial: 60
|
||||||
|
|
||||||
|
kontor_timeout_day:
|
||||||
|
name: Kontor timeout dag
|
||||||
|
min: 1
|
||||||
|
max: 60
|
||||||
|
step: 1
|
||||||
|
unit_of_measurement: min
|
||||||
|
initial: 5
|
||||||
|
|
||||||
|
kontor_timeout_night:
|
||||||
|
name: Kontor timeout nat
|
||||||
|
min: 1
|
||||||
|
max: 30
|
||||||
|
step: 1
|
||||||
|
unit_of_measurement: min
|
||||||
|
initial: 2
|
||||||
@@ -4,7 +4,7 @@ shelly_bagdor_event_cnt:
|
|||||||
max: 99999
|
max: 99999
|
||||||
step: 1
|
step: 1
|
||||||
mode: box
|
mode: box
|
||||||
initial: -1
|
initial: 67
|
||||||
|
|
||||||
shelly_fordor_event_cnt:
|
shelly_fordor_event_cnt:
|
||||||
name: Shelly fordoer event count
|
name: Shelly fordoer event count
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
stue_lux_threshold:
|
||||||
|
name: Stue lux grænse
|
||||||
|
min: 0
|
||||||
|
max: 500
|
||||||
|
step: 5
|
||||||
|
unit_of_measurement: lux
|
||||||
|
initial: 60
|
||||||
|
|
||||||
|
stue_timeout_morgen:
|
||||||
|
name: Stue timeout morgen (06-16)
|
||||||
|
min: 10
|
||||||
|
max: 180
|
||||||
|
step: 5
|
||||||
|
unit_of_measurement: min
|
||||||
|
initial: 30
|
||||||
|
|
||||||
|
stue_timeout_eftermiddag:
|
||||||
|
name: Stue timeout eftermiddag (16-19)
|
||||||
|
min: 10
|
||||||
|
max: 240
|
||||||
|
step: 5
|
||||||
|
unit_of_measurement: min
|
||||||
|
initial: 120
|
||||||
|
|
||||||
|
stue_timeout_aften:
|
||||||
|
name: Stue timeout aften (19-00, TV slukket)
|
||||||
|
min: 1
|
||||||
|
max: 60
|
||||||
|
step: 1
|
||||||
|
unit_of_measurement: min
|
||||||
|
initial: 5
|
||||||
|
|
||||||
|
stue_timeout_nat:
|
||||||
|
name: Stue timeout nat (00-06)
|
||||||
|
min: 5
|
||||||
|
max: 120
|
||||||
|
step: 5
|
||||||
|
unit_of_measurement: min
|
||||||
|
initial: 5
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
# Komforttemperaturer per rum (Roth gulvvarme)
|
||||||
|
varme_komfort_andreas:
|
||||||
|
name: Komfort - Andreas
|
||||||
|
min: 15
|
||||||
|
max: 28
|
||||||
|
step: 0.5
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
initial: 19
|
||||||
|
icon: mdi:thermometer
|
||||||
|
|
||||||
|
varme_komfort_daniel:
|
||||||
|
name: Komfort - Daniel
|
||||||
|
min: 15
|
||||||
|
max: 28
|
||||||
|
step: 0.5
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
initial: 19
|
||||||
|
icon: mdi:thermometer
|
||||||
|
|
||||||
|
varme_komfort_sovevaerelse:
|
||||||
|
name: Komfort - Soveværelse
|
||||||
|
min: 15
|
||||||
|
max: 28
|
||||||
|
step: 0.5
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
initial: 20
|
||||||
|
icon: mdi:thermometer
|
||||||
|
|
||||||
|
varme_komfort_kontor:
|
||||||
|
name: Komfort - Kontor
|
||||||
|
min: 15
|
||||||
|
max: 28
|
||||||
|
step: 0.5
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
initial: 20
|
||||||
|
icon: mdi:thermometer
|
||||||
|
|
||||||
|
varme_komfort_gang:
|
||||||
|
name: Komfort - Gang
|
||||||
|
min: 15
|
||||||
|
max: 28
|
||||||
|
step: 0.5
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
initial: 20
|
||||||
|
icon: mdi:thermometer
|
||||||
|
|
||||||
|
varme_komfort_forgang:
|
||||||
|
name: Komfort - Forgang
|
||||||
|
min: 15
|
||||||
|
max: 28
|
||||||
|
step: 0.5
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
initial: 24
|
||||||
|
icon: mdi:thermometer
|
||||||
|
|
||||||
|
varme_komfort_lille_bad:
|
||||||
|
name: Komfort - Lille bad
|
||||||
|
min: 15
|
||||||
|
max: 28
|
||||||
|
step: 0.5
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
initial: 24
|
||||||
|
icon: mdi:thermometer
|
||||||
|
|
||||||
|
varme_komfort_badevarelse:
|
||||||
|
name: Komfort - Badeværelse
|
||||||
|
min: 15
|
||||||
|
max: 28
|
||||||
|
step: 0.5
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
initial: 21.5
|
||||||
|
icon: mdi:thermometer
|
||||||
|
|
||||||
|
varme_komfort_stue:
|
||||||
|
name: Komfort - Stue
|
||||||
|
min: 15
|
||||||
|
max: 28
|
||||||
|
step: 0.5
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
initial: 25
|
||||||
|
icon: mdi:thermometer
|
||||||
|
|
||||||
|
# Globale sænkninger
|
||||||
|
varme_nat_saenkning:
|
||||||
|
name: Natsænkning
|
||||||
|
min: 0
|
||||||
|
max: 8
|
||||||
|
step: 0.5
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
initial: 3
|
||||||
|
icon: mdi:weather-night
|
||||||
|
|
||||||
|
varme_vaek_saenkning:
|
||||||
|
name: Sænkning - ingen hjemme
|
||||||
|
min: 0
|
||||||
|
max: 8
|
||||||
|
step: 0.5
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
initial: 3
|
||||||
|
icon: mdi:home-export-outline
|
||||||
|
|
||||||
|
varme_ferie_temp:
|
||||||
|
name: Ferietemperatur (frostbeskyttelse)
|
||||||
|
min: 10
|
||||||
|
max: 18
|
||||||
|
step: 0.5
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
initial: 11
|
||||||
|
icon: mdi:beach
|
||||||
@@ -105,7 +105,6 @@
|
|||||||
name: Indkørsel
|
name: Indkørsel
|
||||||
unique_id: lys_indkorsel
|
unique_id: lys_indkorsel
|
||||||
entities:
|
entities:
|
||||||
- light.indkorsel_plug
|
|
||||||
- light.udendors_forgang
|
- light.udendors_forgang
|
||||||
- light.hue_ambiance_lamp_1_2
|
- light.hue_ambiance_lamp_1_2
|
||||||
- light.hue_ambiance_lamp_1_3
|
- light.hue_ambiance_lamp_1_3
|
||||||
@@ -116,7 +115,6 @@
|
|||||||
unique_id: lys_udenfor
|
unique_id: lys_udenfor
|
||||||
entities:
|
entities:
|
||||||
- light.garage
|
- light.garage
|
||||||
- light.indkorsel_plug
|
|
||||||
- light.fordoer
|
- light.fordoer
|
||||||
- light.julelys
|
- light.julelys
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,54 @@
|
|||||||
|
godnat_sovevaerelse:
|
||||||
|
alias: Godnat - dæmp lys i soveværelse over 15 min
|
||||||
|
mode: restart
|
||||||
|
sequence:
|
||||||
|
- service: light.turn_on
|
||||||
|
target:
|
||||||
|
entity_id:
|
||||||
|
- light.claus
|
||||||
|
- light.anne
|
||||||
|
- light.sov_dor
|
||||||
|
- light.sov_midt
|
||||||
|
- light.sov_vindue
|
||||||
|
data:
|
||||||
|
brightness_pct: 40
|
||||||
|
transition: 300
|
||||||
|
- delay: "00:05:00"
|
||||||
|
- service: light.turn_on
|
||||||
|
target:
|
||||||
|
entity_id:
|
||||||
|
- light.claus
|
||||||
|
- light.anne
|
||||||
|
- light.sov_dor
|
||||||
|
- light.sov_midt
|
||||||
|
- light.sov_vindue
|
||||||
|
data:
|
||||||
|
brightness_pct: 10
|
||||||
|
transition: 300
|
||||||
|
- delay: "00:05:00"
|
||||||
|
- service: light.turn_on
|
||||||
|
target:
|
||||||
|
entity_id:
|
||||||
|
- light.claus
|
||||||
|
- light.anne
|
||||||
|
- light.sov_dor
|
||||||
|
- light.sov_midt
|
||||||
|
- light.sov_vindue
|
||||||
|
data:
|
||||||
|
brightness_pct: 1
|
||||||
|
transition: 300
|
||||||
|
- delay: "00:05:00"
|
||||||
|
- service: light.turn_off
|
||||||
|
target:
|
||||||
|
entity_id:
|
||||||
|
- light.claus
|
||||||
|
- light.anne
|
||||||
|
- light.sov_dor
|
||||||
|
- light.sov_midt
|
||||||
|
- light.sov_vindue
|
||||||
|
data:
|
||||||
|
transition: 5
|
||||||
|
|
||||||
bed_standard_colors:
|
bed_standard_colors:
|
||||||
sequence:
|
sequence:
|
||||||
- service: light.turn_on
|
- service: light.turn_on
|
||||||
|
|||||||
@@ -10,53 +10,13 @@ doorbell:
|
|||||||
sequence:
|
sequence:
|
||||||
- parallel:
|
- parallel:
|
||||||
- sequence:
|
- sequence:
|
||||||
- choose:
|
- variables:
|
||||||
- conditions:
|
lille_bad_volumen: "{{ volumennat if (now().hour >= 20 or now().hour < 6) else volumendag }}"
|
||||||
- condition: time
|
lille_bad_lydfil: "{{ 'doorbell-shortened-100308.mp3' if (now().hour >= 20 or now().hour < 6) else 'doorbell.mp3' }}"
|
||||||
after: '20:00:00'
|
- service: script.spil_paa_lille_bad
|
||||||
before: '06:00:00'
|
|
||||||
sequence:
|
|
||||||
- service: media_player.volume_set
|
|
||||||
data:
|
data:
|
||||||
volume_level: "{{ volumennat }}"
|
lydfil: "{{ lille_bad_lydfil }}"
|
||||||
target:
|
volumen: "{{ lille_bad_volumen }}"
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
- service: media_player.play_media
|
|
||||||
target:
|
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
data:
|
|
||||||
media_content_id: media-source://media_source/local/doorbell-shortened-100308.mp3
|
|
||||||
media_content_type: audio/mpeg
|
|
||||||
metadata:
|
|
||||||
title: doorbell-shortened-100308.mp3
|
|
||||||
thumbnail:
|
|
||||||
media_class: music
|
|
||||||
children_media_class:
|
|
||||||
navigateIds:
|
|
||||||
- {}
|
|
||||||
- media_content_type: app
|
|
||||||
media_content_id: media-source://media_source
|
|
||||||
default:
|
|
||||||
- service: media_player.volume_set
|
|
||||||
data:
|
|
||||||
volume_level: "{{ volumendag }}"
|
|
||||||
target:
|
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
- service: media_player.play_media
|
|
||||||
target:
|
|
||||||
entity_id: media_player.lille_badevaerelse
|
|
||||||
data:
|
|
||||||
media_content_id: media-source://media_source/local/doorbell.mp3
|
|
||||||
media_content_type: audio/mpeg
|
|
||||||
metadata:
|
|
||||||
title: doorbell.mp3
|
|
||||||
thumbnail:
|
|
||||||
media_class: music
|
|
||||||
children_media_class:
|
|
||||||
navigateIds:
|
|
||||||
- {}
|
|
||||||
- media_content_type: app
|
|
||||||
media_content_id: media-source://media_source
|
|
||||||
- sequence:
|
- sequence:
|
||||||
- service: media_player.volume_set
|
- service: media_player.volume_set
|
||||||
data:
|
data:
|
||||||
|
|||||||
@@ -1,3 +1,105 @@
|
|||||||
|
vi_laver_mad:
|
||||||
|
alias: Vi laver mad
|
||||||
|
sequence:
|
||||||
|
- choose:
|
||||||
|
- conditions:
|
||||||
|
- condition: state
|
||||||
|
entity_id: person.andreas_schusler_dethlefsen
|
||||||
|
state: home
|
||||||
|
sequence:
|
||||||
|
- service: notify.mobile_app_andreas_iphone_12
|
||||||
|
data:
|
||||||
|
message: >-
|
||||||
|
{% set meal = states('sensor.dagens_aftensmad') %}
|
||||||
|
{% if meal and meal not in ['unknown','unavailable','Ingen planlagt'] %}
|
||||||
|
Vi laver mad! I dag: {{ meal }}
|
||||||
|
{% else %}
|
||||||
|
Vi laver mad!
|
||||||
|
{% endif %}
|
||||||
|
- choose:
|
||||||
|
- conditions:
|
||||||
|
- condition: state
|
||||||
|
entity_id: person.daniel_schusler_dethlefsen
|
||||||
|
state: home
|
||||||
|
sequence:
|
||||||
|
- service: notify.mobile_app_daniels_iphone_13_mini
|
||||||
|
data:
|
||||||
|
message: >-
|
||||||
|
{% set meal = states('sensor.dagens_aftensmad') %}
|
||||||
|
{% if meal and meal not in ['unknown','unavailable','Ingen planlagt'] %}
|
||||||
|
Vi laver mad! I dag: {{ meal }}
|
||||||
|
{% else %}
|
||||||
|
Vi laver mad!
|
||||||
|
{% endif %}
|
||||||
|
- choose:
|
||||||
|
- conditions:
|
||||||
|
- condition: state
|
||||||
|
entity_id: person.andreas_schusler_dethlefsen
|
||||||
|
state: home
|
||||||
|
sequence:
|
||||||
|
- service: sonos.snapshot
|
||||||
|
data:
|
||||||
|
entity_id: media_player.andreas
|
||||||
|
with_group: true
|
||||||
|
- service: media_player.media_stop
|
||||||
|
target:
|
||||||
|
entity_id: media_player.andreas
|
||||||
|
- service: media_player.volume_set
|
||||||
|
target:
|
||||||
|
entity_id: media_player.andreas
|
||||||
|
data:
|
||||||
|
volume_level: 0.35
|
||||||
|
- service: tts.speak
|
||||||
|
target:
|
||||||
|
entity_id: tts.google_ai_tts
|
||||||
|
data:
|
||||||
|
media_player_entity_id: media_player.andreas
|
||||||
|
message: >-
|
||||||
|
{% set meal = states('sensor.dagens_aftensmad') %}
|
||||||
|
{% if meal and meal not in ['unknown','unavailable','Ingen planlagt'] %}
|
||||||
|
Vi laver mad! I dag spiser vi {{ meal }}
|
||||||
|
{% else %}
|
||||||
|
Vi laver mad!
|
||||||
|
{% endif %}
|
||||||
|
- delay: "00:00:08"
|
||||||
|
- service: sonos.restore
|
||||||
|
data:
|
||||||
|
entity_id: media_player.andreas
|
||||||
|
- choose:
|
||||||
|
- conditions:
|
||||||
|
- condition: state
|
||||||
|
entity_id: person.daniel_schusler_dethlefsen
|
||||||
|
state: home
|
||||||
|
sequence:
|
||||||
|
- service: sonos.snapshot
|
||||||
|
data:
|
||||||
|
entity_id: media_player.daniel
|
||||||
|
with_group: true
|
||||||
|
- service: media_player.media_stop
|
||||||
|
target:
|
||||||
|
entity_id: media_player.daniel
|
||||||
|
- service: media_player.volume_set
|
||||||
|
target:
|
||||||
|
entity_id: media_player.daniel
|
||||||
|
data:
|
||||||
|
volume_level: 0.35
|
||||||
|
- service: tts.speak
|
||||||
|
target:
|
||||||
|
entity_id: tts.google_ai_tts
|
||||||
|
data:
|
||||||
|
media_player_entity_id: media_player.daniel
|
||||||
|
message: >-
|
||||||
|
{% set meal = states('sensor.dagens_aftensmad') %}
|
||||||
|
{% if meal and meal not in ['unknown','unavailable','Ingen planlagt'] %}
|
||||||
|
Vi laver mad! I dag spiser vi {{ meal }}
|
||||||
|
{% else %}
|
||||||
|
Vi laver mad!
|
||||||
|
{% endif %}
|
||||||
|
- delay: "00:00:08"
|
||||||
|
- service: sonos.restore
|
||||||
|
data:
|
||||||
|
entity_id: media_player.daniel
|
||||||
|
|
||||||
mad_announcement:
|
mad_announcement:
|
||||||
alias: Der er mad
|
alias: Der er mad
|
||||||
sequence:
|
sequence:
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
mealie_shopping_refresh:
|
||||||
|
alias: Mealie shopping refresh
|
||||||
|
sequence:
|
||||||
|
- service: shell_command.mealie_shopping_merge
|
||||||
|
- service: notify.mobile_app_claus_iphone_15pro
|
||||||
|
data:
|
||||||
|
title: "Indkøbsliste opdateret i Mealie"
|
||||||
|
message: "Indkøbslisten 'Bilka ToGo' er opdateret med opskrifter fra fredag til torsdag."
|
||||||
@@ -24,12 +24,6 @@ monthly_standard_colors:
|
|||||||
color_name: 'Gold'
|
color_name: 'Gold'
|
||||||
brightness_pct: 100
|
brightness_pct: 100
|
||||||
transition: 300
|
transition: 300
|
||||||
- service: light.turn_on
|
|
||||||
data:
|
|
||||||
entity_id: light.syd
|
|
||||||
color_name: 'Gold'
|
|
||||||
brightness_pct: 80
|
|
||||||
transition: 300
|
|
||||||
- service: light.turn_on
|
- service: light.turn_on
|
||||||
data:
|
data:
|
||||||
entity_id: light.spisebord
|
entity_id: light.spisebord
|
||||||
@@ -63,12 +57,6 @@ monthly_valentine_colors:
|
|||||||
color_name: 'Crimson'
|
color_name: 'Crimson'
|
||||||
brightness_pct: 100
|
brightness_pct: 100
|
||||||
transition: 900
|
transition: 900
|
||||||
- service: light.turn_on
|
|
||||||
data:
|
|
||||||
entity_id: light.syd
|
|
||||||
color_name: 'Crimson'
|
|
||||||
brightness_pct: 100
|
|
||||||
transition: 900
|
|
||||||
- service: light.turn_on
|
- service: light.turn_on
|
||||||
data:
|
data:
|
||||||
entity_id: light.spisebord
|
entity_id: light.spisebord
|
||||||
@@ -102,12 +90,6 @@ monthly_mardi_gras_colors:
|
|||||||
color_name: 'Purple'
|
color_name: 'Purple'
|
||||||
brightness_pct: 100
|
brightness_pct: 100
|
||||||
transition: 900
|
transition: 900
|
||||||
- service: light.turn_on
|
|
||||||
data:
|
|
||||||
entity_id: light.syd
|
|
||||||
color_name: 'Green'
|
|
||||||
brightness_pct: 100
|
|
||||||
transition: 900
|
|
||||||
- service: light.turn_on
|
- service: light.turn_on
|
||||||
data:
|
data:
|
||||||
entity_id: light.spisebord
|
entity_id: light.spisebord
|
||||||
@@ -141,12 +123,6 @@ monthly_pi_colors:
|
|||||||
rgb_color: [3,14,159]
|
rgb_color: [3,14,159]
|
||||||
brightness_pct: 100
|
brightness_pct: 100
|
||||||
transition: 900
|
transition: 900
|
||||||
- service: light.turn_on
|
|
||||||
data:
|
|
||||||
entity_id: light.syd
|
|
||||||
rgb_color: [3,14,159]
|
|
||||||
brightness_pct: 100
|
|
||||||
transition: 900
|
|
||||||
- service: light.turn_on
|
- service: light.turn_on
|
||||||
data:
|
data:
|
||||||
entity_id: light.spisebord
|
entity_id: light.spisebord
|
||||||
@@ -180,12 +156,6 @@ monthly_st_patty_colors:
|
|||||||
color_name: 'Green'
|
color_name: 'Green'
|
||||||
brightness_pct: 100
|
brightness_pct: 100
|
||||||
transition: 900
|
transition: 900
|
||||||
- service: light.turn_on
|
|
||||||
data:
|
|
||||||
entity_id: light.syd
|
|
||||||
color_name: 'Green'
|
|
||||||
brightness_pct: 100
|
|
||||||
transition: 900
|
|
||||||
- service: light.turn_on
|
- service: light.turn_on
|
||||||
data:
|
data:
|
||||||
entity_id: light.spisebord
|
entity_id: light.spisebord
|
||||||
@@ -219,12 +189,6 @@ monthly_easter_colors:
|
|||||||
color_name: 'Yellow'
|
color_name: 'Yellow'
|
||||||
brightness_pct: 100
|
brightness_pct: 100
|
||||||
transition: 900
|
transition: 900
|
||||||
- service: light.turn_on
|
|
||||||
data:
|
|
||||||
entity_id: light.syd
|
|
||||||
rgb_color: [255,193,204]
|
|
||||||
brightness_pct: 100
|
|
||||||
transition: 900
|
|
||||||
- service: light.turn_on
|
- service: light.turn_on
|
||||||
data:
|
data:
|
||||||
entity_id: light.spisebord
|
entity_id: light.spisebord
|
||||||
@@ -258,12 +222,6 @@ monthly_starwars_colors:
|
|||||||
rgb_color: [204,0,0]
|
rgb_color: [204,0,0]
|
||||||
brightness_pct: 100
|
brightness_pct: 100
|
||||||
transition: 900
|
transition: 900
|
||||||
- service: light.turn_on
|
|
||||||
data:
|
|
||||||
entity_id: light.syd
|
|
||||||
rgb_color: [245,245,245]
|
|
||||||
brightness_pct: 100
|
|
||||||
transition: 900
|
|
||||||
- service: light.turn_on
|
- service: light.turn_on
|
||||||
data:
|
data:
|
||||||
entity_id: light.spisebord
|
entity_id: light.spisebord
|
||||||
@@ -297,12 +255,6 @@ monthly_cinco_de_mayo_colors:
|
|||||||
rgb_color: [204,0,0]
|
rgb_color: [204,0,0]
|
||||||
brightness_pct: 100
|
brightness_pct: 100
|
||||||
transition: 900
|
transition: 900
|
||||||
- service: light.turn_on
|
|
||||||
data:
|
|
||||||
entity_id: light.syd
|
|
||||||
rgb_color: [245,245,245]
|
|
||||||
brightness_pct: 100
|
|
||||||
transition: 900
|
|
||||||
- service: light.turn_on
|
- service: light.turn_on
|
||||||
data:
|
data:
|
||||||
entity_id: light.spisebord
|
entity_id: light.spisebord
|
||||||
@@ -336,12 +288,6 @@ monthly_mothers_day_colors:
|
|||||||
rgb_color: [244,187,255]
|
rgb_color: [244,187,255]
|
||||||
brightness_pct: 100
|
brightness_pct: 100
|
||||||
transition: 900
|
transition: 900
|
||||||
- service: light.turn_on
|
|
||||||
data:
|
|
||||||
entity_id: light.syd
|
|
||||||
rgb_color: [244,187,255]
|
|
||||||
brightness_pct: 100
|
|
||||||
transition: 900
|
|
||||||
- service: light.turn_on
|
- service: light.turn_on
|
||||||
data:
|
data:
|
||||||
entity_id: light.spisebord
|
entity_id: light.spisebord
|
||||||
@@ -375,12 +321,6 @@ monthly_fathers_day_colors:
|
|||||||
color_name: 'Orange'
|
color_name: 'Orange'
|
||||||
brightness_pct: 100
|
brightness_pct: 100
|
||||||
transition: 900
|
transition: 900
|
||||||
- service: light.turn_on
|
|
||||||
data:
|
|
||||||
entity_id: light.syd
|
|
||||||
color_name: 'Blue'
|
|
||||||
brightness_pct: 100
|
|
||||||
transition: 900
|
|
||||||
- service: light.turn_on
|
- service: light.turn_on
|
||||||
data:
|
data:
|
||||||
entity_id: light.spisebord
|
entity_id: light.spisebord
|
||||||
@@ -414,12 +354,6 @@ monthly_halloween_colors:
|
|||||||
rgb_color: [235,97,35]
|
rgb_color: [235,97,35]
|
||||||
brightness_pct: 100
|
brightness_pct: 100
|
||||||
transition: 900
|
transition: 900
|
||||||
- service: light.turn_on
|
|
||||||
data:
|
|
||||||
entity_id: light.syd
|
|
||||||
rgb_color: [235,97,35]
|
|
||||||
brightness_pct: 100
|
|
||||||
transition: 900
|
|
||||||
- service: light.turn_on
|
- service: light.turn_on
|
||||||
data:
|
data:
|
||||||
entity_id: light.spisebord
|
entity_id: light.spisebord
|
||||||
@@ -453,12 +387,6 @@ monthly_thanksgiving_colors:
|
|||||||
color_name: 'Orange'
|
color_name: 'Orange'
|
||||||
brightness_pct: 100
|
brightness_pct: 100
|
||||||
transition: 900
|
transition: 900
|
||||||
- service: light.turn_on
|
|
||||||
data:
|
|
||||||
entity_id: light.syd
|
|
||||||
color_name: 'Orange'
|
|
||||||
brightness_pct: 100
|
|
||||||
transition: 900
|
|
||||||
- service: light.turn_on
|
- service: light.turn_on
|
||||||
data:
|
data:
|
||||||
entity_id: light.spisebord
|
entity_id: light.spisebord
|
||||||
@@ -492,12 +420,6 @@ monthly_christmas_colors:
|
|||||||
color_name: 'Red'
|
color_name: 'Red'
|
||||||
brightness_pct: 100
|
brightness_pct: 100
|
||||||
transition: 900
|
transition: 900
|
||||||
- service: light.turn_on
|
|
||||||
data:
|
|
||||||
entity_id: light.syd
|
|
||||||
color_name: 'Green'
|
|
||||||
brightness_pct: 100
|
|
||||||
transition: 900
|
|
||||||
- service: light.turn_on
|
- service: light.turn_on
|
||||||
data:
|
data:
|
||||||
entity_id: light.spisebord
|
entity_id: light.spisebord
|
||||||
@@ -531,12 +453,6 @@ monthly_new_years_day_colors:
|
|||||||
color_name: 'Blue'
|
color_name: 'Blue'
|
||||||
brightness_pct: 100
|
brightness_pct: 100
|
||||||
transition: 900
|
transition: 900
|
||||||
- service: light.turn_on
|
|
||||||
data:
|
|
||||||
entity_id: light.syd
|
|
||||||
color_name: 'Yellow'
|
|
||||||
brightness_pct: 100
|
|
||||||
transition: 900
|
|
||||||
- service: light.turn_on
|
- service: light.turn_on
|
||||||
data:
|
data:
|
||||||
entity_id: light.spisebord
|
entity_id: light.spisebord
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ overvaagning:
|
|||||||
device_id: cf4f218aae515c84aea9f37f190dcfd5
|
device_id: cf4f218aae515c84aea9f37f190dcfd5
|
||||||
enabled: true
|
enabled: true
|
||||||
action: camera.snapshot
|
action: camera.snapshot
|
||||||
|
- action: homeassistant.update_entity
|
||||||
|
data:
|
||||||
|
entity_id: camera.indkorsel_snapshot
|
||||||
|
enabled: true
|
||||||
- delay:
|
- delay:
|
||||||
hours: 0
|
hours: 0
|
||||||
minutes: 0
|
minutes: 0
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
roborock_manuelt_kokken:
|
||||||
|
alias: "Roborock: Start køkken/bryggers manuelt"
|
||||||
|
sequence:
|
||||||
|
- service: input_boolean.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.roborock_manuelt_startet
|
||||||
|
- service: button.press
|
||||||
|
target:
|
||||||
|
entity_id: button.roborock_s8_pro_ultra_kokken_bryggers
|
||||||
|
|
||||||
|
roborock_manuelt_syd:
|
||||||
|
alias: "Roborock: Start syd manuelt"
|
||||||
|
sequence:
|
||||||
|
- service: input_boolean.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.roborock_manuelt_startet
|
||||||
|
- service: button.press
|
||||||
|
target:
|
||||||
|
entity_id: button.roborock_s8_pro_ultra_syd
|
||||||
|
|
||||||
|
roborock_manuelt_mop:
|
||||||
|
alias: "Roborock: Start mop manuelt"
|
||||||
|
sequence:
|
||||||
|
- service: input_boolean.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.roborock_manuelt_startet
|
||||||
|
- service: button.press
|
||||||
|
target:
|
||||||
|
entity_id: button.roborock_s8_pro_ultra_vac_followed_by_mop
|
||||||
|
|
||||||
|
roborock_manuelt_start:
|
||||||
|
alias: "Roborock: Start alt manuelt"
|
||||||
|
sequence:
|
||||||
|
- service: input_boolean.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.roborock_manuelt_startet
|
||||||
|
- service: vacuum.start
|
||||||
|
target:
|
||||||
|
entity_id: vacuum.roborock_s8_pro_ultra
|
||||||
|
|
||||||
|
ploeneklipper_manuelt_start:
|
||||||
|
alias: "Plæneklipper: Start manuelt"
|
||||||
|
sequence:
|
||||||
|
- service: input_boolean.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.ploeneklipper_manuelt_startet
|
||||||
|
- service: lawn_mower.start_mowing
|
||||||
|
target:
|
||||||
|
entity_id: lawn_mower.husqvarna_automower
|
||||||
|
|
||||||
|
ploeneklipper_manuelt_stop:
|
||||||
|
alias: "Plæneklipper: Stop manuelt"
|
||||||
|
sequence:
|
||||||
|
- service: input_boolean.turn_off
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.ploeneklipper_manuelt_startet
|
||||||
|
- service: lawn_mower.dock
|
||||||
|
target:
|
||||||
|
entity_id: lawn_mower.husqvarna_automower
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
spil_paa_lille_bad:
|
||||||
|
alias: "Spil lyd på lille bad og gendan volumen bagefter"
|
||||||
|
fields:
|
||||||
|
lydfil:
|
||||||
|
description: Filnavn (fx doorbell.mp3)
|
||||||
|
example: doorbell.mp3
|
||||||
|
volumen:
|
||||||
|
description: Volumen der spilles ved (0.0-1.0)
|
||||||
|
example: 0.8
|
||||||
|
sequence:
|
||||||
|
- variables:
|
||||||
|
gammel_volumen: "{{ state_attr('media_player.lille_badevaerelse', 'volume_level') | default(0.3) }}"
|
||||||
|
- service: media_player.volume_set
|
||||||
|
data:
|
||||||
|
volume_level: "{{ volumen }}"
|
||||||
|
target:
|
||||||
|
entity_id: media_player.lille_badevaerelse
|
||||||
|
- service: media_player.play_media
|
||||||
|
target:
|
||||||
|
entity_id: media_player.lille_badevaerelse
|
||||||
|
data:
|
||||||
|
media_content_id: "media-source://media_source/local/{{ lydfil }}"
|
||||||
|
media_content_type: audio/mpeg
|
||||||
|
- delay: "00:00:02"
|
||||||
|
- wait_template: "{{ states('media_player.lille_badevaerelse') not in ['playing'] }}"
|
||||||
|
timeout: "00:01:30"
|
||||||
|
- service: media_player.volume_set
|
||||||
|
data:
|
||||||
|
volume_level: "{{ gammel_volumen }}"
|
||||||
|
target:
|
||||||
|
entity_id: media_player.lille_badevaerelse
|
||||||
@@ -0,0 +1,240 @@
|
|||||||
|
##################################################
|
||||||
|
# Varme: Central genberegning af alle Roth-rum
|
||||||
|
# Kaldes af automations/varme_styring.yaml ved:
|
||||||
|
# - Morgen (06:30), Nat (22:00)
|
||||||
|
# - Tilstedeværelse ændret
|
||||||
|
# - Ferietilstand ændret
|
||||||
|
# - HA genstart
|
||||||
|
# Logik pr. rum:
|
||||||
|
# - Vindue åbent → skip (håndteres af vinduer.yaml)
|
||||||
|
# - Ferie → varme_ferie_temp (frostbeskyttelse)
|
||||||
|
# - Nat (aften_tid–morgen_tid) → komfort - varme_nat_saenkning
|
||||||
|
# - Ingen hjemme → komfort - varme_vaek_saenkning
|
||||||
|
# - Ellers → rum-komforttemperatur
|
||||||
|
##################################################
|
||||||
|
|
||||||
|
varme_recalculate:
|
||||||
|
alias: "Varme: Genberegn alle rum"
|
||||||
|
mode: restart
|
||||||
|
sequence:
|
||||||
|
- variables:
|
||||||
|
vacation: "{{ is_state('input_boolean.vacation_mode', 'on') }}"
|
||||||
|
night: >
|
||||||
|
{% set m = states('input_datetime.varme_morgen_tid') %}
|
||||||
|
{% set a = states('input_datetime.varme_aften_tid') %}
|
||||||
|
{% set t = now().strftime('%H:%M:%S') %}
|
||||||
|
{{ t >= a or t < m }}
|
||||||
|
home: "{{ is_state('binary_sensor.family_presence', 'on') }}"
|
||||||
|
nat_sænk: "{{ states('input_number.varme_nat_saenkning') | float(3) }}"
|
||||||
|
vaek_sænk: "{{ states('input_number.varme_vaek_saenkning') | float(3) }}"
|
||||||
|
ferie_temp: "{{ states('input_number.varme_ferie_temp') | float(15) }}"
|
||||||
|
|
||||||
|
# Sæt alle rum til manuel tilstand (preset: none) så Roth-tidsprogram ikke overstyrer
|
||||||
|
- service: climate.set_preset_mode
|
||||||
|
target:
|
||||||
|
entity_id:
|
||||||
|
- climate.andreas
|
||||||
|
- climate.daniel
|
||||||
|
- climate.sovev_prelse
|
||||||
|
- climate.kontor
|
||||||
|
- climate.fordelingsgang
|
||||||
|
- climate.forgang
|
||||||
|
- climate.lille_bad
|
||||||
|
data:
|
||||||
|
preset_mode: "none"
|
||||||
|
|
||||||
|
# ---- Andreas (vindue: binary_sensor.andreas_vindue) ----
|
||||||
|
- if:
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ not is_state('binary_sensor.andreas_vindue', 'on') }}"
|
||||||
|
then:
|
||||||
|
- service: climate.set_temperature
|
||||||
|
target:
|
||||||
|
entity_id: climate.andreas
|
||||||
|
data:
|
||||||
|
temperature: >
|
||||||
|
{% set k = states('input_number.varme_komfort_andreas') | float(20) %}
|
||||||
|
{% if vacation %} {{ ferie_temp }}
|
||||||
|
{% elif night %} {{ [k - nat_sænk, 15] | max }}
|
||||||
|
{% elif not home %} {{ [k - vaek_sænk, 15] | max }}
|
||||||
|
{% else %} {{ k }}
|
||||||
|
{% endif %}
|
||||||
|
else:
|
||||||
|
- service: climate.set_temperature
|
||||||
|
target:
|
||||||
|
entity_id: climate.andreas
|
||||||
|
data:
|
||||||
|
temperature: "{{ ferie_temp }}"
|
||||||
|
|
||||||
|
# ---- Daniel (vindue: binary_sensor.daniel_vindue) ----
|
||||||
|
- if:
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ not is_state('binary_sensor.daniel_vindue', 'on') }}"
|
||||||
|
then:
|
||||||
|
- service: climate.set_temperature
|
||||||
|
target:
|
||||||
|
entity_id: climate.daniel
|
||||||
|
data:
|
||||||
|
temperature: >
|
||||||
|
{% set k = states('input_number.varme_komfort_daniel') | float(20) %}
|
||||||
|
{% if vacation %} {{ ferie_temp }}
|
||||||
|
{% elif night %} {{ [k - nat_sænk, 15] | max }}
|
||||||
|
{% elif not home %} {{ [k - vaek_sænk, 15] | max }}
|
||||||
|
{% else %} {{ k }}
|
||||||
|
{% endif %}
|
||||||
|
else:
|
||||||
|
- service: climate.set_temperature
|
||||||
|
target:
|
||||||
|
entity_id: climate.daniel
|
||||||
|
data:
|
||||||
|
temperature: "{{ ferie_temp }}"
|
||||||
|
|
||||||
|
# ---- Soveværelse (vindue: binary_sensor.sovevaerelse_vindue) ----
|
||||||
|
- if:
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ not is_state('binary_sensor.sovevaerelse_vindue', 'on') }}"
|
||||||
|
then:
|
||||||
|
- service: climate.set_temperature
|
||||||
|
target:
|
||||||
|
entity_id: climate.sovev_prelse
|
||||||
|
data:
|
||||||
|
temperature: >
|
||||||
|
{% set k = states('input_number.varme_komfort_sovevaerelse') | float(18) %}
|
||||||
|
{% if vacation %} {{ ferie_temp }}
|
||||||
|
{% elif night %} {{ [k - nat_sænk, 15] | max }}
|
||||||
|
{% elif not home %} {{ [k - vaek_sænk, 15] | max }}
|
||||||
|
{% else %} {{ k }}
|
||||||
|
{% endif %}
|
||||||
|
else:
|
||||||
|
- service: climate.set_temperature
|
||||||
|
target:
|
||||||
|
entity_id: climate.sovev_prelse
|
||||||
|
data:
|
||||||
|
temperature: "{{ ferie_temp }}"
|
||||||
|
|
||||||
|
# ---- Kontor (ingen vinduesensor) ----
|
||||||
|
- service: climate.set_temperature
|
||||||
|
target:
|
||||||
|
entity_id: climate.kontor
|
||||||
|
data:
|
||||||
|
temperature: >
|
||||||
|
{% set k = states('input_number.varme_komfort_kontor') | float(21) %}
|
||||||
|
{% if vacation %} {{ ferie_temp }}
|
||||||
|
{% elif night %} {{ [k - nat_sænk, 15] | max }}
|
||||||
|
{% elif not home %} {{ [k - vaek_sænk, 15] | max }}
|
||||||
|
{% else %} {{ k }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# ---- Gang / Fordelingsgang (ingen vinduesensor) ----
|
||||||
|
- service: climate.set_temperature
|
||||||
|
target:
|
||||||
|
entity_id: climate.fordelingsgang
|
||||||
|
data:
|
||||||
|
temperature: >
|
||||||
|
{% set k = states('input_number.varme_komfort_gang') | float(18) %}
|
||||||
|
{% if vacation %} {{ ferie_temp }}
|
||||||
|
{% elif night %} {{ [k - nat_sænk, 15] | max }}
|
||||||
|
{% elif not home %} {{ [k - vaek_sænk, 15] | max }}
|
||||||
|
{% else %} {{ k }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# ---- Forgang (ingen vinduesensor) ----
|
||||||
|
- service: climate.set_temperature
|
||||||
|
target:
|
||||||
|
entity_id: climate.forgang
|
||||||
|
data:
|
||||||
|
temperature: >
|
||||||
|
{% set k = states('input_number.varme_komfort_forgang') | float(17) %}
|
||||||
|
{% if vacation %} {{ ferie_temp }}
|
||||||
|
{% elif night %} {{ [k - nat_sænk, 15] | max }}
|
||||||
|
{% elif not home %} {{ [k - vaek_sænk, 15] | max }}
|
||||||
|
{% else %} {{ k }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# ---- Lille bad (vindue: binary_sensor.lille_bad_vindue) ----
|
||||||
|
- if:
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ not is_state('binary_sensor.lille_bad_vindue', 'on') }}"
|
||||||
|
then:
|
||||||
|
- service: climate.set_temperature
|
||||||
|
target:
|
||||||
|
entity_id: climate.lille_bad
|
||||||
|
data:
|
||||||
|
temperature: >
|
||||||
|
{% set k = states('input_number.varme_komfort_lille_bad') | float(22) %}
|
||||||
|
{% if vacation %} {{ ferie_temp }}
|
||||||
|
{% elif night %} {{ [k - nat_sænk, 15] | max }}
|
||||||
|
{% elif not home %} {{ [k - vaek_sænk, 15] | max }}
|
||||||
|
{% else %} {{ k }}
|
||||||
|
{% endif %}
|
||||||
|
else:
|
||||||
|
- service: climate.set_temperature
|
||||||
|
target:
|
||||||
|
entity_id: climate.lille_bad
|
||||||
|
data:
|
||||||
|
temperature: "{{ ferie_temp }}"
|
||||||
|
|
||||||
|
# ---- Badeværelse – Danfoss Ally (vindue: binary_sensor.badevaerelse_vindue) ----
|
||||||
|
- if:
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ not is_state('binary_sensor.badevaerelse_vindue', 'on') }}"
|
||||||
|
then:
|
||||||
|
- service: climate.set_temperature
|
||||||
|
target:
|
||||||
|
entity_id: climate.badevarelse
|
||||||
|
data:
|
||||||
|
hvac_mode: heat
|
||||||
|
temperature: >
|
||||||
|
{% set k = states('input_number.varme_komfort_badevarelse') | float(20) %}
|
||||||
|
{% if vacation %} {{ ferie_temp }}
|
||||||
|
{% elif night %} {{ [k - nat_sænk, 15] | max }}
|
||||||
|
{% elif not home %} {{ [k - vaek_sænk, 15] | max }}
|
||||||
|
{% else %} {{ k }}
|
||||||
|
{% endif %}
|
||||||
|
else:
|
||||||
|
- service: climate.set_temperature
|
||||||
|
target:
|
||||||
|
entity_id: climate.badevarelse
|
||||||
|
data:
|
||||||
|
hvac_mode: heat
|
||||||
|
temperature: "{{ ferie_temp }}"
|
||||||
|
|
||||||
|
# ---- Stue – Danfoss Ally (vindue: binary_sensor.terrassedor) ----
|
||||||
|
- if:
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ not is_state('binary_sensor.terrassedor', 'on') }}"
|
||||||
|
then:
|
||||||
|
- service: climate.set_temperature
|
||||||
|
target:
|
||||||
|
entity_id: climate.stue
|
||||||
|
data:
|
||||||
|
hvac_mode: heat
|
||||||
|
temperature: >
|
||||||
|
{% set k = states('input_number.varme_komfort_stue') | float(24) %}
|
||||||
|
{% if vacation %} {{ ferie_temp }}
|
||||||
|
{% elif night %} {{ [k - nat_sænk, 15] | max }}
|
||||||
|
{% elif not home %} {{ [k - vaek_sænk, 15] | max }}
|
||||||
|
{% else %} {{ k }}
|
||||||
|
{% endif %}
|
||||||
|
else:
|
||||||
|
- service: climate.set_temperature
|
||||||
|
target:
|
||||||
|
entity_id: climate.stue
|
||||||
|
data:
|
||||||
|
hvac_mode: heat
|
||||||
|
temperature: "{{ ferie_temp }}"
|
||||||
|
|
||||||
|
|
||||||
|
varme_save_defaults:
|
||||||
|
alias: Gem varme-standardværdier
|
||||||
|
icon: mdi:content-save
|
||||||
|
sequence:
|
||||||
|
- action: shell_command.varme_save_defaults
|
||||||
|
response_variable: result
|
||||||
|
- action: persistent_notification.create
|
||||||
|
data:
|
||||||
|
title: Varme-standardværdier gemt
|
||||||
|
message: >
|
||||||
|
{{ result.stdout if result.returncode == 0
|
||||||
|
else 'Fejl: ' ~ result.stderr }}
|
||||||
|
notification_id: varme_save_defaults
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
##################################################
|
||||||
|
# Glidende gennemsnit af anbefalet fjernvarme-ventilposition
|
||||||
|
# Beregner mean over de seneste 21 dage, så anbefalingen
|
||||||
|
# bevæger sig sæsonmæssigt (uger) i stedet for dagligt.
|
||||||
|
##################################################
|
||||||
|
|
||||||
|
- platform: statistics
|
||||||
|
name: "Fjernvarme ventil 3 ugers gennemsnit"
|
||||||
|
entity_id: sensor.fjernvarme_ventil_anbefalet
|
||||||
|
state_characteristic: mean
|
||||||
|
sampling_size: 2000
|
||||||
|
max_age:
|
||||||
|
days: 21
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
indkorsel_generate_gallery: "/usr/local/bin/docker exec homeassistant python3 /config/python_scripts/generate_indkorsel_gallery.py"
|
||||||
|
indkorsel_prune_keep_100: "ls -1 /config/www/snapshots/indkorsel/*.jpg | grep -v '/latest.jpg$' | sort -r | tail -n +101 | xargs rm -f"
|
||||||
@@ -1 +1,2 @@
|
|||||||
mealie_update: "python3 /config/python_scripts/mealie_mealplan.py"
|
mealie_update: "python3 /config/python_scripts/mealie_mealplan.py"
|
||||||
|
mealie_shopping_merge: "python3 /config/python_scripts/mealie_shopping_merge.py"
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
varme_save_defaults: "python3 /config/python_scripts/save_varme_defaults.py"
|
||||||
@@ -41,21 +41,12 @@
|
|||||||
- default_entity_id: switch.daniel_motionlys_toggle
|
- default_entity_id: switch.daniel_motionlys_toggle
|
||||||
name: "Daniel motionlys"
|
name: "Daniel motionlys"
|
||||||
unique_id: daniel_motionlys_toggle
|
unique_id: daniel_motionlys_toggle
|
||||||
state: >-
|
state: "{{ is_state('automation.daniel_lys_via_bevaegelse', 'on') }}"
|
||||||
{{ is_state('automation.lys_daniel_dag_arbejdsdag', 'on')
|
|
||||||
and is_state('automation.lys_daniel_dag_ikke_arbejdsdag', 'on')
|
|
||||||
and is_state('automation.sluk_lys_i_daniel', 'on') }}
|
|
||||||
turn_on:
|
turn_on:
|
||||||
action: automation.turn_on
|
action: automation.turn_on
|
||||||
target:
|
target:
|
||||||
entity_id:
|
entity_id: automation.daniel_lys_via_bevaegelse
|
||||||
- automation.lys_daniel_dag_arbejdsdag
|
|
||||||
- automation.lys_daniel_dag_ikke_arbejdsdag
|
|
||||||
- automation.sluk_lys_i_daniel
|
|
||||||
turn_off:
|
turn_off:
|
||||||
action: automation.turn_off
|
action: automation.turn_off
|
||||||
target:
|
target:
|
||||||
entity_id:
|
entity_id: automation.daniel_lys_via_bevaegelse
|
||||||
- automation.lys_daniel_dag_arbejdsdag
|
|
||||||
- automation.lys_daniel_dag_ikke_arbejdsdag
|
|
||||||
- automation.sluk_lys_i_daniel
|
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
##################################################
|
||||||
|
# Varme: Anbefalet ventilposition (sæsonbaseret)
|
||||||
|
# Bruges til manuelt at justere:
|
||||||
|
# 1. Hovedhanen i sauna (Roth-fordeling)
|
||||||
|
# 2. Hovedhanen i bryggers (fjernvarme indgang)
|
||||||
|
#
|
||||||
|
# Skala 1-5:
|
||||||
|
# 1 = næsten lukket (varmt vejr, >= 15°C)
|
||||||
|
# 5 = fuldt åben (frostgrader, <= -5°C)
|
||||||
|
# Lineær imellem: 1 + (15 - T) / 20 * 4
|
||||||
|
##################################################
|
||||||
|
|
||||||
|
- sensor:
|
||||||
|
- name: "Fjernvarme ventil anbefalet"
|
||||||
|
unique_id: fjernvarme_ventil_anbefalet
|
||||||
|
unit_of_measurement: ""
|
||||||
|
icon: mdi:valve
|
||||||
|
state: >
|
||||||
|
{% set t = state_attr('weather.norgardsvej', 'temperature') | float(10) %}
|
||||||
|
{% set raw = 1 + (15 - t) / 20 * 4 %}
|
||||||
|
{{ [[raw, 1] | max, 5] | min | round(1) }}
|
||||||
|
attributes:
|
||||||
|
anbefaling: >
|
||||||
|
{% set t = state_attr('weather.norgardsvej', 'temperature') | float(10) %}
|
||||||
|
{% set raw = 1 + (15 - t) / 20 * 4 %}
|
||||||
|
{% set pos = [[raw, 1] | max, 5] | min %}
|
||||||
|
{% if pos <= 1.5 %}Position 1 - lukket (varmt vejr)
|
||||||
|
{% elif pos <= 2.5 %}Position 2 - let åben (mildt vejr)
|
||||||
|
{% elif pos <= 3.5 %}Position 3 - halvvejs (køligt vejr)
|
||||||
|
{% elif pos <= 4.5 %}Position 4 - mest åben (koldt vejr)
|
||||||
|
{% else %}Position 5 - fuldt åben (frostgrader)
|
||||||
|
{% endif %}
|
||||||
|
udetemperatur: >
|
||||||
|
{{ state_attr('weather.norgardsvej', 'temperature') | float(10) }}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
- trigger:
|
||||||
|
- platform: homeassistant
|
||||||
|
event: start
|
||||||
|
- platform: time_pattern
|
||||||
|
hours: "/1"
|
||||||
|
action:
|
||||||
|
- action: weather.get_forecasts
|
||||||
|
target:
|
||||||
|
entity_id: weather.norgardsvej
|
||||||
|
data:
|
||||||
|
type: daily
|
||||||
|
response_variable: daily
|
||||||
|
sensor:
|
||||||
|
- name: "Vejr daglig prognose"
|
||||||
|
unique_id: vejr_daglig_prognose
|
||||||
|
state: "{{ daily['weather.norgardsvej'].forecast | length }}"
|
||||||
|
attributes:
|
||||||
|
forecast: "{{ daily['weather.norgardsvej'].forecast }}"
|
||||||
@@ -45,3 +45,9 @@ Vigtige detaljer om min opsaetning:
|
|||||||
- Telefoner: notify.mobile_app_claus_iphone_15pro, notify.mobile_app_annes_iphone_14_pro
|
- Telefoner: notify.mobile_app_claus_iphone_15pro, notify.mobile_app_annes_iphone_14_pro
|
||||||
- Sonos hoejtalere: media_player.alrum, media_player.lille_badevaerelse, m.fl.
|
- Sonos hoejtalere: media_player.alrum, media_player.lille_badevaerelse, m.fl.
|
||||||
- Git repo: gitea.anneclaus.synology.me (SSH)
|
- Git repo: gitea.anneclaus.synology.me (SSH)
|
||||||
|
- Husqvarna Automower (lawn_mower.husqvarna_automower): BLE-baseret via husqvarna_automower_ble integration
|
||||||
|
- MAC: C4:64:E3:B1:16:14, BLE-proxy: ESP32 paa D0:CF:13:0D:01:16
|
||||||
|
- BLE tillader kun EN aktiv forbindelse ad gangen - Husqvarna-appen paa telefon SKAL lukkes helt
|
||||||
|
(ogsaa selvom moweren er slettet i appen - appen kan stadig holde BLE-session aaben i baggrunden)
|
||||||
|
- Fejlsymptom: "could not find device with address C4:64:E3:B1:16:14" i log, "mower returned 1" i UI
|
||||||
|
- Fix: Luk Husqvarna-appen helt paa alle enheder -> genstart integrationen i HA
|
||||||
|
|||||||
@@ -0,0 +1,204 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate HTML gallery for indkorsel person-detection snapshots.
|
||||||
|
Called via shell_command after each new snapshot is saved.
|
||||||
|
Output: /config/www/snapshots/indkorsel_gallery.html
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import glob
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
SNAPSHOT_DIR = "/config/www/snapshots/indkorsel"
|
||||||
|
OUTPUT_FILE = "/config/www/snapshots/indkorsel_gallery.html"
|
||||||
|
LOADER_FILE = "/config/www/snapshots/indkorsel_loader.html"
|
||||||
|
MAX_SNAPSHOTS = 500
|
||||||
|
|
||||||
|
|
||||||
|
def parse_timestamp(filename):
|
||||||
|
base = os.path.basename(filename).replace(".jpg", "")
|
||||||
|
try:
|
||||||
|
dt = datetime.strptime(base, "%Y-%m-%d_%H-%M-%S")
|
||||||
|
return dt.strftime("%-d. %b %Y %H:%M:%S")
|
||||||
|
except Exception:
|
||||||
|
return base
|
||||||
|
|
||||||
|
|
||||||
|
os.makedirs(SNAPSHOT_DIR, exist_ok=True)
|
||||||
|
|
||||||
|
files = sorted(
|
||||||
|
[f for f in glob.glob(os.path.join(SNAPSHOT_DIR, "*.jpg"))
|
||||||
|
if os.path.basename(f) != "latest.jpg"],
|
||||||
|
reverse=True
|
||||||
|
)[:MAX_SNAPSHOTS]
|
||||||
|
|
||||||
|
items_html = ""
|
||||||
|
images_js = [] # [{src, ts}, ...] for JS navigation
|
||||||
|
for f in files:
|
||||||
|
rel = "/local/snapshots/indkorsel/" + os.path.basename(f)
|
||||||
|
ts = parse_timestamp(f)
|
||||||
|
idx = len(images_js)
|
||||||
|
items_html += f"""
|
||||||
|
<div class="thumb" onclick="openModal({idx})">
|
||||||
|
<img src="{rel}" loading="lazy" alt="{ts}"/>
|
||||||
|
<div class="ts">{ts}</div>
|
||||||
|
</div>"""
|
||||||
|
images_js.append({"src": rel, "ts": ts})
|
||||||
|
|
||||||
|
import json as _json
|
||||||
|
images_js_str = _json.dumps(images_js)
|
||||||
|
version = datetime.now().strftime("%Y%m%d%H%M%S")
|
||||||
|
|
||||||
|
html = f"""<!DOCTYPE html>
|
||||||
|
<html lang="da"><head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Indkorsel snapshots</title>
|
||||||
|
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||||||
|
<meta http-equiv="Pragma" content="no-cache">
|
||||||
|
<meta http-equiv="Expires" content="0">
|
||||||
|
<style>
|
||||||
|
.prune-btn{{display:inline-block;margin-left:12px;padding:3px 10px;font-size:11px;border:1px solid #c44;background:transparent;color:#c88;border-radius:12px;cursor:pointer;vertical-align:middle;transition:background .15s,color .15s}}
|
||||||
|
.prune-btn:hover{{background:#c44;color:#fff}}
|
||||||
|
.prune-btn:disabled{{opacity:.4;cursor:default}}
|
||||||
|
*{{box-sizing:border-box;margin:0;padding:0}}
|
||||||
|
body{{background:#111;color:#ddd;font-family:sans-serif;padding:10px}}
|
||||||
|
h2{{padding:8px 0 14px;font-size:13px;opacity:.5;font-weight:normal}}
|
||||||
|
.grid{{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:6px}}
|
||||||
|
.thumb{{cursor:pointer;border-radius:7px;overflow:hidden;background:#222;transition:transform .12s,opacity .12s}}
|
||||||
|
.thumb:hover{{transform:scale(1.02);opacity:.9}}
|
||||||
|
.thumb img{{width:100%;aspect-ratio:16/9;object-fit:cover;display:block}}
|
||||||
|
.ts{{font-size:10px;padding:4px 6px;opacity:.5;text-align:center}}
|
||||||
|
.modal{{display:none;position:fixed;inset:0;background:rgba(0,0,0,.93);z-index:9999;flex-direction:column;justify-content:center;align-items:center}}
|
||||||
|
.modal.show{{display:flex}}
|
||||||
|
.modal img{{max-width:96vw;max-height:82vh;border-radius:8px;object-fit:contain;box-shadow:0 4px 40px rgba(0,0,0,.6)}}
|
||||||
|
.modal-ts{{margin-top:12px;font-size:14px;opacity:.6;letter-spacing:.3px}}
|
||||||
|
.modal-counter{{margin-top:4px;font-size:11px;opacity:.35;letter-spacing:.3px}}
|
||||||
|
.close{{position:absolute;top:14px;right:18px;font-size:32px;cursor:pointer;opacity:.5;line-height:1;user-select:none}}
|
||||||
|
.close:hover{{opacity:1}}
|
||||||
|
.nav{{position:absolute;top:50%;transform:translateY(-50%);font-size:44px;cursor:pointer;opacity:.35;user-select:none;padding:0 18px;line-height:1}}
|
||||||
|
.nav:hover{{opacity:.9}}
|
||||||
|
.nav.prev{{left:0}} .nav.next{{right:0}}
|
||||||
|
.nav.disabled{{opacity:.08;cursor:default;pointer-events:none}}
|
||||||
|
.empty{{padding:40px;text-align:center;opacity:.4;font-size:14px}}
|
||||||
|
.reload-badge{{position:fixed;bottom:14px;right:14px;background:#1a73e8;color:#fff;padding:6px 14px;border-radius:20px;font-size:12px;cursor:pointer;display:none;z-index:9998}}
|
||||||
|
</style>
|
||||||
|
</head><body>
|
||||||
|
<h2>Viser {len(files)} person-snapshots – Indkorsel
|
||||||
|
<button class="prune-btn" id="pruneBtn" onclick="pruneSnapshots()" title="Slet alle undtagen de 100 nyeste">Behold sidste 100</button>
|
||||||
|
</h2>
|
||||||
|
{"<div class='grid'>" + items_html + "</div>" if files else "<div class='empty'>Ingen snapshots endnu.</div>"}
|
||||||
|
<div id="reload-badge" class="reload-badge" onclick="window.location.reload(true)">Nye billeder – tryk for at opdatere</div>
|
||||||
|
<div class="modal" id="modal">
|
||||||
|
<span class="close" onclick="closeModal()">✕</span>
|
||||||
|
<span class="nav prev" id="navPrev" onclick="navigate(-1)">‹</span>
|
||||||
|
<img id="mimg" src="" alt=""/>
|
||||||
|
<span class="nav next" id="navNext" onclick="navigate(1)">›</span>
|
||||||
|
<div class="modal-ts" id="mts"></div>
|
||||||
|
<div class="modal-counter" id="mcounter"></div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
const IMGS = {images_js_str};
|
||||||
|
const VERSION = '{version}';
|
||||||
|
let cur = 0;
|
||||||
|
function openModal(idx){{
|
||||||
|
cur = idx;
|
||||||
|
render();
|
||||||
|
document.getElementById('modal').classList.add('show');
|
||||||
|
}}
|
||||||
|
function render(){{
|
||||||
|
const item = IMGS[cur];
|
||||||
|
document.getElementById('mimg').src = item.src;
|
||||||
|
document.getElementById('mts').textContent = item.ts;
|
||||||
|
document.getElementById('mcounter').textContent = (cur+1) + ' / ' + IMGS.length;
|
||||||
|
document.getElementById('navPrev').classList.toggle('disabled', cur === 0);
|
||||||
|
document.getElementById('navNext').classList.toggle('disabled', cur === IMGS.length - 1);
|
||||||
|
}}
|
||||||
|
function navigate(dir){{
|
||||||
|
const next = cur + dir;
|
||||||
|
if(next >= 0 && next < IMGS.length){{ cur = next; render(); }}
|
||||||
|
}}
|
||||||
|
function closeModal(){{
|
||||||
|
document.getElementById('modal').classList.remove('show');
|
||||||
|
document.getElementById('mimg').src = '';
|
||||||
|
}}
|
||||||
|
document.addEventListener('keydown', e => {{
|
||||||
|
if(!document.getElementById('modal').classList.contains('show')) return;
|
||||||
|
if(e.key === 'ArrowLeft') navigate(-1);
|
||||||
|
if(e.key === 'ArrowRight') navigate(1);
|
||||||
|
if(e.key === 'Escape') closeModal();
|
||||||
|
}});
|
||||||
|
document.getElementById('modal').addEventListener('click', function(e){{
|
||||||
|
if(e.target === this) closeModal();
|
||||||
|
}});
|
||||||
|
// Touch swipe support (iPhone/iPad)
|
||||||
|
let _tx = null;
|
||||||
|
document.getElementById('modal').addEventListener('touchstart', e => {{
|
||||||
|
_tx = e.changedTouches[0].clientX;
|
||||||
|
}}, {{passive: true}});
|
||||||
|
document.getElementById('modal').addEventListener('touchend', e => {{
|
||||||
|
if(_tx === null) return;
|
||||||
|
const dx = e.changedTouches[0].clientX - _tx;
|
||||||
|
_tx = null;
|
||||||
|
if(Math.abs(dx) < 40) return; // ignore taps
|
||||||
|
navigate(dx < 0 ? 1 : -1); // swipe left = næste, swipe right = forrige
|
||||||
|
}}, {{passive: true}});
|
||||||
|
function pruneSnapshots(){{
|
||||||
|
if(!confirm('Slet alle undtagen de 100 nyeste billeder?')) return;
|
||||||
|
const btn = document.getElementById('pruneBtn');
|
||||||
|
btn.disabled = true;
|
||||||
|
btn.textContent = 'Sletter...';
|
||||||
|
fetch('/api/webhook/indkorsel_prune_100', {{method:'POST'}})
|
||||||
|
.then(() => {{
|
||||||
|
btn.textContent = 'Færdig – genindlæser...';
|
||||||
|
setTimeout(() => window.location.reload(true), 3500);
|
||||||
|
}})
|
||||||
|
.catch(() => {{
|
||||||
|
btn.disabled = false;
|
||||||
|
btn.textContent = 'Fejl – prøv igen';
|
||||||
|
}});
|
||||||
|
}}
|
||||||
|
// Tjek hvert 60 sek om der er en nyere version af galleriet
|
||||||
|
setInterval(() => {{
|
||||||
|
fetch('/local/snapshots/indkorsel_loader.html?_=' + Date.now())
|
||||||
|
.then(r => r.text())
|
||||||
|
.then(html => {{
|
||||||
|
const m = html.match(/[?]v=(\\d+)/);
|
||||||
|
if(m && m[1] !== VERSION) document.getElementById('reload-badge').style.display = 'block';
|
||||||
|
}}).catch(() => {{}});
|
||||||
|
}}, 60000);
|
||||||
|
</script>
|
||||||
|
</body></html>"""
|
||||||
|
|
||||||
|
with open(OUTPUT_FILE, "w", encoding="utf-8") as fh:
|
||||||
|
fh.write(html)
|
||||||
|
|
||||||
|
# Write a tiny loader page that always redirects to gallery with current timestamp
|
||||||
|
# so the iframe in HA never serves a cached version
|
||||||
|
cache_bust = version
|
||||||
|
loader = f"""<!DOCTYPE html>
|
||||||
|
<html><head>
|
||||||
|
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||||||
|
<meta http-equiv="Pragma" content="no-cache">
|
||||||
|
<meta http-equiv="Expires" content="0">
|
||||||
|
<meta http-equiv="refresh" content="0; url=/local/snapshots/indkorsel_gallery.html?v={cache_bust}">
|
||||||
|
</head><body></body></html>"""
|
||||||
|
with open(LOADER_FILE, "w", encoding="utf-8") as fh:
|
||||||
|
fh.write(loader)
|
||||||
|
|
||||||
|
print(f"Gallery updated: {len(files)} snapshots -> {OUTPUT_FILE}")
|
||||||
|
|
||||||
|
# Update dashboard YAML iframe URL version so browser always fetches fresh loader
|
||||||
|
DASHBOARD_VIEW = "/config/dashboards/views/06c_indkorsel_snapshots.yaml"
|
||||||
|
try:
|
||||||
|
import re as _re
|
||||||
|
with open(DASHBOARD_VIEW, "r", encoding="utf-8") as fh:
|
||||||
|
dash = fh.read()
|
||||||
|
updated = _re.sub(
|
||||||
|
r'(url: /local/snapshots/indkorsel_loader\.html)(\?v=\d+)?',
|
||||||
|
rf'\1?v={version}',
|
||||||
|
dash
|
||||||
|
)
|
||||||
|
if updated != dash:
|
||||||
|
with open(DASHBOARD_VIEW, "w", encoding="utf-8") as fh:
|
||||||
|
fh.write(updated)
|
||||||
|
except Exception as _e:
|
||||||
|
print(f"Warning: could not update dashboard YAML: {_e}")
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Import Valdemarsro favorites into Mealie (deduplicated by source URL).
|
||||||
|
|
||||||
|
This script imports a fixed list of favorite recipes from Valdemarsro by URL,
|
||||||
|
skipping recipes already present in Mealie.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import urllib.error
|
||||||
|
import urllib.parse
|
||||||
|
import urllib.request
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
BASE_URL = "http://10.0.0.142:9925"
|
||||||
|
SECRETS = Path("/Volumes/homeassistant/secrets.yaml")
|
||||||
|
|
||||||
|
FAVORITE_SLUGS = [
|
||||||
|
"hjemmelavet-pizza",
|
||||||
|
"lasagne",
|
||||||
|
"musli-opskrift",
|
||||||
|
"lakselasagne",
|
||||||
|
"mexicansk-burger-med-hjemmelavet-guacamole",
|
||||||
|
"grontsagsfad",
|
||||||
|
"humus",
|
||||||
|
"indisk_curry_med_kylling",
|
||||||
|
"pariserbof",
|
||||||
|
"skipperlabskovs",
|
||||||
|
"pizzasnegle",
|
||||||
|
"luksus-stjerneskud",
|
||||||
|
"jordbaer-og-fetasalat-med-glaserede-pecannoedder",
|
||||||
|
"spaghetti-bolognese",
|
||||||
|
"kylling-med-cornflakes",
|
||||||
|
"tarteletter-hoens-asparges",
|
||||||
|
"one-pot-pasta",
|
||||||
|
"cacio-e-pepe",
|
||||||
|
"citronpasta",
|
||||||
|
"blomkaalssalat",
|
||||||
|
"koedsovs-onepotpasta",
|
||||||
|
"kikaertegryde",
|
||||||
|
"moerbradboeffer-med-bloede-loeg",
|
||||||
|
"stegt-spidskaal",
|
||||||
|
"bagt-kylling",
|
||||||
|
"ristede-kartoffelskiver-fad",
|
||||||
|
"vikingegryde",
|
||||||
|
"spidskaalssalat-opskrift",
|
||||||
|
"kylling-med-parmesan",
|
||||||
|
"bagt-broccoli",
|
||||||
|
"pastasalat-med-pesto",
|
||||||
|
"nachos-bowl",
|
||||||
|
"barbecuesauce",
|
||||||
|
"congee-rissuppe-kylling",
|
||||||
|
"macaroni-and-cheese",
|
||||||
|
"halloween-dessert",
|
||||||
|
"feta-pasta-med-tomat",
|
||||||
|
"tortellini-i-fad",
|
||||||
|
"flyvende-jacob",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def read_token() -> str:
|
||||||
|
for line in SECRETS.read_text().splitlines():
|
||||||
|
if line.strip().startswith("mealie_bearer_token:"):
|
||||||
|
return line.split(":", 1)[1].strip().strip('"')
|
||||||
|
raise RuntimeError("mealie_bearer_token not found in secrets.yaml")
|
||||||
|
|
||||||
|
|
||||||
|
def api_request(path: str, token: str, method: str = "GET", payload: dict | None = None) -> dict:
|
||||||
|
headers = {"Authorization": token}
|
||||||
|
data = None
|
||||||
|
if payload is not None:
|
||||||
|
headers["Content-Type"] = "application/json"
|
||||||
|
data = json.dumps(payload).encode("utf-8")
|
||||||
|
|
||||||
|
req = urllib.request.Request(f"{BASE_URL}{path}", headers=headers, data=data, method=method)
|
||||||
|
with urllib.request.urlopen(req, timeout=90) as resp:
|
||||||
|
raw = resp.read()
|
||||||
|
return json.loads(raw) if raw else {}
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_existing_org_urls(token: str) -> set[str]:
|
||||||
|
urls: set[str] = set()
|
||||||
|
page = 1
|
||||||
|
per_page = 100
|
||||||
|
|
||||||
|
while True:
|
||||||
|
path = f"/api/recipes?page={page}&perPage={per_page}"
|
||||||
|
payload = api_request(path, token)
|
||||||
|
items = payload.get("items", []) or []
|
||||||
|
if not items:
|
||||||
|
break
|
||||||
|
|
||||||
|
for recipe in items:
|
||||||
|
org_url = (recipe.get("orgURL") or "").strip()
|
||||||
|
if org_url:
|
||||||
|
urls.add(org_url.rstrip("/"))
|
||||||
|
|
||||||
|
if len(items) < per_page:
|
||||||
|
break
|
||||||
|
page += 1
|
||||||
|
|
||||||
|
return urls
|
||||||
|
|
||||||
|
|
||||||
|
def import_recipe_by_url(token: str, url: str) -> tuple[bool, str]:
|
||||||
|
try:
|
||||||
|
api_request(
|
||||||
|
"/api/recipes/create/url",
|
||||||
|
token,
|
||||||
|
method="POST",
|
||||||
|
payload={"url": url, "includeTags": True},
|
||||||
|
)
|
||||||
|
return True, "imported"
|
||||||
|
except urllib.error.HTTPError as exc:
|
||||||
|
body = ""
|
||||||
|
try:
|
||||||
|
body = exc.read().decode("utf-8", errors="ignore")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if exc.code == 422:
|
||||||
|
return False, f"422 {body[:200]}"
|
||||||
|
return False, f"HTTP {exc.code} {body[:200]}"
|
||||||
|
except Exception as exc: # noqa: BLE001
|
||||||
|
return False, str(exc)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
token = read_token()
|
||||||
|
urls = [f"https://www.valdemarsro.dk/{slug}/" for slug in FAVORITE_SLUGS]
|
||||||
|
|
||||||
|
existing_urls = fetch_existing_org_urls(token)
|
||||||
|
|
||||||
|
to_import = [u for u in urls if u.rstrip("/") not in existing_urls]
|
||||||
|
skipped = [u for u in urls if u.rstrip("/") in existing_urls]
|
||||||
|
|
||||||
|
imported: list[str] = []
|
||||||
|
failed: list[tuple[str, str]] = []
|
||||||
|
|
||||||
|
for url in to_import:
|
||||||
|
ok, msg = import_recipe_by_url(token, url)
|
||||||
|
if ok:
|
||||||
|
imported.append(url)
|
||||||
|
else:
|
||||||
|
failed.append((url, msg))
|
||||||
|
|
||||||
|
print(f"TOTAL_LISTED={len(urls)}")
|
||||||
|
print(f"SKIPPED_EXISTING={len(skipped)}")
|
||||||
|
print(f"IMPORTED={len(imported)}")
|
||||||
|
print(f"FAILED={len(failed)}")
|
||||||
|
|
||||||
|
if skipped:
|
||||||
|
print("-- skipped existing --")
|
||||||
|
for url in skipped:
|
||||||
|
print(url)
|
||||||
|
|
||||||
|
if imported:
|
||||||
|
print("-- imported --")
|
||||||
|
for url in imported:
|
||||||
|
print(url)
|
||||||
|
|
||||||
|
if failed:
|
||||||
|
print("-- failed --")
|
||||||
|
for url, reason in failed:
|
||||||
|
print(url)
|
||||||
|
print(f" reason: {reason}")
|
||||||
|
|
||||||
|
# Exit non-zero only if everything failed.
|
||||||
|
if failed and not imported:
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
@@ -20,17 +20,26 @@ else:
|
|||||||
try:
|
try:
|
||||||
req = urllib.request.Request(url, headers={"Authorization": token})
|
req = urllib.request.Request(url, headers={"Authorization": token})
|
||||||
raw = json.loads(urllib.request.urlopen(req, timeout=10).read())
|
raw = json.loads(urllib.request.urlopen(req, timeout=10).read())
|
||||||
items = [
|
items = []
|
||||||
{
|
for i in raw.get("items", []):
|
||||||
|
recipe = i.get("recipe")
|
||||||
|
title = i.get("title") or ""
|
||||||
|
if recipe:
|
||||||
|
items.append({
|
||||||
"date": i["date"],
|
"date": i["date"],
|
||||||
"recipe": {
|
"recipe": {
|
||||||
"name": i.get("recipe", {}).get("name", ""),
|
"name": recipe.get("name", ""),
|
||||||
"slug": i.get("recipe", {}).get("slug", ""),
|
"slug": recipe.get("slug", ""),
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
for i in raw.get("items", [])
|
elif title:
|
||||||
if i.get("recipe")
|
items.append({
|
||||||
]
|
"date": i["date"],
|
||||||
|
"recipe": {
|
||||||
|
"name": title,
|
||||||
|
"slug": "",
|
||||||
|
},
|
||||||
|
})
|
||||||
data = {"count": len(items), "items": items}
|
data = {"count": len(items), "items": items}
|
||||||
except Exception:
|
except Exception:
|
||||||
data = {"count": 0, "items": []}
|
data = {"count": 0, "items": []}
|
||||||
|
|||||||
@@ -0,0 +1,286 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Build Mealie shopping list for the Friday-Thursday meal plan window.
|
||||||
|
|
||||||
|
Flow:
|
||||||
|
1) Compute next Friday-Thursday window.
|
||||||
|
2) Ensure/clear the 'Bilka ToGo' Mealie shopping list.
|
||||||
|
3) Add all recipes from the meal plan window to the list.
|
||||||
|
4) Fetch the resulting shopping items and write a styled HTML file for display in HA.
|
||||||
|
|
||||||
|
Output:
|
||||||
|
- /www/bilka_togo_checklist.html (iframe-renderable in HA)
|
||||||
|
- /www/bilka_togo_checklist.json (machine-readable backup)
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import urllib.request
|
||||||
|
from collections import defaultdict
|
||||||
|
from datetime import date, timedelta
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
ROOT_CANDIDATES = [Path('/Volumes/homeassistant'), Path('/config')]
|
||||||
|
MEALIE_BASE_URL = 'http://10.0.0.142:9925'
|
||||||
|
TARGET_SHOPPING_LIST_NAME = 'Bilka ToGo'
|
||||||
|
CATEGORY_RULES = {
|
||||||
|
'frugt & grønt': ['banan', 'aeble', 'æble', 'citron', 'lime', 'tomat', 'salat', 'agurk', 'gulerod', 'kartoffel', 'log', 'løg', 'hvidlog', 'hvidløg', 'broccoli', 'spidskal', 'spidskål', 'avocado', 'peberfrugt'],
|
||||||
|
'kød & fisk': ['kylling', 'oksekød', 'hakket', 'ribeye', 'bacon', 'laks', 'fisk', 'skinke', 'pølse'],
|
||||||
|
'mejeri & æg': ['mælk', 'yoghurt', 'smør', 'ost', 'feta', 'mozzarella', 'æg', 'fløde', 'creme fraiche'],
|
||||||
|
'kolonial': ['ris', 'pasta', 'mel', 'havregryn', 'olie', 'eddike', 'krydder', 'bønne', 'linse', 'dåse', 'sukker', 'salt', 'tomatpuré', 'tomatpure', 'nori', 'soja'],
|
||||||
|
'frost': ['frost', 'frossen', 'is', 'edamame'],
|
||||||
|
'husholdning': ['toiletpapir', 'køkkenrulle', 'kokkenrulle', 'sæbe', 'sæb', 'opvasketabs', 'vaskemiddel', 'affaldspose'],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def detect_root() -> Path:
|
||||||
|
# Prefer repository-relative root so the script works both in HA (/config)
|
||||||
|
# and when run locally from the mounted workspace (/Volumes/homeassistant).
|
||||||
|
local_root = Path(__file__).resolve().parent.parent
|
||||||
|
if (local_root / 'secrets.yaml').exists():
|
||||||
|
return local_root
|
||||||
|
for root in ROOT_CANDIDATES:
|
||||||
|
if (root / 'secrets.yaml').exists():
|
||||||
|
return root
|
||||||
|
raise RuntimeError('Could not locate Home Assistant config root')
|
||||||
|
|
||||||
|
|
||||||
|
def read_bearer_token(secrets_path: Path) -> str:
|
||||||
|
for line in secrets_path.read_text().splitlines():
|
||||||
|
if line.strip().startswith('mealie_bearer_token:'):
|
||||||
|
return line.split(':', 1)[1].strip().strip('"')
|
||||||
|
raise RuntimeError('mealie_bearer_token not found in secrets.yaml')
|
||||||
|
|
||||||
|
|
||||||
|
def api_get(url: str, token: str) -> dict:
|
||||||
|
req = urllib.request.Request(url, headers={'Authorization': token})
|
||||||
|
with urllib.request.urlopen(req, timeout=20) as resp:
|
||||||
|
raw = resp.read()
|
||||||
|
return json.loads(raw) if raw else {}
|
||||||
|
|
||||||
|
|
||||||
|
def api_request(
|
||||||
|
base_url: str,
|
||||||
|
path: str,
|
||||||
|
token: str,
|
||||||
|
method: str = 'GET',
|
||||||
|
payload: dict | list | None = None,
|
||||||
|
timeout: int = 90,
|
||||||
|
):
|
||||||
|
data = None
|
||||||
|
headers = {'Authorization': token}
|
||||||
|
if payload is not None:
|
||||||
|
data = json.dumps(payload).encode('utf-8')
|
||||||
|
headers['Content-Type'] = 'application/json'
|
||||||
|
req = urllib.request.Request(f'{base_url}{path}', headers=headers, data=data, method=method)
|
||||||
|
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
||||||
|
raw = resp.read()
|
||||||
|
if not raw:
|
||||||
|
return {}
|
||||||
|
try:
|
||||||
|
return json.loads(raw)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_name(value: str) -> str:
|
||||||
|
value = value.lower().strip()
|
||||||
|
value = value.replace('å', 'aa').replace('æ', 'ae').replace('ø', 'oe')
|
||||||
|
value = re.sub(r'\s+', ' ', value)
|
||||||
|
value = re.sub(r'[^a-z0-9 ]', '', value)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def classify_category(name: str) -> str:
|
||||||
|
key = normalize_name(name)
|
||||||
|
for category, needles in CATEGORY_RULES.items():
|
||||||
|
for needle in needles:
|
||||||
|
if needle in key:
|
||||||
|
return category
|
||||||
|
return 'andet'
|
||||||
|
|
||||||
|
|
||||||
|
def read_keep_items(_keep_path: Path) -> list[str]: # noqa: ARG001 (kept for backward compat)
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def friday_to_thursday_window(today: date) -> tuple[date, date]:
|
||||||
|
days_until_friday = (4 - today.weekday()) % 7
|
||||||
|
start = today + timedelta(days=days_until_friday)
|
||||||
|
end = start + timedelta(days=6)
|
||||||
|
return start, end
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_shopping_list(base_url: str, token: str, name: str) -> str:
|
||||||
|
lists = api_request(base_url, '/api/households/shopping/lists?perPage=200', token).get('items', []) or []
|
||||||
|
for shopping_list in lists:
|
||||||
|
if (shopping_list.get('name') or '').strip().lower() == name.lower():
|
||||||
|
return shopping_list['id']
|
||||||
|
|
||||||
|
created = api_request(
|
||||||
|
base_url,
|
||||||
|
'/api/households/shopping/lists',
|
||||||
|
token,
|
||||||
|
method='POST',
|
||||||
|
payload={'name': name},
|
||||||
|
)
|
||||||
|
return created['id']
|
||||||
|
|
||||||
|
|
||||||
|
def reset_shopping_list(base_url: str, token: str, name: str, shopping_list_id: str) -> str:
|
||||||
|
"""Delete the list and recreate it. Returns new list id."""
|
||||||
|
api_request(base_url, f'/api/households/shopping/lists/{shopping_list_id}', token, method='DELETE')
|
||||||
|
created = api_request(
|
||||||
|
base_url,
|
||||||
|
'/api/households/shopping/lists',
|
||||||
|
token,
|
||||||
|
method='POST',
|
||||||
|
payload={'name': name},
|
||||||
|
)
|
||||||
|
return created['id']
|
||||||
|
|
||||||
|
|
||||||
|
def get_mealplan_recipe_ids(base_url: str, token: str, start_date: date, end_date: date) -> list[str]:
|
||||||
|
entries: list[dict] = []
|
||||||
|
current = start_date
|
||||||
|
while current <= end_date:
|
||||||
|
path = (
|
||||||
|
f"/api/households/mealplans?start_date={current.isoformat()}"
|
||||||
|
f"&end_date={current.isoformat()}&perPage=100"
|
||||||
|
)
|
||||||
|
day_payload = api_request(base_url, path, token)
|
||||||
|
day_items = day_payload.get('items', []) if isinstance(day_payload, dict) else []
|
||||||
|
entries.extend(day_items or [])
|
||||||
|
current = current + timedelta(days=1)
|
||||||
|
|
||||||
|
result: list[str] = []
|
||||||
|
seen: set[str] = set()
|
||||||
|
for entry in entries:
|
||||||
|
recipe_id = entry.get('recipeId')
|
||||||
|
if not recipe_id:
|
||||||
|
continue
|
||||||
|
if recipe_id in seen:
|
||||||
|
continue
|
||||||
|
seen.add(recipe_id)
|
||||||
|
result.append(recipe_id)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def add_recipes_to_shopping_list(base_url: str, token: str, shopping_list_id: str, recipe_ids: list[str]) -> int:
|
||||||
|
if not recipe_ids:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
payload = [{'recipeId': rid, 'recipeIncrementQuantity': 1} for rid in recipe_ids]
|
||||||
|
api_request(
|
||||||
|
base_url,
|
||||||
|
f'/api/households/shopping/lists/{shopping_list_id}/recipe',
|
||||||
|
token,
|
||||||
|
method='POST',
|
||||||
|
payload=payload,
|
||||||
|
)
|
||||||
|
return len(recipe_ids)
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_mealie_items(base_url: str, token: str, shopping_list_id: str | None = None) -> list[dict]:
|
||||||
|
data = api_request(base_url, '/api/households/shopping/items?perPage=1000', token)
|
||||||
|
items = data.get('items', []) or []
|
||||||
|
if not shopping_list_id:
|
||||||
|
return items
|
||||||
|
return [item for item in items if item.get('shoppingListId') == shopping_list_id]
|
||||||
|
|
||||||
|
|
||||||
|
def extract_item_name(item: dict) -> str:
|
||||||
|
display = (item.get('display') or '').strip()
|
||||||
|
if display:
|
||||||
|
return display
|
||||||
|
food = item.get('food') or {}
|
||||||
|
return (food.get('name') or '').strip()
|
||||||
|
|
||||||
|
|
||||||
|
def build_items(mealie_items: list[dict]) -> list[dict]:
|
||||||
|
merged: dict[str, dict] = {}
|
||||||
|
for item in mealie_items:
|
||||||
|
name = extract_item_name(item)
|
||||||
|
if not name:
|
||||||
|
continue
|
||||||
|
norm = normalize_name(name)
|
||||||
|
if norm not in merged:
|
||||||
|
merged[norm] = {
|
||||||
|
'name': name,
|
||||||
|
'category': classify_category(name),
|
||||||
|
}
|
||||||
|
|
||||||
|
result = [v for v in merged.values()]
|
||||||
|
result.sort(key=lambda x: (x['category'], normalize_name(x['name'])))
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def write_outputs(root: Path, items: list[dict], start_date: date, end_date: date) -> None:
|
||||||
|
www = root / 'www'
|
||||||
|
www.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
payload = {'count': len(items), 'items': items}
|
||||||
|
(www / 'bilka_togo_checklist.json').write_text(json.dumps(payload, ensure_ascii=False, indent=2))
|
||||||
|
|
||||||
|
grouped: dict[str, list[dict]] = defaultdict(list)
|
||||||
|
for item in items:
|
||||||
|
grouped[item['category']].append(item)
|
||||||
|
|
||||||
|
rows_html = ''
|
||||||
|
for category in sorted(grouped.keys()):
|
||||||
|
rows_html += f'<tr><th colspan="2" class="cat">{category.title()}</th></tr>\n'
|
||||||
|
for item in grouped[category]:
|
||||||
|
rows_html += f'<tr><td class="cb"><input type="checkbox"></td><td>{item["name"]}</td></tr>\n'
|
||||||
|
|
||||||
|
html = f"""<!DOCTYPE html>
|
||||||
|
<html lang="da">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>Bilka ToGo</title>
|
||||||
|
<style>
|
||||||
|
body {{ font-family: sans-serif; padding: 8px; background: #1c1c1e; color: #e5e5ea; margin: 0; }}
|
||||||
|
h1 {{ font-size: 1.1em; margin-bottom: 2px; color: #fff; }}
|
||||||
|
p.sub {{ font-size: 0.8em; color: #8e8e93; margin: 0 0 10px; }}
|
||||||
|
table {{ width: 100%; border-collapse: collapse; }}
|
||||||
|
td, th {{ padding: 5px 6px; text-align: left; border-bottom: 1px solid #2c2c2e; }}
|
||||||
|
th.cat {{ background: #2c2c2e; color: #ffe066; font-size: 0.85em; text-transform: uppercase; letter-spacing: 0.05em; }}
|
||||||
|
td.cb {{ width: 28px; }}
|
||||||
|
input[type=checkbox] {{ width: 18px; height: 18px; accent-color: #30d158; cursor: pointer; }}
|
||||||
|
tr:hover td {{ background: #2c2c2e; }}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>🛒 Bilka ToGo</h1>
|
||||||
|
<p class="sub">Plan {start_date.strftime('%d/%m')} – {end_date.strftime('%d/%m')} · {len(items)} varer</p>
|
||||||
|
<table>
|
||||||
|
{rows_html}</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
(www / 'bilka_togo_checklist.html').write_text(html)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
root = detect_root()
|
||||||
|
token = read_bearer_token(root / 'secrets.yaml')
|
||||||
|
|
||||||
|
start_date, end_date = friday_to_thursday_window(date.today())
|
||||||
|
|
||||||
|
shopping_list_id = ensure_shopping_list(MEALIE_BASE_URL, token, TARGET_SHOPPING_LIST_NAME)
|
||||||
|
shopping_list_id = reset_shopping_list(MEALIE_BASE_URL, token, TARGET_SHOPPING_LIST_NAME, shopping_list_id)
|
||||||
|
|
||||||
|
recipe_ids = get_mealplan_recipe_ids(MEALIE_BASE_URL, token, start_date, end_date)
|
||||||
|
add_recipes_to_shopping_list(MEALIE_BASE_URL, token, shopping_list_id, recipe_ids)
|
||||||
|
|
||||||
|
print(
|
||||||
|
'OK: '
|
||||||
|
f'window={start_date.isoformat()}..{end_date.isoformat()} '
|
||||||
|
f'recipes_added={added_recipes} items={len(items)}'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Gem nuværende varme-indstillinger som nye 'initial' standardværdier i YAML-filen.
|
||||||
|
Køres inde i homeassistant Docker-containeren.
|
||||||
|
"""
|
||||||
|
import re
|
||||||
|
import json
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
YAML_FILE = "/config/include/input/number/varme.yaml"
|
||||||
|
SECRETS_FILE = "/config/secrets.yaml"
|
||||||
|
HA_URL = "http://localhost:8123"
|
||||||
|
|
||||||
|
ENTITIES = [
|
||||||
|
"varme_komfort_andreas",
|
||||||
|
"varme_komfort_daniel",
|
||||||
|
"varme_komfort_sovevaerelse",
|
||||||
|
"varme_komfort_kontor",
|
||||||
|
"varme_komfort_gang",
|
||||||
|
"varme_komfort_forgang",
|
||||||
|
"varme_komfort_lille_bad",
|
||||||
|
"varme_komfort_badevarelse",
|
||||||
|
"varme_komfort_stue",
|
||||||
|
"varme_nat_saenkning",
|
||||||
|
"varme_vaek_saenkning",
|
||||||
|
"varme_ferie_temp",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def get_token():
|
||||||
|
with open(SECRETS_FILE) as f:
|
||||||
|
for line in f:
|
||||||
|
if line.startswith("ha_token:"):
|
||||||
|
return line.split(":", 1)[1].strip()
|
||||||
|
raise ValueError("ha_token ikke fundet i secrets.yaml")
|
||||||
|
|
||||||
|
|
||||||
|
def get_states(token):
|
||||||
|
req = urllib.request.Request(
|
||||||
|
f"{HA_URL}/api/states",
|
||||||
|
headers={"Authorization": f"Bearer {token}"},
|
||||||
|
)
|
||||||
|
with urllib.request.urlopen(req) as resp:
|
||||||
|
return {d["entity_id"]: d["state"] for d in json.loads(resp.read())}
|
||||||
|
|
||||||
|
|
||||||
|
def format_value(state_str):
|
||||||
|
val = float(state_str)
|
||||||
|
return str(int(val)) if val == int(val) else str(val)
|
||||||
|
|
||||||
|
|
||||||
|
def update_initial(content, entity_name, new_value):
|
||||||
|
"""Erstat initial-værdien for en given entity i YAML-indholdet."""
|
||||||
|
pattern = rf"(^{re.escape(entity_name)}:\n(?: [^\n]*\n)*? initial: )\S+"
|
||||||
|
new_content, count = re.subn(
|
||||||
|
pattern, rf"\g<1>{new_value}", content, flags=re.MULTILINE, count=1
|
||||||
|
)
|
||||||
|
if count == 0:
|
||||||
|
print(f" ADVARSEL: {entity_name} ikke fundet i YAML")
|
||||||
|
return new_content
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
token = get_token()
|
||||||
|
states = get_states(token)
|
||||||
|
|
||||||
|
with open(YAML_FILE) as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
saved = []
|
||||||
|
for name in ENTITIES:
|
||||||
|
entity_id = f"input_number.{name}"
|
||||||
|
state = states.get(entity_id)
|
||||||
|
if state in (None, "unavailable", "unknown"):
|
||||||
|
print(f" SPRING OVER {entity_id}: {state}")
|
||||||
|
continue
|
||||||
|
val_str = format_value(state)
|
||||||
|
content = update_initial(content, name, val_str)
|
||||||
|
saved.append(f"{entity_id} = {val_str}")
|
||||||
|
|
||||||
|
with open(YAML_FILE, "w") as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
print(f"Gemt {len(saved)} standardværdier -> {YAML_FILE}")
|
||||||
|
for line in saved:
|
||||||
|
print(f" {line}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -37,6 +37,11 @@ synology_password: boss3LEY8ogh!saub
|
|||||||
## old nas was 10.0.0.189
|
## old nas was 10.0.0.189
|
||||||
# ########################################
|
# ########################################
|
||||||
|
|
||||||
|
npm_url: http://10.0.0.142:81
|
||||||
|
npm_email: claus.dethlefsen@gmail.com
|
||||||
|
npm_password: Hwli03yitw
|
||||||
|
# ########################################
|
||||||
|
|
||||||
hue_ip: 10.0.0.154
|
hue_ip: 10.0.0.154
|
||||||
|
|
||||||
# ########################################
|
# ########################################
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="da">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>Bilka ToGo</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: sans-serif; padding: 8px; background: #1c1c1e; color: #e5e5ea; margin: 0; }
|
||||||
|
h1 { font-size: 1.1em; margin-bottom: 2px; color: #fff; }
|
||||||
|
p.sub { font-size: 0.8em; color: #8e8e93; margin: 0 0 10px; }
|
||||||
|
table { width: 100%; border-collapse: collapse; }
|
||||||
|
td, th { padding: 5px 6px; text-align: left; border-bottom: 1px solid #2c2c2e; }
|
||||||
|
th.cat { background: #2c2c2e; color: #ffe066; font-size: 0.85em; text-transform: uppercase; letter-spacing: 0.05em; }
|
||||||
|
td.cb { width: 28px; }
|
||||||
|
input[type=checkbox] { width: 18px; height: 18px; accent-color: #30d158; cursor: pointer; }
|
||||||
|
tr:hover td { background: #2c2c2e; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>🛒 Bilka ToGo</h1>
|
||||||
|
<p class="sub">Plan 15/05 – 21/05 · 69 varer</p>
|
||||||
|
<table>
|
||||||
|
<tr><th colspan="2" class="cat">Andet</th></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>0,50 dl hvidvin</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>0,50 tsk stødt spidskommen</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>1 æg</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>1 æggehvider</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>1 dl cremefraiche 18 %</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>1 dl mælk</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>1 dl rødvin, eller grøntsagsboullion</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>1 fed hvidløg, presset</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>1 knivspids muskatnød, fintrevet</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>1 knivspids røget paprika</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>1 knivspids sød paprika</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>1 spsk smør, til stegning</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>1 squash, groftrevet</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>1 tsk sød paprika</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>1 tsk tørret timian</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>100 g rejer</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>100 g stenbiderrogn</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>2 spsk kapers (valgfrit)</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>2 spsk smør</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>2 tsk tørret oregano</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>200 g lasagneplader</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>3 dl mælk</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>4 grønne asparges</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>4 gulerødder, groftrevet</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>4 hvide asparges</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>4 skiver brød</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>5 stængler bladselleri, groftrevet</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>50 g rasp</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>75 g smør</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>8 rødspættefilet</td></tr>
|
||||||
|
<tr><th colspan="2" class="cat">Frost</th></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>2 spsk mayonnaise</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>2,50 dl piskefløde</td></tr>
|
||||||
|
<tr><th colspan="2" class="cat">Frugt & Grønt</th></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>1 citron – saft og fintrevet skal</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>1 citron, 1 spsk saft herfra</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>1 citron, skåret i både</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>12 skiver agurk</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>2 banan, skåret i skiver på ca. 1 cm</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>2 spsk koncentreret tomatpuré</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>4 håndfulde grøn salat, til anretning</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>50 g koncentreret tomatpuré</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>70 g blandet salat</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>800 g hakkede tomater på dåse</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>Grøntsager eller salat</td></tr>
|
||||||
|
<tr><th colspan="2" class="cat">Kolonial</th></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>1 dl mild chilipasta</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>1 spsk hvedemel</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>1 spsk olivenolie, til stegning</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>1 spsk smør eller olie til stegning</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>2 håndfulde frisk dild, til pynt</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>2 spsk finvalset havregryn</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>2 spsk hvedemel</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>2 spsk olivenolie</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>3 dl basmati ris</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>3 spsk olivenolie, til stegning</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>50 g saltede peanuts</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>Ris eller kartofler</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>salt og friskkværnet peber</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>Salt og peber</td></tr>
|
||||||
|
<tr><th colspan="2" class="cat">Kød & Fisk</th></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>1 fed hvidløg, finthakket</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>1 løg, finthakket</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>2 løg, finthakket</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>2 spsk frisk persille, hakket</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>2 spsk persille, finthakket</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>4 fed hvidløg, finthakket</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>4 laksefileter med skind (ca. 150 g pr. stk)</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>400 g hakket oksekød</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>500 g hakket svinekød</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>600 g kyllingebryst, skåret i grove tern</td></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>75 g bacon, i skiver</td></tr>
|
||||||
|
<tr><th colspan="2" class="cat">Mejeri & Æg</th></tr>
|
||||||
|
<tr><td class="cb"><input type="checkbox"></td><td>125 g frisk mozzarella</td></tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,281 @@
|
|||||||
|
{
|
||||||
|
"count": 69,
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"name": "0,50 dl hvidvin",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "0,50 tsk stødt spidskommen",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1 æg",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1 æggehvider",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1 dl cremefraiche 18 %",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1 dl mælk",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1 dl rødvin, eller grøntsagsboullion",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1 fed hvidløg, presset",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1 knivspids muskatnød, fintrevet",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1 knivspids røget paprika",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1 knivspids sød paprika",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1 spsk smør, til stegning",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1 squash, groftrevet",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1 tsk sød paprika",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1 tsk tørret timian",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "100 g rejer",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "100 g stenbiderrogn",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2 spsk kapers (valgfrit)",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2 spsk smør",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2 tsk tørret oregano",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "200 g lasagneplader",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "3 dl mælk",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "4 grønne asparges",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "4 gulerødder, groftrevet",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "4 hvide asparges",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "4 skiver brød",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "5 stængler bladselleri, groftrevet",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "50 g rasp",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "75 g smør",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "8 rødspættefilet",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2 spsk mayonnaise",
|
||||||
|
"category": "frost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2,50 dl piskefløde",
|
||||||
|
"category": "frost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1 citron – saft og fintrevet skal",
|
||||||
|
"category": "frugt & grønt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1 citron, 1 spsk saft herfra",
|
||||||
|
"category": "frugt & grønt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1 citron, skåret i både",
|
||||||
|
"category": "frugt & grønt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "12 skiver agurk",
|
||||||
|
"category": "frugt & grønt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2 banan, skåret i skiver på ca. 1 cm",
|
||||||
|
"category": "frugt & grønt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2 spsk koncentreret tomatpuré",
|
||||||
|
"category": "frugt & grønt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "4 håndfulde grøn salat, til anretning",
|
||||||
|
"category": "frugt & grønt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "50 g koncentreret tomatpuré",
|
||||||
|
"category": "frugt & grønt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "70 g blandet salat",
|
||||||
|
"category": "frugt & grønt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "800 g hakkede tomater på dåse",
|
||||||
|
"category": "frugt & grønt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Grøntsager eller salat",
|
||||||
|
"category": "frugt & grønt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1 dl mild chilipasta",
|
||||||
|
"category": "kolonial"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1 spsk hvedemel",
|
||||||
|
"category": "kolonial"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1 spsk olivenolie, til stegning",
|
||||||
|
"category": "kolonial"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1 spsk smør eller olie til stegning",
|
||||||
|
"category": "kolonial"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2 håndfulde frisk dild, til pynt",
|
||||||
|
"category": "kolonial"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2 spsk finvalset havregryn",
|
||||||
|
"category": "kolonial"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2 spsk hvedemel",
|
||||||
|
"category": "kolonial"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2 spsk olivenolie",
|
||||||
|
"category": "kolonial"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "3 dl basmati ris",
|
||||||
|
"category": "kolonial"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "3 spsk olivenolie, til stegning",
|
||||||
|
"category": "kolonial"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "50 g saltede peanuts",
|
||||||
|
"category": "kolonial"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ris eller kartofler",
|
||||||
|
"category": "kolonial"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "salt og friskkværnet peber",
|
||||||
|
"category": "kolonial"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Salt og peber",
|
||||||
|
"category": "kolonial"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1 fed hvidløg, finthakket",
|
||||||
|
"category": "kød & fisk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1 løg, finthakket",
|
||||||
|
"category": "kød & fisk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2 løg, finthakket",
|
||||||
|
"category": "kød & fisk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2 spsk frisk persille, hakket",
|
||||||
|
"category": "kød & fisk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2 spsk persille, finthakket",
|
||||||
|
"category": "kød & fisk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "4 fed hvidløg, finthakket",
|
||||||
|
"category": "kød & fisk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "4 laksefileter med skind (ca. 150 g pr. stk)",
|
||||||
|
"category": "kød & fisk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "400 g hakket oksekød",
|
||||||
|
"category": "kød & fisk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "500 g hakket svinekød",
|
||||||
|
"category": "kød & fisk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "600 g kyllingebryst, skåret i grove tern",
|
||||||
|
"category": "kød & fisk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "75 g bacon, i skiver",
|
||||||
|
"category": "kød & fisk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "125 g frisk mozzarella",
|
||||||
|
"category": "mejeri & æg"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
# Bilka ToGo - Kryds-af-liste
|
||||||
|
|
||||||
|
Gå listen igennem derhjemme først, og bestil kun de varer du mangler.
|
||||||
|
Plan-vindue: 2026-04-24 til 2026-04-30
|
||||||
|
|
||||||
|
## Andet
|
||||||
|
- [ ] 0,50 squash (Mealie)
|
||||||
|
- [ ] 0,50 tsk mediumstærk karry (Mealie)
|
||||||
|
- [ ] 0,50 tsk tørret rosmarin (Mealie)
|
||||||
|
- [ ] 1 dl cremefraiche 38% (Mealie)
|
||||||
|
- [ ] 1 dl grøntsagsbouillon (Mealie)
|
||||||
|
- [ ] 1 dl pickles (Mealie)
|
||||||
|
- [ ] 1 dl rødvin, eller grøntsagsboullion (Mealie)
|
||||||
|
- [ ] 1 dl tør hvidvin (Mealie)
|
||||||
|
- [ ] 1 fed hvidløg, presset (Mealie)
|
||||||
|
- [ ] 1 gulerødder, i tern (Mealie)
|
||||||
|
- [ ] 1 håndfuld persille (Mealie)
|
||||||
|
- [ ] 1 kg kartofler (Mealie)
|
||||||
|
- [ ] 1 knivspids muskatnød, fintrevet (Mealie)
|
||||||
|
- [ ] 1 knivspids sød paprika (Mealie)
|
||||||
|
- [ ] 1 løg, i tern (Mealie)
|
||||||
|
- [ ] 1 rødløg (Mealie)
|
||||||
|
- [ ] 1 spsk dijon sennep (Mealie)
|
||||||
|
- [ ] 1 spsk hampefrø (Mealie)
|
||||||
|
- [ ] 1 spsk honning (Mealie)
|
||||||
|
- [ ] 1 spsk majsstivelse (Mealie)
|
||||||
|
- [ ] 1 spsk smør, til stegning (Mealie)
|
||||||
|
- [ ] 1 spsk solsikkekerner (Mealie)
|
||||||
|
- [ ] 1 squash, groftrevet (Mealie)
|
||||||
|
- [ ] 1 tsk tørret timian (Mealie)
|
||||||
|
- [ ] 10 g smør, til stegning (Mealie)
|
||||||
|
- [ ] 100 g parmesan, fintrevet (Mealie)
|
||||||
|
- [ ] 12 tarteletter (Mealie)
|
||||||
|
- [ ] 2 æg (Mealie)
|
||||||
|
- [ ] 2 dl hønsebouillon (Mealie)
|
||||||
|
- [ ] 2 hønsebryst (Mealie)
|
||||||
|
- [ ] 2 laurbærblade (Mealie)
|
||||||
|
- [ ] 2 spsk grov sennep (Mealie)
|
||||||
|
- [ ] 2 spsk rosiner (Mealie)
|
||||||
|
- [ ] 2 spsk smør (Mealie)
|
||||||
|
- [ ] 2 tsk tørret oregano (Mealie)
|
||||||
|
- [ ] 200 g aspargessnitter (Mealie)
|
||||||
|
- [ ] 200 g lasagneplader (Mealie)
|
||||||
|
- [ ] 25 g smør, til stegning (Mealie)
|
||||||
|
- [ ] 3 dl grøntsagsbouillon (Mealie)
|
||||||
|
- [ ] 3 dl mælk (Mealie)
|
||||||
|
- [ ] 3 gulerødder, groftrevet (Mealie)
|
||||||
|
- [ ] 300 g torskefilet (Mealie)
|
||||||
|
- [ ] 4 dl mælk (Mealie)
|
||||||
|
- [ ] 4 fed hvidløg, fintrevet (Mealie)
|
||||||
|
- [ ] 4 gulerødder, groftrevet (Mealie)
|
||||||
|
- [ ] 40 g smør (Mealie)
|
||||||
|
- [ ] 5 stængler bladselleri, groftrevet (Mealie)
|
||||||
|
|
||||||
|
## Frost
|
||||||
|
- [ ] 1 dl piskefløde (Mealie)
|
||||||
|
- [ ] 2 spsk mayonnaise (Mealie)
|
||||||
|
- [ ] 2,50 dl piskefløde (Mealie)
|
||||||
|
|
||||||
|
## Frugt & Grønt
|
||||||
|
- [ ] 0,50 citron, saft herfra (Mealie)
|
||||||
|
- [ ] 0,50 øko citron (Mealie)
|
||||||
|
- [ ] 15 g koncentreret tomatpuré (Mealie)
|
||||||
|
- [ ] 2 æble, groftrevet (Mealie)
|
||||||
|
- [ ] 50 g koncentreret tomatpuré (Mealie)
|
||||||
|
- [ ] 75 g soltørrede tomater i olie, finthakket (Mealie)
|
||||||
|
- [ ] 800 g hakkede tomater på dåse (Mealie)
|
||||||
|
|
||||||
|
## Kolonial
|
||||||
|
- [ ] 1 håndfuld frisk basilikum (Mealie)
|
||||||
|
- [ ] 1 håndfuld frisk dild (Mealie)
|
||||||
|
- [ ] 1 spsk olivenolie (Mealie)
|
||||||
|
- [ ] 1 spsk olivenolie, til stegning (Mealie)
|
||||||
|
- [ ] 1 tsk olivenolie (Mealie)
|
||||||
|
- [ ] 2 spsk hvedemel (Mealie)
|
||||||
|
- [ ] 2 spsk olivenolie (Mealie)
|
||||||
|
- [ ] 25 g hvedemel (Mealie)
|
||||||
|
- [ ] 3 dl basmati ris, kogt efter anvisning på emballagen (Mealie)
|
||||||
|
- [ ] 30 g hvedemel (Mealie)
|
||||||
|
- [ ] 40 g hvedemel (Mealie)
|
||||||
|
- [ ] 400 g pasta (Mealie)
|
||||||
|
- [ ] flagesalt (Mealie)
|
||||||
|
- [ ] salt og friskkværnet peber (Mealie)
|
||||||
|
|
||||||
|
## Kød & Fisk
|
||||||
|
- [ ] 0,50 dl frisk estragon, finthakket (Mealie)
|
||||||
|
- [ ] 1 løg, finthakket (Mealie)
|
||||||
|
- [ ] 2 løg, finthakket (Mealie)
|
||||||
|
- [ ] 300 g laks, uden skind (Mealie)
|
||||||
|
- [ ] 4 fed hvidløg, finthakket (Mealie)
|
||||||
|
- [ ] 4 kyllingebryst (Mealie)
|
||||||
|
- [ ] 400 g hakket oksekød (Mealie)
|
||||||
|
- [ ] 600 g kyllingebryst (Mealie)
|
||||||
|
- [ ] 75 g bacon, i skiver (Mealie)
|
||||||
|
|
||||||
|
## Mejeri & Æg
|
||||||
|
- [ ] 125 g frisk mozzarella (Mealie)
|
||||||
|
- [ ] 400 g haricots verts, fra frost (Mealie)
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
# Bilka ToGo - Klar til bestilling
|
||||||
|
|
||||||
|
Kryds af hvad I allerede har i huset, og bestil resten.
|
||||||
|
Plan-vindue: 2026-04-24 til 2026-04-30
|
||||||
|
|
||||||
|
## Andet
|
||||||
|
- [ ] 0,50 squash
|
||||||
|
- [ ] 0,50 tsk mediumstærk karry
|
||||||
|
- [ ] 0,50 tsk tørret rosmarin
|
||||||
|
- [ ] 1 dl cremefraiche 38%
|
||||||
|
- [ ] 1 dl grøntsagsbouillon
|
||||||
|
- [ ] 1 dl pickles
|
||||||
|
- [ ] 1 dl rødvin, eller grøntsagsboullion
|
||||||
|
- [ ] 1 dl tør hvidvin
|
||||||
|
- [ ] 1 fed hvidløg, presset
|
||||||
|
- [ ] 1 gulerødder, i tern
|
||||||
|
- [ ] 1 håndfuld persille
|
||||||
|
- [ ] 1 kg kartofler
|
||||||
|
- [ ] 1 knivspids muskatnød, fintrevet
|
||||||
|
- [ ] 1 knivspids sød paprika
|
||||||
|
- [ ] 1 løg, i tern
|
||||||
|
- [ ] 1 rødløg
|
||||||
|
- [ ] 1 spsk dijon sennep
|
||||||
|
- [ ] 1 spsk hampefrø
|
||||||
|
- [ ] 1 spsk honning
|
||||||
|
- [ ] 1 spsk majsstivelse
|
||||||
|
- [ ] 1 spsk smør, til stegning
|
||||||
|
- [ ] 1 spsk solsikkekerner
|
||||||
|
- [ ] 1 squash, groftrevet
|
||||||
|
- [ ] 1 tsk tørret timian
|
||||||
|
- [ ] 10 g smør, til stegning
|
||||||
|
- [ ] 100 g parmesan, fintrevet
|
||||||
|
- [ ] 12 tarteletter
|
||||||
|
- [ ] 2 æg
|
||||||
|
- [ ] 2 dl hønsebouillon
|
||||||
|
- [ ] 2 hønsebryst
|
||||||
|
- [ ] 2 laurbærblade
|
||||||
|
- [ ] 2 spsk grov sennep
|
||||||
|
- [ ] 2 spsk rosiner
|
||||||
|
- [ ] 2 spsk smør
|
||||||
|
- [ ] 2 tsk tørret oregano
|
||||||
|
- [ ] 200 g aspargessnitter
|
||||||
|
- [ ] 200 g lasagneplader
|
||||||
|
- [ ] 25 g smør, til stegning
|
||||||
|
- [ ] 3 dl grøntsagsbouillon
|
||||||
|
- [ ] 3 dl mælk
|
||||||
|
- [ ] 3 gulerødder, groftrevet
|
||||||
|
- [ ] 300 g torskefilet
|
||||||
|
- [ ] 4 dl mælk
|
||||||
|
- [ ] 4 fed hvidløg, fintrevet
|
||||||
|
- [ ] 4 gulerødder, groftrevet
|
||||||
|
- [ ] 40 g smør
|
||||||
|
- [ ] 5 stængler bladselleri, groftrevet
|
||||||
|
|
||||||
|
## Frost
|
||||||
|
- [ ] 1 dl piskefløde
|
||||||
|
- [ ] 2 spsk mayonnaise
|
||||||
|
- [ ] 2,50 dl piskefløde
|
||||||
|
|
||||||
|
## Frugt & Grønt
|
||||||
|
- [ ] 0,50 citron, saft herfra
|
||||||
|
- [ ] 0,50 øko citron
|
||||||
|
- [ ] 15 g koncentreret tomatpuré
|
||||||
|
- [ ] 2 æble, groftrevet
|
||||||
|
- [ ] 50 g koncentreret tomatpuré
|
||||||
|
- [ ] 75 g soltørrede tomater i olie, finthakket
|
||||||
|
- [ ] 800 g hakkede tomater på dåse
|
||||||
|
|
||||||
|
## Kolonial
|
||||||
|
- [ ] 1 håndfuld frisk basilikum
|
||||||
|
- [ ] 1 håndfuld frisk dild
|
||||||
|
- [ ] 1 spsk olivenolie
|
||||||
|
- [ ] 1 spsk olivenolie, til stegning
|
||||||
|
- [ ] 1 tsk olivenolie
|
||||||
|
- [ ] 2 spsk hvedemel
|
||||||
|
- [ ] 2 spsk olivenolie
|
||||||
|
- [ ] 25 g hvedemel
|
||||||
|
- [ ] 3 dl basmati ris, kogt efter anvisning på emballagen
|
||||||
|
- [ ] 30 g hvedemel
|
||||||
|
- [ ] 40 g hvedemel
|
||||||
|
- [ ] 400 g pasta
|
||||||
|
- [ ] flagesalt
|
||||||
|
- [ ] salt og friskkværnet peber
|
||||||
|
|
||||||
|
## Kød & Fisk
|
||||||
|
- [ ] 0,50 dl frisk estragon, finthakket
|
||||||
|
- [ ] 1 løg, finthakket
|
||||||
|
- [ ] 2 løg, finthakket
|
||||||
|
- [ ] 300 g laks, uden skind
|
||||||
|
- [ ] 4 fed hvidløg, finthakket
|
||||||
|
- [ ] 4 kyllingebryst
|
||||||
|
- [ ] 400 g hakket oksekød
|
||||||
|
- [ ] 600 g kyllingebryst
|
||||||
|
- [ ] 75 g bacon, i skiver
|
||||||
|
|
||||||
|
## Mejeri & Æg
|
||||||
|
- [ ] 125 g frisk mozzarella
|
||||||
|
- [ ] 400 g haricots verts, fra frost
|
||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"count": 44, "items": [{"date": "2026-04-28", "recipe": {"name": "Hjemmelavet pizza", "slug": "hjemmelavet-pizza"}}, {"date": "2026-04-29", "recipe": {"name": "M\u00fcsli", "slug": "musli-opskrift"}}, {"date": "2026-04-30", "recipe": {"name": "Lakselasagne med spinat", "slug": "lakselasagne"}}, {"date": "2026-05-01", "recipe": {"name": "Mexicansk burger med guacamole", "slug": "mexicansk-burger-med-hjemmelavet-guacamole"}}, {"date": "2026-05-02", "recipe": {"name": "Gr\u00f8ntsagsfad", "slug": "grontsagsfad"}}, {"date": "2026-05-03", "recipe": {"name": "Hummus", "slug": "humus"}}, {"date": "2026-05-04", "recipe": {"name": "Indisk Curry med kylling", "slug": "indisk_curry_med_kylling"}}, {"date": "2026-05-05", "recipe": {"name": "Pariserbøf", "slug": "pariserbof"}}, {"date": "2026-05-06", "recipe": {"name": "Skipperlabskovs", "slug": "skipperlabskovs"}}, {"date": "2026-05-07", "recipe": {"name": "Pizzasnegle", "slug": "pizzasnegle"}}, {"date": "2026-05-08", "recipe": {"name": "Luksus stjerneskud", "slug": "luksus-stjerneskud"}}, {"date": "2026-05-09", "recipe": {"name": "Jordbærsalat med feta og pekan", "slug": "jordbaer-og-fetasalat-med-glaserede-pecannoedder"}}, {"date": "2026-05-10", "recipe": {"name": "Spaghetti Bolognese", "slug": "spaghetti-bolognese"}}, {"date": "2026-05-11", "recipe": {"name": "Kylling med cornflakes", "slug": "kylling-med-cornflakes"}}, {"date": "2026-05-12", "recipe": {"name": "Tarteletter med høns", "slug": "tarteletter-hoens-asparges"}}, {"date": "2026-05-13", "recipe": {"name": "One pot pasta", "slug": "one-pot-pasta"}}, {"date": "2026-05-14", "recipe": {"name": "Cacio e Pepe", "slug": "cacio-e-pepe"}}, {"date": "2026-05-15", "recipe": {"name": "Citronpasta", "slug": "citronpasta"}}, {"date": "2026-05-16", "recipe": {"name": "Blomk\u00e5lssalat", "slug": "blomkaalssalat"}}, {"date": "2026-05-17", "recipe": {"name": "One Pot Pasta med kødsovs", "slug": "koedsovs-onepotpasta"}}, {"date": "2026-05-18", "recipe": {"name": "Kikærtegryde", "slug": "kikaertegryde"}}, {"date": "2026-05-19", "recipe": {"name": "Mørbradbøffer", "slug": "moerbradboeffer-med-bloede-loeg"}}, {"date": "2026-05-20", "recipe": {"name": "Stegt spidskål", "slug": "stegt-spidskaal"}}, {"date": "2026-05-21", "recipe": {"name": "Bagt kylling", "slug": "bagt-kylling"}}, {"date": "2026-05-22", "recipe": {"name": "Ristede kartoffelskiver", "slug": "ristede-kartoffelskiver-fad"}}, {"date": "2026-05-23", "recipe": {"name": "Vikingegryde", "slug": "vikingegryde"}}, {"date": "2026-05-24", "recipe": {"name": "Spidskålssalat", "slug": "spidskaalssalat-opskrift"}}, {"date": "2026-05-25", "recipe": {"name": "Kylling med parmesan", "slug": "kylling-med-parmesan"}}, {"date": "2026-05-26", "recipe": {"name": "Bagt broccoli", "slug": "bagt-broccoli"}}, {"date": "2026-05-27", "recipe": {"name": "Pastasalat med pesto", "slug": "pastasalat-med-pesto"}}, {"date": "2026-05-28", "recipe": {"name": "Nachos bowl", "slug": "nachos-bowl"}}, {"date": "2026-05-29", "recipe": {"name": "Barbecuesauce", "slug": "barbecuesauce"}}, {"date": "2026-05-30", "recipe": {"name": "Congee Rissuppe", "slug": "congee-rissuppe-kylling"}}, {"date": "2026-05-31", "recipe": {"name": "Macaroni and Cheese", "slug": "macaroni-and-cheese"}}, {"date": "2026-06-01", "recipe": {"name": "Halloween dessert", "slug": "halloween-dessert"}}, {"date": "2026-06-02", "recipe": {"name": "Feta pasta", "slug": "feta-pasta-med-tomat"}}, {"date": "2026-06-03", "recipe": {"name": "Tortellini i fad", "slug": "tortellini-i-fad"}}, {"date": "2026-06-04", "recipe": {"name": "Flyvende Jacob", "slug": "flyvende-jacob"}}, {"date": "2026-04-26", "recipe": {"name": "Lasagne", "slug": "lasagne"}}, {"date": "2026-04-24", "recipe": {"name": "Fiskefrikadeller med remoulade og r\u00e5kost", "slug": "fiskefrikadeller-med-remoulade-og-rakost"}}, {"date": "2026-04-27", "recipe": {"name": "Marry Me Chicken", "slug": "marry-me-chicken"}}, {"date": "2026-04-25", "recipe": {"name": "Cheeseburger Tacos", "slug": "cheeseburger-tacos"}}, {"date": "2026-04-23", "recipe": {"name": "K\u00e5lfad med hakket oksek\u00f8d", "slug": "kalfad-med-hakket-oksekod"}}, {"date": "2026-04-22", "recipe": {"name": "Kylling med honning og sennep", "slug": "kylling-i-fad-med-honning-og-sennep"}}]}
|
{"count": 7, "items": [{"date": "2026-05-23", "recipe": {"name": "Ingen hjemme", "slug": ""}}, {"date": "2026-05-24", "recipe": {"name": "Kylling i cremet sennepssauce", "slug": "kylling-i-cremet-sennepssauce"}}, {"date": "2026-05-22", "recipe": {"name": "M\u00f8rbradb\u00f8ffer med bl\u00f8de l\u00f8g og fl\u00f8desauce", "slug": "morbradboffer-med-blode-log-og-flodesauce"}}, {"date": "2026-05-21", "recipe": {"name": "Frikadeller", "slug": "frikadeller"}}, {"date": "2026-05-20", "recipe": {"name": "Rester fra mandag (Lasagne)", "slug": ""}}, {"date": "2026-05-19", "recipe": {"name": "Rester fra s\u00f8ndag (Flyvende Jacob)", "slug": ""}}, {"date": "2026-05-18", "recipe": {"name": "Lasagne", "slug": "lasagne"}}]}
|
||||||
Reference in New Issue
Block a user