Qolsys IQ Panel 2 and 3rd party integration

I didn’t realize there were multiple WiFi configurations. Thanks for that! I’m thinking of writing a stream reader just so I can detect the sensors (open/close) for now. I’m not sure I’m going to invest any more on this platform but would like to use what’s here.

I miss my Elk M1G. This is a pretty panel with limited brains IMO.

IMO, the qolsys iqpanel 2+ is a great device.
The only issue is that they locked it down. If they had 4 additional pins on the back available then it would be a perfect device. Alternatively they can allow an open API. They currently do for control 4 and a few others in the future, which means they are willing to but are limiting it for the services I’m sure they know of. I don’t think they know about HA. If they did , I’m sure they would allow the API for HA. There is also an alarm.com integration for HA which does the integrations. That involves monitoring though.

System comparisons:
Any panel alarm system:
16x16 panel enclosure
Backup battery
External siren
External communicator
External keypads
Trained Pro’s for programming
Iq Panel 2+
All in one
Panel
Battery
Siren
Communicator
Keypad
Easy programming
All in a keypad size device

Just adding my interest on this possible integration :slight_smile:

I’m not sure that this helps at all, but these are the instructions for the Control4 setup with the IQ2 Panel. It’s one of the 3rd part integrations mentioned in the Qolsys email.

https://qolsys.reamaze.com/kb/faqs-for-iqp2/how-to-integrate-your-iq-system-with-control-4

The setup says it requires the IQ2 Panel drivers:
https://drivers.control4.com/solr/drivers/browse?&q=&fq=manufacturer:"Qolsys"

I don’t know if those drivers might give any insight on how the Control4 panel is connecting to the IQ2 Panel with the security Token to get sensor information?

Perhaps if Qolsys doesn’t want to help, someone at Control4 would be kind enough to help with understanding how their integration with the panel works?

PS when you load the ip in chrome https://panelip:8333 and inspect the page, under security there are 3 certificates. The key wouldn’t happen to be in one of those certificates would it? There is a long string under the thumprint field and a public key. Are either of those good for anything?

From the Control4 instructions it seems that all that is needed in the driver and the token from the panel, so maybe the rest is in the page certificate?

Great find! Inside that panel driver there is a encrypted lua file, I’m finding some information on how to decrypt it. Maybe the ssl keys we need are in there?

That decryption blog post looks very promising, but went quickly over my head :confused:

I wanted to be able to react to the sensors on the QolSys panel. In my use case, I want to turn on certain lights when certain doors are open.
Using @mzac’s curl command above I made a Node-Red flow to monitor the zones and call HA actions. It’s not a complex thing, but anyone searching might find it useful.

