Double tap, triple tap, long press, etc inputs



  • For home automation lighting I'm guessing you'd connect digital inputs to momentary contact light switches.

    In such an application it would be very useful to have double tap, triple tap, long press events detected. I'm guessing the best way to do that is in the evok layer and have it spit out different events.

    What about sustained holding of an input? Would that be best handled with an 'input on' / 'input off' event pair? Here I'm thinking for controlling dimmable lights or blinds manually rather than setting to predefined levels.

    Is any of this handled? Should it be a feature request on Github?



  • For home automation lighting I'm guessing you'd connect digital inputs to momentary contact light switches.

    In such an application it would be very useful to have double tap, triple tap, long press events detected. I'm guessing the best way to do that is in the evok layer and have it spit out different events.

    What about sustained holding of an input? Would that be best handled with an 'input on' / 'input off' event pair? Here I'm thinking for controlling dimmable lights or blinds manually rather than setting to predefined levels.

    Is any of this handled? Should it be a feature request on Github?



  • Good question, it might be possible to configure evok in such way it would also tell the upper layer how log the input was on. Please make a new feature request… Or maybe a pull request?



  • Well, I don't have a UniPi device (yet?!) so sadly cannot do any development. Perhaps if there was a simulator available?

    Have raise an issue in GitHub to track this though: https://github.com/UniPiTechnology/evok/issues/3

    Thanks



  • So… after playing a bit with Node-RED and that very trivial simulator I knocked up... I can see even more need for a single 'click' (and double, etc) event thrown out of the UniPi. Configurable of course.

    From what I can guess from the examples, if you connect a momentary push-to-make switch to a UniPi input and press it as you would to turn on a light, Evok will spit out two messages. Something like this when the switch is pressed down...

    {"dev":"input","circuit":2,"value":1}

    ... and this when the switch is released a few milliseconds later...

    {"dev":"input","circuit":2,"value":0}

    Is that correct?

    I'm guessing here, but iif that is correct then in the Node-RED blocks one then has to filter out one of the above messages and toggle a relay value based on the other. Slightly annoying in that system TBH, although the same filtering would have to be done in any higher level controller.



  • Exactly, thare are more details in the message like the time of the event etc.

    See this NODE flow: http://flows.nodered.org/flow/4aa7ddad87f20f92031f



  • @tomas_hora:3u26qw6v:

    See this NODE flow: http://flows.nodered.org/flow/4aa7ddad87f20f92031f

    I did look at that flow. It's incredibly simplistic (simply takes the input state and copies to an output relay). For my purposes, which is trying to implement momentary push switches to toggle outputs things get rather more complicated. See attached.

    [attachment=0:3u26qw6v]Screenshot from 2016-02-02 19-46-27.png[/attachment:3u26qw6v]

    I'm not loving Node-RED at all and think this will the last I see of it. You can imagine from this still trivial example how hard it will be to get complex control going.

    Sorry about the inline code. You cannot attach JSON files here.

    [
      {
        "id": "41677656.be9888",
        "type": "websocket-client",
        "z": "",
        "path": "ws:\/\/127.0.0.1:3030\/ws",
        "wholemsg": "false"
      },
      {
        "id": "e6b167b1.194e98",
        "type": "websocket out",
        "z": "5fde8238.a0217c",
        "name": "",
        "server": "",
        "client": "41677656.be9888",
        "x": 810,
        "y": 840,
        "wires": [
    
        ]
      },
      {
        "id": "60da21c2.9f25e",
        "type": "inject",
        "z": "5fde8238.a0217c",
        "name": "R1 ON",
        "topic": "",
        "payload": "{\"dev\":\"relay\", \"circuit\":\"1\", \"value\":\"1\"}",
        "payloadType": "str",
        "repeat": "",
        "crontab": "",
        "once": false,
        "x": 190,
        "y": 740,
        "wires": [
          [
            "83442183.7cbbe"
          ]
        ]
      },
      {
        "id": "4512875.fbaed78",
        "type": "inject",
        "z": "5fde8238.a0217c",
        "name": "R1 OFF",
        "topic": "",
        "payload": "{\"dev\":\"relay\", \"circuit\":\"1\", \"value\":\"0\"}",
        "payloadType": "string",
        "repeat": "",
        "crontab": "",
        "once": false,
        "x": 190,
        "y": 780,
        "wires": [
          [
            "83442183.7cbbe"
          ]
        ]
      },
      {
        "id": "3a6482df.c59b7e",
        "type": "websocket in",
        "z": "5fde8238.a0217c",
        "name": "",
        "server": "",
        "client": "41677656.be9888",
        "x": 120,
        "y": 260,
        "wires": [
          [
            "a9fbd05a.56043"
          ]
        ]
      },
      {
        "id": "4394f136.bc6b1",
        "type": "debug",
        "z": "5fde8238.a0217c",
        "name": "",
        "active": true,
        "console": "false",
        "complete": "true",
        "x": 730,
        "y": 800,
        "wires": [
    
        ]
      },
      {
        "id": "df8e864e.207178",
        "type": "switch",
        "z": "5fde8238.a0217c",
        "name": "dev",
        "property": "payload.dev",
        "propertyType": "msg",
        "rules": [
          {
            "t": "eq",
            "v": "input",
            "vt": "str"
          }
        ],
        "checkall": "true",
        "outputs": 1,
        "x": 390,
        "y": 380,
        "wires": [
          [
            "e9d92594.1626d8"
          ]
        ]
      },
      {
        "id": "a9fbd05a.56043",
        "type": "function",
        "z": "5fde8238.a0217c",
        "name": "Parse payload",
        "func": "msg.payload = JSON.parse(msg.payload);\ndelete msg._session;\ndelete msg._msg_id;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 360,
        "y": 260,
        "wires": [
          [
            "295b06c9.d6a4fa",
            "df8e864e.207178"
          ]
        ]
      },
      {
        "id": "295b06c9.d6a4fa",
        "type": "debug",
        "z": "5fde8238.a0217c",
        "name": "",
        "active": false,
        "console": "false",
        "complete": "true",
        "x": 550,
        "y": 320,
        "wires": [
    
        ]
      },
      {
        "id": "e9d92594.1626d8",
        "type": "switch",
        "z": "5fde8238.a0217c",
        "name": "value",
        "property": "payload.value",
        "propertyType": "msg",
        "rules": [
          {
            "t": "eq",
            "v": "1",
            "vt": "str"
          }
        ],
        "checkall": "true",
        "outputs": 1,
        "x": 390,
        "y": 440,
        "wires": [
          [
            "9c9ea2fd.63616"
          ]
        ]
      },
      {
        "id": "9c9ea2fd.63616",
        "type": "function",
        "z": "5fde8238.a0217c",
        "name": "relay",
        "func": "msg.payload.dev = \"relay\";\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 390,
        "y": 500,
        "wires": [
          [
            "99b5a5e1.664a58"
          ]
        ]
      },
      {
        "id": "ce9e5829.3161a8",
        "type": "function",
        "z": "5fde8238.a0217c",
        "name": "toggle",
        "func": "msg.payload.value = (context.global.relays[msg.payload.circuit] + 1) % 2;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 570,
        "y": 680,
        "wires": [
          [
            "ab1c340c.54e3c8"
          ]
        ]
      },
      {
        "id": "55a551aa.aa5ab",
        "type": "inject",
        "z": "5fde8238.a0217c",
        "name": "reset",
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "repeat": "",
        "crontab": "",
        "once": true,
        "x": 190,
        "y": 200,
        "wires": [
          [
            "4154287.fbeabd8"
          ]
        ]
      },
      {
        "id": "4154287.fbeabd8",
        "type": "function",
        "z": "5fde8238.a0217c",
        "name": "reset",
        "func": "context.global.relays = [];\nfor (var lp = 1; lp <= 8; lp++) {\n    context.global.relays[lp] = 0;\n}\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 330,
        "y": 200,
        "wires": [
          [
    
          ]
        ]
      },
      {
        "id": "99b5a5e1.664a58",
        "type": "switch",
        "z": "5fde8238.a0217c",
        "name": "circuit",
        "property": "payload.circuit",
        "propertyType": "msg",
        "rules": [
          {
            "t": "eq",
            "v": "1",
            "vt": "str"
          },
          {
            "t": "eq",
            "v": "2",
            "vt": "str"
          },
          {
            "t": "gte",
            "v": "3",
            "vt": "str"
          }
        ],
        "checkall": "true",
        "outputs": 3,
        "x": 390,
        "y": 560,
        "wires": [
          [
            "da015797.25fea8"
          ],
          [
            "da015797.25fea8"
          ],
          [
            "ce9e5829.3161a8"
          ]
        ]
      },
      {
        "id": "da015797.25fea8",
        "type": "function",
        "z": "5fde8238.a0217c",
        "name": "circuit 1",
        "func": "msg.payload.circuit = 1;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 580,
        "y": 620,
        "wires": [
          [
            "ce9e5829.3161a8"
          ]
        ]
      },
      {
        "id": "ab1c340c.54e3c8",
        "type": "function",
        "z": "5fde8238.a0217c",
        "name": "store",
        "func": "\/* Make sure we always store an integer value *\/\ncontext.global.relays[msg.payload.circuit] = Math.floor(msg.payload.value);\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 570,
        "y": 740,
        "wires": [
          [
            "e6b167b1.194e98",
            "4394f136.bc6b1"
          ]
        ]
      },
      {
        "id": "7dfd41dd.8202c",
        "type": "comment",
        "z": "5fde8238.a0217c",
        "name": "Filter out anything that isn't from an input dev.",
        "info": "",
        "x": 680,
        "y": 380,
        "wires": [
    
        ]
      },
      {
        "id": "455ff2ee.baa00c",
        "type": "comment",
        "z": "5fde8238.a0217c",
        "name": "Filterout anything that isn't a '1' value.",
        "info": "",
        "x": 650,
        "y": 440,
        "wires": [
    
        ]
      },
      {
        "id": "8eb261e7.714da",
        "type": "comment",
        "z": "5fde8238.a0217c",
        "name": "Switch based on circuit (so 1 & 2 have same effect).",
        "info": "",
        "x": 700,
        "y": 560,
        "wires": [
    
        ]
      },
      {
        "id": "185fc24c.e7a03e",
        "type": "comment",
        "z": "5fde8238.a0217c",
        "name": "Toggle our internal value of the chosen output.",
        "info": "",
        "x": 860,
        "y": 680,
        "wires": [
    
        ]
      },
      {
        "id": "cbaf94f2.345068",
        "type": "comment",
        "z": "5fde8238.a0217c",
        "name": "Force everything that comes through here to act on circuit 1.",
        "info": "",
        "x": 900,
        "y": 620,
        "wires": [
    
        ]
      },
      {
        "id": "91da42d.f6e25c",
        "type": "comment",
        "z": "5fde8238.a0217c",
        "name": "Store the value for this relay so we can toggle later.",
        "info": "",
        "x": 880,
        "y": 740,
        "wires": [
    
        ]
      },
      {
        "id": "7b1c576.f84e3a8",
        "type": "comment",
        "z": "5fde8238.a0217c",
        "name": "Switch device to relay for output.",
        "info": "",
        "x": 640,
        "y": 500,
        "wires": [
    
        ]
      },
      {
        "id": "71c45311.8e3bac",
        "type": "comment",
        "z": "5fde8238.a0217c",
        "name": "We parse the payload as it appears to arrive as a string. This also strips incoming data that messes up output.",
        "info": "",
        "x": 880,
        "y": 260,
        "wires": [
    
        ]
      },
      {
        "id": "737dd583.8c822c",
        "type": "comment",
        "z": "5fde8238.a0217c",
        "name": "In order to toggle relays on input we have to store them. This resets values on first start.",
        "info": "",
        "x": 810,
        "y": 200,
        "wires": [
    
        ]
      },
      {
        "id": "83442183.7cbbe",
        "type": "function",
        "z": "5fde8238.a0217c",
        "name": "Parse payload",
        "func": "msg.payload = JSON.parse(msg.payload);\ndelete msg._session;\ndelete msg._msg_id;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 400,
        "y": 740,
        "wires": [
          [
            "ab1c340c.54e3c8"
          ]
        ]
      }
    ]
    
    


  • Here is a simple flow that implements a Click or a Press and Hold functionality in Node-Red. Clicking the input will toggle the relay state while a press and hold will turn the relay on until released.

    [{"id":"5605b7b2.a9fa48","type":"websocket-client","path":"ws://127.0.0.1/ws","wholemsg":"false"},{"id":"2538f19e.dac70e","type":"websocket out","z":"12603337.ed9fcd","name":"","server":"","client":"5605b7b2.a9fa48","x":969,"y":185,"wires":[]},{"id":"8c6ee30f.73912","type":"websocket in","z":"12603337.ed9fcd","name":"","server":"","client":"5605b7b2.a9fa48","x":192,"y":170,"wires":[["27c90225.d836fe"]]},{"id":"aacdc1c4.55324","type":"function","z":"12603337.ed9fcd","name":"Latch Relay - Toggle","func":"var unipistatus = JSON.parse(msg.payload);\nvar modulestatus = context.global.modulestatus;\nvar inDevice = {};\nvar outDevice = {};\nvar msg = {};\n\nfor (var indev in modulestatus){\n if (modulestatus[indev] == unipistatus.circuit){\n inDevice = modulestatus[indev];\n \n }\n}\n \nfor (var outdev in modulestatus){\n if (modulestatus[outdev].dev == "relay" && modulestatus[outdev].circuit == unipistatus.circuit){\n outDevice = modulestatus[outdev];\n }\n}\n \nif (unipistatus.dev == "input" && unipistatus.circuit<8) {\n \n\tif (unipistatus.value === 0) {\n if (outDevice.value == "0"){\n msg.payload = {"dev":"relay", "circuit":"", "value":"1"};\n\t\t msg.payload.circuit = unipistatus.circuit;\n\t\t outDevice.value = "1";\n\t }\n\t else{\n\t msg.payload = {"dev":"relay", "circuit":"", "value":"0"};\n\t msg.payload.circuit = unipistatus.circuit;\n\t outDevice.value = "0";\n\t }\n\t}\n\telse {\n\t \n\t}\n\treturn msg;\n}\nelse return;\n","outputs":1,"noerr":0,"x":734,"y":163,"wires":[["2538f19e.dac70e"]]},{"id":"16044675.e9fbba","type":"http request","z":"12603337.ed9fcd","name":"Initial Status","method":"GET","ret":"txt","url":"http://127.0.0.1/rest/all","x":316,"y":77,"wires":[["9291954d.6d6e68"]]},{"id":"bf8a9779.407568","type":"inject","z":"12603337.ed9fcd","name":"Startup","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"x":175,"y":77,"wires":[["16044675.e9fbba"]]},{"id":"9291954d.6d6e68","type":"function","z":"12603337.ed9fcd","name":"Store Status","func":"context.global.modulestatus = JSON.parse(msg.payload);\nreturn msg;","outputs":1,"noerr":0,"x":472,"y":77,"wires":[["ec3278b5.13cd88"]]},{"id":"ec3278b5.13cd88","type":"debug","z":"12603337.ed9fcd","name":"","active":false,"console":"false","complete":"false","x":630,"y":77,"wires":[]},{"id":"27c90225.d836fe","type":"function","z":"12603337.ed9fcd","name":"Click or Hold","func":"var unipistatus = JSON.parse(msg.payload);\nvar msg1 = {};\n\nif (unipistatus.dev == "input"){\n msg1 = msg;\n if (unipistatus.value == "0"){\n msg1.reset = 1;\n return [msg,msg1];\n }\n else {\n return [null,msg1];\n }\n}\n\nreturn;","outputs":"2","noerr":0,"x":379,"y":170,"wires":[["aacdc1c4.55324"],["65fe55ef.9a01ac"]]},{"id":"65fe55ef.9a01ac","type":"trigger","z":"12603337.ed9fcd","op1":"1","op2":"0","op1type":"nul","op2type":"pay","duration":"500","extend":false,"units":"ms","name":"Press and Hold","x":554,"y":206,"wires":[["1f48c204.e0b73e"]]},{"id":"1f48c204.e0b73e","type":"function","z":"12603337.ed9fcd","name":"On Until Release","func":"var unipistatus = JSON.parse(msg.payload);\nvar modulestatus = context.global.modulestatus;\nvar inDevice = {};\nvar outDevice = {};\nvar msg = {};\n\nfor (var indev in modulestatus){\n if (modulestatus[indev] == unipistatus.circuit){\n inDevice = modulestatus[indev];\n \n }\n}\n \nfor (var outdev in modulestatus){\n if (modulestatus[outdev].dev == "relay" && modulestatus[outdev].circuit == unipistatus.circuit){\n outDevice = modulestatus[outdev];\n }\n}\n \nif (unipistatus.dev == "input" && unipistatus.circuit<8) {\n \n\tif (unipistatus.value == "1") {\n msg.payload = {"dev":"relay", "circuit":"", "value":"1"};\n\t\t msg.payload.circuit = unipistatus.circuit;\n\t\t outDevice.value = "1";\n\t\t inDevice.value = "1";\n\t}\n\telse {\n\t msg.payload = {"dev":"relay", "circuit":"", "value":"0"};\n\t msg.payload.circuit = unipistatus.circuit;\n\t outDevice.value = "0";\n\t inDevice.value = "0";\n\t}\n\t\n\treturn msg;\n}\nelse return;\n","outputs":1,"noerr":0,"x":745,"y":206,"wires":[["2538f19e.dac70e"]]}]

    Thanks,
    Brett.



  • Coming back on this point, where to look if I want to implement this functionnality inside Evok itself and not based on Node-Red ?
    I will like to have the ability to just add parameters for exemple on an input to say on change act on relay.
    Start reading the code of evok and try to understand how it's working, look not so hard but still, some help on the design and what file for exemple can be used.