Differences

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

Link to this comparison view

livebox:hah_xap_python [2010/04/25 16:18] (current)
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}}