Using openHASP with Node-Red

Like many others thinking of you @BartButenaers :wink:

I have long been looking for a good solution to interacting with NR from a wall mounted device.
I have seen and made various iterations of small screens etc, but they always look a bit too homemade to be acceptable to the non technicals in the home :roll_eyes:

However there now seems to be a plethora of relatively cheap and powerful, touch screen switch plates on the market. I'm currently experimenting with one at the moment.

see https://discourse.nodered.org/t/new-shelly-wall-display/80961

I'm posting this to raise awareness of @fvanroie excellent openHASP project in this community and hopefully help to move the project on with some more collaborators :wink:

You can find out more here - openHASP

There is also a drag and drop dashboard designer that could do with some help if anyone is able !

4 Likes

Ok so here is a simple flow I made to allow 3 buttons on the openHasp plate to control 3 ESP based devices flashed with Tasmota firmware and provide state feedback on the display.

Follow instructions on openHasp to get a compatible screen flashed with firmware, connected to your MQTT server and network.

This layout is based on a 480x480 touch screen, so if yours is different you will need to adjust the size and position of the buttons to suit. As I wasn't keen on the built in switch widget, I decided to use a button and style it up a bit :wink:

Add this code to your pages.jsonl file on the openHasp file system.

{"page":1,"comment":"---------- page 1 ------- "}

{"page":1,"id":1,"obj":"btn","x":20,"y":250,"w":120,"h":160,"radius":15,"border_opa":255, "outline_width":3, "outline_pad":5, "outline_opa":0, "outline_color":"#E6523A","shadow_spread":15, "shadow_width":8, "shadow_color":"#E6523A", "shadow_opa":0,  "text":"  \uE91C\nWall","text_font":46,"text_color":"#FFFFFF"}
{"page":1,"id":2,"obj":"btn","x":180,"y":250,"w":120,"h":160,"radius":15,"border_opa":255, "outline_width":3, "outline_pad":5, "outline_opa":0, "outline_color":"#E6523A","shadow_spread":15, "shadow_width":8, "shadow_color":"#E6523A", "shadow_opa":0, "text":"  \uF1E1\nMain","text_font":46,"text_color":"#FFFFFF"}
{"page":1,"id":3,"obj":"btn","x":340,"y":250,"w":120,"h":160,"radius":15,"border_opa":255, "outline_width":3, "outline_pad":5, "outline_opa":0, "outline_color":"#E6523A","shadow_spread":15, "shadow_width":8, "shadow_color":"#E6523A", "shadow_opa":0, "text":"  \uE8DD\nLamp","text_font":46,"text_color":"#FFFFFF"}

What 1 switch json looks like -

{
    "page": 1, 
    "id": 1,
    "obj": "btn",
    "x": 20,
    "y": 250,
    "w": 120,
    "h": 160,
    "radius": 15,
    "border_opa": 255,
    "outline_width": 3,
    "outline_pad": 5,
    "outline_opa": 0,
    "outline_color": "#E6523A",
    "shadow_spread": 15,
    "shadow_width": 8,
    "shadow_color": "#E6523A",
    "shadow_opa": 0,
    "text": "  \uE91C\nWall",  <--- Is the icon and text in the button 
    "text_font": 46,
    "text_color": "#FFFFFF"
}

Change the text to suit your needs. The "page" and "id" properties define the page to display the object on (up to 12) and the id of the object. These are sent in the MQTT topic along with the event type when touched eg topic --> hasp/plate/state/p1b1 payload --> event: "up"

The other properties are similar to general web styling and define the look of the object.

In this case I have added an outline and shadow in red to indicate the button is ON. However the opacity is set to 0 so it cannot normally be seen.

When the screen starts up it will display the objects defined in the pages.jsonl file
Thereafter pressing a button will send the topic and payload as above.

To minimize the code I decided to use a lookup table, which translates the openHasp button topic into a smarthome device (Tasmota in my case) device topic. Since the flow is quite simple and doesn't know the state of the device yet, it sends a toggle command, so the device will just swap to the opposite of what it is now.

The next part of the flow listens for incoming messages from all my devices and then does a reverse lookup to find out which (if any) button needs to be updated with the current state of the device.
It then sends either the ON or OFF styling to the correct button. All this needs to do is update (in memory) the 3 opacity settings to either show or hide the red elements, and for good measure it sets the text colour white or yellow.

