Qolsys IQ Panel 2 and 3rd party integration

Are you running the latest version of NodeRed? I’ve had the QolSys monitoring flow running for 4+ days now with no problems or interruptions. I’m running 1.29 in a Docker container on my Synology NAS.

Yes, I’m running the latest version of NodeRed from Supervisor in HA on Virtual Box. The connectivity issue is unpredictable, it could be a day or over a week before losing the connection. At least 3 other people have reported the same behavior in this thread so it appears to be a common issue. Let me know when you go two weeks without an issue lol.

Is there a way for NodeRed to detect when the websocket connection is lost, and then reconnect?

If I experience the connection loss, I’ll definitely look into that. There’s probably a few different ways you could do about it. The panel emits status messages fairly frequently, but I imagine that depends on how many devices you have.

You could set a timestamp every time a message comes in, and periodically check that. In my case I get a message at least once a minute, but whatever the proper interval, if you don’t detect anything then kill and restart the curl command.

You could also check the exit code from the curl command, which you should never get unless curl exits which normally shouldn’t be the case. So if an exit command is detected, restart the flow.

Now, if you really have to restart NodeRed in order to get things moving again, you’d need some external watchdog.

I’m actually rewritting the node-red to use openssl instead of curl, it does seems to be working a bit better.

One issue that I’m seeing, and maybe someone can help me out with this. When sending a arm stay or disarm command, it works perfectly. However, when sending an arm away command, it goes through the countdown and shows that it was armed away, however, once the count down is done, it swaps over to arm stay.

this is the command I’m sending.

{"partition_id":0,"action":"ARMING","arming_type":"ARM_AWAY","version":0,"nonce":"","usercode":"XXXX","source":"C4","version_key":1,"source_key":"C4","token":"XXXXXXX"}

{"version_key":1,"source_key":"C4","token":"XXXXXX","partition_id":0,"action":"ARMING","arming_type":"ARM_AWAY","bypass":false,"version":0,"nonce":"","source":"C4","delay":60}

and a few others.

Now the nonce key is used between C4 and Qolsys so that C4 knows what panel to send the message back to, so the nonce is not needed in this aspect as all it does is send the nonce value back in the reply message.

Anyone want to help me figure out how to actually get it to arm away?

Might be able to do some testing later tonight, after my wife goes to bed. She would set off the alarm if I armed it during the day because of the motion sensors.

Hey guys, try this flow and see if it works for you.

I can also get a homebridge flow if anyone needs it.

