livebox:hah_xap_python

no way to compare when less than two revisions

Differences

This shows you the differences between two versions of the page.


Next revision
livebox:hah_xap_python [2010/04/25 16:18] – external edit 127.0.0.1
Line 1: Line 1:
 +====== Using xAP and Python ======
  
 +{{:livebox:python-xap.jpg }} In this section we discuss how to interface with xAP devices, with a bias to the [[livebox:hah|Home Automation Hub]], using the Python programming language.  Even with very minimal skills you can modify and change the sample programs provided to create your own solutions.
 +
 +As Python run on Windows as well as Linux these programs will work on both platforms.  As well as natively on the livebox if you grab the [[development#a_native_mips_toolchain|Python for MIPS]] bundle.
 +
 +===== Simplistic samples =====
 +
 +Remember as xAP is a Broadcast protocol none of these programs need to be executed on the Livebox to interact with its hardware.  As these programs are demonstrators none of them are xAP-hub aware so only one can be ran on a computer at a time.  They demonstrate the absolute minimum amount of code that is necessary to send and receive xAP messages.
 +
 +==== xAP message snooper ====
 +
 +Listening to xAP messages on the LAN
 +<code python>
 +#!/usr/bin/env python
 +# xAP message listener - simplistic
 +
 +import socket, traceback
 +
 +host = ''
 +s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 +s.bind((host, 3639))
 +
 +while 1:
 + try:
 + message, address = s.recvfrom(8192)
 + print "%s: %s" % (address, message)
 + except (KeyboardInterrupt, SystemError):
 + raise
 + except:
 + traceback.print_exc()
 +</code>
 +
 +==== xAP relay toggle ====
 +
 +Toggling the relays on and off using python is very easy.  Each will go TICK on/off in sequence.
 +
 +<code python>
 +import socket
 +import time
 +
 +s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 +s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
 +
 +for relay in range(1,5):
 +    for state in ['on','off']:
 +        msg="""xap-header
 +{
 +v=12
 +hop=1
 +uid=FF000F00
 +class=xAPBSC.cmd
 +source=dbzoo.livebox.demo
 +target=dbzoo.livebox.controller:relay.%s
 +}
 +output.state.1
 +{
 +id=*
 +state=%s
 +}
 +"""
 +        s.sendto(msg % (relay, state), ('<broadcast>', 3639))
 +        time.sleep(1)
 +</code>
 +
 +==== xAP LCD Clock ====
 +
 +A simplistic xAP CLOCK in python.  Every minute the LCD of the livebox will be updated.
 +<code python>
 +#!/usr/bin/env python
 +# xAP - Simplistic LCD Clock
 +
 +from socket import *
 +from time import localtime, strftime, sleep
 +
 +s = socket(AF_INET, SOCK_DGRAM)
 +s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
 +while 1:
 +    msg="""xap-header
 +{
 +v=12
 +hop=1
 +uid=FF000F00
 +class=xAPBSC.cmd
 +source=dbzoo.livebox.demo
 +target=dbzoo.livebox.Controller:lcd
 +}
 +output.state.1
 +{
 +id=*
 +text=%s
 +}
 +"""
 +    t = localtime()
 +    s.sendto(msg % strftime("%d %b, %H:%M", t), ('<broadcast>', 3639))
 +    sleep(60 - t[5])
 +</code>
 +
 +===== Need an xAP HUB =====
 +
 +Without a HUB we are only able to run a single xAP program on a computer at a time, as port 3639, the default xAP port, can only be accessed by one program.
 +
 +To get around this limitation we run an xAP hub, this performs the same function a the hardware variant of the same name.  It allows multiple xAP applications to share the 3639 port which the HUB will manage for us.
 +
 +Various hub implementations can be found on the [[http://www.xapautomation.org/index.php?title=xAP_Hub|xAP Automation site]]
 +
 +Personally I'm a fan of http://patrick.lidstone.net/haweb/hub.htm
 +
 +===== xAP support Library =====
 +
 +The support library {{xaplib.zip}} provides additional code to handle integration with a HUB.
 +
 +<code python>
 +#!/usr/bin/env python
 +#
 +# XAP support library
 +
 +import socket, traceback, time
 +
 +class Xap:
 +        def __init__(self, uid, source):
 +                self.heartbeat_tick = 0;
 +                self.uid = uid
 +                self.source = source
 +                self.port = 0
 +                self.running = 1
 +
 +        def run(self, func):
 +                self.connect()
 +                while self.running:
 +                        if self.port:
 +                                self.heartbeat(60)
 +                        try:
 +                                func(self)
 +                        except KeyboardInterrupt:
 +                                self.done()
 +                                pass
 +                        except socket.timeout:
 +                                pass
 +                        except:
 +                                traceback.print_exc()
 +                                self.done()
 +
 +        def done(self):
 +                self.gout.close()
 +                self.gin.close()
 +                self.running = 0
 +
 +        def connect(self):
 +                host = ''
 +                self.gin = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 +                self.gin.settimeout(60)
 +                try:
 +                        self.gin.bind((host, 3639))
 +                except socket.error, msg:
 +                        print "Broadcast socket port 3639 in use"
 +                        print "Assuming a hub is active"
 +                        host = '127.0.0.1'
 +                        for self.port in range(3639,4639):
 +                                try:
 +                                        self.gin.bind((host, self.port))
 +                                except socket.error, msg:
 +                                        print "Socket port %s in use" % self.port
 +                                        continue
 +                                print "Discovered port %s" % self.port
 +                                break
 +
 +                self.gout = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 +                self.gout.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
 +
 +        def send(self ,msg):
 +                self.gout.sendto(msg, ('<broadcast>', 3639))
 +
 +        def sendMsg(self, clazz, target, msg):
 +                msg = """xap-header
 +{
 +v=12
 +hop=1
 +uid=%s
 +class=%s
 +source=%s
 +target=%s
 +}
 +%s""" % (self.uid, clazz, self.source, target, msg)
 +                self.send(msg)
 +
 +        def sendLCDMsg(self, msg):
 +                msg = "output.state.1\n{\nid=*\ntext=%s\n}" % msg
 +                self.sendMsg("xAPBSC.cmd","dbzoo.livebox.Controller:lcd", msg)
 +
 +        def sendSMS(self, num, msg):
 +                msg = "outbound\n{\nnum=%s\nmsg=%s\n}" % (num, msg)
 +                self.sendMsg("sms.message","dbzoo.livebox.sms", msg)
 +
 +        def receive(self):
 +                try:
 +                        return self.gin.recvfrom(8192)
 +                except KeyboardInterrupt:
 +                        self.done()
 +                        pass
 +
 +# The HUB won't relay messages to us until it see's our heartbeat knows our listening port.
 +# This must be periodically sent to keep it active on the HUB.
 +        def heartbeat(self, interval):
 +                now = time.time()
 +                if now - self.heartbeat_tick > interval or self.heartbeat_tick == 0:
 +                        self.heartbeat_tick = now
 +                        msg="""xap-hbeat
 +{
 +nv=12
 +hop=1
 +uid=%s
 +class=xap-hbeat-alive
 +source=%s
 +interval=%s
 +port=%s
 +}"""
 +                        self.send(msg % (self.uid, self.source, interval, self.port))
 +</code>
 +
 +===== HUB aware samples =====
 +
 +The support library greatly simplifies the process of writing a small xaplet.
 +
 +==== xAP message snoop ====
 +
 +A message snooper is now trivially implemented
 +<code python>
 +#!/usr/bin/env python
 +# xAP message listener - HUB aware
 +
 +from xaplib import Xap
 +
 +def snoopTraffic(xap):
 +    print "MSG: %s ADDR: %s" % xap.receive()
 +
 +Xap("FF000F00","dbzoo.livebox.Snoop").run(snoopTraffic)
 +</code>
 +
 +==== xAP LCD Clock ====
 +
 +Revisiting our simplistic clock we can rewrite the code using fewer lines and at the same time making it hub friendly.
 +
 +<code python>
 +#!/usr/bin/env python
 +# LCD Clock
 +
 +from xaplib import Xap
 +from time import localtime, strftime, sleep
 +
 +def clock(xap):
 +    t = localtime()
 +    xap.sendLCDMsg( strftime("%d %b, %H:%M", t) )
 +    sleep(60 - t[5])
 +
 +Xap("FF000F00","dbzoo.livebox.demo").run(clock)
 +</code>
 +
 +====== Demo code ======
 +Some other samples which might be useful
 +  * {{xap-sms-event.zip}} - Watch out for an RF or RELAY event when one if detected send an SMS of the change
 +  * {{xap-sms-lcd.zip}} - Watch out for inbound SMS messages when one if detected display the message on the livebox LCD
 +  * {{xap-input-smtp.zip}} - Send an email on an input event
 +
 +{{tag>livebox xap python programming}}
  • livebox/hah_xap_python.txt
  • Last modified: 2019/09/03 23:29
  • by brett