Compare commits
62 Commits
4e13ccf658
...
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 |
+1
-1
@@ -1 +1 @@
|
|||||||
2026.4.3
|
2026.5.1
|
||||||
@@ -38,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/
|
||||||
@@ -66,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
|
||||||
|
|||||||
@@ -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/
|
||||||
|
|||||||
+26
-338
@@ -33,275 +33,19 @@ cards:
|
|||||||
name: I morgen
|
name: I morgen
|
||||||
icon: mdi:briefcase-outline
|
icon: mdi:briefcase-outline
|
||||||
|
|
||||||
# 👨👩👧👦 Familien – tryk for at toggle syg/rask
|
# 👨👩👧👦 Familien
|
||||||
- type: grid
|
- type: glance
|
||||||
columns: 4
|
entities:
|
||||||
square: false
|
- entity: person.daniel_schusler_dethlefsen
|
||||||
cards:
|
|
||||||
|
|
||||||
- type: custom:button-card
|
|
||||||
entity: person.daniel_schusler_dethlefsen
|
|
||||||
name: Daniel
|
name: Daniel
|
||||||
show_name: true
|
- entity: person.claus_dethlefsen
|
||||||
show_state: false
|
|
||||||
show_label: true
|
|
||||||
show_icon: false
|
|
||||||
show_entity_picture: true
|
|
||||||
label: >
|
|
||||||
[[[
|
|
||||||
const s = entity.state;
|
|
||||||
const sick = states['input_select.daniel_status'] &&
|
|
||||||
states['input_select.daniel_status'].state === 'syg';
|
|
||||||
const loc = s === 'home' ? 'Hjemme' : s === 'not_home' ? 'Ude' : s;
|
|
||||||
return sick ? loc + ' · Syg' : loc;
|
|
||||||
]]]
|
|
||||||
styles:
|
|
||||||
card:
|
|
||||||
- padding: 8px 4px
|
|
||||||
- border: >
|
|
||||||
[[[
|
|
||||||
return states['input_select.daniel_status'] &&
|
|
||||||
states['input_select.daniel_status'].state === 'syg'
|
|
||||||
? '2px solid rgba(220,50,50,0.8)' : '2px solid transparent';
|
|
||||||
]]]
|
|
||||||
- border-radius: 12px
|
|
||||||
entity_picture:
|
|
||||||
- width: 60px
|
|
||||||
- height: 60px
|
|
||||||
- border-radius: 50%
|
|
||||||
- object-fit: cover
|
|
||||||
- filter: >
|
|
||||||
[[[
|
|
||||||
return states['input_select.daniel_status'] &&
|
|
||||||
states['input_select.daniel_status'].state === 'syg'
|
|
||||||
? 'grayscale(100%)' : 'none';
|
|
||||||
]]]
|
|
||||||
name:
|
|
||||||
- font-size: 12px
|
|
||||||
- font-weight: 600
|
|
||||||
- padding-top: 6px
|
|
||||||
- color: >
|
|
||||||
[[[
|
|
||||||
return states['input_select.daniel_status'] &&
|
|
||||||
states['input_select.daniel_status'].state === 'syg'
|
|
||||||
? 'rgb(220,50,50)' : 'var(--primary-text-color)';
|
|
||||||
]]]
|
|
||||||
label:
|
|
||||||
- font-size: 10px
|
|
||||||
- color: var(--secondary-text-color)
|
|
||||||
- padding-bottom: 2px
|
|
||||||
tap_action:
|
|
||||||
action: call-service
|
|
||||||
service: input_select.select_option
|
|
||||||
service_data:
|
|
||||||
entity_id: input_select.daniel_status
|
|
||||||
option: >
|
|
||||||
[[[
|
|
||||||
return states['input_select.daniel_status'] &&
|
|
||||||
states['input_select.daniel_status'].state === 'syg'
|
|
||||||
? 'hjemme' : 'syg';
|
|
||||||
]]]
|
|
||||||
hold_action:
|
|
||||||
action: more-info
|
|
||||||
entity: person.daniel_schusler_dethlefsen
|
|
||||||
|
|
||||||
- type: custom:button-card
|
|
||||||
entity: person.claus_dethlefsen
|
|
||||||
name: Claus
|
name: Claus
|
||||||
show_name: true
|
- entity: person.anne_schusler_dethlefsen
|
||||||
show_state: false
|
|
||||||
show_label: true
|
|
||||||
show_icon: false
|
|
||||||
show_entity_picture: true
|
|
||||||
label: >
|
|
||||||
[[[
|
|
||||||
const s = entity.state;
|
|
||||||
const sick = states['input_select.claus_status'] &&
|
|
||||||
states['input_select.claus_status'].state === 'syg';
|
|
||||||
const loc = s === 'home' ? 'Hjemme' : s === 'not_home' ? 'Ude' : s;
|
|
||||||
return sick ? loc + ' · Syg' : loc;
|
|
||||||
]]]
|
|
||||||
styles:
|
|
||||||
card:
|
|
||||||
- padding: 8px 4px
|
|
||||||
- border: >
|
|
||||||
[[[
|
|
||||||
return states['input_select.claus_status'] &&
|
|
||||||
states['input_select.claus_status'].state === 'syg'
|
|
||||||
? '2px solid rgba(220,50,50,0.8)' : '2px solid transparent';
|
|
||||||
]]]
|
|
||||||
- border-radius: 12px
|
|
||||||
entity_picture:
|
|
||||||
- width: 60px
|
|
||||||
- height: 60px
|
|
||||||
- border-radius: 50%
|
|
||||||
- object-fit: cover
|
|
||||||
- filter: >
|
|
||||||
[[[
|
|
||||||
return states['input_select.claus_status'] &&
|
|
||||||
states['input_select.claus_status'].state === 'syg'
|
|
||||||
? 'grayscale(100%)' : 'none';
|
|
||||||
]]]
|
|
||||||
name:
|
|
||||||
- font-size: 12px
|
|
||||||
- font-weight: 600
|
|
||||||
- padding-top: 6px
|
|
||||||
- color: >
|
|
||||||
[[[
|
|
||||||
return states['input_select.claus_status'] &&
|
|
||||||
states['input_select.claus_status'].state === 'syg'
|
|
||||||
? 'rgb(220,50,50)' : 'var(--primary-text-color)';
|
|
||||||
]]]
|
|
||||||
label:
|
|
||||||
- font-size: 10px
|
|
||||||
- color: var(--secondary-text-color)
|
|
||||||
- padding-bottom: 2px
|
|
||||||
tap_action:
|
|
||||||
action: call-service
|
|
||||||
service: input_select.select_option
|
|
||||||
service_data:
|
|
||||||
entity_id: input_select.claus_status
|
|
||||||
option: >
|
|
||||||
[[[
|
|
||||||
return states['input_select.claus_status'] &&
|
|
||||||
states['input_select.claus_status'].state === 'syg'
|
|
||||||
? 'hjemme' : 'syg';
|
|
||||||
]]]
|
|
||||||
hold_action:
|
|
||||||
action: more-info
|
|
||||||
entity: person.claus_dethlefsen
|
|
||||||
|
|
||||||
- type: custom:button-card
|
|
||||||
entity: person.anne_schusler_dethlefsen
|
|
||||||
name: Anne
|
name: Anne
|
||||||
show_name: true
|
- entity: person.andreas_schusler_dethlefsen
|
||||||
show_state: false
|
|
||||||
show_label: true
|
|
||||||
show_icon: false
|
|
||||||
show_entity_picture: true
|
|
||||||
label: >
|
|
||||||
[[[
|
|
||||||
const s = entity.state;
|
|
||||||
const sick = states['input_select.anne_status'] &&
|
|
||||||
states['input_select.anne_status'].state === 'syg';
|
|
||||||
const loc = s === 'home' ? 'Hjemme' : s === 'not_home' ? 'Ude' : s;
|
|
||||||
return sick ? loc + ' · Syg' : loc;
|
|
||||||
]]]
|
|
||||||
styles:
|
|
||||||
card:
|
|
||||||
- padding: 8px 4px
|
|
||||||
- border: >
|
|
||||||
[[[
|
|
||||||
return states['input_select.anne_status'] &&
|
|
||||||
states['input_select.anne_status'].state === 'syg'
|
|
||||||
? '2px solid rgba(220,50,50,0.8)' : '2px solid transparent';
|
|
||||||
]]]
|
|
||||||
- border-radius: 12px
|
|
||||||
entity_picture:
|
|
||||||
- width: 60px
|
|
||||||
- height: 60px
|
|
||||||
- border-radius: 50%
|
|
||||||
- object-fit: cover
|
|
||||||
- filter: >
|
|
||||||
[[[
|
|
||||||
return states['input_select.anne_status'] &&
|
|
||||||
states['input_select.anne_status'].state === 'syg'
|
|
||||||
? 'grayscale(100%)' : 'none';
|
|
||||||
]]]
|
|
||||||
name:
|
|
||||||
- font-size: 12px
|
|
||||||
- font-weight: 600
|
|
||||||
- padding-top: 6px
|
|
||||||
- color: >
|
|
||||||
[[[
|
|
||||||
return states['input_select.anne_status'] &&
|
|
||||||
states['input_select.anne_status'].state === 'syg'
|
|
||||||
? 'rgb(220,50,50)' : 'var(--primary-text-color)';
|
|
||||||
]]]
|
|
||||||
label:
|
|
||||||
- font-size: 10px
|
|
||||||
- color: var(--secondary-text-color)
|
|
||||||
- padding-bottom: 2px
|
|
||||||
tap_action:
|
|
||||||
action: call-service
|
|
||||||
service: input_select.select_option
|
|
||||||
service_data:
|
|
||||||
entity_id: input_select.anne_status
|
|
||||||
option: >
|
|
||||||
[[[
|
|
||||||
return states['input_select.anne_status'] &&
|
|
||||||
states['input_select.anne_status'].state === 'syg'
|
|
||||||
? 'hjemme' : 'syg';
|
|
||||||
]]]
|
|
||||||
hold_action:
|
|
||||||
action: more-info
|
|
||||||
entity: person.anne_schusler_dethlefsen
|
|
||||||
|
|
||||||
- type: custom:button-card
|
|
||||||
entity: person.andreas_schusler_dethlefsen
|
|
||||||
name: Andreas
|
name: Andreas
|
||||||
show_name: true
|
- entity: binary_sensor.family_presence
|
||||||
show_state: false
|
name: Familie
|
||||||
show_label: true
|
|
||||||
show_icon: false
|
|
||||||
show_entity_picture: true
|
|
||||||
label: >
|
|
||||||
[[[
|
|
||||||
const s = entity.state;
|
|
||||||
const sick = states['input_select.andreas_status'] &&
|
|
||||||
states['input_select.andreas_status'].state === 'syg';
|
|
||||||
const loc = s === 'home' ? 'Hjemme' : s === 'not_home' ? 'Ude' : s;
|
|
||||||
return sick ? loc + ' · Syg' : loc;
|
|
||||||
]]]
|
|
||||||
styles:
|
|
||||||
card:
|
|
||||||
- padding: 8px 4px
|
|
||||||
- border: >
|
|
||||||
[[[
|
|
||||||
return states['input_select.andreas_status'] &&
|
|
||||||
states['input_select.andreas_status'].state === 'syg'
|
|
||||||
? '2px solid rgba(220,50,50,0.8)' : '2px solid transparent';
|
|
||||||
]]]
|
|
||||||
- border-radius: 12px
|
|
||||||
entity_picture:
|
|
||||||
- width: 60px
|
|
||||||
- height: 60px
|
|
||||||
- border-radius: 50%
|
|
||||||
- object-fit: cover
|
|
||||||
- filter: >
|
|
||||||
[[[
|
|
||||||
return states['input_select.andreas_status'] &&
|
|
||||||
states['input_select.andreas_status'].state === 'syg'
|
|
||||||
? 'grayscale(100%)' : 'none';
|
|
||||||
]]]
|
|
||||||
name:
|
|
||||||
- font-size: 12px
|
|
||||||
- font-weight: 600
|
|
||||||
- padding-top: 6px
|
|
||||||
- color: >
|
|
||||||
[[[
|
|
||||||
return states['input_select.andreas_status'] &&
|
|
||||||
states['input_select.andreas_status'].state === 'syg'
|
|
||||||
? 'rgb(220,50,50)' : 'var(--primary-text-color)';
|
|
||||||
]]]
|
|
||||||
label:
|
|
||||||
- font-size: 10px
|
|
||||||
- color: var(--secondary-text-color)
|
|
||||||
- padding-bottom: 2px
|
|
||||||
tap_action:
|
|
||||||
action: call-service
|
|
||||||
service: input_select.select_option
|
|
||||||
service_data:
|
|
||||||
entity_id: input_select.andreas_status
|
|
||||||
option: >
|
|
||||||
[[[
|
|
||||||
return states['input_select.andreas_status'] &&
|
|
||||||
states['input_select.andreas_status'].state === 'syg'
|
|
||||||
? 'hjemme' : 'syg';
|
|
||||||
]]]
|
|
||||||
hold_action:
|
|
||||||
action: more-info
|
|
||||||
entity: person.andreas_schusler_dethlefsen
|
|
||||||
|
|
||||||
# 🪟 Gardiner
|
# 🪟 Gardiner
|
||||||
- type: grid
|
- type: grid
|
||||||
@@ -389,66 +133,31 @@ cards:
|
|||||||
icon: mdi:floor-plan
|
icon: mdi:floor-plan
|
||||||
tap_action:
|
tap_action:
|
||||||
action: call-service
|
action: call-service
|
||||||
service: script.turn_on
|
service: button.press
|
||||||
target:
|
target:
|
||||||
entity_id: script.roborock_manuelt_kokken
|
entity_id: button.roborock_s8_pro_ultra_kokken_bryggers
|
||||||
|
|
||||||
- type: button
|
- type: button
|
||||||
name: Syd
|
name: Syd
|
||||||
icon: mdi:floor-plan
|
icon: mdi:floor-plan
|
||||||
tap_action:
|
tap_action:
|
||||||
action: call-service
|
action: call-service
|
||||||
service: script.turn_on
|
service: button.press
|
||||||
target:
|
target:
|
||||||
entity_id: script.roborock_manuelt_syd
|
entity_id: button.roborock_s8_pro_ultra_syd
|
||||||
|
|
||||||
- type: button
|
- type: button
|
||||||
name: Mop
|
name: Mop
|
||||||
icon: mdi:floor-plan
|
icon: mdi:floor-plan
|
||||||
tap_action:
|
tap_action:
|
||||||
action: call-service
|
action: call-service
|
||||||
service: script.turn_on
|
service: button.press
|
||||||
target:
|
target:
|
||||||
entity_id: script.roborock_manuelt_mop
|
entity_id: button.roborock_s8_pro_ultra_vac_followed_by_mop
|
||||||
|
|
||||||
- type: custom:button-card
|
- type: button
|
||||||
entity: vacuum.roborock_s8_pro_ultra
|
name: Gå til dock
|
||||||
name: Start
|
icon: mdi:home-import-outline
|
||||||
icon: mdi:robot-vacuum
|
|
||||||
tap_action:
|
|
||||||
action: call-service
|
|
||||||
service: script.turn_on
|
|
||||||
target:
|
|
||||||
entity_id: script.roborock_manuelt_start
|
|
||||||
state:
|
|
||||||
- value: cleaning
|
|
||||||
name: Dock
|
|
||||||
styles:
|
|
||||||
card:
|
|
||||||
- background-color: rgba(255, 200, 0, 0.25)
|
|
||||||
- border: 1px solid rgba(255, 200, 0, 0.8)
|
|
||||||
tap_action:
|
|
||||||
action: call-service
|
|
||||||
service: vacuum.return_to_base
|
|
||||||
target:
|
|
||||||
entity_id: vacuum.roborock_s8_pro_ultra
|
|
||||||
- value: returning
|
|
||||||
name: Dock
|
|
||||||
styles:
|
|
||||||
card:
|
|
||||||
- background-color: rgba(255, 200, 0, 0.25)
|
|
||||||
- border: 1px solid rgba(255, 200, 0, 0.8)
|
|
||||||
tap_action:
|
|
||||||
action: call-service
|
|
||||||
service: vacuum.return_to_base
|
|
||||||
target:
|
|
||||||
entity_id: vacuum.roborock_s8_pro_ultra
|
|
||||||
- value: paused
|
|
||||||
name: Dock
|
|
||||||
styles:
|
|
||||||
card:
|
|
||||||
- background-color: rgba(255, 200, 0, 0.25)
|
|
||||||
- border: 1px solid rgba(255, 200, 0, 0.8)
|
|
||||||
tap_action:
|
tap_action:
|
||||||
action: call-service
|
action: call-service
|
||||||
service: vacuum.return_to_base
|
service: vacuum.return_to_base
|
||||||
@@ -462,25 +171,8 @@ cards:
|
|||||||
entity: input_datetime.ploeneklipper_sidst_koert
|
entity: input_datetime.ploeneklipper_sidst_koert
|
||||||
show_icon: false
|
show_icon: false
|
||||||
show_name: true
|
show_name: true
|
||||||
show_state: false
|
show_state: true
|
||||||
show_label: true
|
|
||||||
name: Sidst klippet
|
name: Sidst klippet
|
||||||
label: >
|
|
||||||
[[[
|
|
||||||
const s = entity.state;
|
|
||||||
if (!s || s === 'unknown') return 'Ukendt';
|
|
||||||
const d = new Date(s.replace(' ', 'T'));
|
|
||||||
if (isNaN(d)) return s;
|
|
||||||
const now = new Date();
|
|
||||||
const isToday = d.toDateString() === now.toDateString();
|
|
||||||
const yesterday = new Date(now);
|
|
||||||
yesterday.setDate(now.getDate() - 1);
|
|
||||||
const isYesterday = d.toDateString() === yesterday.toDateString();
|
|
||||||
const t = d.toLocaleTimeString('da-DK', {hour: '2-digit', minute: '2-digit'});
|
|
||||||
if (isToday) return 'I dag ' + t;
|
|
||||||
if (isYesterday) return 'I g\u00e5r ' + t;
|
|
||||||
return d.toLocaleDateString('da-DK', {day: 'numeric', month: 'short'}) + ' ' + t;
|
|
||||||
]]]
|
|
||||||
tap_action:
|
tap_action:
|
||||||
action: none
|
action: none
|
||||||
styles:
|
styles:
|
||||||
@@ -490,29 +182,25 @@ cards:
|
|||||||
- font-size: 11px
|
- font-size: 11px
|
||||||
- color: var(--secondary-text-color)
|
- color: var(--secondary-text-color)
|
||||||
- padding-bottom: 4px
|
- padding-bottom: 4px
|
||||||
label:
|
state:
|
||||||
- white-space: normal
|
- white-space: normal
|
||||||
- word-break: break-word
|
- word-break: break-word
|
||||||
- line-height: 1.2
|
- line-height: 1.2
|
||||||
- font-size: 13px
|
- font-size: 13px
|
||||||
- text-align: center
|
- text-align: center
|
||||||
|
|
||||||
- type: custom:button-card
|
- type: button
|
||||||
entity: lawn_mower.husqvarna_automower
|
|
||||||
name: Klip
|
name: Klip
|
||||||
icon: mdi:robot-mower
|
icon: mdi:robot-mower
|
||||||
tap_action:
|
tap_action:
|
||||||
action: call-service
|
action: call-service
|
||||||
service: script.turn_on
|
service: lawn_mower.start_mowing
|
||||||
target:
|
target:
|
||||||
entity_id: script.ploeneklipper_manuelt_start
|
entity_id: lawn_mower.husqvarna_automower
|
||||||
state:
|
|
||||||
- value: mowing
|
- type: button
|
||||||
name: Stop
|
name: Stop
|
||||||
styles:
|
icon: mdi:home-import-outline
|
||||||
card:
|
|
||||||
- background-color: rgba(255, 200, 0, 0.25)
|
|
||||||
- border: 1px solid rgba(255, 200, 0, 0.8)
|
|
||||||
tap_action:
|
tap_action:
|
||||||
action: call-service
|
action: call-service
|
||||||
service: lawn_mower.dock
|
service: lawn_mower.dock
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ sections:
|
|||||||
action: call-service
|
action: call-service
|
||||||
service: light.turn_off
|
service: light.turn_off
|
||||||
service_data:
|
service_data:
|
||||||
entity_id: light.bedroom
|
entity_id: light.sovevaerelse
|
||||||
styles:
|
styles:
|
||||||
card:
|
card:
|
||||||
- height: 64px
|
- height: 64px
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -79,7 +86,7 @@ cards:
|
|||||||
{%- set slug = recipe.slug if recipe else '' -%}
|
{%- set slug = recipe.slug if recipe else '' -%}
|
||||||
{%- set label = 'I dag' if offset == 0 else days[day.weekday()] -%}
|
{%- set label = 'I dag' if offset == 0 else days[day.weekday()] -%}
|
||||||
{%- if slug -%}
|
{%- if slug -%}
|
||||||
{%- set ns.rows = ns.rows + "| **" + label + "** | [" + name + "](http://anneclaus.dk:9925/g/home/r/" + slug + ") |\n" -%}
|
{%- set ns.rows = ns.rows + "| **" + label + "** | [" + name + "](https://mealie.anneclaus.dk/g/home/r/" + slug + ") |\n" -%}
|
||||||
{%- elif name -%}
|
{%- elif name -%}
|
||||||
{%- set ns.rows = ns.rows + "| **" + label + "** | " + name + " |\n" -%}
|
{%- set ns.rows = ns.rows + "| **" + label + "** | " + name + " |\n" -%}
|
||||||
{%- else -%}
|
{%- else -%}
|
||||||
@@ -92,21 +99,598 @@ cards:
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
{{ ns.rows }}
|
{{ ns.rows }}
|
||||||
|
|
||||||
# 🛒 Bilka ToGo - opdater og vis kryds-af liste
|
# 🎵 Sonos Køkken
|
||||||
- type: vertical-stack
|
- type: media-control
|
||||||
|
entity: media_player.kokken
|
||||||
|
name: Sonos Køkken
|
||||||
|
|
||||||
|
- type: grid
|
||||||
|
columns: 2
|
||||||
|
square: false
|
||||||
cards:
|
cards:
|
||||||
- type: markdown
|
- type: custom:button-card
|
||||||
content: |
|
name: Volumen ned
|
||||||
## Bilka ToGo - kryds-af
|
icon: mdi:volume-minus
|
||||||
Tryk på knappen for at hente ingredienser fra ugeplanen (fredag–torsdag).
|
|
||||||
|
|
||||||
- type: button
|
|
||||||
name: Opdater Bilka ToGo-liste nu
|
|
||||||
icon: mdi:cart-check
|
|
||||||
tap_action:
|
tap_action:
|
||||||
action: call-service
|
action: perform-action
|
||||||
service: script.mealie_shopping_refresh
|
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:
|
||||||
|
card:
|
||||||
|
- height: 72px
|
||||||
|
- font-size: 16px
|
||||||
|
- background: var(--primary-color)
|
||||||
|
icon:
|
||||||
|
- color: white
|
||||||
|
- width: 36px
|
||||||
|
name:
|
||||||
|
- color: white
|
||||||
|
- font-size: 13px
|
||||||
|
|
||||||
|
- type: custom:button-card
|
||||||
|
name: Volumen op
|
||||||
|
icon: mdi:volume-plus
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.volume_up
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
hold_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.volume_up
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 72px
|
||||||
|
- font-size: 16px
|
||||||
|
- background: var(--primary-color)
|
||||||
|
icon:
|
||||||
|
- color: white
|
||||||
|
- width: 36px
|
||||||
|
name:
|
||||||
|
- color: white
|
||||||
|
- font-size: 13px
|
||||||
|
|
||||||
|
- type: grid
|
||||||
|
columns: 3
|
||||||
|
square: false
|
||||||
|
cards:
|
||||||
|
- type: custom:button-card
|
||||||
|
name: DR P3
|
||||||
|
icon: mdi:radio
|
||||||
|
tap_action:
|
||||||
|
action: perform-action
|
||||||
|
perform_action: media_player.select_source
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kokken
|
||||||
|
data:
|
||||||
|
source: "0 DR P3"
|
||||||
|
styles:
|
||||||
|
card:
|
||||||
|
- height: 52px
|
||||||
|
- padding: 6px 8px
|
||||||
|
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
|
||||||
|
|
||||||
- type: iframe
|
|
||||||
url: /local/bilka_togo_checklist.html
|
|
||||||
aspect_ratio: 100%
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -159,6 +159,8 @@ sections:
|
|||||||
series:
|
series:
|
||||||
- entity: sensor.kontor_motion_temperatur
|
- entity: sensor.kontor_motion_temperatur
|
||||||
name: Hue
|
name: Hue
|
||||||
|
- entity: sensor.annes_havesensor_indoor_temperature
|
||||||
|
name: Havesensor inde
|
||||||
- entity: climate.kontor
|
- entity: climate.kontor
|
||||||
attribute: current_temperature
|
attribute: current_temperature
|
||||||
name: Roth aktuelt
|
name: Roth aktuelt
|
||||||
@@ -357,6 +359,84 @@ sections:
|
|||||||
curve: stepline
|
curve: stepline
|
||||||
color: "#ff8800"
|
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
|
# Indstillinger: Komforttemperaturer og sænkninger
|
||||||
- type: grid
|
- type: grid
|
||||||
cards:
|
cards:
|
||||||
@@ -372,6 +452,9 @@ sections:
|
|||||||
- entity: input_number.varme_komfort_lille_bad
|
- entity: input_number.varme_komfort_lille_bad
|
||||||
- entity: input_number.varme_komfort_badevarelse
|
- entity: input_number.varme_komfort_badevarelse
|
||||||
- entity: input_number.varme_komfort_stue
|
- 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
|
- type: entities
|
||||||
title: Sænkninger og ferie
|
title: Sænkninger og ferie
|
||||||
@@ -381,8 +464,6 @@ sections:
|
|||||||
- entity: input_number.varme_nat_saenkning
|
- entity: input_number.varme_nat_saenkning
|
||||||
- entity: input_number.varme_vaek_saenkning
|
- entity: input_number.varme_vaek_saenkning
|
||||||
- entity: input_number.varme_ferie_temp
|
- entity: input_number.varme_ferie_temp
|
||||||
- entity: input_datetime.vacation_end
|
|
||||||
name: Hjemkomst (ferie slutter)
|
|
||||||
|
|
||||||
- type: button
|
- type: button
|
||||||
name: Genberegn varme nu
|
name: Genberegn varme nu
|
||||||
@@ -391,12 +472,19 @@ sections:
|
|||||||
action: call-service
|
action: call-service
|
||||||
service: script.varme_recalculate
|
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
|
# Ventilposition
|
||||||
- type: grid
|
- type: grid
|
||||||
cards:
|
cards:
|
||||||
- type: gauge
|
- type: gauge
|
||||||
entity: sensor.fjernvarme_ventil_anbefalet
|
entity: sensor.fjernvarme_ventil_3_ugers_gennemsnit
|
||||||
name: Anbefalet ventilposition (1–5)
|
name: Anbefalet ventilposition – 3 ugers snit (1–5)
|
||||||
min: 1
|
min: 1
|
||||||
max: 5
|
max: 5
|
||||||
needle: true
|
needle: true
|
||||||
@@ -414,9 +502,11 @@ sections:
|
|||||||
|
|
||||||
- type: markdown
|
- type: markdown
|
||||||
content: |-
|
content: |-
|
||||||
**{{ state_attr('sensor.fjernvarme_ventil_anbefalet', 'anbefaling') }}**
|
**Anbefalet stilling (3 ugers snit): {{ states('sensor.fjernvarme_ventil_3_ugers_gennemsnit') | float(0) | round(1) }}**
|
||||||
|
|
||||||
Udetemperatur: {{ state_attr('sensor.fjernvarme_ventil_anbefalet', 'udetemperatur') }}°C
|
Ø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:
|
Gælder for begge manuelle hoveddrejehaner:
|
||||||
- Roth-fordeler (sauna)
|
- Roth-fordeler (sauna)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
+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,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*
|
||||||
+73
-10
@@ -1,6 +1,25 @@
|
|||||||
# Ø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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -10,7 +29,7 @@
|
|||||||
|
|
||||||
| Antal | Rum | Beskrivelse | Status |
|
| Antal | Rum | Beskrivelse | Status |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| 1 | Badeværelse | Danfoss Ally TRV (Zigbee) | 🔧 To be fixed (kan ikke skrues ordentligt fast) |
|
| 1 | Badeværelse | Danfoss Ally TRV (Zigbee) | ✅ Monteret og online (7. maj 2026) |
|
||||||
|
|
||||||
**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.
|
||||||
|
|
||||||
@@ -46,22 +65,42 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### 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
|
### Vindues-/dørsensorer – Aqara
|
||||||
|
|
||||||
| Antal | Placering | Beskrivelse | Status |
|
| Antal | Placering | Beskrivelse | Status |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| 2 | Køkken | Aqara kontaktsensor (vindue) | ⬜ Ønsket |
|
| 4 | Køkken | Aqara kontaktsensor (vindue) | ⬜ Ønsket |
|
||||||
| 1 | Bryggers | Aqara kontaktsensor (vindue) | ⬜ Ønsket |
|
| 2 | Bryggers | Aqara kontaktsensor (vindue) | ⬜ Ønsket |
|
||||||
| 2 | Forgang | Aqara kontaktsensor (vindue/dør) | ⬜ Ønsket |
|
| 4 | Forgang | Aqara kontaktsensor (vindue/dør) | ⬜ Ønsket |
|
||||||
| 1 | Stue | Aqara kontaktsensor (vindue) | ⬜ Ø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:** 6 stk. Aqara Door & Window Sensor (Zigbee)
|
**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.
|
**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:** Forgang-sensorer dækker sandsynligvis yderdøren + en vinduesramme — afklar placering inden køb.
|
**Bemærk:** Afklar præcis placering (vinduesramme vs. dørfals) inden køb, særligt forgang og fordør/bagdør.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -69,11 +108,11 @@
|
|||||||
|
|
||||||
| Antal | Beskrivelse | Status |
|
| Antal | Beskrivelse | Status |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| 1 stk | Crucial 4GB DDR4-2666 SODIMM — CT4G4SFS8266 | ⬜ Ønsket |
|
| 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.
|
**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.
|
||||||
|
|
||||||
**Model:** DS920+ har 4GB loddet + 1 ledig SODIMM-slot. Crucial CT4G4SFS8266 giver 4+4=8GB total. Pris ca. 150–200 kr. hos Komplett/Dustin.
|
**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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -98,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 ✅
|
||||||
|
|||||||
@@ -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`
|
||||||
@@ -214,31 +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'
|
|
||||||
- condition: template
|
|
||||||
value_template: >-
|
|
||||||
{{ not is_state('input_select.anne_status', 'syg') and
|
|
||||||
not is_state('input_select.claus_status', 'syg') }}
|
|
||||||
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
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -62,11 +92,91 @@
|
|||||||
action:
|
action:
|
||||||
- delay:
|
- delay:
|
||||||
minutes: >
|
minutes: >
|
||||||
{{ states('input_number.badevaerelse_timeout_day') | int if is_dag
|
{% if is_state('input_boolean.badevaerelse_manuel_tilstand', 'on') %}
|
||||||
else states('input_number.badevaerelse_timeout_night') | int }}
|
10
|
||||||
|
{% elif is_dag %}
|
||||||
|
{{ states('input_number.badevaerelse_timeout_day') | int }}
|
||||||
|
{% else %}
|
||||||
|
{{ states('input_number.badevaerelse_timeout_night') | int }}
|
||||||
|
{% endif %}
|
||||||
- condition: state
|
- condition: state
|
||||||
entity_id: binary_sensor.badevaerelse_bevaegelse
|
entity_id: binary_sensor.badevaerelse_bevaegelse
|
||||||
state: "off"
|
state: "off"
|
||||||
- service: light.turn_off
|
- service: light.turn_off
|
||||||
target:
|
target:
|
||||||
area_id: badevaerelse
|
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
|
||||||
|
target:
|
||||||
|
area_id: badevaerelse
|
||||||
|
|||||||
@@ -173,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:
|
||||||
|
|||||||
@@ -24,7 +24,8 @@
|
|||||||
{% set t = now().strftime('%H%M') | int %}
|
{% set t = now().strftime('%H%M') | int %}
|
||||||
{% if 600 <= t < 1600 %}morgen
|
{% if 600 <= t < 1600 %}morgen
|
||||||
{% elif 1600 <= t < 1900 %}eftermiddag
|
{% elif 1600 <= t < 1900 %}eftermiddag
|
||||||
{% elif 1900 <= t %}aften
|
{% elif 1900 <= t < 2100 %}aften_lys
|
||||||
|
{% elif 2100 <= t %}aften
|
||||||
{% else %}nat{% endif %}
|
{% else %}nat{% endif %}
|
||||||
timeout_min: >
|
timeout_min: >
|
||||||
{% set t = now().strftime('%H%M') | int %}
|
{% set t = now().strftime('%H%M') | int %}
|
||||||
@@ -32,7 +33,9 @@
|
|||||||
{{ states('input_number.stue_timeout_morgen') | int }}
|
{{ states('input_number.stue_timeout_morgen') | int }}
|
||||||
{% elif 1600 <= t < 1900 %}
|
{% elif 1600 <= t < 1900 %}
|
||||||
{{ states('input_number.stue_timeout_eftermiddag') | int }}
|
{{ states('input_number.stue_timeout_eftermiddag') | int }}
|
||||||
{% elif 1900 <= t %}
|
{% elif 1900 <= t < 2100 %}
|
||||||
|
{{ states('input_number.stue_timeout_aften') | int }}
|
||||||
|
{% elif 2100 <= t %}
|
||||||
{{ states('input_number.stue_timeout_aften') | int }}
|
{{ states('input_number.stue_timeout_aften') | int }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ states('input_number.stue_timeout_nat') | int }}
|
{{ states('input_number.stue_timeout_nat') | int }}
|
||||||
@@ -50,6 +53,15 @@
|
|||||||
{{ states('sensor.stue_belysningsstyrke') | int < lux_limit }}
|
{{ states('sensor.stue_belysningsstyrke') | int < lux_limit }}
|
||||||
sequence:
|
sequence:
|
||||||
- choose:
|
- 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:
|
- conditions:
|
||||||
- condition: template
|
- condition: template
|
||||||
value_template: "{{ dagperiode == 'morgen' }}"
|
value_template: "{{ dagperiode == 'morgen' }}"
|
||||||
@@ -64,6 +76,20 @@
|
|||||||
- service: scene.turn_on
|
- service: scene.turn_on
|
||||||
target:
|
target:
|
||||||
entity_id: scene.stue_annes_favorit
|
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:
|
||||||
|
- service: scene.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: scene.stue_annes_favorit
|
||||||
default:
|
default:
|
||||||
- service: scene.turn_on
|
- service: scene.turn_on
|
||||||
target:
|
target:
|
||||||
@@ -76,7 +102,7 @@
|
|||||||
id: motion_off
|
id: motion_off
|
||||||
- condition: template
|
- condition: template
|
||||||
value_template: >
|
value_template: >
|
||||||
{{ dagperiode != 'aften' or
|
{{ dagperiode not in ('aften','aften_lys') or
|
||||||
is_state('media_player.samsung_s95ca_55_3', 'off') }}
|
is_state('media_player.samsung_s95ca_55_3', 'off') }}
|
||||||
sequence:
|
sequence:
|
||||||
- delay:
|
- delay:
|
||||||
@@ -86,7 +112,7 @@
|
|||||||
state: "off"
|
state: "off"
|
||||||
- condition: template
|
- condition: template
|
||||||
value_template: >
|
value_template: >
|
||||||
{{ dagperiode != 'aften' or
|
{{ dagperiode not in ('aften','aften_lys') or
|
||||||
is_state('media_player.samsung_s95ca_55_3', 'off') }}
|
is_state('media_player.samsung_s95ca_55_3', 'off') }}
|
||||||
- service: light.turn_off
|
- service: light.turn_off
|
||||||
target:
|
target:
|
||||||
@@ -97,7 +123,7 @@
|
|||||||
- condition: trigger
|
- condition: trigger
|
||||||
id: tv_off
|
id: tv_off
|
||||||
- condition: template
|
- condition: template
|
||||||
value_template: "{{ dagperiode == 'aften' }}"
|
value_template: "{{ dagperiode in ('aften','aften_lys') }}"
|
||||||
sequence:
|
sequence:
|
||||||
- delay:
|
- delay:
|
||||||
minutes: "{{ timeout_min }}"
|
minutes: "{{ timeout_min }}"
|
||||||
|
|||||||
@@ -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,13 +1,3 @@
|
|||||||
- id: mealie_update_mealplan
|
|
||||||
alias: "Mealie opdater madplan"
|
|
||||||
trigger:
|
|
||||||
- platform: homeassistant
|
|
||||||
event: start
|
|
||||||
- platform: time_pattern
|
|
||||||
minutes: "/30"
|
|
||||||
action:
|
|
||||||
- service: shell_command.mealie_update
|
|
||||||
|
|
||||||
- id: mealie_generate_bilka_checklist_wednesday
|
- id: mealie_generate_bilka_checklist_wednesday
|
||||||
alias: "Mealie indkøbsliste - onsdag morgen"
|
alias: "Mealie indkøbsliste - onsdag morgen"
|
||||||
trigger:
|
trigger:
|
||||||
@@ -19,3 +9,14 @@
|
|||||||
- wed
|
- wed
|
||||||
action:
|
action:
|
||||||
- service: script.mealie_shopping_refresh
|
- service: script.mealie_shopping_refresh
|
||||||
|
|
||||||
|
- id: mealie_update_mealplan
|
||||||
|
alias: "Mealie opdater madplan"
|
||||||
|
trigger:
|
||||||
|
- platform: homeassistant
|
||||||
|
event: start
|
||||||
|
- platform: time_pattern
|
||||||
|
minutes: "/30"
|
||||||
|
action:
|
||||||
|
- 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
|
||||||
@@ -124,12 +124,72 @@
|
|||||||
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'
|
- alias: 'Plæneklipper - reset manuelt startet flag'
|
||||||
description: 'Nulstil manuelt-startet flag når klipperen dokker'
|
description: 'Nulstil manuelt-startet flag når klipperen dokker efter kl. 20 (i dagtimerne håndteres genstarten af genstart-automation)'
|
||||||
trigger:
|
trigger:
|
||||||
- platform: state
|
- platform: state
|
||||||
entity_id: lawn_mower.husqvarna_automower
|
entity_id: lawn_mower.husqvarna_automower
|
||||||
to: 'docked'
|
to: 'docked'
|
||||||
|
condition:
|
||||||
|
- condition: time
|
||||||
|
after: '20:00:00'
|
||||||
action:
|
action:
|
||||||
- service: input_boolean.turn_off
|
- service: input_boolean.turn_off
|
||||||
target:
|
target:
|
||||||
entity_id: input_boolean.ploeneklipper_manuelt_startet
|
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) }}.
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -7,7 +7,8 @@
|
|||||||
##################################################
|
##################################################
|
||||||
|
|
||||||
# ---- Andreas ----
|
# ---- 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 ----
|
# ---- Daniel ----
|
||||||
|
|
||||||
@@ -60,7 +61,6 @@
|
|||||||
entity_id:
|
entity_id:
|
||||||
- switch.sonos_alarm_1782 # Soft wakeup
|
- switch.sonos_alarm_1782 # Soft wakeup
|
||||||
- switch.sonos_alarm_298 # Badeværelse
|
- switch.sonos_alarm_298 # Badeværelse
|
||||||
- switch.sonos_alarm_1899 # Badeværelse afsted
|
|
||||||
|
|
||||||
- alias: "Syg - Soveværelse - genaktiver alarmer"
|
- alias: "Syg - Soveværelse - genaktiver alarmer"
|
||||||
id: syg_sovevaerelse_genaktiver_alarmer
|
id: syg_sovevaerelse_genaktiver_alarmer
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -75,6 +75,17 @@
|
|||||||
action:
|
action:
|
||||||
- service: script.varme_recalculate
|
- 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"
|
- alias: "Varme - Ferieopvarmning: Start 2 dage før hjemkomst"
|
||||||
id: varme_ferie_forvarm
|
id: varme_ferie_forvarm
|
||||||
description: >
|
description: >
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
id: varme_vindue_trigger
|
id: varme_vindue_trigger
|
||||||
description: "Kalder varme_recalculate når et vindue eller terrassedøren skifter tilstand"
|
description: "Kalder varme_recalculate når et vindue eller terrassedøren skifter tilstand"
|
||||||
mode: queued
|
mode: queued
|
||||||
max: 3
|
max: 10
|
||||||
trigger:
|
trigger:
|
||||||
- platform: state
|
- platform: state
|
||||||
entity_id:
|
entity_id:
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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"
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ varme_komfort_sovevaerelse:
|
|||||||
max: 28
|
max: 28
|
||||||
step: 0.5
|
step: 0.5
|
||||||
unit_of_measurement: "°C"
|
unit_of_measurement: "°C"
|
||||||
initial: 18
|
initial: 20
|
||||||
icon: mdi:thermometer
|
icon: mdi:thermometer
|
||||||
|
|
||||||
varme_komfort_kontor:
|
varme_komfort_kontor:
|
||||||
@@ -32,7 +32,7 @@ varme_komfort_kontor:
|
|||||||
max: 28
|
max: 28
|
||||||
step: 0.5
|
step: 0.5
|
||||||
unit_of_measurement: "°C"
|
unit_of_measurement: "°C"
|
||||||
initial: 19
|
initial: 20
|
||||||
icon: mdi:thermometer
|
icon: mdi:thermometer
|
||||||
|
|
||||||
varme_komfort_gang:
|
varme_komfort_gang:
|
||||||
@@ -41,7 +41,7 @@ varme_komfort_gang:
|
|||||||
max: 28
|
max: 28
|
||||||
step: 0.5
|
step: 0.5
|
||||||
unit_of_measurement: "°C"
|
unit_of_measurement: "°C"
|
||||||
initial: 19
|
initial: 20
|
||||||
icon: mdi:thermometer
|
icon: mdi:thermometer
|
||||||
|
|
||||||
varme_komfort_forgang:
|
varme_komfort_forgang:
|
||||||
@@ -50,7 +50,7 @@ varme_komfort_forgang:
|
|||||||
max: 28
|
max: 28
|
||||||
step: 0.5
|
step: 0.5
|
||||||
unit_of_measurement: "°C"
|
unit_of_measurement: "°C"
|
||||||
initial: 22
|
initial: 24
|
||||||
icon: mdi:thermometer
|
icon: mdi:thermometer
|
||||||
|
|
||||||
varme_komfort_lille_bad:
|
varme_komfort_lille_bad:
|
||||||
@@ -59,7 +59,7 @@ varme_komfort_lille_bad:
|
|||||||
max: 28
|
max: 28
|
||||||
step: 0.5
|
step: 0.5
|
||||||
unit_of_measurement: "°C"
|
unit_of_measurement: "°C"
|
||||||
initial: 22
|
initial: 24
|
||||||
icon: mdi:thermometer
|
icon: mdi:thermometer
|
||||||
|
|
||||||
varme_komfort_badevarelse:
|
varme_komfort_badevarelse:
|
||||||
@@ -68,7 +68,7 @@ varme_komfort_badevarelse:
|
|||||||
max: 28
|
max: 28
|
||||||
step: 0.5
|
step: 0.5
|
||||||
unit_of_measurement: "°C"
|
unit_of_measurement: "°C"
|
||||||
initial: 20
|
initial: 21.5
|
||||||
icon: mdi:thermometer
|
icon: mdi:thermometer
|
||||||
|
|
||||||
varme_komfort_stue:
|
varme_komfort_stue:
|
||||||
@@ -77,7 +77,7 @@ varme_komfort_stue:
|
|||||||
max: 28
|
max: 28
|
||||||
step: 0.5
|
step: 0.5
|
||||||
unit_of_measurement: "°C"
|
unit_of_measurement: "°C"
|
||||||
initial: 24
|
initial: 25
|
||||||
icon: mdi:thermometer
|
icon: mdi:thermometer
|
||||||
|
|
||||||
# Globale sænkninger
|
# Globale sænkninger
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -4,28 +4,48 @@ godnat_sovevaerelse:
|
|||||||
sequence:
|
sequence:
|
||||||
- service: light.turn_on
|
- service: light.turn_on
|
||||||
target:
|
target:
|
||||||
entity_id: light.bedroom
|
entity_id:
|
||||||
|
- light.claus
|
||||||
|
- light.anne
|
||||||
|
- light.sov_dor
|
||||||
|
- light.sov_midt
|
||||||
|
- light.sov_vindue
|
||||||
data:
|
data:
|
||||||
brightness_pct: 40
|
brightness_pct: 40
|
||||||
transition: 300
|
transition: 300
|
||||||
- delay: "00:05:00"
|
- delay: "00:05:00"
|
||||||
- service: light.turn_on
|
- service: light.turn_on
|
||||||
target:
|
target:
|
||||||
entity_id: light.bedroom
|
entity_id:
|
||||||
|
- light.claus
|
||||||
|
- light.anne
|
||||||
|
- light.sov_dor
|
||||||
|
- light.sov_midt
|
||||||
|
- light.sov_vindue
|
||||||
data:
|
data:
|
||||||
brightness_pct: 10
|
brightness_pct: 10
|
||||||
transition: 300
|
transition: 300
|
||||||
- delay: "00:05:00"
|
- delay: "00:05:00"
|
||||||
- service: light.turn_on
|
- service: light.turn_on
|
||||||
target:
|
target:
|
||||||
entity_id: light.bedroom
|
entity_id:
|
||||||
|
- light.claus
|
||||||
|
- light.anne
|
||||||
|
- light.sov_dor
|
||||||
|
- light.sov_midt
|
||||||
|
- light.sov_vindue
|
||||||
data:
|
data:
|
||||||
brightness_pct: 1
|
brightness_pct: 1
|
||||||
transition: 300
|
transition: 300
|
||||||
- delay: "00:05:00"
|
- delay: "00:05:00"
|
||||||
- service: light.turn_off
|
- service: light.turn_off
|
||||||
target:
|
target:
|
||||||
entity_id: light.bedroom
|
entity_id:
|
||||||
|
- light.claus
|
||||||
|
- light.anne
|
||||||
|
- light.sov_dor
|
||||||
|
- light.sov_midt
|
||||||
|
- light.sov_vindue
|
||||||
data:
|
data:
|
||||||
transition: 5
|
transition: 5
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -4,5 +4,5 @@ mealie_shopping_refresh:
|
|||||||
- service: shell_command.mealie_shopping_merge
|
- service: shell_command.mealie_shopping_merge
|
||||||
- service: notify.mobile_app_claus_iphone_15pro
|
- service: notify.mobile_app_claus_iphone_15pro
|
||||||
data:
|
data:
|
||||||
title: "Bilka ToGo liste opdateret"
|
title: "Indkøbsliste opdateret i Mealie"
|
||||||
message: "Mealie-indkøb (fredag til torsdag) er flettet med Keep-basislisten."
|
message: "Indkøbslisten 'Bilka ToGo' er opdateret med opskrifter fra fredag til torsdag."
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -47,3 +47,13 @@ ploeneklipper_manuelt_start:
|
|||||||
- service: lawn_mower.start_mowing
|
- service: lawn_mower.start_mowing
|
||||||
target:
|
target:
|
||||||
entity_id: lawn_mower.husqvarna_automower
|
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
|
||||||
|
|||||||
@@ -223,3 +223,18 @@ varme_recalculate:
|
|||||||
data:
|
data:
|
||||||
hvac_mode: heat
|
hvac_mode: heat
|
||||||
temperature: "{{ ferie_temp }}"
|
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"
|
||||||
@@ -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,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}")
|
||||||
@@ -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": []}
|
||||||
|
|||||||
@@ -273,11 +273,7 @@ def main() -> None:
|
|||||||
shopping_list_id = reset_shopping_list(MEALIE_BASE_URL, token, TARGET_SHOPPING_LIST_NAME, shopping_list_id)
|
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)
|
recipe_ids = get_mealplan_recipe_ids(MEALIE_BASE_URL, token, start_date, end_date)
|
||||||
added_recipes = add_recipes_to_shopping_list(MEALIE_BASE_URL, token, shopping_list_id, recipe_ids)
|
add_recipes_to_shopping_list(MEALIE_BASE_URL, token, shopping_list_id, recipe_ids)
|
||||||
|
|
||||||
mealie_items = fetch_mealie_items(MEALIE_BASE_URL, token, shopping_list_id=shopping_list_id)
|
|
||||||
items = build_items(mealie_items)
|
|
||||||
write_outputs(root, items, start_date, end_date)
|
|
||||||
|
|
||||||
print(
|
print(
|
||||||
'OK: '
|
'OK: '
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
# ########################################
|
# ########################################
|
||||||
|
|||||||
@@ -18,96 +18,83 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>🛒 Bilka ToGo</h1>
|
<h1>🛒 Bilka ToGo</h1>
|
||||||
<p class="sub">Plan 24/04 – 30/04 · 82 varer</p>
|
<p class="sub">Plan 15/05 – 21/05 · 69 varer</p>
|
||||||
<table>
|
<table>
|
||||||
<tr><th colspan="2" class="cat">Andet</th></tr>
|
<tr><th colspan="2" class="cat">Andet</th></tr>
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>0,50 squash</td></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 mediumstærk karry</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>0,50 tsk tørret rosmarin</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 dl cremefraiche 38%</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 grøntsagsbouillon</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 pickles</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 dl rødvin, eller grøntsagsboullion</td></tr>
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>1 dl tør hvidvin</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 fed hvidløg, presset</td></tr>
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>1 gulerødder, i tern</td></tr>
|
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>1 håndfuld persille</td></tr>
|
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>1 kg kartofler</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 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 knivspids sød paprika</td></tr>
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>1 løg, i tern</td></tr>
|
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>1 rødløg</td></tr>
|
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>1 spsk dijon sennep</td></tr>
|
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>1 spsk hampefrø</td></tr>
|
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>1 spsk honning</td></tr>
|
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>1 spsk majsstivelse</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 spsk smør, til stegning</td></tr>
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>1 spsk solsikkekerner</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 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>1 tsk tørret timian</td></tr>
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>10 g smør, til stegning</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 parmesan, fintrevet</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>12 tarteletter</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 æg</td></tr>
|
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>2 dl hønsebouillon</td></tr>
|
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>2 hønsebryst</td></tr>
|
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>2 laurbærblade</td></tr>
|
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>2 spsk grov sennep</td></tr>
|
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>2 spsk rosiner</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 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>2 tsk tørret oregano</td></tr>
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>200 g aspargessnitter</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>200 g lasagneplader</td></tr>
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>25 g smør, til stegning</td></tr>
|
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>3 dl grøntsagsbouillon</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>3 dl mælk</td></tr>
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>3 gulerødder, groftrevet</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>300 g torskefilet</td></tr>
|
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>4 dl mælk</td></tr>
|
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>4 fed hvidløg, fintrevet</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 gulerødder, groftrevet</td></tr>
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>40 g smør</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>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><th colspan="2" class="cat">Frost</th></tr>
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>1 dl piskefløde</td></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 spsk mayonnaise</td></tr>
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>2,50 dl piskefløde</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><th colspan="2" class="cat">Frugt & Grønt</th></tr>
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>0,50 citron, saft herfra</td></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>0,50 øko citron</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>15 g koncentreret tomatpuré</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>2 æble, groftrevet</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>50 g koncentreret tomatpuré</td></tr>
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>75 g soltørrede tomater i olie, finthakket</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>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><th colspan="2" class="cat">Kolonial</th></tr>
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>1 håndfuld frisk basilikum</td></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 håndfuld frisk dild</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</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 olivenolie, til stegning</td></tr>
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>1 tsk olivenolie</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 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>2 spsk olivenolie</td></tr>
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>25 g hvedemel</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 dl basmati ris, kogt efter anvisning på emballagen</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>30 g hvedemel</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>40 g hvedemel</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>400 g pasta</td></tr>
|
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>flagesalt</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 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><th colspan="2" class="cat">Kød & Fisk</th></tr>
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>0,50 dl frisk estragon, finthakket</td></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>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 løg, finthakket</td></tr>
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>300 g laks, uden skind</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 fed hvidløg, finthakket</td></tr>
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>4 kyllingebryst</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>400 g hakket oksekød</td></tr>
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>600 g kyllingebryst</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><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><th colspan="2" class="cat">Mejeri & Æg</th></tr>
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>125 g frisk mozzarella</td></tr>
|
<tr><td class="cb"><input type="checkbox"></td><td>125 g frisk mozzarella</td></tr>
|
||||||
<tr><td class="cb"><input type="checkbox"></td><td>400 g haricots verts, fra frost</td></tr>
|
|
||||||
</table>
|
</table>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+84
-136
@@ -1,96 +1,60 @@
|
|||||||
{
|
{
|
||||||
"count": 82,
|
"count": 69,
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"name": "0,50 squash",
|
"name": "0,50 dl hvidvin",
|
||||||
"category": "andet"
|
"category": "andet"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "0,50 tsk mediumstærk karry",
|
"name": "0,50 tsk stødt spidskommen",
|
||||||
"category": "andet"
|
"category": "andet"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "0,50 tsk tørret rosmarin",
|
"name": "1 æg",
|
||||||
"category": "andet"
|
"category": "andet"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "1 dl cremefraiche 38%",
|
"name": "1 æggehvider",
|
||||||
"category": "andet"
|
"category": "andet"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "1 dl grøntsagsbouillon",
|
"name": "1 dl cremefraiche 18 %",
|
||||||
"category": "andet"
|
"category": "andet"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "1 dl pickles",
|
"name": "1 dl mælk",
|
||||||
"category": "andet"
|
"category": "andet"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "1 dl rødvin, eller grøntsagsboullion",
|
"name": "1 dl rødvin, eller grøntsagsboullion",
|
||||||
"category": "andet"
|
"category": "andet"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "1 dl tør hvidvin",
|
|
||||||
"category": "andet"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "1 fed hvidløg, presset",
|
"name": "1 fed hvidløg, presset",
|
||||||
"category": "andet"
|
"category": "andet"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "1 gulerødder, i tern",
|
|
||||||
"category": "andet"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "1 håndfuld persille",
|
|
||||||
"category": "andet"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "1 kg kartofler",
|
|
||||||
"category": "andet"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "1 knivspids muskatnød, fintrevet",
|
"name": "1 knivspids muskatnød, fintrevet",
|
||||||
"category": "andet"
|
"category": "andet"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "1 knivspids røget paprika",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "1 knivspids sød paprika",
|
"name": "1 knivspids sød paprika",
|
||||||
"category": "andet"
|
"category": "andet"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "1 løg, i tern",
|
|
||||||
"category": "andet"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "1 rødløg",
|
|
||||||
"category": "andet"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "1 spsk dijon sennep",
|
|
||||||
"category": "andet"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "1 spsk hampefrø",
|
|
||||||
"category": "andet"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "1 spsk honning",
|
|
||||||
"category": "andet"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "1 spsk majsstivelse",
|
|
||||||
"category": "andet"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "1 spsk smør, til stegning",
|
"name": "1 spsk smør, til stegning",
|
||||||
"category": "andet"
|
"category": "andet"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "1 spsk solsikkekerner",
|
"name": "1 squash, groftrevet",
|
||||||
"category": "andet"
|
"category": "andet"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "1 squash, groftrevet",
|
"name": "1 tsk sød paprika",
|
||||||
"category": "andet"
|
"category": "andet"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -98,39 +62,15 @@
|
|||||||
"category": "andet"
|
"category": "andet"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "10 g smør, til stegning",
|
"name": "100 g rejer",
|
||||||
"category": "andet"
|
"category": "andet"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "100 g parmesan, fintrevet",
|
"name": "100 g stenbiderrogn",
|
||||||
"category": "andet"
|
"category": "andet"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "12 tarteletter",
|
"name": "2 spsk kapers (valgfrit)",
|
||||||
"category": "andet"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "2 æg",
|
|
||||||
"category": "andet"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "2 dl hønsebouillon",
|
|
||||||
"category": "andet"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "2 hønsebryst",
|
|
||||||
"category": "andet"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "2 laurbærblade",
|
|
||||||
"category": "andet"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "2 spsk grov sennep",
|
|
||||||
"category": "andet"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "2 spsk rosiner",
|
|
||||||
"category": "andet"
|
"category": "andet"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -141,40 +81,16 @@
|
|||||||
"name": "2 tsk tørret oregano",
|
"name": "2 tsk tørret oregano",
|
||||||
"category": "andet"
|
"category": "andet"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "200 g aspargessnitter",
|
|
||||||
"category": "andet"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "200 g lasagneplader",
|
"name": "200 g lasagneplader",
|
||||||
"category": "andet"
|
"category": "andet"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "25 g smør, til stegning",
|
|
||||||
"category": "andet"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "3 dl grøntsagsbouillon",
|
|
||||||
"category": "andet"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "3 dl mælk",
|
"name": "3 dl mælk",
|
||||||
"category": "andet"
|
"category": "andet"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "3 gulerødder, groftrevet",
|
"name": "4 grønne asparges",
|
||||||
"category": "andet"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "300 g torskefilet",
|
|
||||||
"category": "andet"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "4 dl mælk",
|
|
||||||
"category": "andet"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "4 fed hvidløg, fintrevet",
|
|
||||||
"category": "andet"
|
"category": "andet"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -182,7 +98,11 @@
|
|||||||
"category": "andet"
|
"category": "andet"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "40 g smør",
|
"name": "4 hvide asparges",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "4 skiver brød",
|
||||||
"category": "andet"
|
"category": "andet"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -190,8 +110,16 @@
|
|||||||
"category": "andet"
|
"category": "andet"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "1 dl piskefløde",
|
"name": "50 g rasp",
|
||||||
"category": "frost"
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "75 g smør",
|
||||||
|
"category": "andet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "8 rødspættefilet",
|
||||||
|
"category": "andet"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "2 spsk mayonnaise",
|
"name": "2 spsk mayonnaise",
|
||||||
@@ -202,19 +130,31 @@
|
|||||||
"category": "frost"
|
"category": "frost"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "0,50 citron, saft herfra",
|
"name": "1 citron – saft og fintrevet skal",
|
||||||
"category": "frugt & grønt"
|
"category": "frugt & grønt"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "0,50 øko citron",
|
"name": "1 citron, 1 spsk saft herfra",
|
||||||
"category": "frugt & grønt"
|
"category": "frugt & grønt"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "15 g koncentreret tomatpuré",
|
"name": "1 citron, skåret i både",
|
||||||
"category": "frugt & grønt"
|
"category": "frugt & grønt"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "2 æble, groftrevet",
|
"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"
|
"category": "frugt & grønt"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -222,7 +162,7 @@
|
|||||||
"category": "frugt & grønt"
|
"category": "frugt & grønt"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "75 g soltørrede tomater i olie, finthakket",
|
"name": "70 g blandet salat",
|
||||||
"category": "frugt & grønt"
|
"category": "frugt & grønt"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -230,15 +170,15 @@
|
|||||||
"category": "frugt & grønt"
|
"category": "frugt & grønt"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "1 håndfuld frisk basilikum",
|
"name": "Grøntsager eller salat",
|
||||||
|
"category": "frugt & grønt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1 dl mild chilipasta",
|
||||||
"category": "kolonial"
|
"category": "kolonial"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "1 håndfuld frisk dild",
|
"name": "1 spsk hvedemel",
|
||||||
"category": "kolonial"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "1 spsk olivenolie",
|
|
||||||
"category": "kolonial"
|
"category": "kolonial"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -246,7 +186,15 @@
|
|||||||
"category": "kolonial"
|
"category": "kolonial"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "1 tsk olivenolie",
|
"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"
|
"category": "kolonial"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -258,27 +206,19 @@
|
|||||||
"category": "kolonial"
|
"category": "kolonial"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "25 g hvedemel",
|
"name": "3 dl basmati ris",
|
||||||
"category": "kolonial"
|
"category": "kolonial"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "3 dl basmati ris, kogt efter anvisning på emballagen",
|
"name": "3 spsk olivenolie, til stegning",
|
||||||
"category": "kolonial"
|
"category": "kolonial"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "30 g hvedemel",
|
"name": "50 g saltede peanuts",
|
||||||
"category": "kolonial"
|
"category": "kolonial"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "40 g hvedemel",
|
"name": "Ris eller kartofler",
|
||||||
"category": "kolonial"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "400 g pasta",
|
|
||||||
"category": "kolonial"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "flagesalt",
|
|
||||||
"category": "kolonial"
|
"category": "kolonial"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -286,7 +226,11 @@
|
|||||||
"category": "kolonial"
|
"category": "kolonial"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "0,50 dl frisk estragon, finthakket",
|
"name": "Salt og peber",
|
||||||
|
"category": "kolonial"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1 fed hvidløg, finthakket",
|
||||||
"category": "kød & fisk"
|
"category": "kød & fisk"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -298,7 +242,11 @@
|
|||||||
"category": "kød & fisk"
|
"category": "kød & fisk"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "300 g laks, uden skind",
|
"name": "2 spsk frisk persille, hakket",
|
||||||
|
"category": "kød & fisk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2 spsk persille, finthakket",
|
||||||
"category": "kød & fisk"
|
"category": "kød & fisk"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -306,7 +254,7 @@
|
|||||||
"category": "kød & fisk"
|
"category": "kød & fisk"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "4 kyllingebryst",
|
"name": "4 laksefileter med skind (ca. 150 g pr. stk)",
|
||||||
"category": "kød & fisk"
|
"category": "kød & fisk"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -314,7 +262,11 @@
|
|||||||
"category": "kød & fisk"
|
"category": "kød & fisk"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "600 g kyllingebryst",
|
"name": "500 g hakket svinekød",
|
||||||
|
"category": "kød & fisk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "600 g kyllingebryst, skåret i grove tern",
|
||||||
"category": "kød & fisk"
|
"category": "kød & fisk"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -324,10 +276,6 @@
|
|||||||
{
|
{
|
||||||
"name": "125 g frisk mozzarella",
|
"name": "125 g frisk mozzarella",
|
||||||
"category": "mejeri & æg"
|
"category": "mejeri & æg"
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "400 g haricots verts, fra frost",
|
|
||||||
"category": "mejeri & æg"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
+10
-14
@@ -13,15 +13,16 @@ Plan-vindue: 2026-04-24 til 2026-04-30
|
|||||||
- [ ] 1 dl rødvin, eller grøntsagsboullion (Mealie)
|
- [ ] 1 dl rødvin, eller grøntsagsboullion (Mealie)
|
||||||
- [ ] 1 dl tør hvidvin (Mealie)
|
- [ ] 1 dl tør hvidvin (Mealie)
|
||||||
- [ ] 1 fed hvidløg, presset (Mealie)
|
- [ ] 1 fed hvidløg, presset (Mealie)
|
||||||
- [ ] 1 gulerødder, Groftrevet (Mealie)
|
- [ ] 1 gulerødder, i tern (Mealie)
|
||||||
|
- [ ] 1 håndfuld persille (Mealie)
|
||||||
- [ ] 1 kg kartofler (Mealie)
|
- [ ] 1 kg kartofler (Mealie)
|
||||||
- [ ] 1 knivspids muskatnød, fintrevet (Mealie)
|
- [ ] 1 knivspids muskatnød, fintrevet (Mealie)
|
||||||
- [ ] 1 knivspids sød paprika (Mealie)
|
- [ ] 1 knivspids sød paprika (Mealie)
|
||||||
|
- [ ] 1 løg, i tern (Mealie)
|
||||||
- [ ] 1 rødløg (Mealie)
|
- [ ] 1 rødløg (Mealie)
|
||||||
- [ ] 1 spsk dijon sennep (Mealie)
|
- [ ] 1 spsk dijon sennep (Mealie)
|
||||||
- [ ] 1 spsk hampefrø (Mealie)
|
- [ ] 1 spsk hampefrø (Mealie)
|
||||||
- [ ] 1 spsk honning (Mealie)
|
- [ ] 1 spsk honning (Mealie)
|
||||||
- [ ] 1 spsk ingefær, fintrevet (Mealie)
|
|
||||||
- [ ] 1 spsk majsstivelse (Mealie)
|
- [ ] 1 spsk majsstivelse (Mealie)
|
||||||
- [ ] 1 spsk smør, til stegning (Mealie)
|
- [ ] 1 spsk smør, til stegning (Mealie)
|
||||||
- [ ] 1 spsk solsikkekerner (Mealie)
|
- [ ] 1 spsk solsikkekerner (Mealie)
|
||||||
@@ -29,27 +30,30 @@ Plan-vindue: 2026-04-24 til 2026-04-30
|
|||||||
- [ ] 1 tsk tørret timian (Mealie)
|
- [ ] 1 tsk tørret timian (Mealie)
|
||||||
- [ ] 10 g smør, til stegning (Mealie)
|
- [ ] 10 g smør, til stegning (Mealie)
|
||||||
- [ ] 100 g parmesan, fintrevet (Mealie)
|
- [ ] 100 g parmesan, fintrevet (Mealie)
|
||||||
|
- [ ] 12 tarteletter (Mealie)
|
||||||
- [ ] 2 æg (Mealie)
|
- [ ] 2 æg (Mealie)
|
||||||
- [ ] 2 dl hønsebouillon (Mealie)
|
- [ ] 2 dl hønsebouillon (Mealie)
|
||||||
|
- [ ] 2 hønsebryst (Mealie)
|
||||||
|
- [ ] 2 laurbærblade (Mealie)
|
||||||
- [ ] 2 spsk grov sennep (Mealie)
|
- [ ] 2 spsk grov sennep (Mealie)
|
||||||
- [ ] 2 spsk rosiner (Mealie)
|
- [ ] 2 spsk rosiner (Mealie)
|
||||||
- [ ] 2 spsk smør (Mealie)
|
- [ ] 2 spsk smør (Mealie)
|
||||||
- [ ] 2 tsk tørret oregano (Mealie)
|
- [ ] 2 tsk tørret oregano (Mealie)
|
||||||
|
- [ ] 200 g aspargessnitter (Mealie)
|
||||||
- [ ] 200 g lasagneplader (Mealie)
|
- [ ] 200 g lasagneplader (Mealie)
|
||||||
- [ ] 25 g smør, til stegning (Mealie)
|
- [ ] 25 g smør, til stegning (Mealie)
|
||||||
|
- [ ] 3 dl grøntsagsbouillon (Mealie)
|
||||||
- [ ] 3 dl mælk (Mealie)
|
- [ ] 3 dl mælk (Mealie)
|
||||||
- [ ] 3 gulerødder, groftrevet (Mealie)
|
- [ ] 3 gulerødder, groftrevet (Mealie)
|
||||||
- [ ] 30 forårsrulleplader (Mealie)
|
|
||||||
- [ ] 300 g torskefilet (Mealie)
|
- [ ] 300 g torskefilet (Mealie)
|
||||||
- [ ] 35 g glasnudler (Mealie)
|
- [ ] 4 dl mælk (Mealie)
|
||||||
- [ ] 4 fed hvidløg, fintrevet (Mealie)
|
- [ ] 4 fed hvidløg, fintrevet (Mealie)
|
||||||
- [ ] 4 gulerødder, groftrevet (Mealie)
|
- [ ] 4 gulerødder, groftrevet (Mealie)
|
||||||
|
- [ ] 40 g smør (Mealie)
|
||||||
- [ ] 5 stængler bladselleri, groftrevet (Mealie)
|
- [ ] 5 stængler bladselleri, groftrevet (Mealie)
|
||||||
- [ ] vand til pensling (Mealie)
|
|
||||||
|
|
||||||
## Frost
|
## Frost
|
||||||
- [ ] 1 dl piskefløde (Mealie)
|
- [ ] 1 dl piskefløde (Mealie)
|
||||||
- [ ] 1 spsk fishsauce (Mealie)
|
|
||||||
- [ ] 2 spsk mayonnaise (Mealie)
|
- [ ] 2 spsk mayonnaise (Mealie)
|
||||||
- [ ] 2,50 dl piskefløde (Mealie)
|
- [ ] 2,50 dl piskefløde (Mealie)
|
||||||
|
|
||||||
@@ -65,13 +69,9 @@ Plan-vindue: 2026-04-24 til 2026-04-30
|
|||||||
## Kolonial
|
## Kolonial
|
||||||
- [ ] 1 håndfuld frisk basilikum (Mealie)
|
- [ ] 1 håndfuld frisk basilikum (Mealie)
|
||||||
- [ ] 1 håndfuld frisk dild (Mealie)
|
- [ ] 1 håndfuld frisk dild (Mealie)
|
||||||
- [ ] 1 liter fritureolie (Mealie)
|
|
||||||
- [ ] 1 spsk olivenolie (Mealie)
|
- [ ] 1 spsk olivenolie (Mealie)
|
||||||
- [ ] 1 spsk olivenolie, til stegning (Mealie)
|
- [ ] 1 spsk olivenolie, til stegning (Mealie)
|
||||||
- [ ] 1 spsk soja (Mealie)
|
|
||||||
- [ ] 1 tsk olivenolie (Mealie)
|
- [ ] 1 tsk olivenolie (Mealie)
|
||||||
- [ ] 1 tsk sesamolie, eller anden olie til stegning (Mealie)
|
|
||||||
- [ ] 1 tsk sukker (Mealie)
|
|
||||||
- [ ] 2 spsk hvedemel (Mealie)
|
- [ ] 2 spsk hvedemel (Mealie)
|
||||||
- [ ] 2 spsk olivenolie (Mealie)
|
- [ ] 2 spsk olivenolie (Mealie)
|
||||||
- [ ] 25 g hvedemel (Mealie)
|
- [ ] 25 g hvedemel (Mealie)
|
||||||
@@ -85,15 +85,11 @@ Plan-vindue: 2026-04-24 til 2026-04-30
|
|||||||
## Kød & Fisk
|
## Kød & Fisk
|
||||||
- [ ] 0,50 dl frisk estragon, finthakket (Mealie)
|
- [ ] 0,50 dl frisk estragon, finthakket (Mealie)
|
||||||
- [ ] 1 løg, finthakket (Mealie)
|
- [ ] 1 løg, finthakket (Mealie)
|
||||||
- [ ] 1 tsk rød chili, finthakket (Mealie)
|
|
||||||
- [ ] 2 løg, finthakket (Mealie)
|
- [ ] 2 løg, finthakket (Mealie)
|
||||||
- [ ] 250 g champignon, finthakket (Mealie)
|
|
||||||
- [ ] 3 fed hvidløg, finthakket (Mealie)
|
|
||||||
- [ ] 300 g laks, uden skind (Mealie)
|
- [ ] 300 g laks, uden skind (Mealie)
|
||||||
- [ ] 4 fed hvidløg, finthakket (Mealie)
|
- [ ] 4 fed hvidløg, finthakket (Mealie)
|
||||||
- [ ] 4 kyllingebryst (Mealie)
|
- [ ] 4 kyllingebryst (Mealie)
|
||||||
- [ ] 400 g hakket oksekød (Mealie)
|
- [ ] 400 g hakket oksekød (Mealie)
|
||||||
- [ ] 400 g hakket svinekød (Mealie)
|
|
||||||
- [ ] 600 g kyllingebryst (Mealie)
|
- [ ] 600 g kyllingebryst (Mealie)
|
||||||
- [ ] 75 g bacon, i skiver (Mealie)
|
- [ ] 75 g bacon, i skiver (Mealie)
|
||||||
|
|
||||||
|
|||||||
@@ -13,15 +13,16 @@ Plan-vindue: 2026-04-24 til 2026-04-30
|
|||||||
- [ ] 1 dl rødvin, eller grøntsagsboullion
|
- [ ] 1 dl rødvin, eller grøntsagsboullion
|
||||||
- [ ] 1 dl tør hvidvin
|
- [ ] 1 dl tør hvidvin
|
||||||
- [ ] 1 fed hvidløg, presset
|
- [ ] 1 fed hvidløg, presset
|
||||||
- [ ] 1 gulerødder, Groftrevet
|
- [ ] 1 gulerødder, i tern
|
||||||
|
- [ ] 1 håndfuld persille
|
||||||
- [ ] 1 kg kartofler
|
- [ ] 1 kg kartofler
|
||||||
- [ ] 1 knivspids muskatnød, fintrevet
|
- [ ] 1 knivspids muskatnød, fintrevet
|
||||||
- [ ] 1 knivspids sød paprika
|
- [ ] 1 knivspids sød paprika
|
||||||
|
- [ ] 1 løg, i tern
|
||||||
- [ ] 1 rødløg
|
- [ ] 1 rødløg
|
||||||
- [ ] 1 spsk dijon sennep
|
- [ ] 1 spsk dijon sennep
|
||||||
- [ ] 1 spsk hampefrø
|
- [ ] 1 spsk hampefrø
|
||||||
- [ ] 1 spsk honning
|
- [ ] 1 spsk honning
|
||||||
- [ ] 1 spsk ingefær, fintrevet
|
|
||||||
- [ ] 1 spsk majsstivelse
|
- [ ] 1 spsk majsstivelse
|
||||||
- [ ] 1 spsk smør, til stegning
|
- [ ] 1 spsk smør, til stegning
|
||||||
- [ ] 1 spsk solsikkekerner
|
- [ ] 1 spsk solsikkekerner
|
||||||
@@ -29,27 +30,30 @@ Plan-vindue: 2026-04-24 til 2026-04-30
|
|||||||
- [ ] 1 tsk tørret timian
|
- [ ] 1 tsk tørret timian
|
||||||
- [ ] 10 g smør, til stegning
|
- [ ] 10 g smør, til stegning
|
||||||
- [ ] 100 g parmesan, fintrevet
|
- [ ] 100 g parmesan, fintrevet
|
||||||
|
- [ ] 12 tarteletter
|
||||||
- [ ] 2 æg
|
- [ ] 2 æg
|
||||||
- [ ] 2 dl hønsebouillon
|
- [ ] 2 dl hønsebouillon
|
||||||
|
- [ ] 2 hønsebryst
|
||||||
|
- [ ] 2 laurbærblade
|
||||||
- [ ] 2 spsk grov sennep
|
- [ ] 2 spsk grov sennep
|
||||||
- [ ] 2 spsk rosiner
|
- [ ] 2 spsk rosiner
|
||||||
- [ ] 2 spsk smør
|
- [ ] 2 spsk smør
|
||||||
- [ ] 2 tsk tørret oregano
|
- [ ] 2 tsk tørret oregano
|
||||||
|
- [ ] 200 g aspargessnitter
|
||||||
- [ ] 200 g lasagneplader
|
- [ ] 200 g lasagneplader
|
||||||
- [ ] 25 g smør, til stegning
|
- [ ] 25 g smør, til stegning
|
||||||
|
- [ ] 3 dl grøntsagsbouillon
|
||||||
- [ ] 3 dl mælk
|
- [ ] 3 dl mælk
|
||||||
- [ ] 3 gulerødder, groftrevet
|
- [ ] 3 gulerødder, groftrevet
|
||||||
- [ ] 30 forårsrulleplader
|
|
||||||
- [ ] 300 g torskefilet
|
- [ ] 300 g torskefilet
|
||||||
- [ ] 35 g glasnudler
|
- [ ] 4 dl mælk
|
||||||
- [ ] 4 fed hvidløg, fintrevet
|
- [ ] 4 fed hvidløg, fintrevet
|
||||||
- [ ] 4 gulerødder, groftrevet
|
- [ ] 4 gulerødder, groftrevet
|
||||||
|
- [ ] 40 g smør
|
||||||
- [ ] 5 stængler bladselleri, groftrevet
|
- [ ] 5 stængler bladselleri, groftrevet
|
||||||
- [ ] vand til pensling
|
|
||||||
|
|
||||||
## Frost
|
## Frost
|
||||||
- [ ] 1 dl piskefløde
|
- [ ] 1 dl piskefløde
|
||||||
- [ ] 1 spsk fishsauce
|
|
||||||
- [ ] 2 spsk mayonnaise
|
- [ ] 2 spsk mayonnaise
|
||||||
- [ ] 2,50 dl piskefløde
|
- [ ] 2,50 dl piskefløde
|
||||||
|
|
||||||
@@ -65,13 +69,9 @@ Plan-vindue: 2026-04-24 til 2026-04-30
|
|||||||
## Kolonial
|
## Kolonial
|
||||||
- [ ] 1 håndfuld frisk basilikum
|
- [ ] 1 håndfuld frisk basilikum
|
||||||
- [ ] 1 håndfuld frisk dild
|
- [ ] 1 håndfuld frisk dild
|
||||||
- [ ] 1 liter fritureolie
|
|
||||||
- [ ] 1 spsk olivenolie
|
- [ ] 1 spsk olivenolie
|
||||||
- [ ] 1 spsk olivenolie, til stegning
|
- [ ] 1 spsk olivenolie, til stegning
|
||||||
- [ ] 1 spsk soja
|
|
||||||
- [ ] 1 tsk olivenolie
|
- [ ] 1 tsk olivenolie
|
||||||
- [ ] 1 tsk sesamolie, eller anden olie til stegning
|
|
||||||
- [ ] 1 tsk sukker
|
|
||||||
- [ ] 2 spsk hvedemel
|
- [ ] 2 spsk hvedemel
|
||||||
- [ ] 2 spsk olivenolie
|
- [ ] 2 spsk olivenolie
|
||||||
- [ ] 25 g hvedemel
|
- [ ] 25 g hvedemel
|
||||||
@@ -85,15 +85,11 @@ Plan-vindue: 2026-04-24 til 2026-04-30
|
|||||||
## Kød & Fisk
|
## Kød & Fisk
|
||||||
- [ ] 0,50 dl frisk estragon, finthakket
|
- [ ] 0,50 dl frisk estragon, finthakket
|
||||||
- [ ] 1 løg, finthakket
|
- [ ] 1 løg, finthakket
|
||||||
- [ ] 1 tsk rød chili, finthakket
|
|
||||||
- [ ] 2 løg, finthakket
|
- [ ] 2 løg, finthakket
|
||||||
- [ ] 250 g champignon, finthakket
|
|
||||||
- [ ] 3 fed hvidløg, finthakket
|
|
||||||
- [ ] 300 g laks, uden skind
|
- [ ] 300 g laks, uden skind
|
||||||
- [ ] 4 fed hvidløg, finthakket
|
- [ ] 4 fed hvidløg, finthakket
|
||||||
- [ ] 4 kyllingebryst
|
- [ ] 4 kyllingebryst
|
||||||
- [ ] 400 g hakket oksekød
|
- [ ] 400 g hakket oksekød
|
||||||
- [ ] 400 g hakket svinekød
|
|
||||||
- [ ] 600 g kyllingebryst
|
- [ ] 600 g kyllingebryst
|
||||||
- [ ] 75 g bacon, i skiver
|
- [ ] 75 g bacon, i skiver
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"count": 6, "items": [{"date": "2026-04-28", "recipe": {"name": "Lasagne", "slug": "lasagne"}}, {"date": "2026-04-27", "recipe": {"name": "Kylling i cremet sennepssauce", "slug": "kylling-i-cremet-sennepssauce"}}, {"date": "2026-04-26", "recipe": {"name": "Lasagne", "slug": "lasagne"}}, {"date": "2026-04-24", "recipe": {"name": "Marry Me Chicken", "slug": "marry-me-chicken"}}, {"date": "2026-04-22", "recipe": {"name": "Kylling i cremet sennepssauce", "slug": "kylling-i-cremet-sennepssauce"}}, {"date": "2026-04-23", "recipe": {"name": "K\u00e5lfad med hakket oksek\u00f8d", "slug": "kalfad-med-hakket-oksekod"}}]}
|
{"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