Clean repo, add Verbraucher Rangliste (Power Ranking Card) with README

This commit is contained in:
Claude
2026-08-14 17:47:10 +00:00
parent c19759abbd
commit 17daf8c95c
10 changed files with 481 additions and 3682 deletions
-37
View File
@@ -1,37 +0,0 @@
# Compiled source #
###################
*.com
*.class
*.dll
*.exe
*.o
*.so
# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
# Logs and databases #
######################
*.log
*.sql
*.sqlite
# OS generated files #
######################
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
-535
View File
@@ -1,535 +0,0 @@
[
{
"id": "23b8f90b282ac8a2",
"type": "group",
"z": "a1055886e0cbd337",
"name": "Diele Nachtbeleuchtung",
"style": {
"fill": "#bfdbef",
"label": true
},
"nodes": [
"ec44baea08cc2923",
"c51f9311d8d05015",
"c8a63c6fa6d37a51",
"5b5ec57984037a5c",
"f0c5e4339b494c3a",
"43d024d394d94663",
"00b3ef7322366374",
"87806c641c86c3d9",
"14c3a212afc4cd78",
"7e7074a18d177e16",
"38767c1f739f3c8d",
"4f22d1a57648e607",
"2b525828bc96835e",
"c730df6b04bdaf41",
"fa8d1668235782d3"
],
"x": 14,
"y": 399,
"w": 1412,
"h": 282
},
{
"id": "ec44baea08cc2923",
"type": "server-state-changed",
"z": "a1055886e0cbd337",
"g": "23b8f90b282ac8a2",
"name": "Diele Bewegung erkannt",
"server": "387b3091.65c5",
"version": 6,
"outputs": 2,
"exposeAsEntityConfig": "",
"entities": {
"entity": [
"binary_sensor.diele_bewegungsmelder_bewegung"
],
"substring": [],
"regex": []
},
"outputInitially": false,
"stateType": "str",
"ifState": "on",
"ifStateType": "str",
"ifStateOperator": "is",
"outputOnlyOnStateChange": true,
"for": "0",
"forType": "num",
"forUnits": "minutes",
"ignorePrevStateNull": false,
"ignorePrevStateUnknown": false,
"ignorePrevStateUnavailable": false,
"ignoreCurrentStateUnknown": false,
"ignoreCurrentStateUnavailable": false,
"outputProperties": [
{
"property": "payload",
"propertyType": "msg",
"value": "",
"valueType": "entityState"
},
{
"property": "data",
"propertyType": "msg",
"value": "",
"valueType": "eventData"
},
{
"property": "topic",
"propertyType": "msg",
"value": "",
"valueType": "triggerId"
}
],
"x": 150,
"y": 520,
"wires": [
[
"c51f9311d8d05015",
"00b3ef7322366374",
"38767c1f739f3c8d"
],
[]
]
},
{
"id": "c51f9311d8d05015",
"type": "api-current-state",
"z": "a1055886e0cbd337",
"g": "23b8f90b282ac8a2",
"name": "Diele Bewegungsmelder Helligkeit",
"server": "387b3091.65c5",
"version": 3,
"outputs": 2,
"halt_if": "50",
"halt_if_type": "num",
"halt_if_compare": "lte",
"entity_id": "sensor.diele_bewegungsmelder_helligkeit",
"state_type": "num",
"blockInputOverrides": false,
"outputProperties": [
{
"property": "payload",
"propertyType": "msg",
"value": "",
"valueType": "entityState"
},
{
"property": "data",
"propertyType": "msg",
"value": "",
"valueType": "entity"
}
],
"for": "0",
"forType": "num",
"forUnits": "minutes",
"override_topic": false,
"state_location": "payload",
"override_payload": "msg",
"entity_location": "data",
"override_data": "msg",
"x": 440,
"y": 460,
"wires": [
[
"c8a63c6fa6d37a51",
"7e7074a18d177e16"
],
[]
]
},
{
"id": "c8a63c6fa6d37a51",
"type": "time-range-switch",
"z": "a1055886e0cbd337",
"d": true,
"g": "23b8f90b282ac8a2",
"name": "Nur Nachts 20-8",
"lat": "51.2115333",
"lon": "6.7448851",
"startTime": "20:00",
"endTime": "08:00",
"startOffset": 0,
"endOffset": 0,
"x": 800,
"y": 480,
"wires": [
[
"5b5ec57984037a5c"
],
[]
]
},
{
"id": "5b5ec57984037a5c",
"type": "api-call-service",
"z": "a1055886e0cbd337",
"g": "23b8f90b282ac8a2",
"name": "Diele und Bad LED Streifen AN",
"server": "387b3091.65c5",
"version": 7,
"debugenabled": false,
"action": "light.turn_on",
"floorId": [],
"areaId": [],
"deviceId": [],
"entityId": [
"light.led_spiegel",
"light.led_streifen"
],
"labelId": [],
"data": "{\"brightness\":\"40\"\t,\"profile\":\"relax\"}",
"dataType": "jsonata",
"mergeContext": "",
"mustacheAltTags": false,
"outputProperties": [],
"queue": "none",
"blockInputOverrides": false,
"domain": "light",
"service": "turn_on",
"x": 1270,
"y": 440,
"wires": [
[]
]
},
{
"id": "f0c5e4339b494c3a",
"type": "api-call-service",
"z": "a1055886e0cbd337",
"g": "23b8f90b282ac8a2",
"name": "Diele und Bad LED Streifen AUS",
"server": "387b3091.65c5",
"version": 7,
"debugenabled": false,
"action": "light.turn_off",
"floorId": [],
"areaId": [],
"deviceId": [],
"entityId": [
"light.led_spiegel",
"light.led_streifen"
],
"labelId": [],
"data": "",
"dataType": "jsonata",
"mergeContext": "",
"mustacheAltTags": false,
"outputProperties": [],
"queue": "none",
"blockInputOverrides": false,
"domain": "light",
"service": "turn_off",
"x": 1270,
"y": 580,
"wires": [
[]
]
},
{
"id": "43d024d394d94663",
"type": "delay",
"z": "a1055886e0cbd337",
"g": "23b8f90b282ac8a2",
"name": "",
"pauseType": "delayv",
"timeout": "3",
"timeoutUnits": "minutes",
"rate": "1",
"nbRateUnits": "1",
"rateUnits": "second",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": false,
"allowrate": false,
"outputs": 1,
"x": 1020,
"y": 580,
"wires": [
[
"fa8d1668235782d3"
]
]
},
{
"id": "00b3ef7322366374",
"type": "time-range-switch",
"z": "a1055886e0cbd337",
"d": true,
"g": "23b8f90b282ac8a2",
"name": "Nur Nachts 20-8",
"lat": "51.2115333",
"lon": "6.7448851",
"startTime": "20:00",
"endTime": "08:00",
"startOffset": 0,
"endOffset": 0,
"x": 380,
"y": 540,
"wires": [
[
"87806c641c86c3d9"
],
[]
]
},
{
"id": "87806c641c86c3d9",
"type": "api-current-state",
"z": "a1055886e0cbd337",
"g": "23b8f90b282ac8a2",
"name": "Get Timer Value",
"server": "387b3091.65c5",
"version": 3,
"outputs": 2,
"halt_if": "0",
"halt_if_type": "num",
"halt_if_compare": "gt",
"entity_id": "input_number.diele_nachtlicht_timer",
"state_type": "num",
"blockInputOverrides": true,
"outputProperties": [
{
"property": "payload",
"propertyType": "msg",
"value": "",
"valueType": "entityState"
},
{
"property": "data",
"propertyType": "msg",
"value": "",
"valueType": "entity"
}
],
"for": "0",
"forType": "num",
"forUnits": "minutes",
"override_topic": false,
"state_location": "payload",
"override_payload": "msg",
"entity_location": "data",
"override_data": "msg",
"x": 600,
"y": 580,
"wires": [
[
"14c3a212afc4cd78"
],
[]
]
},
{
"id": "14c3a212afc4cd78",
"type": "function",
"z": "a1055886e0cbd337",
"g": "23b8f90b282ac8a2",
"name": "Min2Millisekunden",
"func": "// Annahme: msg.payload enthält den Wert in Minuten\nlet minuten = msg.payload;\nmsg.delay = minuten * 60 * 1000; // Minuten → Millisekunden\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 810,
"y": 580,
"wires": [
[
"43d024d394d94663"
]
]
},
{
"id": "7e7074a18d177e16",
"type": "api-current-state",
"z": "a1055886e0cbd337",
"g": "23b8f90b282ac8a2",
"name": "Nachtlich Zeitplan",
"server": "387b3091.65c5",
"version": 3,
"outputs": 2,
"halt_if": "on",
"halt_if_type": "str",
"halt_if_compare": "is",
"entity_id": "schedule.diele_nachtlicht_zeitplan",
"state_type": "str",
"blockInputOverrides": true,
"outputProperties": [
{
"property": "payload",
"propertyType": "msg",
"value": "",
"valueType": "entityState"
},
{
"property": "data",
"propertyType": "msg",
"value": "",
"valueType": "entity"
}
],
"for": "0",
"forType": "num",
"forUnits": "minutes",
"override_topic": false,
"state_location": "payload",
"override_payload": "msg",
"entity_location": "data",
"override_data": "msg",
"x": 790,
"y": 440,
"wires": [
[
"2b525828bc96835e"
],
[]
]
},
{
"id": "38767c1f739f3c8d",
"type": "api-current-state",
"z": "a1055886e0cbd337",
"g": "23b8f90b282ac8a2",
"name": "Nachtlich Zeitplan",
"server": "387b3091.65c5",
"version": 3,
"outputs": 2,
"halt_if": "on",
"halt_if_type": "str",
"halt_if_compare": "is",
"entity_id": "schedule.diele_nachtlicht_zeitplan",
"state_type": "str",
"blockInputOverrides": true,
"outputProperties": [
{
"property": "payload",
"propertyType": "msg",
"value": "",
"valueType": "entityState"
},
{
"property": "data",
"propertyType": "msg",
"value": "",
"valueType": "entity"
}
],
"for": "0",
"forType": "num",
"forUnits": "minutes",
"override_topic": false,
"state_location": "payload",
"override_payload": "msg",
"entity_location": "data",
"override_data": "msg",
"x": 390,
"y": 580,
"wires": [
[
"87806c641c86c3d9"
],
[]
]
},
{
"id": "4f22d1a57648e607",
"type": "api-call-service",
"z": "a1055886e0cbd337",
"g": "23b8f90b282ac8a2",
"name": "Küche LED Streifen AN",
"server": "387b3091.65c5",
"version": 7,
"debugenabled": false,
"action": "light.turn_on",
"floorId": [],
"areaId": [],
"deviceId": [],
"entityId": [
"light.arbeitsplatte"
],
"labelId": [],
"data": "{\"brightness\":\"40\"\t,\"profile\":\"relax\"}",
"dataType": "jsonata",
"mergeContext": "",
"mustacheAltTags": false,
"outputProperties": [],
"queue": "none",
"blockInputOverrides": false,
"domain": "light",
"service": "turn_on",
"x": 1250,
"y": 500,
"wires": [
[]
]
},
{
"id": "2b525828bc96835e",
"type": "junction",
"z": "a1055886e0cbd337",
"g": "23b8f90b282ac8a2",
"x": 1080,
"y": 440,
"wires": [
[
"5b5ec57984037a5c",
"4f22d1a57648e607"
]
]
},
{
"id": "c730df6b04bdaf41",
"type": "api-call-service",
"z": "a1055886e0cbd337",
"g": "23b8f90b282ac8a2",
"name": "Küche LED Streifen AUS",
"server": "387b3091.65c5",
"version": 7,
"debugenabled": false,
"action": "light.turn_off",
"floorId": [],
"areaId": [],
"deviceId": [],
"entityId": [
"light.arbeitsplatte"
],
"labelId": [],
"data": "",
"dataType": "jsonata",
"mergeContext": "",
"mustacheAltTags": false,
"outputProperties": [],
"queue": "none",
"blockInputOverrides": false,
"domain": "light",
"service": "turn_off",
"x": 1250,
"y": 640,
"wires": [
[]
]
},
{
"id": "fa8d1668235782d3",
"type": "junction",
"z": "a1055886e0cbd337",
"g": "23b8f90b282ac8a2",
"x": 1100,
"y": 580,
"wires": [
[
"f0c5e4339b494c3a",
"c730df6b04bdaf41"
]
]
},
{
"id": "387b3091.65c5",
"type": "server",
"name": "Home Assistant",
"addon": true
}
]
File diff suppressed because it is too large Load Diff
-182
View File
@@ -1,182 +0,0 @@
[
{
"id": "ec8c530269d9809c",
"type": "group",
"z": "74eab2802feb3cf4",
"name": "Heimkino Pause bei Klingel",
"style": {
"fill": "#dbcbe7",
"label": true
},
"nodes": [
"bbe2bd314f9ccfdd",
"a53fb4f4f48cc404",
"3bdb09facf75d941"
],
"x": 14,
"y": 319,
"w": 1172,
"h": 82
},
{
"id": "bbe2bd314f9ccfdd",
"type": "server-state-changed",
"z": "74eab2802feb3cf4",
"g": "ec8c530269d9809c",
"name": "Klingel",
"server": "162e4f7136cd343a",
"version": 6,
"outputs": 2,
"exposeAsEntityConfig": "",
"entities": {
"entity": [
"binary_sensor.diele_klingelsensor"
],
"substring": [],
"regex": []
},
"outputInitially": false,
"stateType": "str",
"ifState": "on",
"ifStateType": "str",
"ifStateOperator": "is",
"outputOnlyOnStateChange": true,
"for": "0",
"forType": "num",
"forUnits": "minutes",
"ignorePrevStateNull": false,
"ignorePrevStateUnknown": false,
"ignorePrevStateUnavailable": false,
"ignoreCurrentStateUnknown": false,
"ignoreCurrentStateUnavailable": false,
"outputProperties": [
{
"property": "payload",
"propertyType": "msg",
"value": "Jemand hat an der Haustüre geschellt",
"valueType": "str"
},
{
"property": "topic",
"propertyType": "msg",
"value": "Klingel",
"valueType": "str"
},
{
"property": "sound",
"propertyType": "msg",
"value": "bike",
"valueType": "str"
}
],
"x": 90,
"y": 360,
"wires": [
[
"a53fb4f4f48cc404"
],
[]
]
},
{
"id": "a53fb4f4f48cc404",
"type": "api-current-state",
"z": "74eab2802feb3cf4",
"g": "ec8c530269d9809c",
"name": "State Apple TV",
"server": "162e4f7136cd343a",
"version": 3,
"outputs": 2,
"halt_if": "playing",
"halt_if_type": "str",
"halt_if_compare": "is",
"entity_id": "media_player.multimedia",
"state_type": "str",
"blockInputOverrides": true,
"outputProperties": [
{
"property": "payload",
"propertyType": "msg",
"value": "",
"valueType": "entityState"
},
{
"property": "data",
"propertyType": "msg",
"value": "",
"valueType": "entity"
}
],
"for": "0",
"forType": "num",
"forUnits": "minutes",
"override_topic": false,
"state_location": "payload",
"override_payload": "msg",
"entity_location": "data",
"override_data": "msg",
"x": 420,
"y": 360,
"wires": [
[
"3bdb09facf75d941"
],
[]
]
},
{
"id": "3bdb09facf75d941",
"type": "api-call-service",
"z": "74eab2802feb3cf4",
"g": "ec8c530269d9809c",
"name": "Apple TV Pause",
"server": "162e4f7136cd343a",
"version": 7,
"debugenabled": false,
"action": "media_player.media_pause",
"floorId": [],
"areaId": [],
"deviceId": [],
"entityId": [
"media_player.multimedia"
],
"labelId": [],
"data": "",
"dataType": "jsonata",
"mergeContext": "",
"mustacheAltTags": false,
"outputProperties": [],
"queue": "none",
"blockInputOverrides": true,
"domain": "media_player",
"service": "media_pause",
"x": 1080,
"y": 360,
"wires": [
[]
]
},
{
"id": "162e4f7136cd343a",
"type": "server",
"name": "Home Assistant",
"version": 5,
"addon": false,
"rejectUnauthorizedCerts": true,
"ha_boolean": "y|yes|true|on|home|open",
"connectionDelay": true,
"cacheJson": true,
"heartbeat": true,
"heartbeatInterval": "10",
"areaSelector": "friendlyName",
"deviceSelector": "friendlyName",
"entitySelector": "friendlyName",
"statusSeparator": ": ",
"statusYear": "hidden",
"statusMonth": "short",
"statusDay": "numeric",
"statusHourCycle": "default",
"statusTimeFormat": "h:m",
"enableGlobalContextStore": false
}
]
-162
View File
@@ -1,162 +0,0 @@
[
{
"id": "a0b2588596805a6b",
"type": "group",
"z": "97ac338125ef7574",
"name": "Küche Milch Auto Aus",
"style": {
"fill": "#e3f3d3",
"label": true
},
"nodes": [
"9098a3b94fafb8d1",
"b1fad812fd4fcba0",
"581fb8dd7042b734"
],
"x": 34,
"y": 359,
"w": 1132,
"h": 82
},
{
"id": "9098a3b94fafb8d1",
"type": "server-state-changed",
"z": "97ac338125ef7574",
"g": "a0b2588596805a6b",
"name": "Küche Milch",
"server": "162e4f7136cd343a",
"version": 6,
"outputs": 2,
"exposeAsEntityConfig": "",
"entities": {
"entity": [
"switch.kuche_baby_breezer"
],
"substring": [],
"regex": []
},
"outputInitially": false,
"stateType": "str",
"ifState": "on",
"ifStateType": "str",
"ifStateOperator": "is",
"outputOnlyOnStateChange": true,
"for": "",
"forType": "num",
"forUnits": "minutes",
"ignorePrevStateNull": false,
"ignorePrevStateUnknown": false,
"ignorePrevStateUnavailable": false,
"ignoreCurrentStateUnknown": false,
"ignoreCurrentStateUnavailable": false,
"outputProperties": [
{
"property": "payload",
"propertyType": "msg",
"value": "",
"valueType": "entityState"
},
{
"property": "data",
"propertyType": "msg",
"value": "",
"valueType": "eventData"
},
{
"property": "topic",
"propertyType": "msg",
"value": "",
"valueType": "triggerId"
}
],
"x": 130,
"y": 400,
"wires": [
[
"581fb8dd7042b734"
],
[]
]
},
{
"id": "b1fad812fd4fcba0",
"type": "api-call-service",
"z": "97ac338125ef7574",
"g": "a0b2588596805a6b",
"name": "Küche Milch Aus",
"server": "162e4f7136cd343a",
"version": 7,
"debugenabled": false,
"action": "switch.turn_off",
"floorId": [],
"areaId": [],
"deviceId": [],
"entityId": [
"switch.kuche_baby_breezer"
],
"labelId": [],
"data": "",
"dataType": "jsonata",
"mergeContext": "",
"mustacheAltTags": false,
"outputProperties": [],
"queue": "none",
"blockInputOverrides": true,
"domain": "switch",
"service": "turn_off",
"x": 1050,
"y": 400,
"wires": [
[]
]
},
{
"id": "581fb8dd7042b734",
"type": "delay",
"z": "97ac338125ef7574",
"g": "a0b2588596805a6b",
"name": "",
"pauseType": "delay",
"timeout": "5",
"timeoutUnits": "minutes",
"rate": "1",
"nbRateUnits": "1",
"rateUnits": "second",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": false,
"allowrate": false,
"outputs": 1,
"x": 480,
"y": 400,
"wires": [
[
"b1fad812fd4fcba0"
]
]
},
{
"id": "162e4f7136cd343a",
"type": "server",
"name": "Home Assistant",
"version": 5,
"addon": false,
"rejectUnauthorizedCerts": true,
"ha_boolean": "y|yes|true|on|home|open",
"connectionDelay": true,
"cacheJson": true,
"heartbeat": true,
"heartbeatInterval": "10",
"areaSelector": "friendlyName",
"deviceSelector": "friendlyName",
"entitySelector": "friendlyName",
"statusSeparator": ": ",
"statusYear": "hidden",
"statusMonth": "short",
"statusDay": "numeric",
"statusHourCycle": "default",
"statusTimeFormat": "h:m",
"enableGlobalContextStore": false
}
]
-162
View File
@@ -1,162 +0,0 @@
[
{
"id": "70de134cfdc04e0d",
"type": "group",
"z": "97ac338125ef7574",
"name": "Küche Wasserkocher Auto Aus",
"style": {
"fill": "#e3f3d3",
"label": true
},
"nodes": [
"83515f35bc98956a",
"0c50f796b7692d83",
"6b1696f0efaa58a4"
],
"x": 34,
"y": 459,
"w": 1132,
"h": 82
},
{
"id": "83515f35bc98956a",
"type": "server-state-changed",
"z": "97ac338125ef7574",
"g": "70de134cfdc04e0d",
"name": "Küche Wasserkocher",
"server": "162e4f7136cd343a",
"version": 6,
"outputs": 2,
"exposeAsEntityConfig": "",
"entities": {
"entity": [
"switch.kuche_wasserkocher"
],
"substring": [],
"regex": []
},
"outputInitially": false,
"stateType": "str",
"ifState": "on",
"ifStateType": "str",
"ifStateOperator": "is",
"outputOnlyOnStateChange": true,
"for": "",
"forType": "num",
"forUnits": "minutes",
"ignorePrevStateNull": false,
"ignorePrevStateUnknown": false,
"ignorePrevStateUnavailable": false,
"ignoreCurrentStateUnknown": false,
"ignoreCurrentStateUnavailable": false,
"outputProperties": [
{
"property": "payload",
"propertyType": "msg",
"value": "",
"valueType": "entityState"
},
{
"property": "data",
"propertyType": "msg",
"value": "",
"valueType": "eventData"
},
{
"property": "topic",
"propertyType": "msg",
"value": "",
"valueType": "triggerId"
}
],
"x": 160,
"y": 500,
"wires": [
[
"6b1696f0efaa58a4"
],
[]
]
},
{
"id": "0c50f796b7692d83",
"type": "api-call-service",
"z": "97ac338125ef7574",
"g": "70de134cfdc04e0d",
"name": "Küche Wasserkocher Aus",
"server": "162e4f7136cd343a",
"version": 7,
"debugenabled": false,
"action": "switch.turn_off",
"floorId": [],
"areaId": [],
"deviceId": [],
"entityId": [
"switch.kuche_wasserkocher"
],
"labelId": [],
"data": "",
"dataType": "jsonata",
"mergeContext": "",
"mustacheAltTags": false,
"outputProperties": [],
"queue": "none",
"blockInputOverrides": true,
"domain": "switch",
"service": "turn_off",
"x": 1030,
"y": 500,
"wires": [
[]
]
},
{
"id": "6b1696f0efaa58a4",
"type": "delay",
"z": "97ac338125ef7574",
"g": "70de134cfdc04e0d",
"name": "",
"pauseType": "delay",
"timeout": "7",
"timeoutUnits": "minutes",
"rate": "1",
"nbRateUnits": "1",
"rateUnits": "second",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": false,
"allowrate": false,
"outputs": 1,
"x": 480,
"y": 500,
"wires": [
[
"0c50f796b7692d83"
]
]
},
{
"id": "162e4f7136cd343a",
"type": "server",
"name": "Home Assistant",
"version": 5,
"addon": false,
"rejectUnauthorizedCerts": true,
"ha_boolean": "y|yes|true|on|home|open",
"connectionDelay": true,
"cacheJson": true,
"heartbeat": true,
"heartbeatInterval": "10",
"areaSelector": "friendlyName",
"deviceSelector": "friendlyName",
"entitySelector": "friendlyName",
"statusSeparator": ": ",
"statusYear": "hidden",
"statusMonth": "short",
"statusDay": "numeric",
"statusHourCycle": "default",
"statusTimeFormat": "h:m",
"enableGlobalContextStore": false
}
]
File diff suppressed because it is too large Load Diff
-223
View File
@@ -1,223 +0,0 @@
alias: Notification Board
mode: queued
max: 10
description: ""
fields:
message:
name: Message
description: Nachricht
selector:
text:
multiline: true
title:
name: Title
description: Titel
selector:
text: null
critical:
name: Critical
description: iPhone Kritische Hinweise
selector:
boolean: {}
required: true
notify_alexa_durchsagen:
name: Notify Alexa (Durchsagen)
description: Sprachausgabe über Amazon-Geräte mit "_durchsagen"
selector:
boolean: {}
required: true
notify_alexa_sprechen:
name: Notify Alexa (Sprechen)
description: Sprachausgabe über Amazon-Geräte mit "_sprechen"
selector:
boolean: {}
required: true
notify_tv:
name: Notify TV
description: Benachrichtigung auf TV Geräten
selector:
boolean: {}
required: true
pushover_oliver:
name: Pushover Oliver
description: Benachrichtigung über Pushover (Oliver)
selector:
boolean: {}
required: true
pushover_lisa:
name: Pushover Lisa
description: Benachrichtigung über Pushover (Lisa)
selector:
boolean: {}
required: true
notify_persistent:
name: Notify Persistent (Nur App)
description: Dauerhafte Benachrichtigung in Home Assistant
selector:
boolean: {}
required: true
notify_oliver:
name: Notify Oliver
description: Benachrichtigung über Mobile App (Oliver)
selector:
boolean: {}
required: true
notify_lisa:
name: Notify Lisa
description: Benachrichtigung über Mobile App (Lisa)
selector:
boolean: {}
required: true
notify_familie:
name: Notify Familie
description: Benachrichtigung über beide Mobile Apps
selector:
boolean: {}
required: true
sequence:
- variables:
persistent_data: >-
{% if notify_persistent %}
{"notification_id": "{{ title | default("benachrichtigung") | lower | replace(" ", "_") }}"}
{% else %} {} {% endif %}
- variables:
tv_on: "{{ is_state('media_player.lg_webos_tv_nano769qa', 'on') }}"
- variables:
notify_targets: >-
{% set t = [] %}
{% if notify_persistent %}
{% set t = t + ['notify.persistent_notification'] %}
{% endif %}
{% if notify_alexa_durchsagen %}
{% set alexas_durchsagen = states.notify
| selectattr('entity_id', 'match', 'notify\\..*_durchsagen')
| rejectattr('entity_id', 'search', 'multiroom_durchsagen')
| rejectattr('entity_id', 'search', 'uberall_durchsagen')
| map(attribute='entity_id')
| list %}
{% set t = t + alexas_durchsagen %}
{% endif %}
{% if notify_alexa_sprechen %}
{% set alexas_sprechen = states.notify
| selectattr('entity_id', 'match', 'notify\\..*_sprechen')
| map(attribute='entity_id')
| list %}
{% set t = t + alexas_sprechen %}
{% endif %}
{% if notify_tv and tv_on %}
{% set t = t + ['notify.lg_webos_tv_nano769qa'] %}
{% endif %}
{% if pushover_oliver %}
{% set t = t + ['notify.pushover_oliver'] %}
{% endif %}
{% if pushover_lisa %}
{% set t = t + ['notify.pushover_lisa'] %}
{% endif %}
{% if notify_oliver %}
{% set t = t + ['notify.mobile_app_finger_weg_du_spasst'] %}
{% endif %}
{% if notify_lisa %}
{% set t = t + ['notify.mobile_app_meins'] %}
{% endif %}
{% if notify_familie %}
{% set t = t + ['notify.mobile_app_finger_weg_du_spasst', 'notify.mobile_app_meins'] %}
{% endif %}
{{ t | map('lower') | map('trim') | unique | list }}
- repeat:
for_each: "{{ notify_targets }}"
sequence:
- choose:
# PERSISTENT NOTIFICATION
- conditions:
- condition: template
value_template: "{{ repeat.item == 'notify.persistent_notification' }}"
sequence:
- data:
message: "{{ message }}"
title: "{{ title | default('Benachrichtigung') }}"
data: "{{ persistent_data | from_json(default={}) }}"
action: notify.persistent_notification
# PUSHOVER
- conditions:
- condition: template
value_template: "{{ repeat.item.startswith('notify.pushover') }}"
sequence:
- choose:
- conditions:
- condition: template
value_template: "{{ critical }}"
sequence:
- data:
message: "{{ message }}"
title: "{{ title | default('Benachrichtigung') }}"
data:
priority: 2
sound: siren
retry: 30
expire: 300
action: "{{ repeat.item }}"
- conditions:
- condition: template
value_template: "{{ not critical }}"
sequence:
- data:
message: "{{ message }}"
title: "{{ title | default('Benachrichtigung') }}"
action: "{{ repeat.item }}"
# ALEXA / SONOS (mit Verzögerung)
- conditions:
- condition: template
value_template: >
{{ repeat.item.endswith('_durchsagen') or repeat.item.endswith('_sprechen') }}
sequence:
- data:
message: "{{ message }}"
title: "{{ title | default('Benachrichtigung') }}"
target:
entity_id: "{{ repeat.item }}"
action: notify.send_message
- delay: "00:00:02"
# MOBILE APP (iOS/Android)
- conditions:
- condition: template
value_template: "{{ repeat.item.startswith('notify.mobile_app') }}"
sequence:
- choose:
- conditions:
- condition: template
value_template: "{{ critical }}"
sequence:
- data:
message: "{{ message }}"
title: "{{ title | default('Benachrichtigung') }}"
data:
push:
sound:
name: default
critical: 1
volume: 1.0
action: "{{ repeat.item }}"
- conditions:
- condition: template
value_template: "{{ not critical }}"
sequence:
- data:
message: "{{ message }}"
title: "{{ title | default('Benachrichtigung') }}"
action: "{{ repeat.item }}"
# FALLBACK (TV, andere)
- conditions:
- condition: template
value_template: "{{ True }}"
sequence:
- data:
message: "{{ message }}"
title: "{{ title | default('Benachrichtigung') }}"
action: "{{ repeat.item }}"
+110
View File
@@ -0,0 +1,110 @@
# Verbraucher Rangliste (Power Ranking Card)
Custom Lovelace-Karte für Home Assistant, die alle Verbraucher (Entities mit
Einheit `W` oder `kW`) absteigend nach aktueller Leistung als Rangliste mit
animierten Balken anzeigt.
Datei in diesem Ordner: [`power-ranking-card.js`](./power-ranking-card.js)
## Installation
### 1. Datei nach `config/www/` kopieren
Die Datei `power-ranking-card.js` aus diesem Ordner in den `www`-Ordner der
Home-Assistant-Konfiguration legen:
```
config/www/power-ranking-card.js
```
Alles unter `config/www/` ist automatisch über `/local/...` erreichbar.
### 2. Dashboard-Resource anlegen
**Einstellungen → Dashboards → Ressourcen (oben rechts, drei Punkte) →
Ressource hinzufügen**
| Feld | Wert |
|---|---|
| URL | `/local/power-ranking-card.js?v=7` |
| Ressourcentyp | JavaScript-Modul (`module`) |
> Der `?v=7`-Parameter ist ein reiner Cache-Buster. Wenn die Datei später
> aktualisiert wird, die Versionsnummer hochzählen (`?v=8`, `?v=9`, …),
> damit Browser-Caches sie neu laden.
### 3. Karte im Dashboard einbinden
Als YAML-Karte in eine beliebige View/Section einfügen, z. B.:
```yaml
type: custom:power-ranking-card
update_interval: 5
bar_duration: 1.2
title: "⚡ Verbraucher Rangliste"
min_watt: 1
max_items: 40
hide_name:
- "*POWER CALC*"
- "*Leistung*"
- "*ENERGY*"
- "*POWER*"
colors:
- above: 2000
color: "#e53935"
- above: 500
color: "#fb8c00"
- above: 100
color: "#fdd835"
- above: 0
color: "#43a047"
exclude:
- "sensor.solcast_*"
- "sensor.solarbank*"
- "sensor.*netzbezug*"
- "sensor.system_home_*"
- "sensor.*akku*"
- "sensor.badezimmer_*phase*"
```
Aktuell so im Einsatz auf dem Dashboard `lovelace` → View `Energie`.
## Konfigurationsoptionen
| Option | Typ | Standard | Beschreibung |
|---|---|---|---|
| `title` | string | `⚡ Verbraucher Rangliste` | Überschrift der Karte |
| `update_interval` | number (Sekunden) | `5` | Throttle: wie oft die Karte höchstens neu berechnet/gerendert wird |
| `bar_duration` | number (Sekunden) | `0.9` | Dauer der Balken-Animation bei Wertänderung |
| `min_watt` | number | `0` | Verbraucher unterhalb dieses Werts werden ausgeblendet |
| `max_items` | number | `20` | Maximale Anzahl angezeigter Zeilen |
| `pulse` | boolean | `true` | Kurzes Aufleuchten/Abdunkeln des Balkens bei Anstieg/Abfall |
| `colors` | list | siehe unten | Farbschwellen, absteigend sortiert. Erster Treffer (`watt > above`) gewinnt |
| `exclude` | list of strings | `[]` | Entities ausschließen (Entity-ID oder Name), `*` als Wildcard möglich |
| `hide_name` | list of strings (oder einzelner String) | `[]` | Textbausteine aus dem angezeigten Namen entfernen (Entity bleibt sichtbar), `*` als Wildcard möglich |
Standard-Farbschwellen, falls `colors` nicht gesetzt ist:
```yaml
colors:
- { above: 2000, color: "#e53935" }
- { above: 500, color: "#fb8c00" }
- { above: 100, color: "#fdd835" }
- { above: 0, color: "#43a047" }
```
## Datenquelle
Die Karte durchsucht automatisch **alle** `hass.states` nach Entities mit
`unit_of_measurement` von `W` oder `kW` — es müssen keine Entities explizit
angegeben werden. Über `exclude` und `min_watt` lässt sich die Liste
eingrenzen, über `hide_name` lassen sich Namensbestandteile (z. B.
technische Suffixe wie „POWER CALC“) aus der Anzeige entfernen, ohne die
Entity selbst auszuschließen.
## Update der Karte
1. `power-ranking-card.js` in diesem Ordner aktualisieren und committen.
2. Aktualisierte Datei nach `config/www/power-ranking-card.js` kopieren.
3. Versionsnummer in der Resource-URL hochzählen (z. B. `?v=8`), damit der
Browser die neue Version lädt statt der gecachten alten.
+371
View File
@@ -0,0 +1,371 @@
/* Power Ranking Card v4
* - Browser/WebView kompatibel (kein ?. / ??)
* - korrektes Wildcard-Exclude (*)
* - Throttle + Change-Detection
* - FLIP-Animation: Zeilen gleiten an neue Position
* - Balken animieren weich; Puls bei Anstieg/Abfall
* - Farb-Schwellen ueber Config einstellbar
*/
class PowerRankingCard extends HTMLElement {
constructor() {
super();
this._initialized = false;
this._lastSnapshot = "";
this._lastRender = 0;
this._throttleTimer = null;
this._rowMap = {}; // entity_id -> <li>
this._lastWatt = {}; // entity_id -> letzter Watt-Wert (fuer Puls-Richtung)
this._lastPct = {}; // entity_id -> letzter Balken-Prozentwert (fuer Animation)
}
setConfig(config) {
this.config = config;
var iv = (config.update_interval != null) ? config.update_interval : 5;
this._throttleMs = iv * 1000;
// ---- Farbschwellen ----
// Default: gleiche Logik wie bisher. Ueberschreibbar via config.colors:
// colors:
// - { above: 2000, color: "#e53935" }
// - { above: 500, color: "#fb8c00" }
// - { above: 100, color: "#fdd835" }
// - { above: 0, color: "#43a047" }
if (config.colors && config.colors.length) {
this._colorStops = config.colors.slice();
} else {
this._colorStops = [
{ above: 2000, color: "#e53935" },
{ above: 500, color: "#fb8c00" },
{ above: 100, color: "#fdd835" },
{ above: 0, color: "#43a047" }
];
}
// absteigend nach Schwelle sortieren, damit erste Treffer gewinnt
this._colorStops.sort(function (a, b) { return b.above - a.above; });
this._pulse = (config.pulse != null) ? config.pulse : true;
var patterns = config.exclude || [];
this._excludeMatchers = [];
for (var i = 0; i < patterns.length; i++) {
this._excludeMatchers.push(this._buildMatcher(patterns[i]));
}
// ---- hide_name: Textteile aus dem ANGEZEIGTEN Namen entfernen ----
// (Entitaet bleibt in der Liste, nur der Text wird gekuerzt)
// Wildcards wie beim exclude: "*POWER CALC*" entfernt diesen Teil ueberall.
var hideList = config.hide_name || [];
// Auch Einzelwert (String) erlauben
if (typeof hideList === "string") hideList = [hideList];
this._hideNameRes = [];
for (var h = 0; h < hideList.length; h++) {
this._hideNameRes.push(this._buildNameStripper(hideList[h]));
}
}
_buildNameStripper(pat) {
// Pattern -> globale RegExp, die den passenden Textteil findet.
// Fuehrende/abschliessende * sind nur "egal was drumrum", der Kern wird entfernt.
// Beispiel: "*POWER CALC*" -> entfernt "POWER CALC" wo immer es vorkommt.
var core = pat.replace(/^\*+/, "").replace(/\*+$/, "");
var parts = core.split("*");
for (var i = 0; i < parts.length; i++) {
parts[i] = parts[i].replace(/[.+^${}()|[\]\\]/g, "\\$&");
}
var reStr = parts.join(".*?");
return new RegExp(reStr, "ig");
}
_displayName(name) {
var out = name;
for (var i = 0; i < this._hideNameRes.length; i++) {
out = out.replace(this._hideNameRes[i], " ");
}
// doppelte Leerzeichen zusammenfassen und Raender trimmen
out = out.replace(/\s{2,}/g, " ").replace(/^\s+|\s+$/g, "");
return out.length ? out : name; // niemals komplett leeren Namen anzeigen
}
_buildMatcher(pat) {
if (pat.indexOf("*") !== -1) {
var parts = pat.split("*");
for (var i = 0; i < parts.length; i++) {
parts[i] = parts[i].replace(/[.+^${}()|[\]\\]/g, "\\$&");
}
var re = new RegExp("^" + parts.join(".*") + "$", "i");
return function (id, name) { return re.test(id) || re.test(name); };
}
var lower = pat.toLowerCase();
return function (id, name) {
return id.toLowerCase().indexOf(lower) !== -1 ||
name.toLowerCase().indexOf(lower) !== -1;
};
}
set hass(hass) {
this._hass = hass;
var self = this;
if (this._throttleTimer) return;
var elapsed = Date.now() - this._lastRender;
var wait = this._throttleMs - elapsed;
if (wait < 0) wait = 0;
this._throttleTimer = setTimeout(function () {
self._throttleTimer = null;
self._maybeUpdate();
}, wait);
}
_isExcluded(id, name) {
for (var i = 0; i < this._excludeMatchers.length; i++) {
if (this._excludeMatchers[i](id, name)) return true;
}
return false;
}
_getColor(watt) {
for (var i = 0; i < this._colorStops.length; i++) {
if (watt > this._colorStops[i].above) return this._colorStops[i].color;
}
// Fallback: letzte (niedrigste) Farbe
return this._colorStops[this._colorStops.length - 1].color;
}
_collectEntries() {
var config = this.config;
var minW = (config.min_watt != null) ? config.min_watt : 0;
var maxN = (config.max_items != null) ? config.max_items : 20;
var states = this._hass.states;
var out = [];
for (var id in states) {
var s = states[id];
var unit = s.attributes.unit_of_measurement;
if (unit !== "W" && unit !== "kW") continue;
var val = parseFloat(s.state);
if (isNaN(val) || val < minW) continue;
var name = s.attributes.friendly_name || s.entity_id;
if (this._isExcluded(s.entity_id, name)) continue;
var watt = (unit === "kW") ? val * 1000 : val;
out.push({ entity_id: s.entity_id, name: name, watt: watt });
}
out.sort(function (a, b) { return b.watt - a.watt; });
return out.slice(0, maxN);
}
_snapshot(entries) {
var parts = [];
for (var i = 0; i < entries.length; i++) {
parts.push(entries[i].entity_id + ":" + Math.round(entries[i].watt));
}
return parts.join("|");
}
_maybeUpdate() {
var entries = this._collectEntries();
var snapshot = this._snapshot(entries);
if (snapshot === this._lastSnapshot) return;
this._lastSnapshot = snapshot;
this._lastRender = Date.now();
if (!this._initialized) this._buildSkeleton();
this._render(entries);
}
_buildSkeleton() {
this.innerHTML = "";
var card = document.createElement("ha-card");
var style = document.createElement("style");
style.textContent =
".prc-wrap{padding:12px 16px 16px;}" +
".prc-title{font-size:1.1em;font-weight:600;margin-bottom:12px;color:var(--primary-text-color);display:flex;align-items:center;gap:6px;}" +
".prc-list{list-style:none;margin:0;padding:0;position:relative;}" +
".prc-row{display:flex;align-items:center;gap:10px;margin-bottom:10px;will-change:transform;}" +
".prc-rank{font-size:0.75em;color:var(--secondary-text-color);min-width:18px;text-align:right;flex-shrink:0;}" +
".prc-info{flex:1;min-width:0;}" +
".prc-name{font-size:0.85em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--primary-text-color);margin-bottom:3px;}" +
".prc-bwrap{height:8px;background:var(--divider-color);border-radius:4px;overflow:hidden;}" +
// Balken: weiche Breiten-Animation
".prc-bar{height:100%;border-radius:4px;transition:background 0.5s ease,box-shadow 0.3s ease;}" +
// Puls-Effekte (Glow) bei Anstieg / Abfall
".prc-bar.up{animation:prcUp 0.7s ease;}" +
".prc-bar.down{animation:prcDown 0.7s ease;}" +
"@keyframes prcUp{0%{box-shadow:0 0 0 0 rgba(255,255,255,0);}30%{box-shadow:0 0 8px 2px rgba(255,255,255,0.55);}100%{box-shadow:0 0 0 0 rgba(255,255,255,0);}}" +
"@keyframes prcDown{0%{filter:brightness(1);}30%{filter:brightness(0.6);}100%{filter:brightness(1);}}" +
".prc-val{font-size:0.82em;font-weight:600;min-width:58px;text-align:right;font-variant-numeric:tabular-nums;transition:color 0.5s ease;}" +
".prc-empty{padding:8px 0;color:var(--secondary-text-color);font-size:0.9em;}";
var wrap = document.createElement("div");
wrap.className = "prc-wrap";
var title = document.createElement("div");
title.className = "prc-title";
title.textContent = (this.config.title != null) ? this.config.title : "\u26A1 Verbraucher Rangliste";
this._list = document.createElement("ul");
this._list.className = "prc-list";
wrap.appendChild(title);
wrap.appendChild(this._list);
card.appendChild(style);
card.appendChild(wrap);
this.appendChild(card);
this._initialized = true;
}
_makeRow() {
var li = document.createElement("li");
li.className = "prc-row";
li.innerHTML =
'<div class="prc-rank"></div>' +
'<div class="prc-info">' +
'<div class="prc-name"></div>' +
'<div class="prc-bwrap"><div class="prc-bar"></div></div>' +
'</div>' +
'<div class="prc-val"></div>';
return li;
}
_pulseBar(barEl, dir) {
if (!this._pulse) return;
barEl.classList.remove("up", "down");
void barEl.offsetHeight; // reflow -> animation neu startbar
barEl.classList.add(dir);
}
_render(entries) {
var list = this._list;
var max = entries.length ? entries[0].watt : 1;
if (!max) max = 1;
// FLIP 1: alte Positionen merken
var firstTops = {};
for (var id in this._rowMap) {
var el0 = this._rowMap[id];
if (el0.parentNode === list) firstTops[id] = el0.getBoundingClientRect().top;
}
var seen = {};
var orderedEls = [];
var barTargets = [];
for (var i = 0; i < entries.length; i++) {
var e = entries[i];
seen[e.entity_id] = true;
var li = this._rowMap[e.entity_id];
var isNew = false;
if (!li) {
li = this._makeRow();
this._rowMap[e.entity_id] = li;
isNew = true;
}
var pct = (e.watt / max) * 100;
var color = this._getColor(e.watt);
var label = (e.watt >= 1000) ? (e.watt / 1000).toFixed(2) + " kW" : Math.round(e.watt) + " W";
var rankEl = li.children[0];
var infoEl = li.children[1];
var nameEl = infoEl.children[0];
var barEl = infoEl.children[1].children[0];
var valEl = li.children[2];
var rankStr = String(i + 1);
if (rankEl.textContent !== rankStr) rankEl.textContent = rankStr;
var shownName = this._displayName(e.name);
if (nameEl.textContent !== shownName) nameEl.textContent = shownName;
// Richtung bestimmen (Puls)
var prev = this._lastWatt[e.entity_id];
if (!isNew && prev != null && Math.round(prev) !== Math.round(e.watt)) {
this._pulseBar(barEl, (e.watt > prev) ? "up" : "down");
}
this._lastWatt[e.entity_id] = e.watt;
if (barEl.style.background !== color) barEl.style.background = color;
if (valEl.textContent !== label) valEl.textContent = label;
if (valEl.style.color !== color) valEl.style.color = color;
// Balken-Breite: explizit von alt -> neu animieren (Web Animations API).
// Das ist unabhaengig davon, was der Browser als "vorherigen Wert" erkennt,
// und springt deshalb nicht.
var oldPct = this._lastPct[e.entity_id];
if (oldPct == null) oldPct = 0; // neue Zeile waechst aus 0 heran
this._lastPct[e.entity_id] = pct;
barTargets.push({ bar: barEl, from: oldPct, to: pct });
if (isNew) li.style.opacity = "0";
orderedEls.push(li);
}
// DOM in neue Reihenfolge bringen
for (var j = 0; j < orderedEls.length; j++) list.appendChild(orderedEls[j]);
// entfernte Zeilen loeschen
for (var id2 in this._rowMap) {
if (!seen[id2]) {
var dead = this._rowMap[id2];
if (dead.parentNode === list) list.removeChild(dead);
delete this._rowMap[id2];
delete this._lastWatt[id2];
delete this._lastPct[id2];
}
}
// FLIP 2: Invert + Play
for (var k = 0; k < orderedEls.length; k++) {
(function (el, eid) {
var newTop = el.getBoundingClientRect().top;
var oldTop = firstTops[eid];
if (oldTop != null) {
var delta = oldTop - newTop;
if (delta) {
el.style.transition = "none";
el.style.transform = "translateY(" + delta + "px)";
void el.offsetHeight;
el.style.transition = "transform 0.5s cubic-bezier(.4,0,.2,1)";
el.style.transform = "";
}
} else {
void el.offsetHeight;
el.style.transition = "opacity 0.4s ease";
el.style.opacity = "1";
}
})(orderedEls[k], entries[k].entity_id);
}
// Balken animieren: von alt -> neu, deterministisch.
var DUR = (this.config.bar_duration != null) ? this.config.bar_duration * 1000 : 900;
for (var t = 0; t < barTargets.length; t++) {
var bt = barTargets[t];
var bar = bt.bar;
var toStr = bt.to + "%";
// Endzustand sofort als Style setzen (bleibt nach der Animation stehen)
bar.style.width = toStr;
if (bt.from !== bt.to && typeof bar.animate === "function") {
try {
bar.animate(
[{ width: bt.from + "%" }, { width: toStr }],
{ duration: DUR, easing: "cubic-bezier(.22,1,.36,1)", fill: "backwards" }
);
} catch (err) { /* aeltere Engine ohne WAA: dann eben ohne Animation */ }
}
}
// Leerzustand
var emptyEl = list.querySelector(".prc-empty");
if (entries.length === 0) {
if (!emptyEl) {
emptyEl = document.createElement("li");
emptyEl.className = "prc-empty";
emptyEl.textContent = "Keine aktiven Verbraucher gefunden.";
list.appendChild(emptyEl);
}
} else if (emptyEl) {
list.removeChild(emptyEl);
}
}
getCardSize() { return 4; }
}
customElements.define("power-ranking-card", PowerRankingCard);
window.customCards = window.customCards || [];
window.customCards.push({
type: "power-ranking-card",
name: "Power Ranking Card",
description: "Verbraucher absteigend nach Watt - FLIP + Balken-Puls, Farben konfigurierbar"
});