Notifications to PC with Snarl

From Domoticz
Jump to navigation Jump to search

Introduction

Domoticz supports notifications to mobile devices with Telegram (free), Pushover, Pushsafer and Prowl (paid apps). A good notification system to the PC desktop was missing because I do not carry the mobile phone at all times. Pushover can push messages to the desktop but this only works in a browser.

When searching for notification receivers I found Snarl which is a great notification system for events like low diskspace, IM events, played music, time, anything you can think of! Only the link from Domoticz to Snarl was missing but in a few hours the author of Snarl popped out a Python script that connects to Snarl from the Pi.

Notifications to Snarl on your desktop or laptop only work on your local network, that is, the network in where Domoticz runs.

Installation

Get the Snarl installer here: Download Snarl-3.1-setup.exe (9.9 MB).

It is also possible to get Snarl notifying on OSX or Linux.

Make sure to allow traffic to Snarl listening on port 9887 in your desktop firewall.

Then get the Python script here: Download snp-send-0.5.zip (3.8 KB) .

Copy snp-send.py to /home/pi/domoticz/scripts, you can use WinSCP for that.

On the Domoticz Pi, make sure you have Python3 installed. The command Python3 will give an error when it is not present or enter Python3 (ctrl-D to leave). This command will install Python3:

	sudo apt-get install python3

About 30MB diskspace is used totally.

Now you can fire your first test command:

	python3 snp-send.py -d 192.168.2.4 -b  'Hello, world' -i '!system-info' -n '!windowslogon' -s 
        snp-send: connecting to 192.168.2.4:9887...connected! 
        snp-send: notification sent

Change the IP address to your desktop's IP. All possible command options are explained in the Readme in the downloaded snp-send-0.5.zip. If all went well, you will see a text message Hello, world with an icon and a sound and see two messages on the Pi command line.

Now create a new bash script, or add a line to your existing Pushover notification script in /home/pi/domoticz/scripts:

	nano snarltest.sh 
        #!/bin/bash 
         
        snp-send: notification sent 
        /usr/bin/python3.2mu /home/pi/domoticz/scripts/snp-send.py -d 192.168.2.4 -b 'Hello, world!' -i '!system-yes' -n 'd:\shared\fluitje.mp3'

Change the IP address to your desktop's, and change or remove the sound option (-n). I borrowed the railway station bell sound from the NS app here :-) Save the file with ctrl+X. Then make it executable:

	chmod +x snarltest.sh

In Domoticz - Switches, edit the switch you want to make notifications. Put this in field On Action:

	script://home/pi/domoticz/scripts/snarltest.sh

If all went well, when you toggle the switch to on (or an event does this), then you will see a notification from Snarl on your desktop!

When the desktop is down or Snarl is not running, a message in the Domoticz log is written:

	Thu Feb 5 08:12:48 2015 Error: Error executing script command (home/pi/domoticz/scripts/snarltest.sh). returned: 512

Other Stuff

I snatched the Domoticz icon from the website and copied it to the Snarl default icon folder: C:\Program Files\Snarl\etc\default_theme\icons (C:\Program Files (x86)\Snarl\etc\default_theme\icons on x64 systems).

Direct link to the icon picture. Rename it to domoticz-icon.png.

	python3.2 snp-send.py -d 192.168.2.4 -b  'Door 2 open for 5 minutes' -i '!domoticz-icon' -n 'd:\shared\fluitje.mp3'


Snarl notification on desktop

Snarl notification on desktop

Python code

Here is the Python 3.2 script for the Raspberry Pi. Just copy it here or get the file here. This is snp-send.py version 0.5.


# snp-send.py -- multi-platform Snarl notification sender
# Copyright (C) 2015 full phat products

BUILD_VER = "0.5"

# Change log:
#
# 0.5 - Even more Python 3.2 friendly
#
# 0.4 - Modified socket exception handling to work better on Python 3.2
#
# 0.3 - No longer dependent on ipaddress module
#
# 0.2 - Added sticky, priority and callback options
#       Better option handling  
#       More robust packet building
#       Validates IP address using ipaddress module
#
# 0.1 - initial version


try:
    import ipaddress
except ImportError:
    pass