The result is this
image

You will need to set MQTT nodes to your own server and modify the mapping as required -

flow.set("mapping", {
    "hasp/plate/state/p1b1": "cmnd/loungelights/POWER1",
    "hasp/plate/state/p1b2": "cmnd/loungelights/POWER2",
    "hasp/plate/state/p1b3": "cmnd/Bulb-20/POWER"
}
)

And here is the flow - More than happy to hear about any improvements etc, have fun :smiley:

[{"id":"f4d4bb01dff95e2f","type":"mqtt in","z":"2df9d4297887de0c","name":"","topic":"hasp/plate/state/#","qos":"2","datatype":"auto-detect","broker":"","nl":false,"rap":true,"rh":0,"inputs":0,"x":345,"y":405,"wires":[["9a4c3bfad7332deb"]]},{"id":"9a4c3bfad7332deb","type":"switch","z":"2df9d4297887de0c","name":"Events only","property":"payload","propertyType":"msg","rules":[{"t":"hask","v":"event","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":575,"y":405,"wires":[["e9b69ba9e2757ce0"]]},{"id":"fcf07d14c9117350","type":"comment","z":"2df9d4297887de0c","name":"openHasp Designs","info":"{\"page\":1,\"comment\":\"---------- page 1 ------- \"}\n\n{\"page\":1,\"id\":1,\"obj\":\"btn\",\"x\":20,\"y\":250,\"w\":120,\"h\":160,\"radius\":15,\"border_opa\":255, \"outline_width\":3, \"outline_pad\":5, \"outline_opa\":0, \"outline_color\":\"#E6523A\",\"shadow_spread\":15, \"shadow_width\":8, \"shadow_color\":\"#E6523A\", \"shadow_opa\":0,  \"text\":\"  \\uE91C\\nWall\",\"text_font\":46,\"text_color\":\"#FFFFFF\"}\n{\"page\":1,\"id\":2,\"obj\":\"btn\",\"x\":180,\"y\":250,\"w\":120,\"h\":160,\"radius\":15,\"border_opa\":255, \"outline_width\":3, \"outline_pad\":5, \"outline_opa\":0, \"outline_color\":\"#E6523A\",\"shadow_spread\":15, \"shadow_width\":8, \"shadow_color\":\"#E6523A\", \"shadow_opa\":0, \"text\":\"  \\uF1E1\\nMain\",\"text_font\":46,\"text_color\":\"#FFFFFF\"}\n{\"page\":1,\"id\":3,\"obj\":\"btn\",\"x\":340,\"y\":250,\"w\":120,\"h\":160,\"radius\":15,\"border_opa\":255, \"outline_width\":3, \"outline_pad\":5, \"outline_opa\":0, \"outline_color\":\"#E6523A\",\"shadow_spread\":15, \"shadow_width\":8, \"shadow_color\":\"#E6523A\", \"shadow_opa\":0, \"text\":\"  \\uE8DD\\nLamp\",\"text_font\":46,\"text_color\":\"#FFFFFF\"}","x":360,"y":180,"wires":[]},{"id":"14ec6408c6aa050b","type":"function","z":"2df9d4297887de0c","name":"Build Control Msg","func":"const topics = flow.get(\"mapping\")\nif (topics[msg.topic] != null) {\n    msg.topic = topics[msg.topic]\n    msg.payload = \"toggle\"\n    node.status({text:msg.topic});\n    return msg;\n}","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":930,"y":405,"wires":[["418084a44c0c3395","18ab92ab2cfd9104"]],"inputLabels":["from Plate"],"outputLabels":["to Device"]},{"id":"418084a44c0c3395","type":"mqtt out","z":"2df9d4297887de0c","name":"","topic":"","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"","x":1150,"y":510,"wires":[]},{"id":"ddcd2b715bcfb696","type":"mqtt in","z":"2df9d4297887de0c","name":"","topic":"stat/+/POWER","qos":"2","datatype":"auto-detect","broker":"","nl":false,"rap":true,"rh":0,"inputs":0,"x":690,"y":510,"wires":[["a8eaea43f8cc818a"]]},{"id":"a8eaea43f8cc818a","type":"function","z":"2df9d4297887de0c","name":"Update Plate Button","func":"const mapping = flow.get(\"mapping\")\nlet foundButton = null;\nlet page = 0\nlet id = 0\n\n// Modify incomming Tasmota topic to match outgoing topic,\n// so we can lookup the Hasp button that called it\nlet topic = msg.topic.split(\"/\")\nlet index = \"cmnd/\" + topic[1] + \"/\" + topic[2]\n\n// Reverse lookup plate topic\nfor (const button in mapping) {\n    if (mapping[button] === index) {\n        foundButton = button;\n        break;\n    }\n}\nif (foundButton === null) {\n    node.warn(\"no matching button \" + msg.topic);\n    return\n}\n\nlet parts = foundButton.split(\"/\")\nlet pxbx = parts[3] //page and id from matching topic\nmsg.topic = \"hasp/\" + parts[1] + \"/command/jsonl\" //hasp topic\nnode.status({ text: pxbx + \" \" + msg.payload });\n\nconst match = pxbx.match(/p(\\d+)b(\\d+)/);\nif (match) {\n    page = match[1]; // Digits after 'p'\n    id = match[2]; // Digits after 'b'\n}\n\n// Build json to send to hasp\n\nconst on = {\n    \"page\": page,\n    \"id\": id,\n    \"border_opa\": 0,\n    \"outline_opa\": 255,\n    \"shadow_opa\": 160,\n    \"text_color\": \"yellow\"\n}\nconst off = {\n    \"page\": page,\n    \"id\": id,\n    \"border_opa\": 255,\n    \"outline_opa\": 0,\n    \"shadow_opa\": 0,\n    \"text_color\": \"white\"\n}\n\n// Send on/off json to change button appearance on hasp\nmsg.payload = (msg.payload === \"ON\" ? on : off)\nreturn msg;\n\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":935,"y":570,"wires":[["418084a44c0c3395","6708a4c84af11fc2"]]},{"id":"3af8e42616953dc7","type":"mqtt in","z":"2df9d4297887de0c","name":"","topic":"stat/+/POWER1","qos":"2","datatype":"auto-detect","broker":"","nl":false,"rap":true,"rh":0,"inputs":0,"x":690,"y":570,"wires":[["a8eaea43f8cc818a"]]},{"id":"7638644341c466cf","type":"mqtt in","z":"2df9d4297887de0c","name":"","topic":"stat/+/POWER2","qos":"2","datatype":"auto-detect","broker":"","nl":false,"rap":true,"rh":0,"inputs":0,"x":690,"y":630,"wires":[["a8eaea43f8cc818a"]]},{"id":"18ab92ab2cfd9104","type":"debug","z":"2df9d4297887de0c","name":"debug 335","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1150,"y":405,"wires":[]},{"id":"a90206d314b0b942","type":"function","z":"2df9d4297887de0c","name":"create flow.mapping","func":"flow.set(\"mapping\", {\n    \"hasp/plate/state/p1b1\": \"cmnd/loungelights/POWER1\",\n    \"hasp/plate/state/p1b2\": \"cmnd/loungelights/POWER2\",\n    \"hasp/plate/state/p1b3\": \"cmnd/Bulb-20/POWER\"\n}\n)\n\nreturn;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":530,"y":240,"wires":[[]]},{"id":"0f676d389b0b07fd","type":"inject","z":"2df9d4297887de0c","name":"1 >","props":[{"p":"payload"}],"repeat":"","crontab":"","once":true,"onceDelay":"1","topic":"","payload":"","payloadType":"date","x":335,"y":240,"wires":[["a90206d314b0b942"]]},{"id":"e9b69ba9e2757ce0","type":"switch","z":"2df9d4297887de0c","name":"up only","property":"payload.event","propertyType":"msg","rules":[{"t":"eq","v":"up","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":730,"y":405,"wires":[["14ec6408c6aa050b"]]},{"id":"6708a4c84af11fc2","type":"debug","z":"2df9d4297887de0c","name":"debug 336","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1150,"y":570,"wires":[]}]
3 Likes

Nice, I'll post my flow in a bit.

1 Like

I look forward to seeing it :wink:

@thatkide I think my hasp skills are improving a bit :wink:

My take on "3D ish" toggle switches.
image
image

2 Likes

Looking good, I really like the analog clock.
Here's what I'm working on for my Sunton 7" screens. It uses pop up screen to control additional functions

3 Likes

I like your buttons, were they inspired by OXRS ?

Also are the weather icons PNGs and how do you handle them, eg are they all stored on the plate or sent on demand.

And where can I download them :wink:

I got the button ideas and pop outs from 8bitmcu/openHASP_HomeKit: Homekit-based cards for openHASP (github.com)

The weather icons can be downloaded from the openhasp example configuration page Example Configurations - openHASP (haswitchplate.com)

1 Like

Here's the direct link for the Light theme
https://openhasp.haswitchplate.com/0.6/assets/users/openhasp-weathericons-day.zip

and the dark theme
https://openhasp.haswitchplate.com/0.6/assets/users/openhasp-weathericons-nigh.zip

I am currently using objects to define the buttons but this week I discovered I can do the same thing with a btn object. This will allow me to drop 2 objects for every button.

I see you are very active on discord & openhasp fora and here.
Most of the examples I find on the internet (not so much) are based on home-assistant and often not simple but full blown pages with all kind of objects..
But I use Node-red and as beginner in openhasp what I need are some very basic demo's
I found on discord a nice example from you for a CT slider.
I am able to create buttons in the pages.jsonl and synchronize them with the my dashboard in node-red.

Can you tell me if it is possible to create pages or objects by sending them with mqtt from node-red, or must a page or object always first been created in the pages.jsonl.
If yes, can you tell me with a simple inject / mqtt-out flow how?
I cannot find the correct command structure to do that.
Thanks in advance

@yogy You don't need to use the pages.jsonl if you don't want to, you can send everything from NR when the screen boots up.

For the example above this is how I set up the switches and the navbar at the bottom.

[{"id":"a09f9c2122f70c6f","type":"mqtt out","z":"c8c7826a005c7f46","name":"","topic":"","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"","x":1600,"y":720,"wires":[]},{"id":"0b6d91a713cf41d0","type":"function","z":"c8c7826a005c7f46","name":"Page 0/1/Nav","func":"msg.topic = \"hasp/plate/command\"\n\n// Define common values \n\nconst text_color = \"#bfbfbf\"\nconst menuValues = {\n    \"page\": 0,\n    \"obj\": \"btn\",\n    \"radius\": 35,\n    \"bg_color\": \"#393939\",\n    \"text_font\": 24,\n    \"align\": \"center\",\n    \"border_opa30\": 0,\n    \"text_color\": text_color,\n    \"y\": 405,\n    \"w\": 108,\n    \"h\": 75,\n};\n\nconst btnValues = {\n    \"page\": 1,\n    \"obj\": \"btn\",\n    \"w\": 160,\n    \"h\": 110,\n    \"x\": 320,\n    \"outline_width\": 1,\n    \"outline_color\": \"#aaaaaa\",\n    \"outline_opa\": 50,\n    \"bg_color\": \"#515151\",\n    \"bg_opa\": 255,\n    \"border_opa\": 255,\n    \"border_side\": 1,\n    \"border_width\": 11,\n    \"border_color\": \"#393939\",\n    \"radius\": 8,\n    \"text_font\": 58,\n    \"text_color\": text_color,\n    \"text_line_space\": -20,\n    \"value_font\": 30,\n    \"value_ofs_y\": 26,\n    \"value_color\": text_color\n\n};\n\n\nmsg.payload = [\n    {\n        \"page\": 0,\n        \"id\": 10,\n        \"obj\": \"obj\",\n        \"x\": 0,\n        \"y\": 405,\n        \"w\": 480,\n        \"h\": 75,\n        \"radius\": 35,\n        \"border_opa30\": 0,\n        \"bg_color\": \"#393939\",\n    },\n\n    {\n        \"page\": 0,\n        \"id\": 11,\n        \"obj\": \"label\",\n        \"x\": 10,\n        \"y\": 0,\n        \"w\": 50,\n        \"h\": 50,\n        \"text\": \"\\uE5A9\",\n        \"text_color\": \"red\",\n        \"text_font\": 30,\n    }\n]\n\nnode.send(msg);\nmsg.payload = [\n    {\n        \"id\": 1,\n        \"x\": 10,\n        \"text\": \"\\uE60C\\nMore\",\n        ...menuValues,\n    },\n    {\n        \"id\": 2,\n        \"x\": 128,\n        \"text\": \"\\uE425\\nSwitch\",\n        ...menuValues,\n    },\n    {\n        \"id\": 3,\n        \"x\": 246,\n        \"text\": \"\\uE425\\nDevice\",\n        ...menuValues,\n    },\n    {\n        \"id\": 4,\n        \"x\": 364,\n        \"text\": \"\\uE493\\nSettings\",\n        ...menuValues,\n    }\n];\n\nnode.send(msg);\n\nmsg.payload = [\n    {\n        \"id\": 1,\n        \"y\": 0,\n        \"text\": \"\\n\",\n        \"value_str\": \"Wall\",\n        ...btnValues\n    },\n    {\n        \"id\": 2,\n        \"y\": 136,\n        \"text\": \"\\n\",\n        \"value_str\": \"Main\",\n        ...btnValues\n    },\n    {\n        \"id\": 3,\n        \"y\": 272,\n        \"obj\": \"btn\",\n        \"text\": \"\\n\",\n        \"value_str\": \"Lamp\",\n        ...btnValues\n    },\n    { \"page\": 1, \"id\": 0, \"bg_color\": \"#000000\" } //set background to black\n]\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1285,"y":645,"wires":[["ade4a70b5089b9d2","a09f9c2122f70c6f"]]},{"id":"367d1c7288a6d800","type":"inject","z":"c8c7826a005c7f46","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":915,"y":645,"wires":[["0b6d91a713cf41d0"]]}]

To change the appearance of the buttons I update a number of properties to show as on or off -

[{"id":"d2a3b5143f7983db","type":"function","z":"c8c7826a005c7f46","name":"Update Hasp Buttons","func":"let onOff = msg.payload\n\n// Build json to send to hasp\nconst on = {\n    \"page\": msg.page,\n    \"id\": msg.id,\n    \"text_color\": \"#dea031\",\n    \"bg_color\": \"#404040\",\n    \"text_line_space\": -40,\n    \"border_side\": 2,\n    \"border_color\": \"#5d5d5d\",\n    \"value_ofs_y\": 36,\n    \"value_color\": \"#a4a4a4\",\n\n}\nconst off = {\n    \"page\": msg.page,\n    \"id\": msg.id,\n    \"text_color\": \"#bfbfbf\",\n    \"bg_color\": \"#515151\",\n    \"text_line_space\": -20,\n    \"border_side\": 1,\n    \"border_color\": \"#393939\",\n    \"value_ofs_y\": 26,\n    \"value_color\": \"#bfbfbf\",\n}\nmsg.payload = onOff === \"ON\" ? on : off\n\nreturn msg\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1660,"y":1695,"wires":[["82e9a3cd930455f8"]],"info":"const mapping = flow.get(\"mapping\")\r\nlet foundButton = null;\r\nlet page = 0\r\nlet id = 0\r\n\r\n// Modify incomming Tasmota topic to match outgoing topic,\r\n// so we can lookup the Hasp button that called it\r\nlet topic = msg.topic.split(\"/\")\r\nlet index = \"cmnd/\" + topic[1] + \"/\" + topic[2]\r\n\r\n// Reverse lookup plate topic\r\nfor (const button in mapping) {\r\n    if (mapping[button] === index) {\r\n        foundButton = button;\r\n        break;\r\n    }\r\n}\r\nif (foundButton === null) {\r\n    node.warn(\"no matching button \" + msg.topic);\r\n    return\r\n}\r\n\r\nlet parts = foundButton.split(\"/\")\r\n\r\ntopic = `hasp/${parts[1]}/command/${parts[3]}` //hasp topic\r\nnode.status({ text: `${parts[3]} ${msg.payload}` });\r\n\r\n// Build json to send to hasp\r\n\r\nconst on = {\r\n    \"bg_color\": \"#616161\",\r\n    \"text_color\": \"#dea031\",\r\n    \r\n}\r\nconst off = {\r\n    \"bg_color\": \"#393939\",\r\n    \"text_color\": \"#bfbfbf\",\r\n   \r\n}\r\n\r\nlet update = (msg.payload === \"ON\" ? on : off)\r\n\r\nObject.keys(update).forEach(key => {\r\n    msg = { \"topic\": `${topic}.${key}`, \"payload\": update[key] }\r\n    node.send(msg)\r\n}\r\n)\r\n\r\nreturn \r\n"},{"id":"82e9a3cd930455f8","type":"mqtt out","z":"c8c7826a005c7f46","name":"","topic":"","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"","x":1865,"y":1695,"wires":[]},{"id":"aacb3ccb7948749f","type":"inject","z":"c8c7826a005c7f46","name":"Button 1 ON","props":[{"p":"payload"},{"p":"topic","vt":"str"},{"p":"page","v":"1","vt":"num"},{"p":"id","v":"1","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"has/pplate/command/jsonl","payload":"ON","payloadType":"str","x":1460,"y":1680,"wires":[["d2a3b5143f7983db"]]},{"id":"8d037f082de31a12","type":"inject","z":"c8c7826a005c7f46","name":"Button 1 OFF","props":[{"p":"payload"},{"p":"topic","vt":"str"},{"p":"page","v":"1","vt":"num"},{"p":"id","v":"1","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"has/pplate/command/jsonl","payload":"OFF","payloadType":"str","x":1460,"y":1725,"wires":[["d2a3b5143f7983db"]]}]
2 Likes

@ smcgann99
Thanks for your explanation.
I am not a js specialist, so some of the command you gave such as ...btnValues did me scratch my head. :grin:
But I got the overal meaning, in special that the command is placed in the topic not the msg.
I have now made an inject with "hasp/plate/command/restart" that works faster and easier then the openhasp web interface restart.
I discovered that there are moments that screen device pauses before (or after?) executing a command.
I use the ESP32-8048S050C from the Sunton store on alieexpress.

Using this kind of displays adds of course a more complex develop when you also want to keep your existing dashboard and keep all the button/switches e.d. in sync.

And there is a lot of graphical stuff to discover.
A whole new playground.

This is just a way to define various parameters that are the same and then add to each button definition, without having to type everything out more than once.

A new PIN entry widget for openHASP.

This is a small flow to create this pin pad on page 0. Based on a 480x480 screen.

There are inject nodes to create, delete, display and hide the pin pad.
The idea is that once created it will stay in memory on the screen, but hidden from view.
When required a simple command can display the pin pad.

The flow handles pin verification, and user feed back. The second output shows the pin entered with a topic of valid or error.

You will need to set topics in inject nodes to match your own, and add your MQTT server.
Pins numbers can be modified in pin entry function.

Use the tick to submit a pin or the cross to start over.

Any feedback for improvements etc welcome.
(flow updated to make it self contained per @stefan24 )

image
image
image
image

[{"id":"0e04c97a19d3c0fa","type":"group","z":"c8c7826a005c7f46","name":"Pin Pad","style":{"fill":"#ffdf7f","label":true,"label-position":"n","color":"#001f60"},"nodes":["c8abc8eb70f93818","8ab2a58397e5d969","92e979f95e07fdfb","66d4dee61c49ce1a","36ce5b7b2a457663","6107268095348e0b","ba372e1be3140c7c","36362c8cf1ebcffb","350c6f31540b7c46","02121c460c8c5344"],"x":1314,"y":409,"w":637,"h":262},{"id":"c8abc8eb70f93818","type":"inject","z":"c8c7826a005c7f46","g":"0e04c97a19d3c0fa","name":" Create Keypad","props":[{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"hasp/hasp01/command","x":1445,"y":450,"wires":[["ba372e1be3140c7c"]]},{"id":"8ab2a58397e5d969","type":"mqtt out","z":"c8c7826a005c7f46","g":"0e04c97a19d3c0fa","name":"","topic":"","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"","x":1825,"y":510,"wires":[]},{"id":"92e979f95e07fdfb","type":"function","z":"c8c7826a005c7f46","g":"0e04c97a19d3c0fa","name":"PIN Entry","func":"const validPins = [\"1234\", \"4321\"]\n\nlet topic = msg.topic.split(\"/\")\n\nif (msg.topic !== `hasp/${topic[1]}/state/p0b121` || msg.payload.event !== \"up\") { // From the keypad object ?\n    return\n}\n\nmsg.topic = `hasp/${topic[1]}/command` \n\nlet pin = flow.get(\"pin\") || \"\"\n\nif (msg.payload.text == \"\") { // tick symbol\n    if (validPins.includes(pin)) {\n        msg.payload = {\n            \"page\": 0,\n            \"id\": 122,\n            \"text\": \"#00FF00 PIN OK#\"\n        }\n        flow.set(\"pin\", \"\")\n        let msg2 = { topic: \"valid\", payload: pin }\n        return [msg, msg2]\n    } else {\n        msg.payload = {\n            \"page\": 0,\n            \"id\": 122,\n            \"text\": \"#FF0000 ERROR#\"\n        }\n        flow.set(\"pin\", \"\")\n        let msg2 = { topic: \"error\", payload: pin }\n        return [msg, msg2]\n    }\n}\n\nif (msg.payload.text == \"\") { // cross symbol\n    msg.payload = {\n        \"page\": 0,\n        \"id\": 122,\n        \"text\": \"#FFFF00 Enter PIN#\"\n    }\n    flow.set(\"pin\", \"\")\n    return msg\n}\n\npin += msg.payload.text\nflow.set(\"pin\", pin)\nlet asterisk = \"*************\".substring(0, pin.length);\nmsg.payload = {\n    \"page\": 0,\n    \"id\": 122,\n    \"text\": asterisk\n}\n\nreturn msg;","outputs":2,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1650,"y":630,"wires":[["8ab2a58397e5d969"],["36ce5b7b2a457663"]]},{"id":"66d4dee61c49ce1a","type":"inject","z":"c8c7826a005c7f46","g":"0e04c97a19d3c0fa","name":"Show Keypad","props":[{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"hasp/hasp01/command","x":1435,"y":495,"wires":[["02121c460c8c5344"]]},{"id":"36ce5b7b2a457663","type":"debug","z":"c8c7826a005c7f46","g":"0e04c97a19d3c0fa","name":"debug 370","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1835,"y":630,"wires":[]},{"id":"6107268095348e0b","type":"inject","z":"c8c7826a005c7f46","g":"0e04c97a19d3c0fa","name":"Delete Keypad","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"hasp/hasp01/command","payload":"p0b120.delete","payloadType":"str","x":1445,"y":585,"wires":[["8ab2a58397e5d969"]]},{"id":"ba372e1be3140c7c","type":"function","z":"c8c7826a005c7f46","g":"0e04c97a19d3c0fa","name":" Create Keypad","func":"msg.payload = [\n    {\n        \"page\": 0,\n        \"id\": 120,\n        \"obj\": \"obj\",\n        \"x\": 0,\n        \"y\": 0,\n        \"w\": 480,\n        \"h\": 480,\n        \"bg_color\": \"#000000\",\n        \"border_opa\": 0,\n        \"text_font\": 48,\n        \"hidden\": true\n    },\n    {\n        \"page\": 0,\n        \"id\": 122,\n        \"parentid\": 120,\n        \"obj\": \"label\",\n        \"x\": 0,\n        \"y\": -10,\n        \"w\": 480,\n        \"h\": 55,\n        \"align\": \"center\",\n        \"text\": \"#FFFF00 Enter PIN#\"\n    }]\nnode.send(msg);\nmsg.payload = [\n    {\n        \"page\": 0,\n        \"id\": 121,\n        \"parentid\": 120,\n        \"obj\": \"btnmatrix\",\n        \"x\": 0,\n        \"y\": 60,\n        \"w\": 480,\n        \"h\": 420,\n        \"bg_color\": \"#555555\",\n        \"bg_color40\": \"#515151\",\n        \"border_opa\": 0,\n        \"text_color40\": \"#bfbfbf\",\n        \"shadow_opa40\": 255,\n        \"shadow_ofs_y40\": 7,\n        \"shadow_ofs_x40\": 6,\n        \"shadow_spread40\": 2,\n        \"shadow_color40\": \"black\",\n        \"shadow_width40\": 25,\n        \"pad_inner30\": 26,\n        \"pad_top30\": 18,\n        \"pad_bottom30\": 24,\n        \"pad_left30\": 22,\n        \"pad_right30\": 22,\n        \"radius\": 30,\n        \"options\": [\n            \"1\",\n            \"2\",\n            \"3\",\n            \"\\n\",\n            \"4\",\n            \"5\",\n            \"6\",\n            \"\\n\",\n            \"7\",\n            \"8\",\n            \"9\",\n            \"\\n\",\n            \"\\uE156\",\n            \"0\",\n            \"\\uE12C\"\n        ],\n        \"val\": -1\n    }\n]\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1635,"y":450,"wires":[["8ab2a58397e5d969"]]},{"id":"36362c8cf1ebcffb","type":"inject","z":"c8c7826a005c7f46","g":"0e04c97a19d3c0fa","name":"Hide Keypad","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"hasp/hasp01/command","payload":"{\"page\":0,\"id\":120,\"hidden\":true}","payloadType":"json","x":1435,"y":540,"wires":[["8ab2a58397e5d969"]]},{"id":"350c6f31540b7c46","type":"mqtt in","z":"c8c7826a005c7f46","g":"0e04c97a19d3c0fa","name":"","topic":"hasp/hasp01/state/p0b121","qos":"2","datatype":"auto-detect","broker":"2acfe3bc.043dcc","nl":false,"rap":true,"rh":0,"inputs":0,"x":1450,"y":630,"wires":[["92e979f95e07fdfb"]]},{"id":"02121c460c8c5344","type":"function","z":"c8c7826a005c7f46","g":"0e04c97a19d3c0fa","name":"Reset and Show","func":"msg.payload =[\n    {\n        \"page\": 0,\n        \"id\": 120,\n        \"hidden\": false\n    },\n    {\n        \"page\": 0,\n        \"id\": 121,\n        \"val\": -1\n    },\n    {\n        \"page\": 0,\n        \"id\": 122,\n        \"text\": \"#FFFF00 Enter PIN#\"\n    }\n]\n\nflow.set (\"pin\",\"\")\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1645,"y":495,"wires":[["8ab2a58397e5d969"]]},{"id":"2acfe3bc.043dcc","type":"mqtt-broker","name":"pi-server","broker":"localhost","port":"1883","clientid":"NR","autoConnect":true,"usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"autoUnsubscribe":true,"birthTopic":"","birthQos":"0","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closePayload":"","closeMsg":{},"willTopic":"lost connection","willQos":"0","willPayload":"0","willMsg":{},"userProps":"","sessionExpiry":""}]
3 Likes

I can create, display, hide and delete the pinpad, but did not understand, how to enter a pin and what data to feed the link in node.

Got it now:

I have to feed the link in node with hasp/plate3/state/p0b121 (plate3 in my case) and filter for "up" events to eliminate double pinpad entries. (at least with OpenHASP 0.7 RC)

[{"id":"589bad919fc85a5c","type":"mqtt in","z":"63025264bf39ecf8","g":"0e04c97a19d3c0fa","name":"","topic":"hasp/plate3/state/p0b121","qos":"2","datatype":"auto-detect","broker":"7f211795.773398","nl":false,"rap":true,"rh":0,"inputs":0,"x":190,"y":260,"wires":[["cfc4f4411ffac462"]]},{"id":"cfc4f4411ffac462","type":"switch","z":"63025264bf39ecf8","g":"0e04c97a19d3c0fa","name":"","property":"payload.event","propertyType":"msg","rules":[{"t":"eq","v":"up","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":410,"y":260,"wires":[["92e979f95e07fdfb"]]},{"id":"7f211795.773398","type":"mqtt-broker","name":"Mosquitto","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]

It looks amazing and works now! :+1:

1 Like

The second output shows both, error or valid in msg.topic!?

Sorry I will update the instructions to be more precise :wink:

1 Like

Still working on this when I get the time, here is a popup that can set colour, colour temperature and brightness for smart light bulbs. I'm using the same technique as the Pin Pad by creating this as a hidden object on page zero. This means it can be displayed over any other page when required.

Parameters are passed so that the name of the device is displayed, along with current settings for that device. Individual elements can also be hidden based on device capabilities e.g. if CT is not supported.

When sliders are moved a "tag" for the device is then passed back along with the colour settings. This makes the popup reusable with any colour device and allows the output to be directed as needed.

image

1 Like

@BartButenaers Teaser for a thermostat I'm working on :wink: (ghost thermostat lookalike)

image

2 Likes