diff --git a/dashboards/views/01_home.yaml.bak b/dashboards/views/01_home.yaml.bak
new file mode 100644
index 0000000..5abee09
--- /dev/null
+++ b/dashboards/views/01_home.yaml.bak
@@ -0,0 +1,913 @@
+title: Overblik
+path: home
+icon: mdi:home
+
+cards:
+
+ # 🌤️ Vejr + dato + sol
+ - type: vertical-stack
+ cards:
+ - type: weather-forecast
+ entity: weather.norgardsvej
+ show_forecast: true
+
+ - type: glance
+ columns: 5
+ show_icon: true
+ show_name: true
+ show_state: true
+ entities:
+ - entity: sensor.dato
+ name: Dato
+ icon: mdi:calendar
+ - entity: sensor.solopgang
+ name: Sol op
+ icon: mdi:weather-sunset-up
+ - entity: sensor.solnedgang
+ name: Sol ned
+ icon: mdi:weather-sunset-down
+ - entity: binary_sensor.arbejdsdag
+ name: I dag
+ icon: mdi:briefcase
+ - entity: binary_sensor.arbejdsdagimorgen
+ name: I morgen
+ icon: mdi:briefcase-outline
+
+ # 👨👩👧👦 Familien – tryk for at toggle syg/rask
+ - type: grid
+ columns: 4
+ square: false
+ cards:
+
+ - type: custom:button-card
+ entity: person.daniel_schusler_dethlefsen
+ name: Daniel
+ show_name: true
+ 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
+ show_name: true
+ 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
+ show_name: true
+ 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
+ show_name: true
+ show_state: false
+ 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
+ - type: grid
+ columns: 6
+ square: false
+ cards:
+ - type: button
+ name: ""
+ icon: mdi:blinds-open
+ tap_action:
+ action: call-service
+ service: scene.turn_on
+ target:
+ entity_id: scene.alle_lidt_nede
+ - type: button
+ name: ""
+ icon: mdi:blinds
+ tap_action:
+ action: call-service
+ service: scene.turn_on
+ target:
+ entity_id: scene.n22_alt_ned
+ - type: button
+ name: ""
+ icon: mdi:blinds-horizontal
+ tap_action:
+ action: call-service
+ service: scene.turn_on
+ target:
+ entity_id: scene.morgen
+ - type: button
+ name: ""
+ icon: mdi:window-shutter
+ tap_action:
+ action: call-service
+ service: scene.turn_on
+ target:
+ entity_id: scene.syd_ned_venstre_halvt_ned
+ - type: button
+ name: ""
+ icon: mdi:television-ambient-light
+ tap_action:
+ action: call-service
+ service: scene.turn_on
+ target:
+ entity_id: scene.n22_tv
+ - type: button
+ name: ""
+ icon: mdi:silverware-fork-knife
+ tap_action:
+ action: call-service
+ service: scene.turn_on
+ target:
+ entity_id: scene.n22_spisetid
+
+ # 🧹 Støvsuger
+ - type: horizontal-stack
+ cards:
+ - type: custom:button-card
+ entity: sensor.roborock_last_clean_end_compact
+ show_icon: false
+ show_name: true
+ show_state: true
+ name: Sidst kørt
+ tap_action:
+ action: none
+ hold_action:
+ action: none
+ styles:
+ card:
+ - padding: 8px
+ name:
+ - font-size: 11px
+ - color: var(--secondary-text-color)
+ - padding-bottom: 4px
+ state:
+ - white-space: normal
+ - word-break: break-word
+ - line-height: 1.2
+ - font-size: 13px
+ - text-align: center
+
+ - type: button
+ name: Køkken
+ icon: mdi:floor-plan
+ tap_action:
+ action: call-service
+ service: button.press
+ target:
+ entity_id: button.roborock_s8_pro_ultra_kokken_bryggers
+
+ - type: button
+ name: Syd
+ icon: mdi:floor-plan
+ tap_action:
+ action: call-service
+ service: button.press
+ target:
+ entity_id: button.roborock_s8_pro_ultra_syd
+
+ - type: button
+ name: Mop
+ icon: mdi:floor-plan
+ tap_action:
+ action: call-service
+ service: button.press
+ target:
+ entity_id: button.roborock_s8_pro_ultra_vac_followed_by_mop
+
+ - type: button
+ name: Gå til dock
+ icon: mdi:home-import-outline
+ tap_action:
+ action: call-service
+ service: vacuum.return_to_base
+ target:
+ entity_id: vacuum.roborock_s8_pro_ultra
+
+ # 🏎️ Plæneklipper
+ - type: horizontal-stack
+ cards:
+ - type: custom:button-card
+ entity: input_datetime.ploeneklipper_sidst_koert
+ show_icon: false
+ show_name: true
+ show_state: true
+ name: Sidst klippet
+ tap_action:
+ action: none
+ styles:
+ card:
+ - padding: 8px
+ name:
+ - font-size: 11px
+ - color: var(--secondary-text-color)
+ - padding-bottom: 4px
+ state:
+ - white-space: normal
+ - word-break: break-word
+ - line-height: 1.2
+ - font-size: 13px
+ - text-align: center
+
+ - type: button
+ name: Klip
+ icon: mdi:robot-mower
+ tap_action:
+ action: call-service
+ service: lawn_mower.start_mowing
+ target:
+ entity_id: lawn_mower.husqvarna_automower
+
+ - type: button
+ name: Stop
+ icon: mdi:home-import-outline
+ tap_action:
+ action: call-service
+ service: lawn_mower.dock
+ target:
+ entity_id: lawn_mower.husqvarna_automower
+
+ # � Vanding
+ - type: horizontal-stack
+ cards:
+ - type: custom:button-card
+ entity: sensor.vanding_sidst_vandet_compact
+ show_icon: false
+ show_name: true
+ show_state: true
+ name: Sidst vandet
+ tap_action:
+ action: none
+ hold_action:
+ action: none
+ styles:
+ card:
+ - padding: 8px
+ name:
+ - font-size: 11px
+ - color: var(--secondary-text-color)
+ - padding-bottom: 4px
+ state:
+ - white-space: normal
+ - word-break: break-word
+ - line-height: 1.2
+ - font-size: 13px
+ - text-align: center
+
+ - type: button
+ name: Smart vanding
+ icon: mdi:sprinkler-variant
+ entity: script.vanding_hojbed_skiftevis
+ tap_action:
+ action: toggle
+
+ - type: custom:button-card
+ entity: sensor.vanding_liter_i_dag_total
+ show_icon: false
+ show_name: false
+ show_state: false
+ tap_action:
+ action: none
+ hold_action:
+ action: none
+ custom_fields:
+ info: |
+ [[[
+ const n = (id) => { const s = states[id]; return s ? Math.round(parseFloat(s.state) || 0) : 0; };
+ const total = n('sensor.vanding_liter_i_dag_total');
+ const rows = [
+ ['Ærter', n('sensor.vanding_liter_hb1')],
+ ['Kartofler', n('sensor.vanding_liter_hb2')],
+ ['Rabarber', n('sensor.vanding_liter_hb3')],
+ ['Drivhus', n('sensor.vanding_liter_drivhus')],
+ ['Kapillær', n('sensor.vanding_liter_kapillaer')],
+ ];
+ const list = rows.map(r => `
${r[0]}${r[1]}
`).join('');
+ return `
+
Liter i dag
+
${total} L
+
${list}
+
`;
+ ]]]
+ styles:
+ card:
+ - padding: 10px
+ grid:
+ - grid-template-areas: '"info"'
+ custom_fields:
+ info:
+ - width: 100%
+
+ - type: custom:button-card
+ entity: sensor.vanding_tid_i_dag_total
+ show_icon: false
+ show_name: false
+ show_state: false
+ tap_action:
+ action: none
+ hold_action:
+ action: none
+ custom_fields:
+ info: |
+ [[[
+ const m = (id) => { const s = states[id]; return s ? Math.round((parseFloat(s.state) || 0) * 60) : 0; };
+ const cap = () => { const s = states['sensor.vanding_kapillaer_tid_i_dag']; return s ? Math.round(parseFloat(s.state) || 0) : 0; };
+ const total = (() => { const s = states['sensor.vanding_tid_i_dag_total']; return s ? Math.round(parseFloat(s.state) || 0) : 0; })();
+ const rows = [
+ ['Ærter', m('sensor.vanding_hojbed_1_i_dag')],
+ ['Kartofler', m('sensor.vanding_hojbed_2_i_dag')],
+ ['Rabarber', m('sensor.vanding_hojbed_3_i_dag')],
+ ['Drivhus', m('sensor.vanding_drivhus_i_dag')],
+ ['Kapillær', cap()],
+ ];
+ const list = rows.map(r => `${r[0]}${r[1]} min
`).join('');
+ return `
+
Tid i dag
+
${total} min
+
${list}
+
`;
+ ]]]
+ styles:
+ card:
+ - padding: 10px
+ grid:
+ - grid-template-areas: '"info"'
+ custom_fields:
+ info:
+ - width: 100%
+
+ # �💡 Lys kontrol
+ - type: horizontal-stack
+ cards:
+ - type: tile
+ entity: light.indendorslamper
+ name: Indenfor
+ icon: mdi:lightbulb-group
+ tap_action:
+ action: toggle
+ hold_action:
+ action: more-info
+ show_state: true
+
+ - type: tile
+ entity: light.udendorslamper
+ name: Udenfor
+ icon: mdi:outdoor-lamp
+ tap_action:
+ action: toggle
+ hold_action:
+ action: more-info
+ show_state: true
+
+ - type: tile
+ entity: binary_sensor.garageport
+ name: Garage
+ features_position: bottom
+ vertical: false
+ tap_action:
+ action: call-service
+ service: cover.toggle
+ target:
+ entity_id: cover.anne
+ show_state: true
+
+ # 🎵 Sonos
+ - type: grid
+ columns: 3
+ square: false
+ cards:
+ - type: button
+ name: Stop alt
+ icon: mdi:stop-circle-outline
+ tap_action:
+ action: call-service
+ service: media_player.media_stop
+ target:
+ entity_id:
+ - media_player.stue
+ - media_player.andreas
+ - media_player.daniel
+ - media_player.kokken
+ - media_player.alrum
+ - media_player.badevaerelse
+ - media_player.sovevaerelse
+ - media_player.lille_badevaerelse
+
+ - type: button
+ name: P3 i bad
+ icon: mdi:radio
+ tap_action:
+ action: call-service
+ service: media_player.select_source
+ target:
+ entity_id: media_player.badevaerelse
+ data:
+ source: "0 DR P3"
+
+ - type: button
+ name: P3 i alrum
+ icon: mdi:radio
+ tap_action:
+ action: call-service
+ service: media_player.select_source
+ target:
+ entity_id: media_player.alrum
+ data:
+ source: "0 DR P3"
+
+ - type: button
+ name: Daily Mix bad
+ icon: mdi:music-circle-outline
+ tap_action:
+ action: call-service
+ service: media_player.select_source
+ target:
+ entity_id: media_player.badevaerelse
+ data:
+ source: "Andreas Daily Mix 1"
+
+ - type: button
+ name: Daily Mix køkken
+ icon: mdi:music-circle-outline
+ tap_action:
+ action: call-service
+ service: media_player.select_source
+ target:
+ entity_id: media_player.kokken
+ data:
+ source: "Andreas Daily Mix 1"
+
+ - type: button
+ name: Family Mix køkken
+ icon: mdi:music-note-outline
+ tap_action:
+ action: call-service
+ service: media_player.select_source
+ target:
+ entity_id: media_player.kokken
+ data:
+ source: "1 Family Mix"
+
+ # 🔊 Receiver presets
+ - type: grid
+ columns: 4
+ square: false
+ cards:
+ - type: button
+ name: Sonos
+ icon: mdi:speaker-multiple
+ tap_action:
+ action: call-service
+ service: script.receiver_sonos_mch_stereo
+ - type: button
+ name: TV Dolby
+ icon: mdi:television-speaker
+ tap_action:
+ action: call-service
+ service: script.receiver_tv_dolby_digital
+ - type: button
+ name: TV Stereo
+ icon: mdi:surround-sound
+ tap_action:
+ action: call-service
+ service: script.receiver_tv_mch_stereo
+ - type: horizontal-stack
+ cards:
+ - type: vertical-stack
+ cards:
+ - type: button
+ name: ""
+ icon: mdi:power
+ tap_action:
+ action: call-service
+ service: media_player.turn_off
+ target:
+ entity_id: media_player.denon_avr_x2300w
+ - type: button
+ name: ""
+ icon: mdi:volume-plus
+ tap_action:
+ action: call-service
+ service: script.receiver_volume_up_5
+ - type: button
+ name: ""
+ icon: mdi:volume-minus
+ tap_action:
+ action: call-service
+ service: script.receiver_volume_down_5
+
+ # 🍽️ Hjemmeknapper
+ - type: grid
+ columns: 2
+ square: false
+ cards:
+ - type: button
+ name: Der er mad
+ icon: mdi:silverware-fork-knife
+ tap_action:
+ action: call-service
+ service: script.mad_announcement
+
+ - type: button
+ name: TV hygge
+ icon: mdi:television-ambient-light
+ tap_action:
+ action: call-service
+ service: script.tv_hygge_announcement
+
+ # 🗑️ Affald
+ - type: glance
+ columns: 3
+ show_icon: true
+ show_name: false
+ show_state: true
+ entities:
+ - entity: sensor.affalddk_norgardsvej_22_restaffald
+ icon: mdi:trash-can-outline
+ - entity: sensor.affalddk_norgardsvej_22_papir_plast
+ icon: mdi:recycle
+ - entity: sensor.affalddk_norgardsvej_22_haveaffald
+ icon: mdi:leaf
+
+ # ⚡ El-priser
+ - type: vertical-stack
+ cards:
+ - type: custom:apexcharts-card
+ graph_span: 24h
+ span:
+ start: hour
+ stacked: false
+ header:
+ show: true
+ title: El-priser næste 24 timer
+ show_states: true
+ colorize_states: true
+ now:
+ show: true
+ label: Nu
+ all_series_config:
+ stroke_width: 0
+ apex_config:
+ chart:
+ height: 260
+ grid:
+ strokeDashArray: 2
+ xaxis:
+ type: datetime
+ labels:
+ datetimeFormatter:
+ hour: HH:mm
+ yaxis:
+ decimalsInFloat: 2
+ tickAmount: 5
+ plotOptions:
+ bar:
+ columnWidth: 82%
+ borderRadius: 3
+ series:
+ - entity: sensor.energi_data_service
+ name: Pris
+ type: column
+ float_precision: 2
+ unit: ' kr/kWh'
+ show:
+ in_header: raw
+ in_chart: true
+ data_generator: |
+ const startOfHour = new Date();
+ startOfHour.setMinutes(0, 0, 0);
+ const endTime = startOfHour.getTime() + (24 * 60 * 60 * 1000);
+
+ const rawToday = entity.attributes.raw_today || [];
+ const rawTomorrow = entity.attributes.tomorrow_valid ? (entity.attributes.raw_tomorrow || []) : [];
+ const forecast = entity.attributes.forecast || [];
+
+ const allKnown = [...rawToday, ...rawTomorrow];
+ const data = [];
+ const seen = new Set();
+
+ const pushPoint = (item) => {
+ const timestamp = new Date(item.hour).getTime();
+ if (Number.isNaN(timestamp) || timestamp < startOfHour.getTime() || timestamp >= endTime || seen.has(timestamp)) {
+ return;
+ }
+
+ const price = Number(item.price);
+ if (Number.isNaN(price)) {
+ return;
+ }
+
+ seen.add(timestamp);
+ data.push({ x: timestamp, y: price });
+ };
+
+ allKnown.forEach(pushPoint);
+
+ if (data.length < 24) {
+ forecast.forEach(pushPoint);
+ }
+
+ data.sort((left, right) => left.x - right.x);
+
+ const trimmed = data.slice(0, 24);
+ if (!trimmed.length) {
+ return [];
+ }
+
+ const prices = trimmed.map((item) => item.y);
+ const minPrice = Math.min(...prices);
+ const maxPrice = Math.max(...prices);
+
+ const mix = (start, end, ratio) => Math.round(start + ((end - start) * ratio));
+ const toHex = (value) => value.toString(16).padStart(2, '0');
+ const rgbToHex = (red, green, blue) => `#${toHex(red)}${toHex(green)}${toHex(blue)}`;
+
+ const colorByValue = (value) => {
+ if (maxPrice === minPrice) {
+ return '#16a34a';
+ }
+
+ const normalized = (value - minPrice) / (maxPrice - minPrice);
+
+ if (normalized <= 0.5) {
+ const ratio = normalized / 0.5;
+ return rgbToHex(
+ mix(22, 250, ratio),
+ mix(163, 204, ratio),
+ mix(74, 21, ratio)
+ );
+ }
+
+ const ratio = (normalized - 0.5) / 0.5;
+ return rgbToHex(
+ mix(250, 220, ratio),
+ mix(204, 38, ratio),
+ mix(21, 38, ratio)
+ );
+ };
+
+ return trimmed.map((item) => ({
+ x: item.x,
+ y: item.y,
+ fillColor: colorByValue(item.y)
+ }));
+
+ # 🏠 Hus kontrol
+ - type: entities
+ title: Modes
+ entities:
+ - entity: input_boolean.guests_mode
+ name: Vi har gæster
+ icon: mdi:account-group
+ - entity: input_boolean.vacation_mode
+ name: 🌴 Vacation Mode
+ - entity: input_datetime.vacation_end
+ name: Slutter
+
+ - type: conditional
+ conditions:
+ - condition: numeric_state
+ entity: sensor.antal_vedligeholdskort
+ above: 0
+ card:
+ type: tile
+ entity: sensor.antal_vedligeholdskort
+ name: Vedligehold
+ icon: mdi:wrench-cog
+
diff --git a/dashboards/views/03_andreas.yaml b/dashboards/views/03_andreas.yaml
index fab22b4..56e4c50 100644
--- a/dashboards/views/03_andreas.yaml
+++ b/dashboards/views/03_andreas.yaml
@@ -255,6 +255,50 @@ sections:
- word-break: break-word
- line-height: 1.15
+ - type: grid
+ cards:
+ - type: heading
+ heading: Ventilator
+
+ - type: custom:button-card
+ entity: light.indkorsel_plug
+ name: Ventilator
+ icon: mdi:fan
+ show_state: true
+ tap_action:
+ action: toggle
+ hold_action:
+ action: more-info
+ styles:
+ card:
+ - padding: 8px 6px
+ - height: 72px
+ icon:
+ - width: 26px
+ name:
+ - font-size: 12px
+
+ - type: entities
+ entities:
+ - entity: input_number.andreas_ventilator_timer
+ name: Sluk-timer (min)
+
+ - type: custom:button-card
+ icon: mdi:fan-off
+ name: |
+ [[[ return 'Sluk om ' + Math.round(states['input_number.andreas_ventilator_timer'].state) + ' min'; ]]]
+ tap_action:
+ action: perform-action
+ perform_action: script.andreas_ventilator_sluk_timer
+ styles:
+ card:
+ - padding: 8px 6px
+ - height: 56px
+ icon:
+ - width: 22px
+ name:
+ - font-size: 12px
+
- type: grid
cards:
- type: heading
diff --git a/dashboards/views/04_daniel.yaml b/dashboards/views/04_daniel.yaml
index f99b73d..ba08738 100644
--- a/dashboards/views/04_daniel.yaml
+++ b/dashboards/views/04_daniel.yaml
@@ -255,6 +255,50 @@ sections:
- word-break: break-word
- line-height: 1.15
+ - type: grid
+ cards:
+ - type: heading
+ heading: Ventilator
+
+ - type: custom:button-card
+ entity: switch.stik_daniel
+ name: Ventilator
+ icon: mdi:fan
+ show_state: true
+ tap_action:
+ action: toggle
+ hold_action:
+ action: more-info
+ styles:
+ card:
+ - padding: 8px 6px
+ - height: 72px
+ icon:
+ - width: 26px
+ name:
+ - font-size: 12px
+
+ - type: entities
+ entities:
+ - entity: input_number.daniel_ventilator_timer
+ name: Sluk-timer (min)
+
+ - type: custom:button-card
+ icon: mdi:fan-off
+ name: |
+ [[[ return 'Sluk om ' + Math.round(states['input_number.daniel_ventilator_timer'].state) + ' min'; ]]]
+ tap_action:
+ action: perform-action
+ perform_action: script.daniel_ventilator_sluk_timer
+ styles:
+ card:
+ - padding: 8px 6px
+ - height: 56px
+ icon:
+ - width: 22px
+ name:
+ - font-size: 12px
+
- type: grid
cards:
- type: heading
diff --git a/dashboards/views/06c_indkorsel_snapshots.yaml b/dashboards/views/06c_indkorsel_snapshots.yaml
index c8f522a..86a677b 100644
--- a/dashboards/views/06c_indkorsel_snapshots.yaml
+++ b/dashboards/views/06c_indkorsel_snapshots.yaml
@@ -5,5 +5,5 @@ panel: true
cards:
- type: iframe
- url: /local/snapshots/indkorsel_loader.html?v=20260619185107
+ url: /local/snapshots/indkorsel_loader.html?v=20260620192035
aspect_ratio: 100%
diff --git a/include/input/number/andreas.yaml b/include/input/number/andreas.yaml
index b5df4b8..be87004 100644
--- a/include/input/number/andreas.yaml
+++ b/include/input/number/andreas.yaml
@@ -21,3 +21,12 @@ andreas_brightness:
step: 1
unit_of_measurement: "%"
initial: 100
+
+andreas_ventilator_timer:
+ name: Andreas ventilator sluk-timer
+ min: 5
+ max: 120
+ step: 5
+ unit_of_measurement: min
+ initial: 30
+ icon: mdi:fan-off
diff --git a/include/input/number/daniel.yaml b/include/input/number/daniel.yaml
index a64050d..373e29c 100644
--- a/include/input/number/daniel.yaml
+++ b/include/input/number/daniel.yaml
@@ -21,3 +21,12 @@ daniel_brightness:
step: 1
unit_of_measurement: "%"
initial: 100
+
+daniel_ventilator_timer:
+ name: Daniel ventilator sluk-timer
+ min: 5
+ max: 120
+ step: 5
+ unit_of_measurement: min
+ initial: 30
+ icon: mdi:fan-off
diff --git a/include/scripts/ventilator_timer.yaml b/include/scripts/ventilator_timer.yaml
new file mode 100644
index 0000000..506b42a
--- /dev/null
+++ b/include/scripts/ventilator_timer.yaml
@@ -0,0 +1,21 @@
+andreas_ventilator_sluk_timer:
+ alias: Andreas ventilator sluk efter timer
+ icon: mdi:fan-off
+ mode: restart
+ sequence:
+ - delay:
+ minutes: "{{ states('input_number.andreas_ventilator_timer') | int(30) }}"
+ - service: light.turn_off
+ target:
+ entity_id: light.indkorsel_plug
+
+daniel_ventilator_sluk_timer:
+ alias: Daniel ventilator sluk efter timer
+ icon: mdi:fan-off
+ mode: restart
+ sequence:
+ - delay:
+ minutes: "{{ states('input_number.daniel_ventilator_timer') | int(30) }}"
+ - service: switch.turn_off
+ target:
+ entity_id: switch.stik_daniel
diff --git a/www/mealie.json b/www/mealie.json
index 010b683..066af15 100644
--- a/www/mealie.json
+++ b/www/mealie.json
@@ -1 +1 @@
-{"count": 8, "items": [{"date": "2026-06-19", "recipe": {"name": "Okse tacos", "slug": "okse-tacos"}}, {"date": "2026-06-20", "recipe": {"name": "Bagte s\u00f8de kartofler med fyld", "slug": "bagte-sode-kartofler-med-fyld"}}, {"date": "2026-06-20", "recipe": {"name": "Engelsk b\u00f8f ", "slug": ""}}, {"date": "2026-06-25", "recipe": {"name": "Macaroni and cheese", "slug": "macaroni-and-cheese"}}, {"date": "2026-06-24", "recipe": {"name": "Rester fra mandag (Tortellini i fad)", "slug": ""}}, {"date": "2026-06-23", "recipe": {"name": "Rester fra s\u00f8ndag (Kylling med cornflakes)", "slug": ""}}, {"date": "2026-06-22", "recipe": {"name": "Tortellini i fad", "slug": "tortellini-i-fad"}}, {"date": "2026-06-21", "recipe": {"name": "Kylling med cornflakes", "slug": "kylling-med-cornflakes"}}]}
\ No newline at end of file
+{"count": 8, "items": [{"date": "2026-06-20", "recipe": {"name": "Bagte s\u00f8de kartofler med fyld", "slug": "bagte-sode-kartofler-med-fyld"}}, {"date": "2026-06-20", "recipe": {"name": "Engelsk b\u00f8f ", "slug": ""}}, {"date": "2026-06-26", "recipe": {"name": "M\u00f8rbradb\u00f8ffer med bl\u00f8de l\u00f8g og fl\u00f8desauce", "slug": "morbradboffer-med-blode-log-og-flodesauce"}}, {"date": "2026-06-25", "recipe": {"name": "Macaroni and cheese", "slug": "macaroni-and-cheese"}}, {"date": "2026-06-24", "recipe": {"name": "Rester fra mandag (Tortellini i fad)", "slug": ""}}, {"date": "2026-06-23", "recipe": {"name": "Rester fra s\u00f8ndag (Kylling med cornflakes)", "slug": ""}}, {"date": "2026-06-22", "recipe": {"name": "Tortellini i fad", "slug": "tortellini-i-fad"}}, {"date": "2026-06-21", "recipe": {"name": "Kylling med cornflakes", "slug": "kylling-med-cornflakes"}}]}
\ No newline at end of file