[{"id":"f56bc79f.7f2488","type":"tab","label":"Monitor QolSys Panel for HomeAssistant","disabled":false,"info":""},{"id":"a1d01192.4adf6","type":"debug","z":"f56bc79f.7f2488","name":"return (exit) codes","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":430,"y":60,"wires":[]},{"id":"955f5912.5939f8","type":"debug","z":"f56bc79f.7f2488","name":"Just outputs the curl progress, not the actual output","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":500,"y":20,"wires":[]},{"id":"d32460c3.8baeb","type":"debug","z":"f56bc79f.7f2488","name":"converted json","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":740,"y":240,"wires":[]},{"id":"29570e98.3853a2","type":"function","z":"f56bc79f.7f2488","name":"Find Open/Closed Status \\n Doors + Windows ","func":"zid = msg.payload.zone.zone_id;\nzstatus = msg.payload[\"zone\"][\"status\"];\n//entity_id = \"\";\nmsg.zstatus = zstatus;\n\nswitch (zid) {\n    case 1:\n        return [msg,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,msg]\n    case 2:\n        return [null,msg,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,msg]\n    case 3:\n        return [null,null,msg,null,null,null,null,null,null,null,null,null,null,null,null,null,null,msg]\n    case 4:\n        return [null,null,null,msg,null,null,null,null,null,null,null,null,null,null,null,null,null,msg]\n    case 5:\n        return [null,null,null,null,msg,null,null,null,null,null,null,null,null,null,null,null,null,msg]\n    case 6:\n        return [null,null,null,null,null,msg,null,null,null,null,null,null,null,null,null,null,null,msg]\n    case 7:\n        return [null,null,null,null,null,null,msg,null,null,null,null,null,null,null,null,null,null,msg]     \n    case 8:\n        return [null,null,null,null,null,null,null,msg,null,null,null,null,null,null,null,null,null,msg]   \n    case 9:\n        return [null,null,null,null,null,null,null,null,msg,null,null,null,null,null,null,null,null,msg]\n    case 10:\n        return [null,null,null,null,null,null,null,null,null,msg,null,null,null,null,null,null,null,msg]\n    case 11:\n        return [null,null,null,null,null,null,null,null,null,null,msg,null,null,null,null,null,null,msg]\n    case 12:\n        return [null,null,null,null,null,null,null,null,null,null,null,msg,null,null,null,null,null,msg]\n    case 13:\n        return [null,null,null,null,null,null,null,null,null,null,null,null,msg,null,null,null,null,msg]\n    case 14:\n        return [null,null,null,null,null,null,null,null,null,null,null,null,null,msg,null,null,null,msg]\n    case 15:\n        return [null,null,null,null,null,null,null,null,null,null,null,null,null,null,msg,null,null,msg]     \n    case 16:\n        return [null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,msg,null,msg]\n    case 17:\n        return [null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,msg,msg]\n}\n\n//node.warn(\"zid: \" + zid + \", status: \" + zstatus + \", entities: \" + entity_ids)","outputs":18,"noerr":0,"initialize":"","finalize":"","x":430,"y":620,"wires":[["b4675df.7b8a9a"],["260014ef.9e567c"],["e0751b3.833ece8"],["78a6cb10.642ac4"],["a7ac8fa2.72b01"],["ce833c23.43ab"],["e0461607.b350a8"],["944cfab2.77e848"],["c94a3d7a.b88d9"],["a4ee01f4.2ce31"],["b4f795b2.f247e8"],["ce8fc1bd.5e2bf"],["ec8a532d.f7ea"],["19174477.0c786c"],["e4415c23.d422"],["9cabcac4.5eac38"],["2179415c.b6e4be"],["378a979c.bb7f58"]]},{"id":"378a979c.bb7f58","type":"debug","z":"f56bc79f.7f2488","name":"added entity ids","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":620,"y":1200,"wires":[]},{"id":"b4675df.7b8a9a","type":"ha-entity","z":"f56bc79f.7f2488","name":"Front Door","server":"7ce906e3.693188","version":1,"debugenabled":false,"outputs":1,"entityType":"binary_sensor","config":[{"property":"name","value":"Front Door"},{"property":"device_class","value":"door"},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"zstatus","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"","outputPayloadType":"str","x":990,"y":340,"wires":[[]]},{"id":"b7dbdb6c.239808","type":"ha-entity","z":"f56bc79f.7f2488","d":true,"name":"Motion Detector","server":"7ce906e3.693188","version":1,"debugenabled":false,"outputs":1,"entityType":"binary_sensor","config":[{"property":"name","value":"Hallway Motion Detector"},{"property":"device_class","value":"motion"},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"zstatus","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"","outputPayloadType":"str","x":460,"y":900,"wires":[[]]},{"id":"5ae77b36.22b6b4","type":"ha-entity","z":"f56bc79f.7f2488","d":true,"name":"Panel Motion","server":"7ce906e3.693188","version":1,"debugenabled":false,"outputs":1,"entityType":"binary_sensor","config":[{"property":"name","value":"IQ2 Panel Motion"},{"property":"device_class","value":"motion"},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"zstatus","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"","outputPayloadType":"str","x":430,"y":1040,"wires":[[]]},{"id":"26a17e23.b68c02","type":"function","z":"f56bc79f.7f2488","d":true,"name":"Find Active Status \\n Motion Sensors","func":"zid = msg.payload[\"zone\"][\"zone_id\"];\nzstatus = msg.payload[\"zone_event_type\"];\n\nmsg.zstatus = zstatus;\n\nswitch (zid) {\n    case 10:\n        return [msg,null]\n    case 15:\n        return [null,msg]\n}\n\n//node.warn(\"zid: \" + zid + \", status: \" + zstatus + \", entities: \" + entity_ids)","outputs":2,"noerr":0,"initialize":"","finalize":"","x":150,"y":880,"wires":[["8cfceb8f.5d73a8"],["3a759b21.164164"]]},{"id":"8cfceb8f.5d73a8","type":"change","z":"f56bc79f.7f2488","d":true,"name":"Define on/off","rules":[{"t":"change","p":"zstatus","pt":"msg","from":"ZONE_ACTIVE","fromt":"str","to":"on","tot":"str"},{"t":"change","p":"zstatus","pt":"msg","from":"ZONE_UPDATE","fromt":"str","to":"off","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":430,"y":860,"wires":[["b7dbdb6c.239808"]]},{"id":"3a759b21.164164","type":"change","z":"f56bc79f.7f2488","d":true,"name":"Define on/off","rules":[{"t":"change","p":"zstatus","pt":"msg","from":"ZONE_ACTIVE","fromt":"str","to":"on","tot":"str"},{"t":"change","p":"zstatus","pt":"msg","from":"ZONE_UPDATE","fromt":"str","to":"off","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":430,"y":980,"wires":[["5ae77b36.22b6b4"]]},{"id":"260014ef.9e567c","type":"ha-entity","z":"f56bc79f.7f2488","name":"Back Door","server":"7ce906e3.693188","version":1,"debugenabled":false,"outputs":1,"entityType":"binary_sensor","config":[{"property":"name","value":"Back Door"},{"property":"device_class","value":"door"},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"zstatus","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"","outputPayloadType":"str","x":990,"y":400,"wires":[[]]},{"id":"e0751b3.833ece8","type":"ha-entity","z":"f56bc79f.7f2488","name":"Laundry Door","server":"7ce906e3.693188","version":1,"debugenabled":false,"outputs":1,"entityType":"binary_sensor","config":[{"property":"name","value":"Laundry Door"},{"property":"device_class","value":"door"},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"zstatus","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"","outputPayloadType":"str","x":1000,"y":460,"wires":[[]]},{"id":"78a6cb10.642ac4","type":"ha-entity","z":"f56bc79f.7f2488","name":"Den Door","server":"7ce906e3.693188","version":1,"debugenabled":false,"outputs":1,"entityType":"binary_sensor","config":[{"property":"name","value":"Den Door"},{"property":"device_class","value":"door"},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"zstatus","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"","outputPayloadType":"str","x":980,"y":520,"wires":[[]]},{"id":"a7ac8fa2.72b01","type":"ha-entity","z":"f56bc79f.7f2488","name":"Dining Room Window 1","server":"7ce906e3.693188","version":1,"debugenabled":false,"outputs":1,"entityType":"binary_sensor","config":[{"property":"name","value":"Dining Room Window 1"},{"property":"device_class","value":"window"},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"zstatus","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"","outputPayloadType":"str","x":1030,"y":580,"wires":[[]]},{"id":"ce833c23.43ab","type":"ha-entity","z":"f56bc79f.7f2488","name":"Dining Room Window 2","server":"7ce906e3.693188","version":1,"debugenabled":false,"outputs":1,"entityType":"binary_sensor","config":[{"property":"name","value":"Dining Room Window 2"},{"property":"device_class","value":"window"},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"zstatus","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"","outputPayloadType":"str","x":1030,"y":640,"wires":[[]]},{"id":"e0461607.b350a8","type":"ha-entity","z":"f56bc79f.7f2488","name":"Den Window 1","server":"7ce906e3.693188","version":1,"debugenabled":false,"outputs":1,"entityType":"binary_sensor","config":[{"property":"name","value":"Den Window 1"},{"property":"device_class","value":"window"},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"zstatus","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"","outputPayloadType":"str","x":1000,"y":700,"wires":[[]]},{"id":"944cfab2.77e848","type":"ha-entity","z":"f56bc79f.7f2488","name":"Den Window 2","server":"7ce906e3.693188","version":1,"debugenabled":false,"outputs":1,"entityType":"binary_sensor","config":[{"property":"name","value":"Den Window 2"},{"property":"device_class","value":"window"},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"zstatus","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"","outputPayloadType":"str","x":1000,"y":760,"wires":[[]]},{"id":"c94a3d7a.b88d9","type":"ha-entity","z":"f56bc79f.7f2488","name":"Den Window 3","server":"7ce906e3.693188","version":1,"debugenabled":false,"outputs":1,"entityType":"binary_sensor","config":[{"property":"name","value":"Den Window 3"},{"property":"device_class","value":"window"},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"zstatus","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"","outputPayloadType":"str","x":1000,"y":820,"wires":[[]]},{"id":"a4ee01f4.2ce31","type":"ha-entity","z":"f56bc79f.7f2488","name":"Kitchen Window","server":"7ce906e3.693188","version":1,"debugenabled":false,"outputs":1,"entityType":"binary_sensor","config":[{"property":"name","value":"Kitchen Window"},{"property":"device_class","value":"window"},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"zstatus","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"","outputPayloadType":"str","x":1000,"y":880,"wires":[[]]},{"id":"b4f795b2.f247e8","type":"ha-entity","z":"f56bc79f.7f2488","name":"Cooper's Window","server":"7ce906e3.693188","version":1,"debugenabled":false,"outputs":1,"entityType":"binary_sensor","config":[{"property":"name","value":"Cooper's Window"},{"property":"device_class","value":"window"},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"zstatus","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"","outputPayloadType":"str","x":1010,"y":940,"wires":[[]]},{"id":"ce8fc1bd.5e2bf","type":"ha-entity","z":"f56bc79f.7f2488","name":"Hannah's Window 1","server":"7ce906e3.693188","version":1,"debugenabled":false,"outputs":1,"entityType":"binary_sensor","config":[{"property":"name","value":"Hannah's Window 1"},{"property":"device_class","value":"window"},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"zstatus","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"","outputPayloadType":"str","x":1020,"y":1000,"wires":[[]]},{"id":"ec8a532d.f7ea","type":"ha-entity","z":"f56bc79f.7f2488","name":"Hannah's Window 2","server":"7ce906e3.693188","version":1,"debugenabled":false,"outputs":1,"entityType":"binary_sensor","config":[{"property":"name","value":"Hannah's Window 2"},{"property":"device_class","value":"window"},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"zstatus","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"","outputPayloadType":"str","x":1020,"y":1060,"wires":[[]]},{"id":"19174477.0c786c","type":"ha-entity","z":"f56bc79f.7f2488","name":"Master Bath Window","server":"7ce906e3.693188","version":1,"debugenabled":false,"outputs":1,"entityType":"binary_sensor","config":[{"property":"name","value":"Master Bath Window"},{"property":"device_class","value":"window"},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"zstatus","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"","outputPayloadType":"str","x":1020,"y":1120,"wires":[[]]},{"id":"e4415c23.d422","type":"ha-entity","z":"f56bc79f.7f2488","name":"Master Bedroom Window","server":"7ce906e3.693188","version":1,"debugenabled":false,"outputs":1,"entityType":"binary_sensor","config":[{"property":"name","value":"Master Bedroom Window"},{"property":"device_class","value":"window"},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"zstatus","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"","outputPayloadType":"str","x":1030,"y":1180,"wires":[[]]},{"id":"9cabcac4.5eac38","type":"ha-entity","z":"f56bc79f.7f2488","name":"Living Room Window 1","server":"7ce906e3.693188","version":1,"debugenabled":false,"outputs":1,"entityType":"binary_sensor","config":[{"property":"name","value":"Living Room Window 1"},{"property":"device_class","value":"window"},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"zstatus","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"","outputPayloadType":"str","x":1030,"y":1240,"wires":[[]]},{"id":"2179415c.b6e4be","type":"ha-entity","z":"f56bc79f.7f2488","name":"Living Room Window 2","server":"7ce906e3.693188","version":1,"debugenabled":false,"outputs":1,"entityType":"binary_sensor","config":[{"property":"name","value":"Living Room Window 2"},{"property":"device_class","value":"window"},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"zstatus","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"","outputPayloadType":"str","x":1030,"y":1300,"wires":[[]]},{"id":"95ecfaf1.e179d8","type":"inject","z":"f56bc79f.7f2488","name":"go","props":[],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","x":90,"y":20,"wires":[[]]},{"id":"7f0292a5.b336ac","type":"json","z":"f56bc79f.7f2488","name":"convert update to json","property":"payload","action":"","pretty":true,"x":380,"y":300,"wires":[["29570e98.3853a2","26a17e23.b68c02","d32460c3.8baeb"]]},{"id":"6aefec2c.bdf514","type":"daemon","z":"f56bc79f.7f2488","name":"","command":"openssl","args":"s_client -host 192.168.7.1 -port 12345","autorun":true,"cr":false,"redo":true,"op":"lines","closer":"SIGKILL","x":160,"y":120,"wires":[["5595fe81.9b7fc"],[],[]]},{"id":"5595fe81.9b7fc","type":"switch","z":"f56bc79f.7f2488","name":"Filter Key information","property":"payload","propertyType":"msg","rules":[{"t":"cont","v":"ACK","vt":"str"},{"t":"cont","v":"{","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":3,"x":360,"y":200,"wires":[[],["7f0292a5.b336ac"],[]]},{"id":"7ce906e3.693188","type":"server","name":"Home Assistant","legacy":false,"addon":false,"rejectUnauthorizedCerts":false,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true}]

You will need the node-red-node-daemon for this flow.

Hi Michael,

I’m trying to test your flow in HA node-red, but for some reason I wasn’t able to get any response from the panel.

Running openssl command directly on the console, here is the only message that I get.

pi:~ $ openssl s_client -host 192.168.0.45 -port 12345
CONNECTED(00000003)

Any ideas?

You don’t see the crypto information after the connection?

I try again and wait a little bit and see this error.

pi:~ $ openssl s_client -host 192.168.0.45 -port 12345
CONNECTED(00000003)
write:errno=104
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 0 bytes and written 283 bytes
Verification: OK
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---

Interesting. If you press enter there, do you get an ACK response?

Anyone had a chance to see if their panel will go from arm away to arm stay at the end of the countdown and a possible way to get it to work right?

I will say that since I changed over to using the OpenSSL command, I haven’t had the first time I’ve needed to reboot my panel. Now granted I’m using it more for homebridge now but I am also sending that over to home assistant too.

I am also working on a flow for SmartThings for just the alarm information, not the sensors.

Wow I haven’t been able to pay attention to this topic for a while. I’m really looking forward to trying out the commands and openssl implementation @Smwoodward! And I’m gonna take a look at the python script - it could replace node-red @mzac!

One thing I spent many hours doing is making my sensor creation much more dynamic. I’m now using MQTT and taking advantage of HA’s autodiscovery. After defining my zone id and mapping them to names, I get sensors in HA immediately. So I’ve moved my automation into YAML in HA now.

I defined my zones (all I have are a few doors) in the “Initialize zone ids and names” Setup step. It’s in javascript but super simple - just a list you can update for your zones.

I use the add-on MQTT broker so my MQTT setup is really simple.

[
    {
        "id": "cbfaf013.0e5e",
        "type": "tab",
        "label": "Monitor QolSys Panel",
        "disabled": false,
        "info": ""
    },
    {
        "id": "a92cf1.8c434b1",
        "type": "inject",
        "z": "cbfaf013.0e5e",
        "name": "go",
        "props": [],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "",
        "x": 90,
        "y": 100,
        "wires": [
            [
                "df1b46c2.dc0748",
                "7593a9f9.4b4dc8"
            ]
        ]
    },
    {
        "id": "df1b46c2.dc0748",
        "type": "exec",
        "z": "cbfaf013.0e5e",
        "command": "curl -kN --http0.9 https://192.168.10.34:12345",
        "addpay": true,
        "append": "",
        "useSpawn": "true",
        "timer": "",
        "oldrc": false,
        "name": "curl",
        "x": 210,
        "y": 300,
        "wires": [
            [
                "1f5ac981.6ccdde",
                "1edaf367.8f44ed"
            ],
            [
                "f17d89c8.de8bf"
            ],
            [
                "cf10d274.0242e",
                "5fe3eb4d.3d8c04"
            ]
        ]
    },
    {
        "id": "cf10d274.0242e",
        "type": "debug",
        "z": "cbfaf013.0e5e",
        "name": "return (exit) codes",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1130,
        "y": 380,
        "wires": []
    },
    {
        "id": "1f5ac981.6ccdde",
        "type": "debug",
        "z": "cbfaf013.0e5e",
        "name": "activity events and updates",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1060,
        "y": 300,
        "wires": []
    },
    {
        "id": "f17d89c8.de8bf",
        "type": "debug",
        "z": "cbfaf013.0e5e",
        "name": "Just outputs the curl progress, not the actual output",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1060,
        "y": 340,
        "wires": []
    },
    {
        "id": "8cb8b681.b122f8",
        "type": "status",
        "z": "cbfaf013.0e5e",
        "name": "",
        "scope": [
            "df1b46c2.dc0748"
        ],
        "x": 160,
        "y": 560,
        "wires": [
            [
                "aff08d86.5d518",
                "938d4ef0.68605"
            ]
        ]
    },
    {
        "id": "aff08d86.5d518",
        "type": "debug",
        "z": "cbfaf013.0e5e",
        "name": "pid",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 310,
        "y": 580,
        "wires": []
    },
    {
        "id": "1edaf367.8f44ed",
        "type": "switch",
        "z": "cbfaf013.0e5e",
        "name": "Ignore ACK",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "cont",
                "v": "ACK",
                "vt": "str"
            },
            {
                "t": "cont",
                "v": "ZONE_UPDATE",
                "vt": "str"
            },
            {
                "t": "cont",
                "v": "ZONE_ACTIVE",
                "vt": "str"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 4,
        "x": 190,
        "y": 400,
        "wires": [
            [],
            [
                "9a07870e.d047d8"
            ],
            [
                "9a07870e.d047d8"
            ],
            [
                "29d0c5a8.b1b4ea"
            ]
        ]
    },
    {
        "id": "9a07870e.d047d8",
        "type": "json",
        "z": "cbfaf013.0e5e",
        "name": "convert update to json",
        "property": "payload",
        "action": "",
        "pretty": true,
        "x": 400,
        "y": 400,
        "wires": [
            [
                "9c24717d.c5031",
                "59822226.0248cc"
            ]
        ]
    },
    {
        "id": "9c24717d.c5031",
        "type": "debug",
        "z": "cbfaf013.0e5e",
        "name": "converted json",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1080,
        "y": 460,
        "wires": []
    },
    {
        "id": "938d4ef0.68605",
        "type": "change",
        "z": "cbfaf013.0e5e",
        "name": "get pid",
        "rules": [
            {
                "t": "set",
                "p": "pid",
                "pt": "flow",
                "to": "$split($lookup(status, \"text\"),\":\")[1]",
                "tot": "jsonata"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 300,
        "y": 520,
        "wires": [
            [
                "50203a30.d5c0a4"
            ]
        ]
    },
    {
        "id": "50203a30.d5c0a4",
        "type": "debug",
        "z": "cbfaf013.0e5e",
        "name": "",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": true,
        "complete": "$flowContext(\"pid\")\t",
        "targetType": "jsonata",
        "statusVal": "$flowContext(\"pid\")\t",
        "statusType": "auto",
        "x": 480,
        "y": 520,
        "wires": []
    },
    {
        "id": "5fe3eb4d.3d8c04",
        "type": "switch",
        "z": "cbfaf013.0e5e",
        "name": "Check and restart",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "jsonata_exp",
                "v": "$lookup(payload, \"code\")=56",
                "vt": "jsonata"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 470,
        "y": 360,
        "wires": [
            [
                "bf874de4.ef135"
            ]
        ]
    },
    {
        "id": "bf874de4.ef135",
        "type": "delay",
        "z": "cbfaf013.0e5e",
        "name": "",
        "pauseType": "delay",
        "timeout": "10",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "x": 660,
        "y": 360,
        "wires": [
            [
                "df1b46c2.dc0748"
            ]
        ]
    },
    {
        "id": "20c47d1f.7e57d2",
        "type": "debug",
        "z": "cbfaf013.0e5e",
        "name": "mqtt out",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 720,
        "y": 460,
        "wires": []
    },
    {
        "id": "a272ddf7.56ba6",
        "type": "debug",
        "z": "cbfaf013.0e5e",
        "name": "Zone id to zone name mapping",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": true,
        "complete": "true",
        "targetType": "full",
        "statusVal": "$flowContext(\"zones\")",
        "statusType": "auto",
        "x": 1050,
        "y": 20,
        "wires": []
    },
    {
        "id": "7593a9f9.4b4dc8",
        "type": "function",
        "z": "cbfaf013.0e5e",
        "name": "initialize zone ids and names",
        "func": "var zones = flow.get(\"zones\")\nmsg.payload = zones\nreturn msg",
        "outputs": 1,
        "noerr": 0,
        "initialize": "// Code added here will be run once\n// whenever the node is deployed.\n\nclass door_window {\n    constructor(zoneid, entity_id=\"\", name, device_class=\"door\", state=\"Closed\") {\n    this.state = state\n    this.id = zoneid\n    this.name = name\n    this.device_class = device_class\n    this.entity_id = entity_id\n    this.payload_on = \"Open\"\n    this.payload_off = \"Closed\"\n    this.config_topic = \"homeassistant/binary_sensor/\" + entity_id + \"/config\"\n    this.state_topic = \"mqtt_states/binary_sensor/\" + entity_id + \"/state\" \n    }\n    \n}\n\nvar zones = []\nzones[1] = new door_window(1, \"front_door\", \"Front Door\")\nzones[2] = new door_window(2, \"trash_gate\", \"Trash Gate\")\nzones[3] = new door_window(3, \"neela_s_garage_entry\", \"Neela's Garage Entry\")\nzones[4] = new door_window(4, \"roopesh_s_garage_entry\", \"Roopesh's Garage Entry\")\nzones[6] = new door_window(6, \"great_room_slider\", \"Great Room Slider\")\nzones[7] = new door_window(7, \"garage_side_door\", \"Garage Side Door\")\n\nflow.set(\"zones\", zones)\n",
        "finalize": "",
        "x": 360,
        "y": 60,
        "wires": [
            [
                "a272ddf7.56ba6",
                "4e0e16a3.7cd0f8"
            ]
        ]
    },
    {
        "id": "29d0c5a8.b1b4ea",
        "type": "debug",
        "z": "cbfaf013.0e5e",
        "name": "Unknown events",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 170,
        "y": 460,
        "wires": []
    },
    {
        "id": "59822226.0248cc",
        "type": "function",
        "z": "cbfaf013.0e5e",
        "name": "Build MQTT Update Payload",
        "func": "var zid = msg.payload[\"zone\"][\"zone_id\"]\nvar zones = flow.get(\"zones\")\nvar state_topic = zones[zid].state_topic\nvar state = msg.payload[\"zone\"][\"status\"]\n\nmsg.payload = state\nmsg.topic = state_topic\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 460,
        "y": 460,
        "wires": [
            [
                "7a8a7809.26cf68",
                "20c47d1f.7e57d2"
            ]
        ]
    },
    {
        "id": "4e0e16a3.7cd0f8",
        "type": "array-loop",
        "z": "cbfaf013.0e5e",
        "name": "loop zones",
        "key": "al4e0e16a37cd0f8",
        "keyType": "msg",
        "reset": true,
        "resetValue": "value-null",
        "array": "zones",
        "arrayType": "flow",
        "x": 650,
        "y": 80,
        "wires": [
            [
                "89ebb812.5ea3a8"
            ],
            [
                "a51867e3.622258"
            ]
        ]
    },
    {
        "id": "89ebb812.5ea3a8",
        "type": "debug",
        "z": "cbfaf013.0e5e",
        "name": "All HA Init'd",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 990,
        "y": 80,
        "wires": []
    },
    {
        "id": "a51867e3.622258",
        "type": "switch",
        "z": "cbfaf013.0e5e",
        "name": "",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "nnull"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 430,
        "y": 200,
        "wires": [
            [
                "48744f25.dce7a"
            ],
            [
                "4e0e16a3.7cd0f8"
            ]
        ]
    },
    {
        "id": "3f8da6fc.a7238a",
        "type": "catch",
        "z": "cbfaf013.0e5e",
        "name": "",
        "scope": null,
        "uncaught": false,
        "x": 540,
        "y": 580,
        "wires": [
            [
                "cd788dfe.d978"
            ]
        ]
    },
    {
        "id": "cd788dfe.d978",
        "type": "debug",
        "z": "cbfaf013.0e5e",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 710,
        "y": 580,
        "wires": []
    },
    {
        "id": "dfd2c176.68a0e",
        "type": "debug",
        "z": "cbfaf013.0e5e",
        "name": "Initialized in HA",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 980,
        "y": 160,
        "wires": []
    },
    {
        "id": "1534a5cd.807cea",
        "type": "mqtt out",
        "z": "cbfaf013.0e5e",
        "name": "",
        "topic": "",
        "qos": "",
        "retain": "",
        "broker": "b1167957.68aa08",
        "x": 930,
        "y": 220,
        "wires": []
    },
    {
        "id": "48744f25.dce7a",
        "type": "function",
        "z": "cbfaf013.0e5e",
        "name": "Build MQTT topic and payload",
        "func": "var topic = \"\"\nvar type = msg.payload[\"type\"]\nvar entity_id = msg.payload[\"entity_id\"]\nvar name = msg.payload[\"name\"]\nvar device_class = msg.payload[\"device_class\"]\nvar state_topic = msg.payload[\"state_topic\"]\nvar config_topic = msg.payload[\"config_topic\"]\nvar payload_on = msg.payload[\"payload_on\"]\nvar payload_off = msg.payload[\"payload_off\"]\nvar config_msg = {\n    \"name\": name,\n    \"device_class\": device_class,\n    \"state_topic\": state_topic,\n    \"payload_on\": payload_on,\n    \"payload_off\": payload_off\n}\n\nmsg.topic = config_topic\n\nmsg.payload = config_msg\nreturn msg",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 690,
        "y": 200,
        "wires": [
            [
                "dfd2c176.68a0e",
                "4e0e16a3.7cd0f8",
                "1534a5cd.807cea"
            ]
        ]
    },
    {
        "id": "7a8a7809.26cf68",
        "type": "mqtt out",
        "z": "cbfaf013.0e5e",
        "name": "",
        "topic": "",
        "qos": "",
        "retain": "",
        "broker": "b1167957.68aa08",
        "x": 710,
        "y": 500,
        "wires": []
    },
    {
        "id": "b1167957.68aa08",
        "type": "mqtt-broker",
        "name": "HA MQTT",
        "broker": "127.0.0.1",
        "port": "1883",
        "clientid": "",
        "usetls": false,
        "compatmode": true,
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "closeTopic": "",
        "closeQos": "0",
        "closePayload": "",
        "willTopic": "",
        "willQos": "0",
        "willPayload": ""
    }
]

If it wasn’t nearly 1AM I would be implementing the openssl and/or python solutions :grinning:.

Also, FWIW, I nearly never have to reboot my panel. Every so often node-red’s curl would fail (dunno why) so I built in a restart if it fails.

The get status command to the panel will allow the panel to send you every sensor attached to the panel, so that would be one of the first steps in getting it to dynamically populate the sensors.

Yeah I realized that after I spent all that time :upside_down_face:. That’s OK, I needed it done… still figuring out how to listen on the websocket and feed data back to HA. I really like the MQTT sensors, so I’ll probably keep using that scheme.

I’ve made some really great progress using your script as a starting point @mzac. I have the socket open in a thread and can have it output as events happen, as well as feed events in. It’s kinda (very) poorly written right now but I should be able to share it soon. I’ve forked the repo and you can see current progress: https://github.com/roopesh/qolsys_client/tree/separate_socket

I was thinking about incorporating the entire MQTT sensor approach into the python script. Then it could run entirely independent (no more node-red). I make no promises on approach and I’m totally open to feedback. I am separating how data gets published from the panel as a callback function so this could be replaced with however you want to get the data. Hopefully I have something fully functioning and semi-solid by the weekend.

1 Like

This is great! I am really a beginner at Python so any improvements that can be made will be great. If it all works would you like to merge it back and I’ll create another release on Pypi? (Or I could make you a developer?)

BTW, in your MQTT section I would probably add options for username/password to connect to MQTT, and possibly also certificate/encryption options. What do you think?

Hey I just wanted to check in and say that I’ve had literally zero restart or communication problems by using Jason’s original Node Red flow from January 2nd. I know that’s anecdotal and not very helpful but I wonder what the differences are. All I’ve really done is:

  • Update my panel’s firmware fully (via Wifi)
  • Enabled the external API access
  • Implemented Jason’s flow according to the directions (which were a little thin but I got there :wink: ). It has been untouched and fully working since then. Even after a few reboots. If there are any communication problems happening I’m not noticing at all.

Thanks again everyone for the awesome work here. I’m trying to find some time to contribute.

So it seems like I might have figured out the arm away function where my panel would count down and then at 0, automatically arm stay.

I primarily use Home Bridge to pipe everything through to Apple home stuff, and when I would run the “arm away” function, I was already out of the house. This is key as I have the security system set to if a door isn’t opened after arming away, it will automatically change over to arm stay. To get around this, I added “delay”:0 to the command so that it instantly arms away. This works fine for me because when I use that function through Apple Homekit, I am already out of the house, so a door will not open at that point. If someone doesn’t have that setting enabled in the alarm panel, then I don’t think my same issue would apply to everyone, however I don’t remember if that is a customizable feature in the alarm or not.

One issue that I need to work out, I noticed it when my house cleaner left the other day, she armed the house through the panel, and Homekit immediately locked the door. lol. So I will need to do an arm away and see what the message is passed from the panel when it starts the countdown and what it says when the countdown is complete and adjust my flow accordingly.

Good catch. That option is enabled by default. It’s under Advanced Settings, Installation, Security and Arming, Auto Stay.