Clean repo, add Verbraucher Rangliste (Power Ranking Card) with README
This commit is contained in:
-37
@@ -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
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
]
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
@@ -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
@@ -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 }}"
|
||||
@@ -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.
|
||||
@@ -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"
|
||||
});
|
||||
Reference in New Issue
Block a user