import socket
import getopt
import sys
#import os


class term:
   PURPLE = '\033[95m'
   CYAN = '\033[96m'
   DARKCYAN = '\033[36m'
   BLUE = '\033[94m'
   GREEN = '\033[92m'
   YELLOW = '\033[93m'
   RED = '\033[91m'
   BOLD = '\033[1m'
   UNDERLINE = '\033[4m'
   END = '\033[0m'



destport = 9887
destip = "127.0.0.1"
content = ''
priority = 0
debugmode = False

copyright = term.BOLD + "\nsnp-send " + BUILD_VER + "\n"  + term.END + \
            "Copyright (c) 2015 full phat products"

usage = "usage: snp-send " \
        "[-b body] [-c callback] [-d ip] [-i icon] [-n sound] " \
        "[-p port] [-r priority] [-s] [-t title] \n"

hint =  "" \
        "  -b    Notification body\n" \
        "  -c    Callback URL, !bang command or @signal\n" \
        "  -d    Destination IP address (default is 127.0.0.1)\n" \
        "  -i    Url, !system icon or UNC path to image file\n" \
        "  -n    Sound to play\n" \
        "  -p    Destination TCP port (default is 9887)\n" \
        "  -r    Notification priority.  Must be between -2 and +2.  Default is 0\n" \
        "  -s    Requests notification to be displayed sticky (infinite duration)\n" \
        "  -t    Notification title\n" \
        "\n" \
        "Notes\n" \
        "  - At least one of title, body or icon must be supplied.\n"

#        "  - At least one of " + term.BOLD + "title" + term.END +", " + term.BOLD + "body" + term.END + " or " + term.BOLD + "icon" + term.END + " must be supplied.\n"

if len(sys.argv) < 2:
    print('snp-send: arg missing')
    print(usage)
    sys.exit(0)

try:
    opts, args = getopt.getopt(sys.argv[1:], "b:c:d:i:n:p:r:st:?", ["body=", "debug", "sticky", "title="])
except getopt.GetoptError:
    print('snp-send: arg missing')
    print(usage)
    sys.exit(2)

for o, a in opts:
    if o == "-d":
        destip = a

    elif o == "-p":
        try:
            destport = int(a)
        except ValueError:
            print('snp-send: port must be an intger between 0 and 65535')
            sys.exit(2)

    elif o in ("-t", "--title"):
        content = content + 'title=' + a + '&'

    elif o in ("-b", "--body"):
        content = content + 'text=' + a + '&'

    elif o == "-c":
        content = content + 'callback=' + a + '&'

    elif o == "-i":
        content = content + 'icon=' + a + '&'

    elif o == "-n":
        content = content + 'sound=' + a + '&'

    elif o == "-r":
        # priority
        try:
            priority = int(a)
        except ValueError:
            print('snp-send: priority must be an intger between -2 and 2')
            sys.exit(2)

        if priority != 0:
            content = content + 'priority=' + a + '&'

    elif o in ("-s", "--sticky"):
        content = content + 'duration=0&'

    elif o == "-?":
        print(copyright)
        print(usage)
        print(hint)
        sys.exit(0)

    elif o == "--debug":
        debugmode = True

# validate args

try:
    ipaddress.ip_address(destip)
except ValueError:
    print('snp-send: invalid destination IP address')
    sys.exit(2)
except NameError:
    pass

# construct the SNP/3.0 packet
packet = 'SNP/3.0\r\nnotify?'

# add rightstr(content, len(content) -1 )
packet = packet + content[:-1] + '\r\n'

if debugmode:
    print("content:\n" + content)

# TO-DO: add headers

# add footer
packet = packet + 'END\r\n'

if debugmode:
    print("packet:\n" + packet)

socket.setdefaulttimeout(5)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

print('snp-send: connecting to ' + destip + ':' + str(destport) + '...', end = '')
sys.stdout.flush()

try:
    s.connect((destip, destport))
except socket.error as e:
    print('failed (' + str(e) + ')')
    sys.exit(2)

else:
    print('connected!')
    s.send(bytes(packet, 'UTF-8'))
    print('snp-send: notification sent')
    data = s.recv(1024)
    s.close()