Skip to content

Commit 0cd735d

Browse files
authored
Merge pull request #13 from balk77/balk77-v0.2
Listen for page specific button topics and translate to generic topics, for buttons
2 parents 34208e7 + d48b792 commit 0cd735d

File tree

3 files changed

+211
-26
lines changed

3 files changed

+211
-26
lines changed

README.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@
22
This Node Red flow creates a page menu structure on you Buttonplus device. Page `main` is always visible, for things like time etc. You can also assign buttons to `main`, for instance as a trigger to change the menu page. See our [Wiki](https://github.com/balk77/node-red-buttonplus-menu/wiki) for documentation.
33

44
**Installation**
5-
* Create a new flow from `node_red_config.flow`
6-
* Open `01 global config` and add device configuration parameters, add page names as well. Keep `main` as first page!
7-
* Edit the node below `03 Contains the default button labels`. The text behind msg.label.3 will for instance appear on the right button, 1st row below display. `toplabel` defines the toplabel and `ledrgb` the color of the led
8-
* Edit the nodes below `04 Displayitems for Main`. Add parameters like position, size, page it belongs to. When you add a field, simply copy a node and place it in the flow. Make sure to update the displayitem to a unique number.
9-
* Do the same for `05 Displayitems from pages`; add node in the page flow you like. Top connection of the Switch node is the first page after `menu`
10-
* Edit `02 pagebutton` to assign buttons to `main`. See `Description` tab inside the node.
11-
* The subflows under `07` contain the actions per page. Signals are split between `press`, `release` and `longpress` (`msg.state`). `msg.button` contains the button number.
12-
* These nodes should not need update when bugs are fixed in the flow with all the logic, which is next:
13-
* Create a new flow from `node_red_menu.flow`
14-
* Add device id to `01 general config`
15-
* Deploy
5+
1. Create a new flow from `node_red_config.flow`
6+
2. Open `01 global config` and add device configuration parameters, add page names as well. Keep `main` as first page!
7+
3. Edit the node below `03 Contains the default button labels`. The text behind msg.label.3 will for instance appear on the right button, 1st row below display. `toplabel` defines the toplabel and `ledrgb` the color of the led
8+
4. Edit the nodes below `04 Displayitems for Main`. Add parameters like position, size, page it belongs to. When you add a field, simply copy a node and place it in the flow. Make sure to update the displayitem to a unique number.
9+
5. Do the same for `05 Displayitems from pages`; add node in the page flow you like. Top connection of the Switch node is the first page after `menu`
10+
6. Edit `02 pagebutton` to assign buttons to `main`. See `Description` tab inside the node.
11+
7. The subflows under `07` contain the actions per page. Signals are split between `press`, `release` and `longpress` (`msg.state`). `msg.button` contains the button number.
12+
8. These nodes should not need update when bugs are fixed in the flow with all the logic, which is next:
13+
9. Create a new flow from `node_red_menu.flow`
14+
10. Add device id to `01 general config`
15+
11. Deploy
1616

1717

1818

node_red_config.flow

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1633,7 +1633,7 @@
16331633
{
16341634
"id": "1044a74b4adb9c61",
16351635
"type": "tab",
1636-
"label": "Button+",
1636+
"label": "B+ config",
16371637
"disabled": false,
16381638
"info": "",
16391639
"env": []

node_red_menu.flow

Lines changed: 199 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
{
33
"id": "18dd279f36d97386",
44
"type": "tab",
5-
"label": "Button+ modes",
5+
"label": "B+ page logic",
66
"disabled": false,
77
"info": "",
88
"env": []
@@ -23,15 +23,37 @@
2323
],
2424
"x": 74,
2525
"y": 19,
26-
"w": 472,
26+
"w": 492,
2727
"h": 142
2828
},
29+
{
30+
"id": "20f91c554b2bf198",
31+
"type": "group",
32+
"z": "18dd279f36d97386",
33+
"name": "Buttons subscribe to page specific topics for labels etc",
34+
"style": {
35+
"label": true
36+
},
37+
"nodes": [
38+
"d5ef9aae41093179",
39+
"44bfea5d2f724782",
40+
"93ec2f7ec45edc5b",
41+
"f48878bc94b26aed",
42+
"41cf2ce5166bbf8a",
43+
"8fdbb046e8c742e4",
44+
"b26898a67589d7e3"
45+
],
46+
"x": 14,
47+
"y": 1099,
48+
"w": 732,
49+
"h": 302
50+
},
2951
{
3052
"id": "1cc89963bcc5d396",
3153
"type": "debug",
3254
"z": "18dd279f36d97386",
3355
"name": "debug 19",
34-
"active": false,
56+
"active": true,
3557
"tosidebar": true,
3658
"console": false,
3759
"tostatus": false,
@@ -140,9 +162,7 @@
140162
"x": 1810,
141163
"y": 580,
142164
"wires": [
143-
[
144-
"1cc89963bcc5d396"
145-
]
165+
[]
146166
]
147167
},
148168
{
@@ -161,7 +181,8 @@
161181
"y": 540,
162182
"wires": [
163183
[
164-
"a2a79e0bbb8e37ee"
184+
"a2a79e0bbb8e37ee",
185+
"1cc89963bcc5d396"
165186
]
166187
],
167188
"icon": "font-awesome/fa-lock"
@@ -447,15 +468,15 @@
447468
"type": "function",
448469
"z": "18dd279f36d97386",
449470
"g": "3dc14376c3d7d50c",
450-
"name": "01 global config",
471+
"name": "01 general config",
451472
"func": "// Configuration goes here:\nvar bplusdevice = \"wk1\"; // the device this page belongs to\n\n// nothing to be changed below this line\n// -------------------------------------\n\n// retrieve existing config from disk, or create when non-existent\n// this retrieves configuration for all configured B+ devices\nvar bplusconfig = global.get(\"bplusconfig\", \"file\");\nvar bplusconfigbuttons = global.get('bplusconfigbuttons','file');\nvar bplusconfigdisplayitems = global.get('bplusconfigdisplayitems', 'file');\n\n\n\n\nvar buttonpages = bplusconfig[bplusdevice].buttonpages;\nvar pages = bplusconfig[bplusdevice].pages;\n\n\n// retrieve existing config from disk, or create when non-existent\n// this retrieves configuration for all configured B+ devices\nvar bplusconfig = global.get(\"bplusconfig\", \"file\");\nif (bplusconfig == undefined) {\n bplusconfig = {};\n\n}\n\n\n\nvar label = bplusconfigbuttons[bplusdevice].label;\nvar toplabel = bplusconfigbuttons[bplusdevice].toplabel;\nvar ledrgb = bplusconfigbuttons[bplusdevice].ledrgb;\nvar mainbutton = bplusconfigbuttons[bplusdevice].mainbutton;\n\nfor (var i in label) {\n for (var j in label[i]) {\n\n if (mainbutton[j] == \"main\") {\n var label_thisbutton = label[i];\n var label_mainbutton = label[0];\n label_thisbutton[j] = label_mainbutton[j];\n }\n label[i] = label_thisbutton;\n }\n}\n\nfor (var i in toplabel) {\n for (var j in toplabel[i]) {\n\n if (mainbutton[j] == \"main\") {\n var label_thisbutton = toplabel[i];\n var label_mainbutton = toplabel[0];\n label_thisbutton[j] = label_mainbutton[j];\n }\n toplabel[i] = label_thisbutton;\n }\n}\n\nfor (var i in ledrgb) {\n for (var j in ledrgb[i]) {\n\n if (mainbutton[j] == \"main\") {\n var label_thisbutton = ledrgb[i];\n var label_mainbutton = ledrgb[0];\n label_thisbutton[j] = label_mainbutton[j];\n }\n ledrgb[i] = label_thisbutton;\n }\n}\n\n\nbplusconfigbuttons = {\n 'label': label,\n 'toplabel': toplabel,\n 'ledrgb': ledrgb,\n 'mainbutton': mainbutton\n}\n\n\n\n\n// save the configuration and parameters to this flow, for use\n// flow.set('bplusdevice', bplusdevice, \"file\");\n// flow.set('bplusconfigdevice', bplusconfig[bplusdevice], \"file\");\nflow.set('pages',pages,\"file\");\nflow.set('buttonpages', buttonpages, \"file\");\nflow.set('configJSONcore', bplusconfig[bplusdevice].configJSONcore, \"file\");\nflow.set('bplusconfigdisplayitems',bplusconfigdisplayitems[bplusdevice]);\n\n// save the configuration and parameters to this flow, for use\nflow.set('bplusdevice', bplusdevice, \"file\");\nflow.set('bplusconfigdevice', bplusconfig[bplusdevice], \"file\");\nflow.set('bplusconfigbuttons', bplusconfigbuttons, 'file')\n\n\n\n// // save the config of all devices back to the disk\n// global.set(\"bplusconfig\", bplusconfig, \"file\")\n\n\nreturn msg;",
452473
"outputs": 1,
453474
"timeout": 0,
454475
"noerr": 0,
455476
"initialize": "",
456477
"finalize": "",
457478
"libs": [],
458-
"x": 440,
479+
"x": 450,
459480
"y": 60,
460481
"wires": [
461482
[
@@ -685,7 +706,8 @@
685706
"wires": [
686707
[
687708
"920ae05b7da02f5e",
688-
"87ee8d61ca60d62d"
709+
"87ee8d61ca60d62d",
710+
"8fdbb046e8c742e4"
689711
]
690712
]
691713
},
@@ -750,8 +772,8 @@
750772
"targetType": "full",
751773
"statusVal": "",
752774
"statusType": "auto",
753-
"x": 280,
754-
"y": 1000,
775+
"x": 320,
776+
"y": 980,
755777
"wires": []
756778
},
757779
{
@@ -1908,7 +1930,7 @@
19081930
"type": "function",
19091931
"z": "18dd279f36d97386",
19101932
"name": "save buttonpageid",
1911-
"func": "var buttonpage = msg.buttonpage;\nvar buttonpages = flow.get(\"buttonpages\",\"file\");\n\nvar buttonpageid = buttonpages.indexOf(buttonpage);\nmsg.buttonpageid = buttonpageid;\nflow.set(\"buttonpageid\", buttonpageid);\nreturn msg;",
1933+
"func": "var buttonpage = msg.buttonpage;\nvar buttonpages = flow.get(\"buttonpages\",\"file\");\n\nvar mainbutton = flow.get(\"bplusconfigbuttons\", \"file\").mainbutton;\n\nvar mainbutton_array = [];\n// Object.keys(mainbutton).forEach((element) => {\n// if (mainbutton[i] != \"main\") {\n// mainbutton_array.push(buttonpage);\n// }else{\n// mainbutton_array.push(\"main\");\n// }\n// })\n\nObject.entries(mainbutton).forEach(([key, value]) => {\n // console.log(`${key}: ${value}`)\n if (value != \"main\") {\n mainbutton_array.push(buttonpage);\n } else {\n mainbutton_array.push(\"main\");\n }\n})\n\n// for (var i = 0; i < mainbutton.length; i++) {\n// if (mainbutton[i] != \"main\" ){\n// mainbutton_array.push(buttonpage);\n// }else{\n// mainbutton_array.push(\"main\");\n// }\n// }\n\n\n\nvar buttonpageid = buttonpages.indexOf(buttonpage);\nmsg.buttonpageid = buttonpageid;\nflow.set(\"buttonpageid\", buttonpageid);\nflow.set(\"mainbutton\", mainbutton_array);\nreturn msg;",
19121934
"outputs": 1,
19131935
"timeout": 0,
19141936
"noerr": 0,
@@ -1918,7 +1940,9 @@
19181940
"x": 580,
19191941
"y": 380,
19201942
"wires": [
1921-
[]
1943+
[
1944+
"20bfd02c0d57ca95"
1945+
]
19221946
]
19231947
},
19241948
{
@@ -2200,6 +2224,167 @@
22002224
"y": 880,
22012225
"wires": []
22022226
},
2227+
{
2228+
"id": "d5ef9aae41093179",
2229+
"type": "comment",
2230+
"z": "18dd279f36d97386",
2231+
"g": "20f91c554b2bf198",
2232+
"name": "Subscription to generic button MQTT topics",
2233+
"info": "Translates the generic button presses\ninto menu specific button presses\n\nFor insance, when buttonpage = music and button 4 is pressed:\n\nbuttonplus/<device>/button/4/state (press/release/etc)\nis translated into\nbuttonplus/<device>/button/music/4/state (press/release/etc)\n\nThis way, you can let your music player always listen to\nbuttonplus/<device>/button/music/4/state (value: press)\nto increase the volume",
2234+
"x": 270,
2235+
"y": 1140,
2236+
"wires": []
2237+
},
2238+
{
2239+
"id": "44bfea5d2f724782",
2240+
"type": "mqtt in",
2241+
"z": "18dd279f36d97386",
2242+
"g": "20f91c554b2bf198",
2243+
"name": "",
2244+
"topic": "",
2245+
"qos": "2",
2246+
"datatype": "auto-detect",
2247+
"broker": "e6dbd5fb3f32ec20",
2248+
"nl": false,
2249+
"rap": true,
2250+
"rh": 0,
2251+
"inputs": 1,
2252+
"x": 310,
2253+
"y": 1240,
2254+
"wires": [
2255+
[
2256+
"93ec2f7ec45edc5b"
2257+
]
2258+
]
2259+
},
2260+
{
2261+
"id": "93ec2f7ec45edc5b",
2262+
"type": "function",
2263+
"z": "18dd279f36d97386",
2264+
"g": "20f91c554b2bf198",
2265+
"name": "Translate to generic B+ topics",
2266+
"func": "// See for more info the function node upstream\n\n// split the received topic:\nvar topic = msg.topic;\ntopic = topic.split(\"/\");\n\n// Button number of the received topic\nvar button = topic[4];\n\n// Page name of the received topic\nvar page = topic[3];\n\n// Array with active mapping of buttons;\n// for instance: [\"main\",\"main\",\"main\",\"page1\",\"page1\",\"page1\"]\n// this is prepared in the \"Save Buttonpageid\" node\nvar mainbutton = flow.get(\"mainbutton\");\n\n// check if received page name equals page name belonging to\n// the mapping in mainbutton\nif (mainbutton[button] == page) {\n // create new topic\n var newtopic = [topic[0], topic[1], topic[2], topic[4], topic[5]];\n msg.topic = newtopic.join(\"/\");\n return msg;\n} else { \n // else do noting\n return null; } \n",
2267+
"outputs": 1,
2268+
"timeout": 0,
2269+
"noerr": 0,
2270+
"initialize": "",
2271+
"finalize": "",
2272+
"libs": [],
2273+
"x": 430,
2274+
"y": 1300,
2275+
"wires": [
2276+
[
2277+
"41cf2ce5166bbf8a",
2278+
"b26898a67589d7e3"
2279+
]
2280+
]
2281+
},
2282+
{
2283+
"id": "f48878bc94b26aed",
2284+
"type": "inject",
2285+
"z": "18dd279f36d97386",
2286+
"g": "20f91c554b2bf198",
2287+
"name": "",
2288+
"props": [
2289+
{
2290+
"p": "payload"
2291+
},
2292+
{
2293+
"p": "buttonpage",
2294+
"v": "overdag",
2295+
"vt": "str"
2296+
}
2297+
],
2298+
"repeat": "",
2299+
"crontab": "",
2300+
"once": true,
2301+
"onceDelay": 0.1,
2302+
"topic": "",
2303+
"payload": "",
2304+
"payloadType": "date",
2305+
"x": 130,
2306+
"y": 1320,
2307+
"wires": [
2308+
[
2309+
"8fdbb046e8c742e4"
2310+
]
2311+
]
2312+
},
2313+
{
2314+
"id": "41cf2ce5166bbf8a",
2315+
"type": "debug",
2316+
"z": "18dd279f36d97386",
2317+
"g": "20f91c554b2bf198",
2318+
"name": "debug 41",
2319+
"active": true,
2320+
"tosidebar": true,
2321+
"console": false,
2322+
"tostatus": false,
2323+
"complete": "true",
2324+
"targetType": "full",
2325+
"statusVal": "",
2326+
"statusType": "auto",
2327+
"x": 460,
2328+
"y": 1360,
2329+
"wires": []
2330+
},
2331+
{
2332+
"id": "8fdbb046e8c742e4",
2333+
"type": "function",
2334+
"z": "18dd279f36d97386",
2335+
"g": "20f91c554b2bf198",
2336+
"name": "Page specific B+ subscription topics",
2337+
"func": "// The purpose of this module is to subscribe to page-specific B+ topics\n// for the buttons\n// This means: you can send a topic to \n// buttonplus/<device>/button/<page>/<buttonid>/label\n// and it will be translated to \n// buttonplus/<device>/button/<buttonid>/label\n// but ONLY when that page is active.\n// This way, you can control the labels for the buttons \n// through MQTT, per page.\n// make sure to switch on the retain flag when issueing messages\n\n// Get base topic\nvar basetopic = flow.get('bplusconfigdevice','file').buttonpublishtopic;\n// Active button page\nvar buttonpage = msg.buttonpage;\n\n//prepare array\nvar topic = [];\n// topics to subscribe to\ntopic[0] = basetopic+buttonpage+\"/+/label\"\ntopic[1] = basetopic + buttonpage + \"/+/toplabel\"\ntopic[2] = basetopic + buttonpage + \"/+/ledrgb\"\ntopic[3] = basetopic + \"main\" + \"/+/label\"\ntopic[4] = basetopic + \"main\" + \"/+/toplabel\"\ntopic[5] = basetopic + \"main\" + \"/+/ledrgb\"\nmsg.topic = topic;\n// Activate the MQTT node\nmsg.action = 'subscribe';\nreturn msg;",
2338+
"outputs": 1,
2339+
"timeout": 0,
2340+
"noerr": 0,
2341+
"initialize": "",
2342+
"finalize": "",
2343+
"libs": [],
2344+
"x": 250,
2345+
"y": 1180,
2346+
"wires": [
2347+
[
2348+
"44bfea5d2f724782"
2349+
]
2350+
]
2351+
},
2352+
{
2353+
"id": "20bfd02c0d57ca95",
2354+
"type": "debug",
2355+
"z": "18dd279f36d97386",
2356+
"name": "debug 43",
2357+
"active": true,
2358+
"tosidebar": true,
2359+
"console": false,
2360+
"tostatus": false,
2361+
"complete": "true",
2362+
"targetType": "full",
2363+
"statusVal": "",
2364+
"statusType": "auto",
2365+
"x": 690,
2366+
"y": 440,
2367+
"wires": []
2368+
},
2369+
{
2370+
"id": "b26898a67589d7e3",
2371+
"type": "mqtt out",
2372+
"z": "18dd279f36d97386",
2373+
"g": "20f91c554b2bf198",
2374+
"name": "",
2375+
"topic": "",
2376+
"qos": "",
2377+
"retain": "",
2378+
"respTopic": "",
2379+
"contentType": "",
2380+
"userProps": "",
2381+
"correl": "",
2382+
"expiry": "",
2383+
"broker": "e6dbd5fb3f32ec20",
2384+
"x": 670,
2385+
"y": 1300,
2386+
"wires": []
2387+
},
22032388
{
22042389
"id": "e6dbd5fb3f32ec20",
22052390
"type": "mqtt-broker",

0 commit comments

Comments
 (0)