Feiertagskalender
Node-RED

Feiertagskalender

Zeigt die deutschen Feiertage an.
A flow by Dieter Rölleke

Download flow Download assets

Flow Details

Zeigt die deutschen Feiertage an. Unterstützt Dashboard 2.0 im Node-Red. Hier kann man das Bundesland einstellen für das die Feiertage angezeigt werden sollen. Ansonsten werden nur bundeseinheitliche Feiertage angezeigt.

[ { "id": "72175f931b3e29ac", "type": "group", "z": "d9d4383b2014cb99", "name": "Feiertage", "style": { "stroke": "#6f2fa0", "fill": "#dbcbe7", "label": true }, "nodes": [ "5127bf18830d4ad3", "fb298cb6c8ec9590", "8a2930e407402daf", "13d51937bd72c388", "fdf27e5ec4e49676", "ef0f8c8001460cd1", "c6e493c70e7beb4d", "6770cec013307649", "b92c198634443888", "ceaf8e559ff49b55", "399d5d84e767d0e3", "44f26464b2a65cc7", "826e9166dab33416" ], "x": 14, "y": 4339, "w": 1152, "h": 222 }, { "id": "5127bf18830d4ad3", "type": "function", "z": "d9d4383b2014cb99", "g": "72175f931b3e29ac", "name": "Definition Feiertage", "func": "\nvar today = new Date();\nvar year = today.getYear();\n// year = \"2027\"\nif (year < 1900) {\n year += 1900\n};\n\nvar Feiertage = [\n {\n Name: \"Neujahr\",\n Datum: \"01.01.\" + year,\n Bundesland: \"all\",\n icon: 26001,\n },\n {\n Name: \"Heilige drei Könige\",\n Datum: \"06.01.\" + year,\n Bundesland:\"BY,BW,AUG,ST\",\n icon: 16528,\n },\n {\n Name: \"Rosenmontag\",\n Datum: \"00.00.\",\n Bundesland:\"all\",\n icon: 4163,\n },\n {\n Name: \"Karfreitag\",\n Datum: \"00.00.\",\n Bundesland:\"all\",\n icon: 1967,\n },\n {\n Name: \"Ostersonntag\",\n Datum: \"00.00.\",\n Bundesland: \"all\",\n icon: 19550,\n },\n {\n Name: \"Ostermontag\",\n Datum: \"00.00.\",\n Bundesland: \"all\",\n icon: 19550,\n },\n {\n Name: \"Himmelfahrt\",\n Datum: \"00.00.\",\n Bundesland: \"all\",\n icon: 1967,\n },\n {\n Name: \"Pfingstsonntag\",\n Datum: \"00.00.\",\n Bundesland: \"all\",\n icon: 41394,\n },\n {\n Name: \"Pfingstmontag\",\n Datum: \"00.00.\",\n Bundesland: \"all\",\n icon: 41394,\n },\n {\n Name: \"Fronleichnam\",\n Datum: \"00.00.\",\n Bundesland: \"BW,BY,AUG,HE,NW,RP,SL,SN,TH\",\n icon: 74987,\n },\n {\n Name: \"Augsburger Friedensfest\",\n Datum: \"08.08.\" + year,\n Bundesland:\"AUG\",\n icon: 41394,\n },\n {\n Name: \"Maria Himmelfahrt\",\n Datum: \"15.08.\" + year,\n Bundesland:\"BY,AUG,SL\",\n icon: 1967,\n },\n {\n Name: \"Tag der deutschen Einheit\",\n Datum: \"03.10.\" + year,\n Bundesland:\"all\",\n icon: 35315,\n },\n {\n Name: \"Reformationstag\",\n Datum: \"31.10.\" + year,\n Bundesland:\"BB,HB,HH,MV,NI,SN,ST,SH,TH\",\n icon: 1967,\n },\n {\n Name: \"Allerheiligen\",\n Datum: \"01.11.\" + year,\n Bundesland:\"BW,BY,AUG,NW,RP,SL\",\n icon: 1967,\n },\n {\n Name: \"Buß- und Bettag\",\n Datum: \"00.00.\",\n Bundesland:\"SN\",\n icon: 26170,\n },\n {\n Name: \"Heiligabend\",\n Datum: \"24.12.\" + year,\n Bundesland:\"all\",\n icon: 9526,\n },\n {\n Name: \"1. Weihnachstag\",\n Datum: \"25.12.\" + year,\n Bundesland:\"all\",\n icon: 1817,\n },\n {\n Name: \"2. Weihnachstag\",\n Datum: \"26.12.\" + year,\n Bundesland:\"all\",\n icon: 1817,\n },\n {\n Name: \"Silvester\",\n Datum: \"31.12.\" + year,\n Bundesland:\"all\",\n icon: 25144,\n },\n {\n Name: \"Tag der Arbeit\",\n Datum: \"01.05.\" + year,\n Bundesland: \"all\",\n icon: 49923,\n },\n {\n Name: \"Frauentag\",\n Datum: \"08.03.\" + year,\n Bundesland:\"BE,MV\",\n icon: 70204, \n },\n {\n Name: \"Weltkindertag\",\n Datum: \"20.09.\" + year,\n Bundesland:\"TH\",\n icon: 6870, \n },\n {\n Name: \"Gründonnerstag\",\n Datum: \"00.00.\" + year,\n Bundesland:\"BW\",\n icon: 110, \n },\n {\n Name: \"Testfeiertag\",\n Datum: \"08.06.\" + year,\n Bundesland:\"TE\",\n icon: 4163, \n }\n];\n\n\n//const ost = new Date(year,0,1);\n//ost.setDate(ost.getDate() + Number(ostern(year)));\n//var easter=ost;\nvar diff = Number(ostern(year));\nmsg.payload = Feiertage;\n// Rosenmontag\nFeiertage[2][\"Datum\"] = osterdate(diff-48);\n//Karfreitag\nFeiertage[3][\"Datum\"] = osterdate(diff-2);\n// Ostern\nFeiertage[4][\"Datum\"] = osterdate(diff);\n//Ostermontag\nFeiertage[5][\"Datum\"] = osterdate(diff+1);\n// Himmelfahrt\nFeiertage[6][\"Datum\"] = osterdate(diff+39);\n// Fronleichnam\nFeiertage[9][\"Datum\"] = osterdate(diff+60);\n// Pfingsten\nFeiertage[7][\"Datum\"] = osterdate(diff+49);\n// Pfingstmontag\nFeiertage[8][\"Datum\"] = osterdate(diff+50);\n// Gründonnerstag\nFeiertage[23][\"Datum\"] = osterdate(diff-3);\n\n// Buß-und Bettag Berechnung\nvar btag = new Date(year, 11, 25);\nvar offset = btag.getDay();\nbtag.setDate(btag.getDate() - (btag.getDay()+32) );\nFeiertage[15][\"Datum\"] = btag.toLocaleDateString('de-DE');\nFeiertage.sort((a, b) => String(a.Datum).split(\".\")[0].localeCompare(String(b.Datum).split(\".\")[0]));\nFeiertage.sort((a, b) => String(a.Datum).split(\".\")[1].localeCompare(String(b.Datum).split(\".\")[1]));\n\nmsg.payload = Feiertage;\nreturn msg;\n\n\nfunction ostern(j) {\n //Schaltjahresberechnung\n let sch = 0;\n if ((j % 4 == 0 && j % 100 != 0) || j % 400 == 0) {\n sch = 1;\n }\n else {\n sch = 0;\n }\n // Osterberechnung\n let dd =0;\n let ja = Math.trunc(j / 100);\n let jb = Math.trunc(j / 400);\n let a = j % 19;\n let u_B = j % 4;\n let C = j % 7;\n let ma = Math.trunc((8 * ja + 13) / 25)\n let m = ma - 2;\n let s = ja - jb - 2;\n let mm = (15 + s - m) % 30;\n let nn = (6 + s) % 7;\n let d = (mm + (19 * a)) % 30;\n if (d == 29) {\n dd = 28;\n }\n else if (d == 28 && a >= 11) {\n dd = 27;\n }\n else {\n dd = d;\n }\n \n let E = ((2 * u_B) + (4 * C) + (6 * dd) + nn) % 7;\n let z = (dd + E + 1);\n let ta = 80 + z + sch;\n var ost = ta - 1;\nreturn ost;\n}\n\nfunction osterdate(diff){\n const options = { \n day: \"2-digit\",\n month: \"2-digit\",\n year: \"numeric\",\n };\n var ost1 = new Date(year,0,1);\n ost1.setDate(ost1.getDate() + diff);\n return ost1.toLocaleDateString(\"de-DE\", {day: \"2-digit\",month: \"2-digit\",year: \"numeric\",});\n}\n", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 420, "y": 4460, "wires": [ [ "13d51937bd72c388", "399d5d84e767d0e3" ] ] }, { "id": "fb298cb6c8ec9590", "type": "debug", "z": "d9d4383b2014cb99", "g": "72175f931b3e29ac", "name": "debug 13", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 1060, "y": 4380, "wires": [] }, { "id": "8a2930e407402daf", "type": "inject", "z": "d9d4383b2014cb99", "g": "72175f931b3e29ac", "name": "Jeden Tag", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "01 00 * * *", "once": true, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 140, "y": 4460, "wires": [ [ "5127bf18830d4ad3" ] ] }, { "id": "13d51937bd72c388", "type": "function", "z": "d9d4383b2014cb99", "g": "72175f931b3e29ac", "name": "Ist Feiertag?", "func": "const Feiertage = msg.payload;\nconst Land = flow.get(\"Bundesland\",\"file\");\n// Annahme: birthdayStorage ist ein Objekt zum Verfolgen bereits angezeigter Geburtstage\n// birthdayStorage sollte initialisiert werden, wenn das Skript zum ersten Mal ausgeführt wird.\nconst FeiertageStorage = {};\n\nconst today = new Date();\nconst currentYear = today.getFullYear();\nvar msg1 = {\"payload\": \"\",}\nvar duration = 10;\n\n// Filtern Sie die Feiertage, um nur diejenigen anzuzeigen, die heute Geburtstag haben und noch nicht in birthdayStorage verzeichnet sind\nconst newFeiertag = Feiertage\n .filter( Tag=> {\n let day, month;\n if (Tag.Datum && Tag.Datum.length >= 5) {\n day = parseInt(Tag.Datum.substring(0, 2));\n month = parseInt(Tag.Datum.substring(3, 5));\n }\n return day === today.getDate() && month === today.getMonth() + 1 && !FeiertageStorage[Tag.Name];\n })\n .map(Tag => {\n return {\n Name: Tag.Name,\n Bundesland: Tag.Bundesland,\n icon: Tag.icon,\n };\n });\n\nlet bday = newFeiertag.length;\nif (bday == 0) {\n node.status({ fill: \"green\", shape: \"dot\", text: \"Kein Feiertag heute.\" });\n\n msg.topic = \"Kein Feiertag\";\n msg.payload ={};\n \n return msg;\n}\n\n//Anzeigen der neuen Feiertage und Aktualisieren von FeiertageStorage\nconst FeiertagMessage = newFeiertag\n .map(Tag => {\n FeiertageStorage[Tag.Name] = true; // Feiertag als angezeigt markieren\n return Tag.Name;\n });\nconst FeiertagFuer = newFeiertag\n .map(Tag => {\n FeiertageStorage[Tag.Bundesland] = true; // Feiertag als angezeigt markieren\n return Tag.Bundesland;\n });\nconst FeiertagIcon = newFeiertag\n .map(Tag => {\n FeiertageStorage[Tag.icon] = true; // Feiertag als angezeigt markieren\n return Tag.icon;\n });\n\n\nif (FeiertagFuer.join().includes(Land) == true || FeiertagFuer.join().includes(\"all\")) {\n duration = calculateDuration(msg.payload, 90);\n msg.topic = `${FeiertagMessage}`;\n node.status({ fill: \"green\", shape: \"dot\", text: \"Es gibt einen Feiertag heute.\" + FeiertagMessage + \" \" + duration + \"sec\" + \" \"+ bday });\n\n msg.payload = {\n \"text\": msg.topic,\n \"icon\":`${FeiertagIcon}`,\n \"textCase\": 2,\n \"pushIcon\": 2,\n \"duration\": duration, \n }\nreturn msg;\n}\nelse {\n msg.topic = \"Kein Feiertag\";\n msg.payload ={};\n \nreturn msg;\n\n return msg; \n}\n\n\n\nfunction calculateDuration(msgPayload, scrollTextDelay = 90) {\n const durationSec = (msgPayload.length + 8) * scrollTextDelay / 250;\n return durationSec;\n}\n", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 710, "y": 4460, "wires": [ [ "fdf27e5ec4e49676", "ef0f8c8001460cd1", "6770cec013307649" ] ] }, { "id": "fdf27e5ec4e49676", "type": "mqtt out", "z": "d9d4383b2014cb99", "g": "72175f931b3e29ac", "name": "", "topic": "awtrix_test/custom/Feiertag", "qos": "", "retain": "", "respTopic": "", "contentType": "", "userProps": "", "correl": "", "expiry": "", "broker": "858457535e7dc82b", "x": 960, "y": 4460, "wires": [] }, { "id": "ef0f8c8001460cd1", "type": "ui-text", "z": "d9d4383b2014cb99", "g": "72175f931b3e29ac", "group": "f8d5e98fd03fa4d9", "order": 1, "width": 0, "height": 0, "name": "", "label": "Heute ist:", "format": "{{msg.payload}}", "layout": "row-spread", "style": false, "font": "", "fontSize": 16, "color": "#717171", "wrapText": false, "className": "", "value": "topic", "valueType": "msg", "x": 900, "y": 4420, "wires": [] }, { "id": "c6e493c70e7beb4d", "type": "ui-dropdown", "z": "d9d4383b2014cb99", "g": "72175f931b3e29ac", "group": "f8d5e98fd03fa4d9", "name": "Bundesland", "label": "Bundesland auswählen:", "tooltip": "", "order": 3, "width": 0, "height": 0, "passthru": true, "multiple": false, "chips": false, "clearable": false, "options": [ { "label": "Baden-Württemberg", "value": "BW", "type": "str" }, { "label": "Bayern", "value": "BY", "type": "str" }, { "label": "Berlin", "value": "BE", "type": "str" }, { "label": "Brandenburg", "value": "BB", "type": "str" }, { "label": "Bremen", "value": "HB", "type": "str" }, { "label": "Hamburg", "value": "HH", "type": "str" }, { "label": "Hessen", "value": "HE", "type": "str" }, { "label": "Mecklenburg-Vorpommern", "value": "MV", "type": "str" }, { "label": "Niedersachsen", "value": "NI", "type": "str" }, { "label": "Nordrhein-Westfalen", "value": "NW", "type": "str" }, { "label": "Rheinland-Pfalz", "value": "RP", "type": "str" }, { "label": "Saarland", "value": "SL", "type": "str" }, { "label": "Sachsen", "value": "SN", "type": "str" }, { "label": "Sachsen-Anhalt", "value": "ST", "type": "str" }, { "label": "Schleswig-Holstein", "value": "SH", "type": "str" }, { "label": "Thüringen", "value": "TH", "type": "str" }, { "label": "Augsburg", "value": "AUG", "type": "str" }, { "label": "Test", "value": "TE", "type": "str" } ], "payload": "", "topic": "payload", "topicType": "msg", "className": "", "typeIsComboBox": true, "msgTrigger": "onChange", "x": 290, "y": 4380, "wires": [ [ "b92c198634443888" ] ] }, { "id": "6770cec013307649", "type": "debug", "z": "d9d4383b2014cb99", "g": "72175f931b3e29ac", "name": "debug 15", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "statusVal": "", "statusType": "auto", "x": 900, "y": 4520, "wires": [] }, { "id": "b92c198634443888", "type": "function", "z": "d9d4383b2014cb99", "g": "72175f931b3e29ac", "name": "Bundesland speichern", "func": "flow.set(\"Bundesland\",msg.payload,\"file\");\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 500, "y": 4380, "wires": [ [ "5127bf18830d4ad3" ] ] }, { "id": "ceaf8e559ff49b55", "type": "ui-table", "z": "d9d4383b2014cb99", "g": "72175f931b3e29ac", "group": "f8d5e98fd03fa4d9", "name": "", "label": "", "order": 4, "width": 0, "height": 0, "maxrows": 0, "passthru": false, "autocols": false, "showSearch": false, "deselect": false, "selectionType": "none", "columns": [ { "title": "Feiertag", "key": "Name", "keyType": "key", "type": "text", "width": "", "align": "start" }, { "title": "Datum", "key": "Datum", "keyType": "key", "type": "text", "width": "", "align": "start" } ], "mobileBreakpoint": "", "mobileBreakpointType": "none", "action": "replace", "className": "", "x": 890, "y": 4380, "wires": [ [] ] }, { "id": "399d5d84e767d0e3", "type": "function", "z": "d9d4383b2014cb99", "g": "72175f931b3e29ac", "name": "Feiertagstabelle", "func": "const Feiertage = msg.payload;\nconst Land = flow.get(\"Bundesland\",\"file\");\n\nconst FTage = Feiertage.filter(({Bundesland}) => Bundesland.includes(Land) ); //.map(({Name}) => Name,({Datum}) => Datum,(({Bundesland}) => Bundesland));\nconst FTage1 = Feiertage.filter(({Bundesland}) => Bundesland.includes(\"all\") );\n\nconst Feiertag = FTage1.concat(FTage);\nFeiertag.sort((a, b) => String(a.Datum).split(\".\")[0].localeCompare(String(b.Datum).split(\".\")[0]));\nFeiertag.sort((a, b) => String(a.Datum).split(\".\")[1].localeCompare(String(b.Datum).split(\".\")[1]));\n\nmsg.payload = Feiertag\nreturn msg", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 720, "y": 4380, "wires": [ [ "ceaf8e559ff49b55", "fb298cb6c8ec9590" ] ] }, { "id": "44f26464b2a65cc7", "type": "inject", "z": "d9d4383b2014cb99", "g": "72175f931b3e29ac", "name": "Bundesland ", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": true, "onceDelay": 0.1, "topic": "", "payload": "#:(file)::Bundesland", "payloadType": "flow", "x": 130, "y": 4380, "wires": [ [ "c6e493c70e7beb4d" ] ] }, { "id": "826e9166dab33416", "type": "ui-template", "z": "d9d4383b2014cb99", "g": "72175f931b3e29ac", "group": "f8d5e98fd03fa4d9", "page": "", "ui": "", "name": "", "order": 2, "width": 0, "height": 0, "head": "", "format": "<template>\n <Teleport v-if=\"mounted\" :to=\"target\">\n <div :class=\"[positionClass, 'collapse-control']\" @click=\"toggle\" style=\"cursor: pointer;\">\n <v-icon class=\"toggle-icon\" aria-hidden=\"false\" style=\"color: inherit; opacity: 0.8;\">\n {{icon}}\n </v-icon>\n </div>\n </Teleport>\n</template>\n\n<script>\n export default {\n data() { \n return {\n mounted: false,\n target: \"\",\n position: \"right\",\n opened: true\n }\n },\n computed: { \n positionClass: function () {\n return this.position == \"left\" ? \"v-card-item__prepend\" : \"v-card-item__append\"\n },\n icon: function() {\n return this.opened ? \"mdi-menu-up\" : \"mdi-menu-down\"\n },\n storageKey: function() {\n return `widget-state-${this.id}`\n }\n },\n mounted() { \n // On cible l'élément par l'ID du groupe passé par les props de Node-RED\n this.target = '#nrdb-ui-group-' + this.props.group + ' .v-card-item'\n \n // 1. Restauration de l'état sauvegardé\n const savedState = localStorage.getItem(this.storageKey)\n if (savedState !== null) {\n this.opened = savedState === 'true'\n }\n\n this.$nextTick(() => {\n const container = document.querySelector(`#nrdb-ui-group-${this.props.group}`)\n if (container) {\n this.updateState(container)\n }\n this.mounted = true\n \n // 2. Envoi de l'état initial à Node-RED\n this.send({ open: this.opened })\n })\n },\n methods: {\n toggle() {\n this.opened = !this.opened\n localStorage.setItem(this.storageKey, this.opened)\n \n const container = document.querySelector(`#nrdb-ui-group-${this.props.group}`)\n this.updateState(container)\n\n // 3. Envoi du nouvel état lors du clic\n this.send({ open: this.opened })\n },\n updateState(el) {\n if (!el) return\n if (!this.opened) {\n el.classList.add('is-collapsed')\n } else {\n el.classList.remove('is-collapsed')\n }\n }\n },\n\n // Cette fonction se déclenche juste avant que le widget ne soit supprimé de l'écran\nunmounted() {\n// On force l'arrêt du flux Node-RED quand on quitte la page\n// Mais on ne touche pas au localStorage pour que ça se rouvre au retour\nthis.send({ open: false });\n}\n}\n</script>\n\n<style>\n /* 1. Cache le conteneur du widget qui porte la classe saisie dans l'UI */\n /* Cela évite qu'un espace vide n'apparaisse dans le groupe */\n .hidden-template {\n display: none !important;\n }\n\n /* Masquage des éléments quand la classe 'is-collapsed' est présente */\n [id^=\"nrdb-ui-group-\"].is-collapsed .v-card-text,\n [id^=\"nrdb-ui-group-\"].is-collapsed .v-divider {\n display: none !important;\n }\n</style>", "storeOutMessages": true, "passthru": true, "resendOnRefresh": true, "templateScope": "local", "className": "", "x": 560, "y": 4520, "wires": [ [] ] }, { "id": "858457535e7dc82b", "type": "mqtt-broker", "name": "FHEM-Nachricht", "broker": "raspberry-4.fritz.box", "port": "1884", "tls": "", "clientid": "storage-3", "autoConnect": true, "usetls": false, "protocolVersion": "4", "keepalive": "60", "cleansession": true, "autoUnsubscribe": true, "birthTopic": "LWT", "birthQos": "0", "birthRetain": "true", "birthPayload": "online", "birthMsg": {}, "closeTopic": "", "closeQos": "0", "closePayload": "", "closeMsg": {}, "willTopic": "LWT", "willQos": "0", "willRetain": "true", "willPayload": "offline", "willMsg": {}, "userProps": "", "sessionExpiry": "" }, { "id": "f8d5e98fd03fa4d9", "type": "ui-group", "name": "Feiertage", "page": "1e38b760668321ec", "width": "4", "height": 1, "order": 10, "showTitle": true, "className": "", "visible": "true", "disabled": "false", "groupType": "default" }, { "id": "1e38b760668321ec", "type": "ui-page", "name": "Awtrix 3.0", "ui": "7013987c94f55176", "path": "/awtrix", "icon": "home", "layout": "grid", "theme": "75ddda7292ca61b7", "breakpoints": [ { "name": "Default", "px": "0", "cols": "3" }, { "name": "Tablet", "px": "576", "cols": "6" }, { "name": "Small Desktop", "px": "768", "cols": "9" }, { "name": "Desktop", "px": "1024", "cols": "12" } ], "order": 2, "className": "", "visible": "true", "disabled": "false" }, { "id": "7013987c94f55176", "type": "ui-base", "name": "AWTRIX", "path": "/dashboard", "appIcon": "http://storage-3:1880/logo/awtrix.png", "includeClientData": true, "acceptsClientConfig": [ "ui-notification", "ui-control" ], "showPathInSidebar": false, "headerContent": "none", "navigationStyle": "default", "titleBarStyle": "default", "showReconnectNotification": true, "notificationDisplayTime": 1, "showDisconnectNotification": true, "allowInstall": true }, { "id": "75ddda7292ca61b7", "type": "ui-theme", "name": "Standardthema", "colors": { "surface": "#517070", "primary": "#0094ce", "bgPage": "#8b8b8b", "groupBg": "#ffffff", "groupOutline": "#000000" }, "sizes": { "density": "compact", "pagePadding": "12px", "groupGap": "6px", "groupBorderRadius": "0px", "widgetGap": "6px" } }, { "id": "0717e4e3b5f5fc06", "type": "global-config", "env": [], "modules": { "@flowfuse/node-red-dashboard": "1.30.2" } } ]
/flows/qREgoJE8ApGm/110.jpg
/flows/qREgoJE8ApGm/6870.gif
/flows/qREgoJE8ApGm/70204.jpg
/flows/qREgoJE8ApGm/49923.jpg
/flows/qREgoJE8ApGm/25144.gif
/flows/qREgoJE8ApGm/1817.gif
/flows/qREgoJE8ApGm/9526.gif
/flows/qREgoJE8ApGm/26170.gif
/flows/qREgoJE8ApGm/35315.gif
/flows/qREgoJE8ApGm/74987.jpg
/flows/qREgoJE8ApGm/41394.jpg
/flows/qREgoJE8ApGm/19550.gif
/flows/qREgoJE8ApGm/1967.jpg
/flows/qREgoJE8ApGm/4163.gif
/flows/qREgoJE8ApGm/16528.jpg
/flows/qREgoJE8ApGm/26001.gif
-- Flow first published on June 8, 2026, last updated on June 9, 2026 at 10:20.