Skip to content

Instantly share code, notes, and snippets.

@borgand
Created April 24, 2023 07:15
Show Gist options
  • Save borgand/9294381189b6d092e6b20b8d4e413a04 to your computer and use it in GitHub Desktop.
Save borgand/9294381189b6d092e6b20b8d4e413a04 to your computer and use it in GitHub Desktop.
Personal notification with Home Assistant + Node Red + OpenAI + Telegram

Personal Notifications with a personal touch

I wanted HA to send me notifications whether I should bring my umbrella today and when the washer or dryer machines finish. Of course, HA has it's built in notifications to mobile apps, and while these would do the job, I wasn't sure all of my family install the mobile apps, so I wanted to tap on something we already have. Comparing different notifications options, I decided to go with Telegram, since it seemed the easiest (no need to create App in dev portal).

Disclaimer: I'm quite new to both HA automations and Node Red, so I assume much of this could be done more efficiently

Node Red Nodes

Install the following contrib nodes to Node Red:

The ChatGPT node takes API_KEY and optional Organization per each usage, while Telegram nodes support for shared configurations that you pick from a dropdown on each node.

ChatGPT Completion

To get similar output every time, I opted for a sub-flow that takes essentially does:

context = "Describe the Persona of the bot to GPT"
get_completion(context + " as the Persona, tell me: " + msg.payload )

Images

Initially I planned to use chatGPT node's image generation that uses DALL-E in line. While this did work, it returned me a Base64 string of the PNG, and I struggled to upload it to Telegram (the sendPhoto API endpoint was supposed to support uploading files as well, but I just couldn't figure out how to achieve this with the sendPhoto node, as it was buried deep in node.js Telegram plugin code.

And since image generation is more costly than pure text completion, I finally opted to pre-generate the images, upload them to Telegram outside of Node Red and just use a CSV file with the prefix,file_id for referencing already uploaded images on Telegram servers.