[
    {
        "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"
            ]
        ]
    },
    {
        "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": 110,
        "y": 180,
        "wires": [
            [
                "1f5ac981.6ccdde",
                "1edaf367.8f44ed"
            ],
            [
                "f17d89c8.de8bf"
            ],
            [
                "cf10d274.0242e"
            ]
        ]
    },
    {
        "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": 1110,
        "y": 120,
        "wires": []
    },
    {
        "id": "1f5ac981.6ccdde",
        "type": "debug",
        "z": "cbfaf013.0e5e",
        "name": "activity events and updates",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1140,
        "y": 32,
        "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": 1220,
        "y": 76,
        "wires": []
    },
    {
        "id": "8cb8b681.b122f8",
        "type": "status",
        "z": "cbfaf013.0e5e",
        "name": "",
        "scope": [
            "df1b46c2.dc0748"
        ],
        "x": 80,
        "y": 460,
        "wires": [
            [
                "aff08d86.5d518",
                "67092ad5.c80174"
            ]
        ]
    },
    {
        "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": 990,
        "y": 540,
        "wires": []
    },
    {
        "id": "67092ad5.c80174",
        "type": "switch",
        "z": "cbfaf013.0e5e",
        "name": "get pid",
        "property": "pid",
        "propertyType": "flow",
        "rules": [
            {
                "t": "eq",
                "v": "$text = $split($lookup(payload.status, \"text\"), \":\")\t\t$prid = $text[1]",
                "vt": "jsonata"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 200,
        "y": 520,
        "wires": [
            []
        ]
    },
    {
        "id": "1edaf367.8f44ed",
        "type": "switch",
        "z": "cbfaf013.0e5e",
        "name": "Ignore ACK",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "cont",
                "v": "ACK",
                "vt": "str"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 230,
        "y": 240,
        "wires": [
            [],
            [
                "9a07870e.d047d8"
            ]
        ]
    },
    {
        "id": "9a07870e.d047d8",
        "type": "json",
        "z": "cbfaf013.0e5e",
        "name": "convert update to json",
        "property": "payload",
        "action": "",
        "pretty": true,
        "x": 420,
        "y": 240,
        "wires": [
            [
                "9c24717d.c5031",
                "6d8421cd.eae33"
            ]
        ]
    },
    {
        "id": "9c24717d.c5031",
        "type": "debug",
        "z": "cbfaf013.0e5e",
        "name": "converted json",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1060,
        "y": 160,
        "wires": []
    },
    {
        "id": "e535c607.f893f8",
        "type": "api-call-service",
        "z": "cbfaf013.0e5e",
        "name": "",
        "server": "8ab3acad.bd0fb",
        "version": 1,
        "debugenabled": true,
        "service_domain": "",
        "service": "",
        "entityId": "",
        "data": "",
        "dataType": "json",
        "mergecontext": "",
        "output_location": "",
        "output_location_type": "none",
        "mustacheAltTags": false,
        "x": 670,
        "y": 300,
        "wires": [
            []
        ]
    },
    {
        "id": "6d8421cd.eae33",
        "type": "function",
        "z": "cbfaf013.0e5e",
        "name": "put entity_ids in msg",
        "func": "zid = msg.payload[\"zone\"][\"zone_id\"]\nzstatus = msg.payload[\"zone\"][\"status\"]\nentity_ids = []\nswitch (zid) {\n    case 1:\n        entity_ids = [\n            \"switch.s_front_door_outside\", \n            \"switch.s_front_door_overhead\",\n            \"switch.s_outside_garage_lights\"\n            ]\n        break\n    case 3:\n        entity_ids = [\n            \"switch.s_neela_s_entry_light\", \n            \"switch.s_neela_s_garage_lights\"\n            ]\n        break\n    case 4:\n        entity_ids = [\n            \"switch.s_roopesh_s_garage_lights_al\", \n            \"switch.s_roopesh_s_entry_light\"\n            ]\n        break\n}\n\n//node.warn(\"zid: \" + zid + \", status: \" + zstatus + \", entities: \" + entity_ids)\n\nmsg.entity_ids = entity_ids\nnode.warn(msg)\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 660,
        "y": 240,
        "wires": [
            [
                "929374ec.595c48",
                "ed36d599.984728"
            ]
        ]
    },
    {
        "id": "7b5ce640.877ee8",
        "type": "template",
        "z": "cbfaf013.0e5e",
        "name": "json for HA",
        "field": "payload",
        "fieldType": "msg",
        "format": "json",
        "syntax": "mustache",
        "template": "{\n    \"domain\":\"switch\",\n    \"service\":\"turn_on\",\n    \"data\":\n        {\n            \"entity_id\":\"{{payload}}\"\n        }\n}",
        "output": "json",
        "x": 450,
        "y": 320,
        "wires": [
            [
                "e535c607.f893f8",
                "aad1aaf7.b1c3e8",
                "929374ec.595c48"
            ]
        ]
    },
    {
        "id": "929374ec.595c48",
        "type": "array-loop",
        "z": "cbfaf013.0e5e",
        "name": "loop entity_ids",
        "key": "al929374ec595c48",
        "keyType": "msg",
        "reset": true,
        "resetValue": "value-null",
        "array": "entity_ids",
        "arrayType": "msg",
        "x": 220,
        "y": 300,
        "wires": [
            [
                "1a32f917.444eb7"
            ],
            [
                "7b5ce640.877ee8",
                "c2a3cc7c.d5ef1"
            ]
        ]
    },
    {
        "id": "aad1aaf7.b1c3e8",
        "type": "debug",
        "z": "cbfaf013.0e5e",
        "name": "json for HA",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1050,
        "y": 380,
        "wires": []
    },
    {
        "id": "c2a3cc7c.d5ef1",
        "type": "debug",
        "z": "cbfaf013.0e5e",
        "name": "looped payload",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1000,
        "y": 500,
        "wires": []
    },
    {
        "id": "1a32f917.444eb7",
        "type": "debug",
        "z": "cbfaf013.0e5e",
        "name": "end of loop payload",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1040,
        "y": 440,
        "wires": []
    },
    {
        "id": "ed36d599.984728",
        "type": "debug",
        "z": "cbfaf013.0e5e",
        "name": "added entity ids",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1050,
        "y": 320,
        "wires": []
    },
    {
        "id": "8ab3acad.bd0fb",
        "type": "server",
        "name": "Home Assistant",
        "legacy": false,
        "addon": true,
        "rejectUnauthorizedCerts": true,
        "ha_boolean": "y|yes|true|on|home|open",
        "connectionDelay": false,
        "cacheJson": true
    }
]
2 Likes

Does your code work by polling the panel? How fast does it react to changes? I’m curious because I have a couple motion sensors linked to the panel that I would like to trigger some lights

It’s not a poll. The service @mzac discovered above publishes an event in real time. Node-Red gets it immediately and, for me, responded in milliseconds.

I will have to build a monitor on top of the curl to make sure it restarts if it fails. Not a high priority for me yet but someday.

1 Like

That sounds perfect for what I need at the moment. I haven’t jumped down the node red rabbit hole yet. Is there anyway to implement your code without node red?

Same here, I personally do not use Node RED and cannot stand it, I prefer to edit my YAML files :wink:

I’m sure you can :smiley:. I thought about writing a python script for it, but decided to use node red for its easy HA integration. I don’t do a lot in node red but this was easier in node red than other options (IMO).

I use the node red add-on so the HA plugin is built in.

Alright, I’m going to try to make this work with your code and Node Red. I curled my panel and can see that it is outputting device status information. My panel has a combination of 13 contact and motion sensors mostly. Each device corresponds to a zone_ID 1-13.

So in your node red code I’m looking to see what I need to modify.

I’m looking at “put entity_ids in msg.” What does case 1, 2, 3 correspond to? Are you only pulling in status information about lights? I’m thinking I need to create binary sensors for all my devices rather than switches. How are your generating entities in HomeAssistant for all the devices connected to your IQ2 Panel?

I got the Node-Red flow to fetch data, but my panel seems to be flaky about keeping that web socket service running if several different systems hitting it. Alternatively, is there some throttling or blocking mechanism that kicks in when messing around over the network? Anyone else experience that and/or have a fix?

{As a note… many thanks to everyone above for finding this and getting this far with it! I’m happy to test things on my IQ2 system as I have time, so feel free to ask.}

Yes, they have iptables running on the panel that limits the connections. I’ll update this post soon with their settings so you can see what they’re doing.

I just ordered this panel so I’m following.

This is the iptables settings on the Qolsys panel. Some good clues here! You will also notice they have set rate limiting.

I found it in the firmware under:
/system/etc/init.qolsys.iptables.sh

IPTABLES="/system/bin/iptables -w"
IP6TABLES="/system/bin/ip6tables -w"

#definitions
LINKLOCAL_INF="lo"
DEF_WAN_INF="wlan0"
DEF_LAN_INF="p2p0"
DEF_ETH_INF="eth0"


# Protocol to Port Definition
HTTP="80"
HTTP_ALT="8080"
HTTP_SERV="8448"
HTTPS="443"
HTTPS_CUSTOM="8443"
DNS="53" #TCP and UDP
PHIGH="2000:65535"
NTP="123" #TCP and udp
SSDP="1900" #TCP and UDP
RIP="520" #UDP
DHCP="67:68" #TCP and UDP
ISAKMP1="500" #TCP and UDP
ISAKMP2="2500" #TCP and UDP
ESP_IP_PROTO="50" # ip protocol number for ESP
AH_IP_PROTO="51" # ip protocol number for AH
IDENT="123" #TCP only
L2TP="1701" # TCP and UDP
PPTP="1723" # TCP and UDP
OPENVPN="1194" # TCP and UDP
GRE_IP_PROTO="47" # ip protocol number for GRE
SYSLOG="514" #UDP
MQTT1="1883" #TCP and UDP
MQTT2="8883" #TCP
MQTT3="8884" #TCP
MQTT4="8081" #TCP
DTLS="4433"  #UDP
STUN_ON_DTLS="5349"  #UDP
RDP="" #Remote Desktop Protocol
ADB="5554:5557" #Android Debugging
FTP="20:21" #TCP #FTP for upgrade
SNMP="161:162" #TCP and UDP
SNMP_S="10161:10162" #TCP
SIP="5060:5070" #TCP and UDP. SIP and RTP
RTSP="554" #TCP and UDP
DTLS_1="59595"
LSOCK="21239"
SRF="9910,9920,9930,9940,9950,9990"
ZWAVE="1900"

#table shortcuts
RAW_TABLE="-t raw"
MANGLE_TABLE="-t mangle"
NAT_TABLE="-t nat"

#the below are commented out since they delete android rules
#======================================================
# Cleanup and Initialization of Tables and Chains
#$IPTABLES -F
#$IPTABLES -t mangle -F
#$IPTABLES -t nat -F
#$IPTABLES -t raw -F

#Deleting user defined chains
#$IPTABLES -X
#$IPTABLES -t mangle -X
#$IPTABLES -t nat -X
#$IPTABLES -t raw -X
#========================================================

# Policy Setup for system chains

$IPTABLES -P FORWARD ACCEPT 
$IPTABLES -P OUTPUT ACCEPT

#ADC Rules in the beginning
$IPTABLES -N ADC_CELL_OUT #this is expected to fail on second creation since not allowed to cleanup in non-boot time
$IPTABLES -I OUTPUT 1 -o rmnet+ -j ADC_CELL_OUT #putting this in top of output chain as requested 
$IP6TABLES -N ADC_CELL_OUT #this is expected to fail on second creation since not allowed to cleanup in non-boot time
$IP6TABLES -I OUTPUT 1 -o rmnet+ -j ADC_CELL_OUT #putting this in top of output chain



$IPTABLES -t mangle -P PREROUTING ACCEPT
$IPTABLES -t mangle -P INPUT ACCEPT
$IPTABLES -t mangle -P FORWARD ACCEPT
$IPTABLES -t mangle -P OUTPUT ACCEPT
$IPTABLES -t mangle -P POSTROUTING ACCEPT

$IPTABLES -t nat -P PREROUTING ACCEPT
$IPTABLES -t nat -P POSTROUTING ACCEPT
$IPTABLES -t nat -P OUTPUT ACCEPT
$IPTABLES -t nat -P INPUT ACCEPT

#Creating User-Defined Chains

$IPTABLES -N IANA_IPs
$IPTABLES -N SYN_FLOOD
$IPTABLES -N HTTP_S_IN
$IPTABLES -N HTTP_S_OUT
$IPTABLES -N ICMP_ALLOWED
$IPTABLES -N ICMP_FLOOD_DROP
$IPTABLES -N ICMP_ECHO_DROP
$IPTABLES -N UNPREV_PORTS
$IPTABLES -N UDP_FLOOD
$IPTABLES -N TCP_ATTACKS_FILTER
$IPTABLES -N TCP_ALLOWED_IN
$IPTABLES -N UDP_ALLOWED_IN
$IPTABLES -N TCP_HANDLE_IN_FILTER
$IPTABLES -N UDP_HANDLE_IN_FILTER
$IPTABLES -N ICMP_HANDLE_IN_FILTER
$IPTABLES -N MOBILE_DATA_OUT
$IPTABLES -N LAN_ZONE_INPUT
$IPTABLES -N WAN_ZONE_FORWARD
$IPTABLES -N INPUT_CONTROL
$IPTABLES -N IF_SPEC_DROP
$IPTABLES -N RMNET_OUT_FILTER

$IPTABLES -t raw -N TCP_ATTACKS_RAW
$IPTABLES -t raw -N CHARGEN_ATTACK

#Populating user Defined chains with static rules


$IPTABLES -A IANA_IPs -s 0.0.0.0 -i wlan0 -p udp --dport 67:68 -m limit --limit 25/s --limit-burst 50 -j ACCEPT #p2p0 are accepted anyway. 
$IPTABLES -A IANA_IPs -s 0.0.0.0/7 -j DROP
$IPTABLES -A IANA_IPs -s 100.64.0.0/10 -j DROP
$IPTABLES -A IANA_IPs -s 127.0.0.0/8 -j DROP
$IPTABLES -A IANA_IPs -s 169.254.0.0/16 -j DROP
$IPTABLES -A IANA_IPs -s 192.0.0.0/29 -j DROP 
$IPTABLES -A IANA_IPs -s 192.0.2.0/24 -j DROP 
$IPTABLES -A IANA_IPs -s 192.88.99.0/24 -j DROP 
$IPTABLES -A IANA_IPs -s 198.18.0.0/15 -j DROP
$IPTABLES -A IANA_IPs -s 198.51.100.0/24 -j DROP 
$IPTABLES -A IANA_IPs -s 203.0.113.0/24 -j DROP 
$IPTABLES -A IANA_IPs -s 224.0.0.0/4 -j DROP 
$IPTABLES -A IANA_IPs -s 240.0.0.0/4 -j DROP 
$IPTABLES -A IANA_IPs -s 255.255.255.255/32 -j DROP

$IPTABLES -A SYN_FLOOD -m limit --limit 4/s --limit-burst 6 -j RETURN
$IPTABLES -A SYN_FLOOD -j DROP

$IPTABLES $RAW_TABLE -A TCP_ATTACKS_RAW -p tcp --tcp-flags ALL NONE -j DROP # No Flags set
$IPTABLES $RAW_TABLE -A TCP_ATTACKS_RAW -p tcp --tcp-flags ALL ALL -j DROP # ALL flags set
$IPTABLES $RAW_TABLE -A TCP_ATTACKS_RAW -p tcp --tcp-flags FIN,SYN FIN,SYN -j DROP # both SYN and FIN set
$IPTABLES $RAW_TABLE -A TCP_ATTACKS_RAW -p tcp --tcp-flags SYN,RST SYN,RST -j DROP # both SYN and RST set
$IPTABLES $RAW_TABLE -A TCP_ATTACKS_RAW -p tcp --tcp-flags FIN,RST FIN,RST -j DROP # both RST and FIN set
$IPTABLES $RAW_TABLE -A TCP_ATTACKS_RAW -p tcp --tcp-flags FIN,ACK FIN -j DROP # FIN is set without ACK
$IPTABLES $RAW_TABLE -A TCP_ATTACKS_RAW -p tcp --tcp-flags ACK,URG URG -j DROP # URG is set without ACK
$IPTABLES $RAW_TABLE -A TCP_ATTACKS_RAW -p tcp --tcp-flags ACK,PSH PSH -j DROP # PSH is set without ACK
$IPTABLES $RAW_TABLE -A TCP_ATTACKS_RAW -p tcp --tcp-flags ALL FIN,PSH,URG -j DROP # X-Mas attack
$IPTABLES $RAW_TABLE -A TCP_ATTACKS_RAW -p tcp --tcp-flags ALL SYN,FIN,URG,PSH -j DROP # Nmap OS fingerprinting
$IPTABLES $RAW_TABLE -A TCP_ATTACKS_RAW -p tcp --tcp-flags RST RST -m limit --limit 10/s --limit-burst 20 -j RETURN # Smurf RST flood
$IPTABLES $RAW_TABLE -A TCP_ATTACKS_RAW -p tcp --tcp-flags RST RST -j DROP # Smurf RST flood                                       
$IPTABLES $RAW_TABLE -A TCP_ATTACKS_RAW -p tcp --tcp-flags URG URG -m limit --limit 10/s --limit-burst 20 -j RETURN # Smurf RST flood
$IPTABLES $RAW_TABLE -A TCP_ATTACKS_RAW -p tcp --tcp-flags URG URG -j DROP # Smurf RST flood

$IPTABLES $RAW_TABLE -A CHARGEN_ATTACK -p udp --sport 19 -j DROP # CHARGEN ATTACK

$IPTABLES -A TCP_ATTACKS_FILTER -p tcp --tcp-flags SYN,ACK SYN,ACK -m state --state NEW -j DROP # Bad SYN Request
$IPTABLES -A TCP_ATTACKS_FILTER -p tcp ! --syn -m state --state NEW -j DROP # New Connection without SYN
$IPTABLES -A TCP_ATTACKS_FILTER -p tcp -f -m state --state NEW -j DROP # fragment attack

$IPTABLES -A HTTP_S_IN -p tcp --dport $HTTPS -m limit --limit 500/s --limit-burst 750 -j ACCEPT # HTTPS Flood
$IPTABLES -A HTTP_S_IN -p tcp --dport $HTTP -m limit --limit 50/s --limit-burst 100 -j ACCEPT # HTTP Flood
$IPTABLES -A HTTP_S_IN -p tcp --dport $HTTP_ALT -m limit --limit 50/s --limit-burst 100 -j ACCEPT # HTTP Flood
$IPTABLES -A HTTP_S_IN -p tcp --syn --dport $HTTP_SERV -m connlimit --connlimit-above 15 -j DROP # Slowloris
$IPTABLES -A HTTP_S_IN -p tcp --dport $HTTP_SERV -m limit --limit 50/s --limit-burst 100 -j ACCEPT # HTTP Flood
$IPTABLES -A HTTP_S_IN -p tcp --syn --dport $MQTT2 -m connlimit --connlimit-above 15 -j DROP # Slowloris

$IPTABLES -A HTTP_S_OUT -p tcp --dport $HTTPS -j ACCEPT
$IPTABLES -A HTTP_S_OUT -p tcp --dport $HTTP -j ACCEPT
$IPTABLES -A HTTP_S_OUT -p tcp --dport $HTTP_ALT -j ACCEPT

$IPTABLES -A ICMP_ALLOWED -p icmp -m icmp --icmp-type 0 -j ACCEPT
$IPTABLES -A ICMP_ALLOWED -p icmp -m icmp --icmp-type 3 -j ACCEPT
$IPTABLES -A ICMP_ALLOWED -p icmp -m icmp --icmp-type 11 -j ACCEPT
$IPTABLES -A ICMP_ALLOWED -p icmp -m icmp --icmp-type 4 -j ACCEPT
$IPTABLES -A ICMP_ALLOWED -p icmp -m icmp --icmp-type 12 -j ACCEPT

$IPTABLES -A ICMP_FLOOD_DROP -p icmp -f -j DROP #icmp fragment attack
$IPTABLES -A ICMP_FLOOD_DROP -p icmp -m icmp --icmp-type 13 -j DROP #smurf attack protection
$IPTABLES -A ICMP_FLOOD_DROP -p icmp -m icmp --icmp-type 17 -j DROP #smurf attack protection
$IPTABLES -A ICMP_FLOOD_DROP -p icmp -m limit --limit 5/s --limit-burst 10 -j RETURN
$IPTABLES -A ICMP_FLOOD_DROP -p icmp -m limit --limit 5/s --limit-burst 10 -j LOG --log-prefix 'ICMP-FLOOD-DROP:'
#$IPTABLES -A ICMP_FLOOD_DROP -p icmp -j DROP

$IPTABLES -A ICMP_ECHO_DROP -p icmp --icmp-type 8 -j DROP #ping drop initially disabled

$IPTABLES -A UDP_FLOOD -p udp -f -j DROP # UDP fragment attack
$IPTABLES -A UDP_FLOOD -p udp -m pkttype --pkt-type broadcast -j DROP # Fraggle attack #drops lot of packets
$IPTABLES -A UDP_FLOOD -p udp -m limit --limit 50/s --limit-burst 100 -j RETURN # limiting to 50 pps
#$IPTABLES -A UDP_FLOOD -p udp -m state --state NEW -m recent --update --seconds 1 --hitcount 10 -j RETURN #DDOS prep #unsupported
$IPTABLES -A UDP_FLOOD -m limit --limit 10/s --limit-burst 15 -j LOG --log-level 4 --log-prefix 'UDP-flood attempt: '
$IPTABLES -A UDP_FLOOD -j DROP


$IPTABLES -A TCP_ALLOWED_IN -p tcp -m tcp --dport $ADB -m limit --limit 200/s --limit-burst 250 -j ACCEPT
$IPTABLES -A TCP_ALLOWED_IN -p tcp -m tcp --dport $FTP -m limit --limit 200/s --limit-burst 250 -j ACCEPT
$IPTABLES -A TCP_ALLOWED_IN -p tcp -m tcp --dport $DNS -m limit --limit 25/s --limit-burst 50 -j ACCEPT
$IPTABLES -A TCP_ALLOWED_IN -p tcp -m tcp --dport $NTP -m limit --limit 25/s --limit-burst 50 -j ACCEPT
$IPTABLES -A TCP_ALLOWED_IN -p tcp -m tcp --dport $SSDP -m limit --limit 25/s --limit-burst 50 -j ACCEPT
$IPTABLES -A TCP_ALLOWED_IN -p tcp -m tcp --dport $SIP -m limit --limit 25/s --limit-burst 50 -j ACCEPT
$IPTABLES -A TCP_ALLOWED_IN -p tcp -m tcp --dport $RTSP -m limit --limit 25/s --limit-burst 50 -j ACCEPT
$IPTABLES -A TCP_ALLOWED_IN -p tcp -m tcp --dport $MQTT1 -m limit --limit 50/s --limit-burst 100 -j ACCEPT
$IPTABLES -A TCP_ALLOWED_IN -p tcp -m tcp --dport $MQTT2 -m limit --limit 50/s --limit-burst 100 -j ACCEPT
$IPTABLES -A TCP_ALLOWED_IN -p tcp -m tcp --dport $MQTT3 -m limit --limit 50/s --limit-burst 100 -j ACCEPT
$IPTABLES -A TCP_ALLOWED_IN -p tcp -m tcp --dport $MQTT4 -m limit --limit 50/s --limit-burst 100 -j ACCEPT



$IPTABLES -A UDP_ALLOWED_IN -i lo -p udp -m multiport --ports $SRF -j ACCEPT
$IPTABLES -A UDP_ALLOWED_IN -i lo -p udp -m multiport --ports $ZWAVE -j ACCEPT
$IPTABLES -A UDP_ALLOWED_IN -i lo -p udp -m multiport --ports $LSOCK -j ACCEPT
$IPTABLES -A UDP_ALLOWED_IN -p udp -m udp --dport $DHCP -m limit --limit 25/s --limit-burst 50 -j ACCEPT
$IPTABLES -A UDP_ALLOWED_IN -p udp -m udp --dport $DNS -m limit --limit 25/s --limit-burst 50 -j ACCEPT
$IPTABLES -A UDP_ALLOWED_IN -p udp -m udp --dport $NTP -m limit --limit 25/s --limit-burst 50 -j ACCEPT
$IPTABLES -A UDP_ALLOWED_IN -p udp -m udp --dport $SSDP -m limit --limit 25/s --limit-burst 50 -j ACCEPT
$IPTABLES -A UDP_ALLOWED_IN -p udp -m udp --dport $SIP -m limit --limit 25/s --limit-burst 50 -j ACCEPT
$IPTABLES -A UDP_ALLOWED_IN -p udp -m udp --dport $RTSP -m limit --limit 25/s --limit-burst 50 -j ACCEPT
$IPTABLES -A UDP_ALLOWED_IN -p udp -m udp --dport $MQTT1 -m limit --limit 50/s --limit-burst 100 -j ACCEPT
$IPTABLES -A UDP_ALLOWED_IN -p udp -m udp --dport $DTLS -m limit --limit 50/s --limit-burst 100 -j ACCEPT
$IPTABLES -A UDP_ALLOWED_IN -p udp -m udp --dport $STUN_ON_DTLS -m limit --limit 50/s --limit-burst 100 -j ACCEPT
$IPTABLES -A UDP_ALLOWED_IN -p udp -m udp --dport $RIP -m limit --limit 25/s --limit-burst 50 -j ACCEPT
$IPTABLES -A UDP_ALLOWED_IN -p udp -m udp --dport $DTLS_1 -m limit --limit 50/s --limit-burst 100 -j ACCEPT



$IPTABLES -A UNPREV_PORTS -p tcp -m tcp --dport $PHIGH -m limit --limit 200/s --limit-burst 250 -j ACCEPT
$IPTABLES -A UNPREV_PORTS -p udp -m udp --dport $PHIGH -m limit --limit 200/s --limit-burst 250 -j ACCEPT

$IPTABLES -A TCP_HANDLE_IN_FILTER -j TCP_ATTACKS_FILTER
$IPTABLES -A TCP_HANDLE_IN_FILTER -j HTTP_S_IN
$IPTABLES -A TCP_HANDLE_IN_FILTER -j TCP_ALLOWED_IN


#$IPTABLES -A UDP_HANDLE_IN_FILTER -j UDP_FLOOD
$IPTABLES -A UDP_HANDLE_IN_FILTER -j UDP_ALLOWED_IN

$IPTABLES -A ICMP_HANDLE_IN_FILTER -j ICMP_ALLOWED

# Not allowing qolsys traffic to be initiated from mobile data interfaces
$IPTABLES -A MOBILE_DATA_OUT -d 54.211.24.81 -j REJECT #iqms2 and iqcloud 
$IPTABLES -A MOBILE_DATA_OUT -d 52.2.96.56 -j REJECT   #myserver
$IPTABLES -A MOBILE_DATA_OUT -d 52.3.208.83 -j REJECT   #haproxy for logsupload
$IPTABLES -A MOBILE_DATA_OUT -d 224.0.0.0/4 -j DROP #Block multicast on lte radio
#$IPTABLES -A MOBILE_DATA_OUT -d 8.8.8.8 -j REJECT
#$IPTABLES -A MOBILE_DATA_OUT -d 8.8.4.4 -j REJECT

$IPTABLES -A RMNET_OUT_FILTER -d 224.0.0.0/4 -j DROP # drop multicast regardless of ports
$IPTABLES -A RMNET_OUT_FILTER -d 8.8.8.8 -j REJECT # No google dns on rmnet
$IPTABLES -A RMNET_OUT_FILTER -d 8.8.4.4 -j REJECT # No google dns on rmnet
$IPTABLES -A RMNET_OUT_FILTER -o rmnet7 -j ACCEPT
$IPTABLES -A RMNET_OUT_FILTER -o rmnet1 -j ACCEPT



#Adding default rules to ZONE chains

$IPTABLES -I WAN_ZONE_FORWARD -i $DEF_WAN_INF -p tcp --syn -j SYN_FLOOD
$IPTABLES -I WAN_ZONE_FORWARD -i $DEF_ETH_INF -p tcp --syn -j SYN_FLOOD
$IPTABLES -A LAN_ZONE_INPUT -i $DEF_LAN_INF -j ACCEPT


$IPTABLES -A INPUT_CONTROL -i $LINKLOCAL_INF -j ACCEPT
$IPTABLES -A INPUT_CONTROL -j LAN_ZONE_INPUT
$IPTABLES -A INPUT_CONTROL -p tcp --syn -j SYN_FLOOD
$IPTABLES -A INPUT_CONTROL -p icmp -j ICMP_FLOOD_DROP
$IPTABLES -A INPUT_CONTROL -p icmp -j ICMP_ECHO_DROP
$IPTABLES -A INPUT_CONTROL -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
$IPTABLES -A INPUT_CONTROL -j IANA_IPs
$IPTABLES -A INPUT_CONTROL -p tcp -j TCP_HANDLE_IN_FILTER
$IPTABLES -A INPUT_CONTROL -p icmp -j ICMP_HANDLE_IN_FILTER
$IPTABLES -A INPUT_CONTROL -p udp -j UDP_HANDLE_IN_FILTER
$IPTABLES -A INPUT_CONTROL -j UNPREV_PORTS #Unprevilaged ports 2000 to 65535


$IPTABLES -A IF_SPEC_DROP -i $DEF_WAN_INF -m limit --limit 50/s --limit-burst 100 -j RETURN #not exceeding 50 limit since legit inputs form established connections
$IPTABLES -A IF_SPEC_DROP -i $DEF_WAN_INF -j DROP
$IPTABLES -A IF_SPEC_DROP -i $DEF_ETH_INF -m limit --limit 50/s --limit-burst 100 -j RETURN
$IPTABLES -A IF_SPEC_DROP -i $DEF_ETH_INF -j DROP

# Populating System chains

#FILTER Table:

#INPUT Chain:
#------------

$IPTABLES -I INPUT -j INPUT_CONTROL
$IPTABLES -A INPUT -i $LINKLOCAL_INF -j ACCEPT #just in case.
$IPTABLES -A INPUT ! -i lo -j IF_SPEC_DROP  #lo packets reaching here will be anyway accepted by default policy
#$IPTABLES -P INPUT DROP  #modifying policy only after chain addition

#Forward Chain:
#--------------
$IPTABLES -I FORWARD -j WAN_ZONE_FORWARD
#$IPTABLES -I FORWARD -i wlan0 -p tcp --syn -j SYN_FLOOD
#android chains here. masquarading happens in them.
$IPTABLES -A FORWARD -i $LINKLOCAL_INF -j ACCEPT
#UDP Flood may be handled here
#inter vlan routing policy may be handled here. Its prioritization must be done in Mangle table

#Output Chain
#-------------
#$IPTABLES -A OUTPUT -j HTTP_S_OUT
$IPTABLES -I  OUTPUT 2 -o rmnet+ -j MOBILE_DATA_OUT #matching all rmnet interfaces
$IPTABLES -I  OUTPUT 3 -o $LINKLOCAL_INF -j ACCEPT		 #putting this above everything in OUTPUT chain
#$IPTABLES -I OUTPUT -o rmnet+ -j RMNET_OUT_FILTER

#RAW Table :
#==================

#Prerouting Chain:
#-----------------
$IPTABLES $RAW_TABLE -I PREROUTING -p udp -j CHARGEN_ATTACK
$IPTABLES $RAW_TABLE -I PREROUTING -p tcp -j TCP_ATTACKS_RAW #this one is to be on top in the chain


$IP6TABLES -N MOBILE_DATA_OUT_V6
$IP6TABLES -I OUTPUT -o rmnet+ -j MOBILE_DATA_OUT_V6 #matching all rmnet interfaces
#$IP6TABLES -I MOBILE_DATA_OUT_V6 ! -o rmnet7 -d ff00::0/8 -j DROP #Block ipv6 multicast on lte radio
$IP6TABLES -A MOBILE_DATA_OUT_V6 -o rmnet+ -d ff00::0/8 -p udp --sport 5353 -j DROP # Block mdns on rmnet7 since not needed for verizon DM

$IP6TABLES -N RMNET_OUT_FILTER_V6
$IP6TABLES -A RMNET_OUT_FILTER_V6 -o rmnet+ -d ff00::0/8 -p udp --sport 5353 -j DROP
$IP6TABLES -A RMNET_OUT_FILTER_V6 -o rmnet7 -j ACCEPT
$IP6TABLES -A RMNET_OUT_FILTER_V6 -o rmnet1 -j ACCEPT
#$IP6TABLES -I OUTPUT -o rmnet+ -j RMNET_OUT_FILTER_V6 #matching all rmnet interfaces

Wait, my total lack of documentation wasn’t super clear? :laughing:

First off, I only focused on the open/close sensors. I don’t have any other sensors.

Secondly, I didn’t create a bunch of sensors in HA. Again, it went through my head (I even had a node in NR to make the sensors)… then I decided not to. As I said above, I’m not sure QolSys is long for my home. So I just have the events firing off the specific call-outs.

My use cases: When certain doors (e.g. a garage entry door) opens, I want to turn on the lights in the associated garage and the entry way in the house. I already have an HA automation that turns off the lights after 15 minutes (unless interrupted or reset… not to bog down here).

So I think you can get the same use case working (then modify from there) by

  1. changing the IP to your QolSys panel in the curl node
  2. specify what service(s) you want to call in the HA node
  3. specify which entities you want to send to HA in the “put entity_ids in msg” node

I hope this helps a bit… let me know!

Ah ok I follow what you are doing. So what you have so far is only being used as a trigger for some entities that aren’t associated with the panel, which you already have in HA.

Yeah my panel is pretty much all contact sensors and a couple motion sensors. Basically all binary sensors.

I keep feeling small burst’s of motivation to get this setup. I would like to setup binary sensors in HA for each of the devices connected to the panel and then feed the states into HA via Node Red.

I think I need to create individual binary sensors in my yamml and use Node Reds MQTT node to feed the states from the panel web socket to the sensors I created. But I’m struggling to figure out how to get that working.

Any chance you can set one sensor up this way with your configuration that I can use as a model to replicate for all my sensors?

After trying for hours to get MQTT to work in Node Red to update the status on a binary sensor I manually created in HA, someone pointed out to me that there is an integration that lets you create controllable sensors in Node Red that populate in HA :confused:

That makes things a lot easier. Now to figure out how to use that Node Red flow to update the sensors created within Node Red.

I just got my panel installed and I don’t have the option to enable 3rd party integration. Was this removed from the firmware? I’ve tried the master, installer, and dealer codes under advanced settings but don’t see the option anywhere.