For this I used two simple Ruby scripts (which is more familiar language for me) and also played around with various wordings and themes to get the images I like (turned out that for sunny weather and washer, Comic style was better, while for cloudy and rainy themes the Digital Art worked best.

Finally I use a Template node to store the contents of CSV and then CSV node to turn this CSV into JS object array, from which next function node picks a random image matching image_prefix

Weather Notifications

I mainly wanted to get a warning when I should bring my umbrella, so the theme is that if the precipitation probability is over 50%, it uses the rainy image and has the word Umbrella in the text. While it should have been pretty straight forward, for some reason weather.home_forecast does not provide precipitation_probability forecast, so I had to calculate the average from the weather.home_hourly_forecast. But this turned out even better, since the overall probability includes 24h, but I'm only interested in the 12 hours of daytime (7:00 - 19:00) mostly.

So, the flow is pretty simple – calculate the weather conditions, feed that into ChatGPT for completion, choose a random image and post all of it to Telegram chat.

Washer and Dryer Notifications

To get notifications when the washer or dryier have completed, there is the excellent power-monitor node that I feed info from my Shelly Plug S. Only thing left is to figure out the power usage between working and non-working states. It turned out that the Washer normally goes below 10W many times during a cycle while the Dryer continues to "tumble" the clothes even after finishing, going high and low every time, both of which produced multiple stop events per cycle for me. So it is probably highly dependant on each machine to fine-tune the thresholds and counts. For me it works if the threshold is 3W and count of 5 measures (15 sec apart), meaning more than a minute of continuous signal to declare a state change.

I also decided that I would implement a reminder feature - 15 min or 1 h delay after which the message is repeated. But since implementing it I've never actually needed this reminder, so I guess I'll drop it in favour of the image+caption style notification like Weather is using.

BTW, seems that the Switch node does not support Markdown formatting, so even if I askged GPT to "format ${machine} in Markdown bold" I got markdown markup, not bold text in Telegram. So instead I opted to ask GPT to UPPERCASE the words instead.

From this it's again pretty straight-forward - feed the stop event with the Name of the machine to a Function node that sets up the text and image prefix, gets completion for full Lembitu text and then sends Telegram notification with three action buttons.

[
{
"id": "5cce7a3f08c1c07a",
"type": "subflow",
"name": "notifyRandomImage",
"info": "",
"category": "",
"in": [
{
"x": 60,
"y": 80,
"wires": [
{
"id": "2"
}
]
}
],
"out": [
{
"x": 880,
"y": 80,
"wires": [
{
"id": "9510cec81cb58250",
"port": 0
}
]
}
],
"env": [],
"meta": {},
"color": "#DDAA99"
},
{
"id": "2",
"type": "template",
"z": "5cce7a3f08c1c07a",
"name": "",
"field": "payload",
"fieldType": "msg",
"format": "handlebars",
"syntax": "plain",
"template": "prefix,filename,file_id\nsunny,images/sunny_1682074587.png,AgACAgQAAxkDAA\ncloudy,images/cloudy_1682074627.png,kDAANQZEOJ\ncloudy,images/cloudy_1682074635.png,AgACAgQAAxkDAANRZEOJuxBzcWc5Wv2XHs70y4pSEwYAAme9MRuewyFSbB8D28gwx8cBAAMCAAN4AAMvBA\nwasher,images/washer_1682074643.png,Rqdiewds\n",
"output": "str",
"x": 200,
"y": 80,
"wires": [
[
"3"
]
]
},
{
"id": "3",
"type": "csv",
"z": "5cce7a3f08c1c07a",
"name": "",
"sep": ",",
"hdrin": true,
"hdrout": "",
"multi": "mult",
"ret": "\\n",
"temp": "prefix,filename,file_id",
"skip": "0",
"strings": true,
"include_empty_strings": false,
"include_null_values": false,
"x": 350,
"y": 80,
"wires": [
[
"4"
]
]
},
{
"id": "4",
"type": "function",
"z": "5cce7a3f08c1c07a",
"name": "randomSendPhoto",
"func": "const prefix = msg.image_prefix || \"sunny\";\nconst rows = msg.payload.filter(row => row.prefix === prefix);\nif (rows.length === 0) {\n return null;\n}\nconst randomRow = rows[Math.floor(Math.random() * rows.length)];\nmsg.payload = { file_id: randomRow.file_id };\n\nmsg.payload = {\n photo: randomRow.file_id,\n caption: msg.image_caption\n}\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 510,
"y": 80,
"wires": [
[
"9510cec81cb58250"
]
]
},
{
"id": "9510cec81cb58250",
"type": "telegrambot-payload",
"z": "5cce7a3f08c1c07a",
"name": "",
"bot": "aff86923977c4991",
"chatId": "",
"sendMethod": "sendPhoto",
"payload": "",
"x": 710,
"y": 80,
"wires": [
[]
]
},
{
"id": "9f582ce2df04a2a8",
"type": "subflow",
"name": "notifyWithReminder",
"info": "",
"category": "",
"in": [
{
"x": 60,
"y": 100,
"wires": [
{
"id": "dbd9eca86536e758"
},
{
"id": "81c593ab7fde2cd0"
}
]
}
],
"out": [],
"env": [],
"meta": {},
"color": "#DDAA99"
},
{
"id": "dbd9eca86536e758",
"type": "telegrambot-switch",
"z": "9f582ce2df04a2a8",
"name": "Decision",
"bot": "aff86923977c4991",
"chatId": "",
"question": "",
"answers": [
"OK",
"Remind 15 min",
"Remind 1 h"
],
"outputs": 4,
"autoAnswerCallback": false,
"verticalAnswers": true,
"timeoutValue": "5",
"timeoutUnits": "min",
"x": 220,
"y": 100,
"wires": [
[
"32010455b393f8a1"
],
[
"6e868584157ceb0e"
],
[
"c127f688299516d5"
],
[]
]
},
{
"id": "32010455b393f8a1",
"type": "function",
"z": "9f582ce2df04a2a8",
"name": "OK",
"func": "const emojis = [\n '👍', '👌', '🙌', '👏', '🎉', '😄', '🤩', '💪', '✅', '🏆',\n '🌟', '🔥', '💯', '😊', '😁', '🙂', '😀', '😆', '😃', '🥳',\n '🤗', '😍', '🥰', '❤️', '💖', '💕', '💓', '💗', '💘', '💝',\n '💟', '💌', '🌈', '🎊', '🎈', '🎆', '🎇', '🍾', '🥂', '🎖️',\n '🥇', '🥈', '🥉', '🏅', '🎯', '🚀', '🌠', '💫', '✨', '🎶'\n ];\n\nfunction getRandomEmoji() {\n const randomIndex = Math.floor(Math.random() * emojis.length);\n return emojis[randomIndex];\n}\n\nmsg.payload = getRandomEmoji();\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 430,
"y": 80,
"wires": [
[
"1f8e3b88e745cf74"
]
]
},
{
"id": "6e868584157ceb0e",
"type": "delay",
"z": "9f582ce2df04a2a8",
"name": "15 min",
"pauseType": "delay",
"timeout": "15",
"timeoutUnits": "minutes",
"rate": "1",
"nbRateUnits": "1",
"rateUnits": "second",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": false,
"allowrate": false,
"outputs": 1,
"x": 510,
"y": 180,
"wires": [
[
"dbd9eca86536e758"
]
]
},
{
"id": "c127f688299516d5",
"type": "delay",
"z": "9f582ce2df04a2a8",
"name": "1 hour",
"pauseType": "delay",
"timeout": "1",
"timeoutUnits": "hours",
"rate": "1",
"nbRateUnits": "1",
"rateUnits": "second",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": false,
"allowrate": false,
"outputs": 1,
"x": 510,
"y": 240,
"wires": [
[
"dbd9eca86536e758"
]
]
},
{
"id": "1f8e3b88e745cf74",
"type": "telegrambot-notify",
"z": "9f582ce2df04a2a8",
"name": "notify",
"bot": "aff86923977c4991",
"chatId": "",
"message": "",
"parseMode": "",
"x": 610,
"y": 80,
"wires": []
},
{
"id": "81c593ab7fde2cd0",
"type": "debug",
"z": "9f582ce2df04a2a8",
"name": "notifyWithReminderInputDebug",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 270,
"y": 280,
"wires": []
},
{
"id": "aff86923977c4991",
"type": "telegrambot-config",
"botname": "Lembitu3000",
"usernames": "myusername",
"chatIds": "-123, 234",
"pollInterval": "300"
},
{
"id": "c06205185ef783a3",
"type": "subflow",
"name": "getLembituText",
"info": "",
"category": "",
"in": [
{
"x": 0,
"y": 80,
"wires": [
{
"id": "88c4fb63d5cc1f86"
},
{
"id": "ed742f166b4e663a"
}
]
}
],
"out": [
{
"x": 800,
"y": 80,
"wires": [
{
"id": "9c4ccb6c1b5718f1",
"port": 0
}
]
}
],
"env": [],
"meta": {},
"color": "#DDAA99"
},
{
"id": "88c4fb63d5cc1f86",
"type": "function",
"z": "c06205185ef783a3",
"name": "gptTextForLembitu",
"func": "const text = msg.payload\nconst history = [\n { \"role\": \"user\", \"content\": \"Lembitu3000 is a Viking-era Estonian leader resurrected as an AI-powered bot, wielding all of the Internet knowledge and IoT sensors. He talks in a elaborate manner, honoring his hosts, old Estonian and Nordic gods.\" },\n]\n\nmsg.history = history\nmsg.payload = \"Mimic Lembitu3000 and tell me: \" + text\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 170,
"y": 80,
"wires": [
[
"ac5d18c279e0d18a"
]
]
},
{
"id": "ac5d18c279e0d18a",
"type": "chatgpt",
"z": "c06205185ef783a3",
"name": "getCompletionForLembitu",
"API_KEY": "OPENAI_API_KEY",
"Organization": "OPENAI_ORGANIZATION",
"topic": "turbo",
"BaseUrl": "https://api.openai.com",
"x": 440,
"y": 80,
"wires": [
[
"9c4ccb6c1b5718f1"
]
]
},
{
"id": "9c4ccb6c1b5718f1",
"type": "function",
"z": "c06205185ef783a3",
"name": "trim",
"func": "msg.payload = msg.payload.trim()\n\n// duplicate for cases where payload is overridden\nmsg.lembitu_text = msg.payload\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 670,
"y": 80,
"wires": [
[
"ed742f166b4e663a"
]
]
},
{
"id": "ed742f166b4e663a",
"type": "debug",
"z": "c06205185ef783a3",
"name": "getLembituTextDebug",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 860,
"y": 180,
"wires": []
},
{
"id": "6a957299a53dbffe",
"type": "tab",
"label": "Lembitu Notifications",
"disabled": false,
"info": "",
"env": []
},
{
"id": "d1c89e331cf05a69",
"type": "group",
"z": "6a957299a53dbffe",
"name": "WeatherNotification",
"style": {
"fill": "#e3f3d3",
"label": true
},
"nodes": [
"978075a5c993246b",
"ac1cd3d656e13fe1",
"d19239b02f0ead49",
"305588db44e4c31f",
"4ebc4f25adc12315",
"371fdf5cfbde3c98",
"983cf5237f55d35e",
"feace7c078d1f583"
],
"x": 14,
"y": 39,
"w": 1012,
"h": 222
},
{
"id": "132c1a061ce516d4",
"type": "group",
"z": "6a957299a53dbffe",
"name": "Washer/Dryer Notification",
"style": {
"fill": "#bfdbef",
"label": true
},
"nodes": [
"01ff5f48bcf907cd",
"8eb2cc983339e5b7",
"29129835ef4851ff",
"e913ebea57e36430",
"0e2c2334e68c7016",
"89b96042f0c7b68f",
"d05bcc7751f77be7",
"bec9e919e4112bce",
"a6d66fc1e6551110",
"753ecbdf953e1a25",
"4ab793e865c920b4",
"73a34495860acf6f",
"1cc71290cd864cab"
],
"x": 14,
"y": 299,
"w": 1012,
"h": 382
},
{
"id": "978075a5c993246b",
"type": "inject",
"z": "6a957299a53dbffe",
"g": "d1c89e331cf05a69",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "00 07 * * *",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 130,
"y": 80,
"wires": [
[
"ac1cd3d656e13fe1"
]
]
},
{
"id": "ac1cd3d656e13fe1",
"type": "api-current-state",
"z": "6a957299a53dbffe",
"g": "d1c89e331cf05a69",
"name": "Forecast Hourly",
"server": "ad7fd8ef.d3a938",
"version": 3,
"outputs": 1,
"halt_if": "",
"halt_if_type": "str",
"halt_if_compare": "is",
"entity_id": "weather.forecast_home_hourly",
"state_type": "str",
"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": 370,
"y": 80,
"wires": [
[
"d19239b02f0ead49"
]
]
},
{
"id": "d19239b02f0ead49",
"type": "function",
"z": "6a957299a53dbffe",
"g": "d1c89e331cf05a69",
"name": "averages",
"func": "const forecast = msg.data.attributes.forecast.slice(0, 12);\n\nconst averagePrecipitation = forecast.reduce((sum, item) => sum + item.precipitation_probability, 0) / forecast.length;\nconst temps = forecast.map(f => f.temperature)\n\nmsg.averagePrecipitation = averagePrecipitation;\nmsg.min_temperature = Math.min(...temps)\nmsg.max_temperature = Math.max(...temps)\nmsg.temperatures = temps\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 570,
"y": 80,
"wires": [
[
"4ebc4f25adc12315"
]
]
},
{
"id": "305588db44e4c31f",
"type": "debug",
"z": "6a957299a53dbffe",
"g": "d1c89e331cf05a69",
"name": "weatherDebug",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 900,
"y": 100,
"wires": []
},
{
"id": "4ebc4f25adc12315",
"type": "function",
"z": "6a957299a53dbffe",
"g": "d1c89e331cf05a69",
"name": "getWeatherText",
"func": "const averagePrecipitation = msg.averagePrecipitation\nlet weather = \"Weather will be \" + msg.payload + ` with temperature from ${msg.min_temperature} to ${msg.max_temperature}`\nlet image_prefix = 'sunny'\n\nif (msg.payload != 'sunny' && averagePrecipitation < 50) {\n image_prefix = 'cloudy'\n} else if (averagePrecipitation > 50) {\n image_prefix = 'rainy'\n weather = weather + \" and bring umbrella\"\n}\n\nmsg.payload = weather\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 180,
"y": 220,
"wires": [
[
"371fdf5cfbde3c98"
]
]
},
{
"id": "371fdf5cfbde3c98",
"type": "subflow:c06205185ef783a3",
"z": "6a957299a53dbffe",
"g": "d1c89e331cf05a69",
"name": "",
"x": 380,
"y": 220,
"wires": [
[
"983cf5237f55d35e"
]
]
},
{
"id": "01ff5f48bcf907cd",
"type": "api-current-state",
"z": "6a957299a53dbffe",
"g": "132c1a061ce516d4",
"name": "kuivati power",
"server": "ad7fd8ef.d3a938",
"version": 3,
"outputs": 1,
"halt_if": "",
"halt_if_type": "str",
"halt_if_compare": "is",
"entity_id": "sensor.kuivati_plug2_power",
"state_type": "str",
"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": 170,
"y": 440,
"wires": [
[
"8eb2cc983339e5b7"
]
]
},
{
"id": "8eb2cc983339e5b7",
"type": "power-monitor",
"z": "6a957299a53dbffe",
"g": "132c1a061ce516d4",
"name": "Dryer",
"startthreshold": "5",
"stopthreshold": "5",
"startafter": "5",
"stopafter": "5",
"energydecimals": 4,
"emitidle": true,
"x": 350,
"y": 440,
"wires": [
[
"89b96042f0c7b68f"
]
]
},
{
"id": "29129835ef4851ff",
"type": "inject",
"z": "6a957299a53dbffe",
"g": "132c1a061ce516d4",
"name": "debugDryerStop",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "{\"name\":\"Dryer\",\"power\":0.3,\"event\":\"stop\",\"energy_delta\":0}",
"payloadType": "json",
"x": 360,
"y": 360,
"wires": [
[
"89b96042f0c7b68f"
]
]
},
{
"id": "e913ebea57e36430",
"type": "debug",
"z": "6a957299a53dbffe",
"g": "132c1a061ce516d4",
"name": "WorkingStateDebugger",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 870,
"y": 340,
"wires": []
},
{
"id": "0e2c2334e68c7016",
"type": "inject",
"z": "6a957299a53dbffe",
"g": "132c1a061ce516d4",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "15",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 130,
"y": 360,
"wires": [
[
"01ff5f48bcf907cd",
"4ab793e865c920b4"
]
]
},
{
"id": "89b96042f0c7b68f",
"type": "switch",
"z": "6a957299a53dbffe",
"g": "132c1a061ce516d4",
"name": "",
"property": "payload.event",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "start",
"vt": "str"
},
{
"t": "eq",
"v": "stop",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 2,
"x": 610,
"y": 440,
"wires": [
[
"e913ebea57e36430"
],
[
"bec9e919e4112bce",
"e913ebea57e36430"
]
]
},
{
"id": "d05bcc7751f77be7",
"type": "subflow:c06205185ef783a3",
"z": "6a957299a53dbffe",
"g": "132c1a061ce516d4",
"name": "",
"x": 720,
"y": 640,
"wires": [
[
"a6d66fc1e6551110"
]
]
},
{
"id": "bec9e919e4112bce",
"type": "function",
"z": "6a957299a53dbffe",
"g": "132c1a061ce516d4",
"name": "getFinishedText",
"func": "const machine = msg.payload.name\nconst text = `${machine} has finished. UPPERCASE the mention of ${machine} in all places.`\n\nmsg.payload = text\nmsg.image_prefix = 'washer'\nmsg.lembitu_text = text\nmsg.telegram = { chat: { id: 234 } }\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 140,
"y": 640,
"wires": [
[
"73a34495860acf6f"
]
]
},
{
"id": "a6d66fc1e6551110",
"type": "subflow:9f582ce2df04a2a8",
"z": "6a957299a53dbffe",
"g": "132c1a061ce516d4",
"name": "",
"x": 910,
"y": 640,
"wires": []
},
{
"id": "753ecbdf953e1a25",
"type": "power-monitor",
"z": "6a957299a53dbffe",
"g": "132c1a061ce516d4",
"name": "Washer",
"startthreshold": "3",
"stopthreshold": "3",
"startafter": "5",
"stopafter": "5",
"energydecimals": 4,
"emitidle": true,
"x": 360,
"y": 520,
"wires": [
[
"89b96042f0c7b68f"
]
]
},
{
"id": "4ab793e865c920b4",
"type": "api-current-state",
"z": "6a957299a53dbffe",
"g": "132c1a061ce516d4",
"name": "pesumasin power",
"server": "ad7fd8ef.d3a938",
"version": 3,
"outputs": 1,
"halt_if": "",
"halt_if_type": "str",
"halt_if_compare": "is",
"entity_id": "sensor.pesumasin_plug1_power",
"state_type": "str",
"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": 150,
"y": 520,
"wires": [
[
"753ecbdf953e1a25"
]
]
},
{
"id": "983cf5237f55d35e",
"type": "function",
"z": "6a957299a53dbffe",
"g": "d1c89e331cf05a69",
"name": "setupNotification",
"func": "msg.image_caption = msg.payload\nmsg.telegram = { chat: { id: -123 } }\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 590,
"y": 220,
"wires": [
[
"feace7c078d1f583"
]
]
},
{
"id": "feace7c078d1f583",
"type": "subflow:5cce7a3f08c1c07a",
"z": "6a957299a53dbffe",
"g": "d1c89e331cf05a69",
"name": "",
"x": 820,
"y": 220,
"wires": [
[
"305588db44e4c31f"
]
]
},
{
"id": "73a34495860acf6f",
"type": "subflow:5cce7a3f08c1c07a",
"z": "6a957299a53dbffe",
"g": "132c1a061ce516d4",
"name": "",
"x": 340,
"y": 640,
"wires": [
[
"1cc71290cd864cab"
]
]
},
{
"id": "1cc71290cd864cab",
"type": "change",
"z": "6a957299a53dbffe",
"g": "132c1a061ce516d4",
"name": "",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "lembitu_text",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 540,
"y": 640,
"wires": [
[
"d05bcc7751f77be7"
]
]
},
{
"id": "ad7fd8ef.d3a938",
"type": "server",
"name": "Home Assistant",
"addon": true
}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment