diff --git a/xpl/data/uml.xmi b/xpl/data/uml.xmi
new file mode 100644
index 00000000..9aadbdee
--- /dev/null
+++ b/xpl/data/uml.xmi
@@ -0,0 +1,371 @@
+
+
+
+
+ umbrello uml modeller http://uml.sf.net
+ 1.5.8
+ UnicodeUTF8
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/xpl/phpxpl/example.php b/xpl/phpxpl/example.php
new file mode 100644
index 00000000..2b5b70d2
--- /dev/null
+++ b/xpl/phpxpl/example.php
@@ -0,0 +1,21 @@
+doEvents()) {
+ usleep(100);
+ }
+ }
+ $xplinstance->detach();
+}
+
+function signalHandler() {
+global $running;
+ $running = false;
+}
diff --git a/xpl/phpxpl/xpldevice.class.php b/xpl/phpxpl/xpldevice.class.php
new file mode 100644
index 00000000..dbce877d
--- /dev/null
+++ b/xpl/phpxpl/xpldevice.class.php
@@ -0,0 +1,66 @@
+_vendor = substr($vendor, 0, $dash);
+ $this->_device = substr($vendor, $dash+1, $dot-$dash-1);
+ $this->_instance = substr($vendor, $dot+1);
+ } else {
+ $this->_vendor = $vendor;
+ $this->_device = $device;
+
+ //Generate an instance
+ $randomNumber = mt_rand(1, 9999); //Generate ranom number between 1-9999
+ $this->_instance = sprintf('default%s', $randomNumber);
+ }
+ }
+
+ public function address() {
+ return $this->_address;
+ }
+
+ public function heartBeatInterval() {
+ return $this->_heartBeatInterval;
+ }
+
+ public function deviceName() {
+ return sprintf('%s-%s.%s', $this->_vendor, $this->_device, $this->_instance);
+ }
+
+ public function lastHeartBeat() {
+ return $this->_lastHeartBeat;
+ }
+
+ public function port() {
+ return $this->_port;
+ }
+
+ public function setAddress( $address ) {
+ $this->_address = $address;
+ }
+
+ public function setHeartBeatInterval( $minutes ) {
+ $this->_heartBeatInterval = $minutes;
+ }
+
+ public function setPort( $p ) {
+ $this->_port = $p;
+ }
+
+ public function setLastHeartBeat( $timestamp ) {
+ $this->_lastHeartBeat = $timestamp;
+ }
+}
diff --git a/xpl/phpxpl/xplinstance.class.php b/xpl/phpxpl/xplinstance.class.php
new file mode 100644
index 00000000..0937c5be
--- /dev/null
+++ b/xpl/phpxpl/xplinstance.class.php
@@ -0,0 +1,210 @@
+_ip = $ip;
+ $this->_thisDevice = new XPLDevice($vendor, $device);
+
+ $this->init();
+ }
+
+ public function attatched() {
+ return $this->_isAttatched;
+ }
+
+ public function detach() {
+ $message = new xPLMessage( xPLMessage::xplstat );
+ $message->setMessageSchemeIdentifier( 'hbeat.end' );
+ $message->setTarget('*');
+ $message->setSource( $this->_thisDevice->deviceName() );
+ $this->sendMessage( $message );
+ socket_close($this->_socket);
+ $this->_socket = null;
+ }
+
+ public function doEvents() {
+ $string = socket_read( $this->_socket, 1500);
+ if ($string != '') {
+ $message = XPLMessage::createMessageFromString($string);
+ if ($message) {
+ $this->processMessage( $message );
+ }
+ return false;
+ }
+ $this->poll();
+ return true;
+ }
+
+ public function sendMessage( $message, $device = null ) {
+ //echo "Sending heartbeat\n";
+ $msg = (string)$message;
+ socket_sendto( $this->_socket, $msg, strlen($msg), 0, '255.255.255.255', XPL_PORT );
+ }
+
+ protected function attatchedToNetwork() {
+ }
+
+ protected function handleMessage( $message ) {
+ return false;
+ }
+
+
+ private function bindToPort() {
+ $port = 49152;
+ //$port = XPL_PORT;
+ $error = 98;
+ while ($error == 98 && $port < 65535) {
+ if (@socket_bind($this->_socket, $this->_ip, $port) === false) {
+ ++$port;
+ } else {
+ echo "Bind succeded as client on: " . $port . "\n";
+ return $port;
+ }
+ }
+ return 0;
+ }
+
+ private function init() {
+ if (($this->_socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP)) === false) {
+ echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
+ return;
+ }
+
+ socket_set_option( $this->_socket, SOL_SOCKET, SO_BROADCAST, 1);
+ socket_set_nonblock( $this->_socket );
+
+ $this->_thisDevice->setPort( $this->bindToPort() );
+ if ($this->_thisDevice->port() == 0) {
+ echo "socket_bind() failed: reason: " . socket_strerror(socket_last_error($this->_socket)) . "\n";
+ return;
+ }
+
+ printf("xPL server started (bound to port %s)\n", $this->_thisDevice->port());
+ $this->sendHeartbeat();
+ }
+
+ private function poll() {
+ if (!$this->attatched()) {
+ $this->sendHeartbeat();
+ return;
+ }
+
+ if (time()-$this->_thisDevice->lastHeartBeat() >= $this->_thisDevice->heartBeatInterval() * 60) {
+ $this->sendHeartbeat();
+ }
+ //Loop all devices to see if they have timed out
+ foreach( $this->_devices as $key => $device ) {
+ if ( time()-$device->lastHeartBeat() >= $device->heartBeatInterval() * 60 * 2) {
+ printf("Device removed (timeout): %s\n", $device->deviceName());
+
+ //This is not a prefeered way of removing the item from an array
+ //Since we are iterating the loop will miss to check the new item.
+ //Here, it will be checked next time poll is called
+ unset($this->_devices[$key]);
+ continue;
+ }
+ }
+ }
+
+ private function processHeartBeat( $message ) {
+ if ($message->messageSchemeIdentifier() == 'hbeat.request') {
+ $this->sendHeartbeat();
+ return true;
+ }
+ foreach( $this->_devices as $key => $device ) {
+ if ( $device->deviceName() == $message->source() ) {
+ if (substr($message->messageSchemeIdentifier(), -3) == 'end') {
+ echo "Device removed: " . $message->source() . "\n";
+ unset($this->_devices[$key]);
+ return true;
+ } else {
+ $device->setLastHeartBeat( time() );
+ $device->setHeartBeatInterval( $message->bodyItem( 'interval' ) );
+ }
+ return true;
+ }
+ }
+ if ( (substr($message->messageSchemeIdentifier(), -3) != 'app') && (substr($message->messageSchemeIdentifier(), -5) != 'basic') ) { //Not a heartbeat
+ return false;
+ }
+
+ printf( "New device: %s\n", $message->source() );
+ $device = new xPLDevice( $message->source() );
+ $device->setHeartBeatInterval( $message->bodyItem('interval') );
+ $device->setLastHeartBeat( time() );
+ if ($message->messageSchemeIdentifier() == 'hbeat.app') {
+ $device->setAddress( $message->bodyItem('remote-ip'));
+ $device->setPort( $message->bodyItem('port') );
+ }
+ $this->_devices[] = $device;
+ return true;
+ }
+
+ private function processMessage( $message ) {
+ if (!$this->attatched() && $message->messageSchemeIdentifier() == 'hbeat.app' && $message->source() == $this->_thisDevice->deviceName()) {
+ //Our own echo
+ $this->setAttatched( true );
+ return;
+ }
+ if ($message->source() == $this->_thisDevice->deviceName()) {
+ //Ignore messages from ourselves
+ return;
+ }
+ if ( (substr($message->messageSchemeIdentifier(), 0, 5) == 'hbeat') || (substr($message->messageSchemeIdentifier(), 0, 6) == 'config') ) {
+ if ($this->processHeartBeat( $message )) {
+ return;
+ }
+ }
+ if ($message->target() != $this->_thisDevice->deviceName() && $message->target() != '*') {
+ //Message not for us
+ return;
+ }
+ if (!$this->handleMessage($message)) {
+ printf("Got message:\n%s", $message);
+ }
+ }
+
+
+ private function sendHeartbeat() {
+// echo "Sending heartbeat\n";
+ $message = new xPLMessage( xPLMessage::xplstat );
+ $message->setMessageSchemeIdentifier( 'hbeat.app' );
+ $message->setTarget('*');
+ $message->setSource( $this->_thisDevice->deviceName() );
+ $message->addBodyItem( 'interval', $this->_thisDevice->heartBeatInterval() );
+ $message->addBodyItem( 'port', $this->_thisDevice->port() );
+ $message->addBodyItem( 'remote-ip', $this->_ip );
+ $this->sendMessage( $message );
+ $this->_thisDevice->setLastHeartBeat( time() );
+ }
+
+ private function setAttatched( $attatched ) {
+ $this->_isAttatched = $attatched;
+ if ($this->_isAttatched) {
+ echo "Attatched to xPL-network\n";
+// timer->setInterval( 60000 ); //Once a minute
+
+ $message = new xPLMessage( xPLMessage::xplcmnd );
+ $message->setTarget( "*" );
+ $message->setSource( $this->_thisDevice->deviceName() );
+ $message->setMessageSchemeIdentifier( 'hbeat.request' );
+ $message->addBodyItem('command', 'request');
+ $this->sendMessage( $message );
+
+ $this->attatchedToNetwork();
+ }
+ }
+
+}
diff --git a/xpl/phpxpl/xpllighting.class.php b/xpl/phpxpl/xpllighting.class.php
new file mode 100644
index 00000000..1799afa4
--- /dev/null
+++ b/xpl/phpxpl/xpllighting.class.php
@@ -0,0 +1,172 @@
+setMessageSchemeIdentifier( 'lighting.gateway' );
+ $message->setTarget('*');
+ $message->setSource( $this->_thisDevice->deviceName() );
+ $message->addBodyItem( 'report', 'gateway-ready' );
+ $this->sendMessage( $message );
+ }
+
+ protected function handleMessage( $message ) {
+ if ($message->messageSchemeIdentifier() == 'lighting.request') {
+ return $this->lightingRequest($message);
+ }
+ return false;
+ }
+
+ private function lightingRequest( $message ) {
+ switch( $message->bodyItem('request') ) {
+ case 'gateinfo':
+ return $this->gateInfo( );
+ case 'netlist':
+ return $this->netList( );
+ case 'netinfo':
+ return $this->netInfo( $message );
+ case 'devlist':
+ return $this->devList( $message );
+ case 'devinfo':
+ return $this->devInfo( $message );
+ }
+ return false;
+ }
+
+ private function gateInfo( ) {
+ $message = new xPLMessage( xPLMessage::xplstat );
+ $message->setMessageSchemeIdentifier( 'lighting.gateinfo' );
+ $message->setTarget('*');
+ $message->setSource( $this->_thisDevice->deviceName() );
+ $message->addBodyItem( 'status', 'ok' );
+ $message->addBodyItem( 'protocol', 'TELLDUS' );
+ $message->addBodyItem( 'description', 'xPL to Telldus TellStick gateway' );
+ $message->addBodyItem( 'version', '1.0' );
+ $message->addBodyItem( 'author', 'Telldus Technologies AB' );
+ $message->addBodyItem( 'info-url', 'http://www.telldus.se' );
+ $message->addBodyItem( 'net-count', '1' );
+ $message->addBodyItem( 'preferred-net', '1' );
+ $message->addBodyItem( 'scenes-ok', 'false' );
+ $message->addBodyItem( 'channels-ok', 'false' );
+ $message->addBodyItem( 'fade-rate-ok', 'false' );
+ $this->sendMessage( $message );
+ return true;
+ }
+
+ private function netList( ) {
+ $message = new xPLMessage( xPLMessage::xplstat );
+ $message->setMessageSchemeIdentifier( 'lighting.netlist' );
+ $message->setTarget('*');
+ $message->setSource( $this->_thisDevice->deviceName() );
+ $message->addBodyItem( 'status', 'ok' );
+ $message->addBodyItem( 'network', '1' );
+ $this->sendMessage( $message );
+ return true;
+ }
+
+ private function netInfo( $message ) {
+ $msg = new xPLMessage( xPLMessage::xplstat );
+ $msg->setMessageSchemeIdentifier( 'lighting.netinfo' );
+ $msg->setTarget('*');
+ $msg->setSource( $this->_thisDevice->deviceName() );
+// print_r($message);
+ $msg->addBodyItem( 'network', $message->bodyItem('network') );
+ if ($msg->bodyItem('network') == '1') {
+ $msg->addBodyItem( 'status', 'ok' );
+ $msg->addBodyItem( 'name', exec('hostname') );
+ $msg->addBodyItem( 'device-count', tdGetNumberOfDevices() );
+ $msg->addBodyItem( 'scene-count', 0 );
+ } else {
+ $msg->addBodyItem( 'status', 'not-found' );
+ }
+ $this->sendMessage( $msg );
+ return true;
+ }
+
+ private function devList( $message ) {
+ $msg = new xPLMessage( xPLMessage::xplstat );
+ $msg->setMessageSchemeIdentifier( 'lighting.devlist' );
+ $msg->setTarget('*');
+ $msg->setSource( $this->_thisDevice->deviceName() );
+ $msg->addBodyItem( 'network', $message->bodyItem('network') );
+ if ($msg->bodyItem('network') == '1') {
+ $msg->addBodyItem( 'status', 'ok' );
+ $count = tdGetNumberOfDevices();
+ $deviceList = array();
+ for( $i = 0; $i < $count; ++$i ) {
+ $deviceList[] = tdGetDeviceId($i);
+ }
+ $msg->addBodyItem( 'device-count', $count );
+ $msg->addBodyItem( 'device', implode(',', $deviceList) );
+ } else {
+ $msg->addBodyItem( 'status', 'not-found' );
+ }
+ $this->sendMessage( $msg );
+ return true;
+ }
+
+ private function devInfo( $message ) {
+ $found = false;
+ $count = tdGetNumberOfDevices();
+ $deviceId = $message->bodyItem('device');
+ for( $i = 0; $i < $count; ++$i ) {
+ if (tdGetDeviceId($i) == $deviceId) {
+ $found = true;
+ break;
+ }
+ }
+
+ $msg = new xPLMessage( xPLMessage::xplstat );
+ $msg->setMessageSchemeIdentifier( 'lighting.devinfo' );
+ $msg->addBodyItem( 'network', $message->bodyItem('network') );
+ $msg->addBodyItem( 'device', $deviceId );
+ $msg->setTarget('*');
+ $msg->setSource( $this->_thisDevice->deviceName() );
+
+ if ($msg->bodyItem('network') == '1' && $found) {
+ $methods = tdMethods($deviceId, TELLDUS_TURNON | TELLDUS_TURNOFF | TELLDUS_DIM );
+ $msg->addBodyItem( 'status', 'ok' );
+ $msg->addBodyItem( 'name', tdGetName($deviceId) );
+ $msg->addBodyItem( 'report-on-manual', 'false' );
+ $msg->addBodyItem( 'channel-count', '1');
+ $msg->addBodyItem( 'primary-channel', '1');
+ $msg->addBodyItem( 'channel', sprintf('1,%s,0,0', ($methods & TELLDUS_DIM ? 'true' : 'false')) );
+ $msg->addBodyItem( 'scene-count', '0');
+ } else {
+ $msg->addBodyItem( 'status', 'not-found' );
+ }
+ $this->sendMessage( $msg );
+ return true;
+ }
+
+ private function scnList( $message ) {
+ $msg = new xPLMessage( xPLMessage::xplstat );
+ $msg->setMessageSchemeIdentifier( 'lighting.scnlist' );
+ $msg->setTarget('*');
+ $msg->setSource( $this->_thisDevice->deviceName() );
+ $msg->addBodyItem( 'network', $message->bodyItem('network') );
+ $msg->addBodyItem( 'status', 'not-found' );
+ $this->sendMessage( $msg );
+ return true;
+ }
+
+ private function scnInfo( $message ) {
+ $msg = new xPLMessage( xPLMessage::xplstat );
+ $msg->setMessageSchemeIdentifier( 'lighting.scninfo' );
+ $msg->addBodyItem( 'network', $message->bodyItem('network') );
+ $msg->addBodyItem( 'scene', $message->bodyItem('scene') );
+ $msg->setTarget('*');
+ $msg->setSource( $this->_thisDevice->deviceName() );
+ $msg->addBodyItem( 'status', 'not-found' );
+ $this->sendMessage( $msg );
+ return true;
+ }
+
+}
diff --git a/xpl/phpxpl/xplmessage.class.php b/xpl/phpxpl/xplmessage.class.php
new file mode 100644
index 00000000..ecfe7c83
--- /dev/null
+++ b/xpl/phpxpl/xplmessage.class.php
@@ -0,0 +1,144 @@
+_identifier = $identifier;
+ }
+
+ public function addBodyItem( $key, $value ) {
+ $this->_body[$key] = $value;
+ }
+
+ public function addHeadItem( $key, $value ) {
+ if ($key == 'hop') {
+ $hop = (int)$value;
+ } else if ($key == 'source') {
+ $this->setSource($value);
+ } else if ($key == 'target') {
+ $this->setTarget($value);
+ }
+ }
+
+ public function bodyItem( $key ) {
+ if (array_key_exists($key, $this->_body)) {
+ return $this->_body[$key];
+ }
+ return '';
+ }
+
+ public function messageSchemeIdentifier() {
+ return $this->_msi;
+ }
+
+ public function source() {
+ return $this->_source;
+ }
+
+ public function target() {
+ return $this->_target;
+ }
+
+ public function setMessageSchemeIdentifier( $messageSchemeIdentifier ) {
+ $this->_msi = $messageSchemeIdentifier;
+ }
+
+ public function setSource( $value ) {
+ $this->_source = $value;
+ }
+
+ public function setTarget( $value ) {
+ $this->_target = $value;
+ }
+
+ public function __tostring() {
+ $message = '';
+
+ switch( $this->_identifier ) {
+ case self::xplcmnd:
+ $message .= "xpl-cmnd\n";
+ break;
+ case self::xplstat:
+ $message .= "xpl-stat\n";
+ break;
+ case self::xpltrig:
+ $message .= "xpl-trig\n";
+ break;
+ default:
+ return "";
+ }
+
+ $message .= "{\n";
+ $message .= sprintf("hop=%s\n", $this->_hop);
+ $message .= sprintf("source=%s\n", $this->_source);
+ $message .= sprintf("target=%s\n", $this->_target);
+ $message .= "}\n";
+ $message .= $this->_msi . "\n";
+ $message .= "{\n";
+ foreach( $this->_body as $key => $value ) {
+ $message .= sprintf("%s=%s\n", $key, $value);
+ }
+ $message .= "}\n";
+
+ return $message;
+ }
+
+ public static function createMessageFromString( $message ) {
+ $lines = explode("\n", $message);
+ $row = 0;
+
+ $i = 0;
+
+ if ($lines[$row] == 'xpl-cmnd') {
+ $i = self::xplcmnd;
+ } else if ($lines[$row] == 'xpl-stat') {
+ $i = self::xplstat;
+ } else if ($lines[$row] == 'xpl-trig') {
+ $i = self::xpltrig;
+ } else {
+ return 0;
+ }
+ ++$row;
+
+ if ($lines[$row] != '{') {
+ return 0;
+ }
+ ++$row;
+
+ $msg = new xPLMessage($i);
+ for(; $row < count($lines) && $lines[$row] != '}'; ++$row) {
+ list($name, $value) = explode( '=', $lines[$row] );
+ $msg->addHeadItem( $name, $value );
+ }
+ if ($row >= count($lines)) {
+ return 0;
+ }
+ ++$row;
+
+ $msg->setMessageSchemeIdentifier( $lines[$row] );
+ ++$row;
+
+ if ($lines[$row] != '{') {
+ return 0;
+ }
+ ++$row;
+
+ for(; $row < count($lines) && $lines[$row] != '}'; ++$row) {
+ list($name, $value) = explode( '=', $lines[$row] );
+ $msg->addBodyItem( $name, $value );
+ }
+
+ return $msg;
+ }
+}
diff --git a/xpl/pyxpl/__init__.py b/xpl/pyxpl/__init__.py
new file mode 100644
index 00000000..35b42be0
--- /dev/null
+++ b/xpl/pyxpl/__init__.py
@@ -0,0 +1 @@
+__all__ = ["xplmessage", "xpldevice", "xplinstance"]
\ No newline at end of file
diff --git a/xpl/pyxpl/xpldevice.py b/xpl/pyxpl/xpldevice.py
new file mode 100644
index 00000000..fe5cd016
--- /dev/null
+++ b/xpl/pyxpl/xpldevice.py
@@ -0,0 +1,22 @@
+###########################################################################
+# Copyright (C) 2009 by Magnus Ahlberg
+#
+#
+# Copyright: See COPYING file that comes with this distribution
+#
+###########################################################################
+
+class XPLDevice:
+ """A class to manage and xPL device"""
+ vendor = ""
+ device = ""
+ instance = ""
+
+ def __init__(devicename):
+ pass
+
+ def __init__(vendor, device):
+ pass
+
+ def __init__(vendor, device, instance):
+ pass
diff --git a/xpl/pyxpl/xplinstance.py b/xpl/pyxpl/xplinstance.py
new file mode 100644
index 00000000..9c0354d9
--- /dev/null
+++ b/xpl/pyxpl/xplinstance.py
@@ -0,0 +1,47 @@
+###########################################################################
+# Copyright (C) 2009 by Magnus Ahlberg
+#
+#
+# Copyright: See COPYING file that comes with this distribution
+#
+###########################################################################
+
+from xpldevice import *
+
+from socket import *
+
+class XPLInstance:
+ """Class to handle an xPL session"""
+ MODE_UNDEFINED, MODE_CLIENT, MODE_DISCONNECTED, MODE_HUB = range(4)
+
+ __thisDevice = XPLDevice()
+ __devices = ()
+ __mode = MODE_UNDEFINED
+
+ def __init__(self, device):
+ self.__thisDevice = device
+
+ def __init__(self, vendor, deviceName):
+ device = XPLDevice(vendor, deviceName)
+ self.__init__(device)
+
+ def attached(self):
+ pass
+
+ def devices(self):
+ return devices
+
+ def operationMode(self):
+ return operationMode
+
+ def sendMessage(self, message):
+ pass
+
+ def sendMessage(self, message, device):
+ pass
+
+ def shutdown(self):
+ pass
+
+ def bindToPort(self):
+ pass
diff --git a/xpl/pyxpl/xplmessage.py b/xpl/pyxpl/xplmessage.py
new file mode 100644
index 00000000..28f96dd5
--- /dev/null
+++ b/xpl/pyxpl/xplmessage.py
@@ -0,0 +1,56 @@
+###########################################################################
+# Copyright (C) 2009 by Magnus Ahlberg
+#
+#
+# Copyright: See COPYING file that comes with this distribution
+#
+###########################################################################
+
+class XPLMessage:
+ """A class for managing xPL messages"""
+ ID_UNDEFINED, ID_XPLCMND, ID_XPLSTAT, ID_XPLTRIG = range(4)
+ __body = {}
+ __hop = 0
+ __source = ""
+ __target = ""
+ __identifier = ID_UNDEFINED
+ __identifierString = ("-", "xpl-cmnd", "xpl-stat", "xpl-trig")
+ __messageSchemaIdentifier = ""
+
+ def __init__(self, identifier, messageSchemaIdentifier):
+ self.__identifier = identifier
+ self.__messageSchemaIdentifier = messageSchemaIdentifier
+
+ def addBodyItem(self, key, value):
+ self.__body[key] = value
+
+ def bodyItem(self, key):
+ return self.__body[key]
+
+ def createMessageFromString(message):
+ pass
+
+ def setSource(self, value):
+ self.__source = value
+
+ def setTarget(self, value):
+ self.__target = value
+
+ def toString(self):
+ if self.__identifier == self.ID_UNDEFINED:
+ return ""
+
+ messageStringList = []
+ messageStringList.append(self.__identifierString[self.__identifier] + "\n")
+ messageStringList.append("{\n")
+ messageStringList.append("hop=" + str(self.__hop) + "\n")
+ messageStringList.append("source=" + self.__source + "\n")
+ messageStringList.append("target=" + self.__target + "\n")
+ messageStringList.append("}\n")
+ messageStringList.append(self.__messageSchemaIdentifier + "\n")
+ messageStringList.append("{\n")
+ for key, value in self.__body.iteritems():
+ messageStringList.append(key + "=" + value + "\n")
+ messageStringList.append("}\n")
+
+ return "".join(messageStringList)
\ No newline at end of file
diff --git a/xpl/qtxpl/CMakeLists.txt b/xpl/qtxpl/CMakeLists.txt
new file mode 100644
index 00000000..480e3796
--- /dev/null
+++ b/xpl/qtxpl/CMakeLists.txt
@@ -0,0 +1,43 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+PROJECT(qtxpl)
+
+FIND_PACKAGE( Qt4 REQUIRED )
+SET(QT_USE_QTNETWORK TRUE)
+INCLUDE( ${QT_USE_FILE} )
+
+SET( QTXPL_SRCS
+ xpldevice.cpp
+ xplinstance.cpp
+ xplmessage.cpp
+)
+
+SET( QTXPL_HDRS
+ xpldevice.h
+)
+
+SET( QTXPL_MOC_HDRS
+ xplinstance.h
+ xplmessage.h
+)
+
+
+QT4_WRAP_CPP( QTXPL_MOC_SRCS ${QTXPL_MOC_HDRS} )
+QT4_AUTOMOC ( ${QTXPL_SRCS} )
+
+
+SET( QTXPL_LIBRARIES
+ ${QT_LIBRARIES}
+)
+
+ADD_DEFINITIONS(${QT_DEFINITIONS})
+ADD_DEFINITIONS(-DQT_NO_DEBUG)
+
+ADD_LIBRARY("qtxpl" SHARED
+ ${QTXPL_SRCS}
+ ${QTXPL_HDRS}
+ ${QTXPL_MOC_HDRS}
+ ${QTXPL_MOC_SRCS}
+)
+
+TARGET_LINK_LIBRARIES( "qtxpl" ${QTXPL_LIBRARIES} )
+
diff --git a/xpl/qtxpl/main.cpp b/xpl/qtxpl/main.cpp
new file mode 100644
index 00000000..d50652dd
--- /dev/null
+++ b/xpl/qtxpl/main.cpp
@@ -0,0 +1,12 @@
+#include
+
+#include "xplinstance.h"
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+
+ /*xPLInstance *instance = */new xPLInstance("telldus", "center");
+
+ return a.exec();
+}
diff --git a/xpl/qtxpl/qtxpl.pro b/xpl/qtxpl/qtxpl.pro
new file mode 100644
index 00000000..89bb1f4e
--- /dev/null
+++ b/xpl/qtxpl/qtxpl.pro
@@ -0,0 +1,15 @@
+# -------------------------------------------------
+# Project created by QtCreator 2009-03-05T16:12:24
+# -------------------------------------------------
+QT += network
+QT -= gui
+TARGET = qtxpl
+CONFIG += console
+CONFIG -= app_bundle
+TEMPLATE = lib
+SOURCES += xpldevice.cpp \
+ xplinstance.cpp \
+ xplmessage.cpp
+HEADERS += xpldevice.h \
+ xplinstance.h \
+ xplmessage.h
diff --git a/xpl/qtxpl/xpldevice.cpp b/xpl/qtxpl/xpldevice.cpp
new file mode 100644
index 00000000..04cee0da
--- /dev/null
+++ b/xpl/qtxpl/xpldevice.cpp
@@ -0,0 +1,105 @@
+#include "xpldevice.h"
+
+#include
+#include
+#include
+
+const int DEFAULT_HBEAT_INTERVAL = 5;
+
+class xPLDevicePrivate {
+public:
+ QString vendor, device, instance;
+ QDateTime lastHeartBeat;
+ int port, heartBeatInterval;
+ QHostAddress address;
+};
+
+xPLDevice::xPLDevice( const xPLDevice &other )
+ : d( new xPLDevicePrivate( *other.d ))
+{
+ //do nothing
+}
+
+xPLDevice& xPLDevice::operator=( xPLDevice other ) {
+ std::swap( this->d, other.d );
+ return *this;
+}
+
+xPLDevice::xPLDevice( const QString &deviceName )
+ :d(new xPLDevicePrivate)
+{
+ d->port = 0;
+ d->heartBeatInterval = DEFAULT_HBEAT_INTERVAL;
+ QString name = deviceName.section('.', 0, 0);
+ d->vendor = name.section('-', 0, 0);
+ d->device = name.section('-', 1, 1);
+ d->instance = deviceName.section('.', 1);
+}
+
+xPLDevice::xPLDevice( const QString &vendor, const QString &device )
+ :d(new xPLDevicePrivate)
+{
+ d->vendor = vendor;
+ d->device = device;
+ d->instance = "";
+ d->port = 0;
+ d->heartBeatInterval = DEFAULT_HBEAT_INTERVAL;
+
+ //Seed the random number generator
+ srand( (unsigned int)time( NULL ) );
+
+ //Generate an instance
+ int randomNumber = rand() % 9999 + 1; //Generate ranom number between 1-9999
+ d->instance = QString("default%1").arg(randomNumber);
+}
+
+xPLDevice::xPLDevice( const QString &vendor, const QString &device, const QString &instance )
+ :d(new xPLDevicePrivate)
+{
+ d->vendor = vendor;
+ d->device = device;
+ d->instance = instance;
+ d->port = 0;
+ d->heartBeatInterval = DEFAULT_HBEAT_INTERVAL;
+
+}
+
+xPLDevice::~xPLDevice() {
+ delete d;
+}
+
+QHostAddress xPLDevice::address() const {
+ return d->address;
+}
+
+int xPLDevice::heartBeatInterval() const {
+ return d->heartBeatInterval;
+}
+
+QString xPLDevice::deviceName() const {
+ return QString("%1-%2.%3").arg(d->vendor).arg(d->device).arg(d->instance);
+}
+
+QDateTime xPLDevice::lastHeartBeat() const {
+ return d->lastHeartBeat;
+}
+
+int xPLDevice::port() const {
+ return d->port;
+}
+
+void xPLDevice::setAddress( const QHostAddress &address ) {
+ d->address = address;
+}
+
+void xPLDevice::setHeartBeatInterval( int minutes ) {
+ d->heartBeatInterval = minutes;
+}
+
+void xPLDevice::setPort( int p ) {
+ d->port = p;
+}
+
+void xPLDevice::setLastHeartBeat( const QDateTime ×tamp ) {
+ d->lastHeartBeat = timestamp;
+}
diff --git a/xpl/qtxpl/xpldevice.h b/xpl/qtxpl/xpldevice.h
new file mode 100644
index 00000000..3c1d2bae
--- /dev/null
+++ b/xpl/qtxpl/xpldevice.h
@@ -0,0 +1,38 @@
+#ifndef XPLDEVICE_H
+#define XPLDEVICE_H
+
+#include "xplmessage.h"
+#include
+#include
+
+class xPLDevicePrivate;
+
+class QTXPL_EXPORT xPLDevice
+{
+public:
+ xPLDevice( const xPLDevice & ); //Copy constructor
+ xPLDevice& operator=( xPLDevice ); //Copy assignment operator
+
+ xPLDevice( const QString &deviceName );
+ xPLDevice( const QString &vendor, const QString &device );
+ xPLDevice( const QString &vendor, const QString &device, const QString &instance );
+
+ ~xPLDevice();
+
+ QString deviceName() const;
+
+ QDateTime lastHeartBeat() const;
+
+ QHostAddress address() const;
+ int heartBeatInterval() const;
+ int port() const;
+ void setAddress( const QHostAddress &address );
+ void setHeartBeatInterval( int minutes );
+ void setPort( int port );
+ void setLastHeartBeat( const QDateTime ×tamp );
+
+private:
+ xPLDevicePrivate *d;
+};
+
+#endif // XPLDEVICE_H
diff --git a/xpl/qtxpl/xplinstance.cpp b/xpl/qtxpl/xplinstance.cpp
new file mode 100644
index 00000000..5d2db3a9
--- /dev/null
+++ b/xpl/qtxpl/xplinstance.cpp
@@ -0,0 +1,226 @@
+#include "xplinstance.h"
+#include "xplmessage.h"
+
+#include
+#include
+#include
+
+#include
+#include
+
+class xPLInstancePrivate {
+public:
+ xPLDevice *thisDevice;
+ QLinkedList devices;
+ QUdpSocket *socket;
+ xPLInstance::OperationMode mode;
+ QHostAddress address;
+ bool isAttatched;
+ QTimer *timer;
+};
+
+xPLInstance::xPLInstance( const xPLDevice &device, QObject *parent )
+ :QObject(parent)
+{
+ d = new xPLInstancePrivate;
+ d->thisDevice = new xPLDevice(device);
+ d->mode = Disconnected;
+ d->timer = new QTimer(this);
+ this->init();
+}
+
+xPLInstance::xPLInstance( const QString &vendor, const QString &device, QObject *parent )
+ :QObject(parent)
+{
+ d = new xPLInstancePrivate;
+ d->thisDevice = new xPLDevice(vendor, device);
+ d->mode = Disconnected;
+ d->timer = new QTimer(this);
+ this->init();
+}
+
+xPLInstance::~xPLInstance() {
+ delete d->thisDevice;
+ delete d;
+}
+
+void xPLInstance::init() {
+ foreach(QHostAddress address, QNetworkInterface::allAddresses()) {
+ if (address.protocol() == QAbstractSocket::IPv4Protocol && address != QHostAddress::LocalHost) {
+ d->address = address;
+ break;
+ }
+ }
+ d->isAttatched = false;
+ d->socket = new QUdpSocket(this);
+ d->thisDevice->setPort(this->bindToPort());
+ if (d->thisDevice->port() == 0) {
+ return;
+ }
+ d->timer->setInterval( 3000 );
+ connect(d->socket, SIGNAL(readyRead()), this, SLOT(processPendingDatagrams()));
+ connect(d->timer, SIGNAL(timeout()), this, SLOT(poll()));
+ this->sendHeartbeat();
+ d->timer->start();
+}
+
+bool xPLInstance::attatched() const {
+ return this->d->isAttatched;
+}
+
+int xPLInstance::bindToPort() {
+ //Hub-mode not supported
+ /*if (d->socket->bind( XPL_PORT )) {
+ d->mode = Hub;
+ qDebug("Bind succeded as hub");
+ return XPL_PORT;
+ }*/
+ for (int port = 49152; port <= 65535; ++port) {
+ if (d->socket->bind( port, QUdpSocket::DontShareAddress )) {
+ d->mode = Client;
+ qDebug() << "Bind succeded as client on" << port;
+ return port;
+ }
+ }
+ qDebug("Bind failed");
+ return 0;
+}
+
+xPLInstance::OperationMode xPLInstance::operationMode() const {
+ return d->mode;
+}
+
+void xPLInstance::processPendingDatagrams() {
+ while (d->socket->hasPendingDatagrams()) {
+ QByteArray datagram;
+ datagram.resize(d->socket->pendingDatagramSize());
+ d->socket->readDatagram(datagram.data(), datagram.size());
+ xPLMessage *message = xPLMessage::createMessageFromString( datagram.data() );
+ if (message) {
+ processMessage( *message );
+ delete message;
+ }
+ }
+}
+
+void xPLInstance::poll() {
+ if (!attatched()) {
+ sendHeartbeat();
+ return;
+ }
+ if (d->thisDevice->lastHeartBeat().secsTo( QDateTime::currentDateTime() ) >= d->thisDevice->heartBeatInterval() * 60) {
+ sendHeartbeat();
+ }
+ //Loop all devices to see if they have timed out
+ for( QLinkedList::iterator it = d->devices.begin(); it != d->devices.end(); ) {
+ if ((*it).lastHeartBeat().secsTo( QDateTime::currentDateTime() ) >= (*it).heartBeatInterval() * 60 * 2) {
+ qDebug() << "Device removed (timeout):" << (*it).deviceName();
+ it = d->devices.erase( it );
+ continue;
+ }
+ ++it;
+ }
+}
+
+void xPLInstance::sendMessage( const xPLMessage &msg ) const {
+ xPLMessage message(msg); //Copy so we don't have const
+ message.setSource(d->thisDevice->deviceName());
+ QByteArray datagram = message.toString().toAscii();
+ d->socket->writeDatagram(datagram.data(), datagram.size(), QHostAddress::Broadcast, XPL_PORT);
+}
+
+void xPLInstance::sendMessage( xPLMessage * message ) const {
+ this->sendMessage(*message);
+}
+
+void xPLInstance::sendMessage( const xPLMessage &message, const xPLDevice &device ) const {
+ QByteArray datagram = message.toString().toAscii();
+ d->socket->writeDatagram(datagram.data(), datagram.size(), device.address(), device.port());
+}
+
+void xPLInstance::processHeartBeat( const xPLMessage &message ) {
+ if (message.messageSchemeIdentifier() == "hbeat.request") {
+ sendHeartbeat();
+ return;
+ }
+ for( QLinkedList::iterator it = d->devices.begin(); it != d->devices.end(); ++it ) {
+ if ( (*it).deviceName() == message.source() ) {
+ if (message.messageSchemeIdentifier() == "hbeat.end") {
+ qDebug() << "Device removed:" << message.source();
+ d->devices.erase( it );
+ return;
+ } else {
+ (*it).setLastHeartBeat( QDateTime::currentDateTime() );
+ (*it).setHeartBeatInterval( message.bodyItem( "interval" ).toInt() );
+ }
+ return;
+ }
+ }
+ if (!message.messageSchemeIdentifier().endsWith("app") && !message.messageSchemeIdentifier().endsWith("basic")) {
+ return;
+ }
+ qDebug() << "New device:" << message.source();
+ xPLDevice device( message.source() );
+ device.setHeartBeatInterval( message.bodyItem("interval").toInt() );
+ device.setLastHeartBeat( QDateTime::currentDateTime() );
+ if (message.messageSchemeIdentifier() == "hbeat.app") {
+ device.setAddress( QHostAddress(message.bodyItem("remote-ip")) );
+ device.setPort( message.bodyItem("port").toInt() );
+ }
+ d->devices.append( device );
+}
+
+void xPLInstance::processMessage( const xPLMessage &message ) {
+ if (!attatched() && message.messageSchemeIdentifier() == "hbeat.app" && message.source() == d->thisDevice->deviceName()) {
+ //Our own echo
+ setAttatched( true );
+ return;
+ }
+ if (message.source() == d->thisDevice->deviceName()) {
+ //Ignore messages from ourselves
+ return;
+ }
+ if (message.messageSchemeIdentifier().startsWith("hbeat") || message.messageSchemeIdentifier().startsWith("config")) {
+ processHeartBeat( message );
+ }
+ if (d->mode == xPLInstance::Hub) {
+ //Reply the message to all clients
+ for( QLinkedList::iterator it = d->devices.begin(); it != d->devices.end(); ++it ) {
+ sendMessage(message, *it);
+ }
+ }
+ if (message.target() != d->thisDevice->deviceName() && message.target() != "*") {
+ //Message not for us
+ return;
+ }
+ xPLMessage newMsg(message);
+ emit messageReceived(&newMsg);
+}
+
+void xPLInstance::sendHeartbeat() {
+ xPLMessage message( xPLMessage::xplstat );
+ message.setMessageSchemeIdentifier( "hbeat.app" );
+ message.setTarget("*");
+ message.setSource( d->thisDevice->deviceName() );
+ message.addBodyItem( "interval", QString::number(d->thisDevice->heartBeatInterval()) );
+ message.addBodyItem( "port", QString::number(d->thisDevice->port()) );
+ message.addBodyItem( "remote-ip", d->address.toString() );
+ sendMessage( message );
+ d->thisDevice->setLastHeartBeat( QDateTime::currentDateTime() );
+}
+
+void xPLInstance::setAttatched( bool attatched ) {
+ d->isAttatched = attatched;
+ if (d->isAttatched) {
+ qDebug() << "Attached to network!";
+ d->timer->setInterval( 60000 ); //Once a minute
+
+ xPLMessage message( xPLMessage::xplcmnd );
+ message.setTarget( "*" );
+ message.setSource( d->thisDevice->deviceName() );
+ message.setMessageSchemeIdentifier( "hbeat.request" );
+ message.addBodyItem("command", "request");
+ sendMessage( message );
+ emit attachedToNetwork();
+ }
+}
diff --git a/xpl/qtxpl/xplinstance.h b/xpl/qtxpl/xplinstance.h
new file mode 100644
index 00000000..61ee990a
--- /dev/null
+++ b/xpl/qtxpl/xplinstance.h
@@ -0,0 +1,55 @@
+#ifndef XPLINSTANCE_H
+#define XPLINSTANCE_H
+
+#include
+#include "xpldevice.h"
+#include "xplmessage.h"
+
+const int XPL_PORT = 3865;
+
+class QTimer;
+class xPLInstancePrivate;
+
+class QTXPL_EXPORT xPLInstance : public QObject
+{
+ Q_OBJECT
+
+ Q_ENUMS( OperationMode );
+
+ Q_PROPERTY( OperationMode operationMode READ operationMode );
+public:
+ enum OperationMode { Client, Disconnected, Hub };
+
+ xPLInstance( const xPLDevice &device, QObject *parent = 0 );
+ xPLInstance( const QString &vendor, const QString &device, QObject *parent = 0 );
+ ~xPLInstance();
+
+public slots:
+ bool attatched() const;
+
+ OperationMode operationMode() const;
+
+ void sendMessage( const xPLMessage &message ) const;
+ void sendMessage( xPLMessage * message ) const;
+ void sendMessage( const xPLMessage &message, const xPLDevice &device ) const;
+
+signals:
+ void messageReceived(xPLMessage *message);
+ void attachedToNetwork();
+
+private slots:
+ void processPendingDatagrams();
+ void sendHeartbeat();
+ void poll();
+
+private:
+ void processHeartBeat( const xPLMessage &message );
+ void processMessage( const xPLMessage &message );
+ void init();
+ int bindToPort();
+ void setAttatched( bool attatched );
+
+ xPLInstancePrivate *d;
+};
+
+#endif // XPLINSTANCE_H
diff --git a/xpl/qtxpl/xplmessage.cpp b/xpl/qtxpl/xplmessage.cpp
new file mode 100644
index 00000000..ced743cf
--- /dev/null
+++ b/xpl/qtxpl/xplmessage.cpp
@@ -0,0 +1,188 @@
+#include "xplmessage.h"
+
+#include
+#include
+#include
+
+class xPLMessage::xPLMessagePrivate {
+public:
+ xPLMessage::Identifier identifier;
+ int hop;
+ QString source, target, msi;
+ QMap headExtra, body;
+};
+
+xPLMessage::xPLMessage( const xPLMessage &other )
+ : QObject(),
+ d( new xPLMessagePrivate( *other.d ))
+{
+ //do nothing
+}
+
+xPLMessage& xPLMessage::operator=( xPLMessage other ) {
+ std::swap( this->d, other.d );
+ return *this;
+}
+
+
+xPLMessage::xPLMessage(Identifier i)
+ :QObject(),
+ d(new xPLMessagePrivate)
+{
+ d->hop = 1;
+ d->identifier = i;
+ d->target = "*";
+}
+
+xPLMessage::xPLMessage()
+ :QObject(),
+ d(new xPLMessagePrivate)
+{
+
+ d->hop = 1;
+ d->identifier = xplcmnd;
+ d->target = "*";
+}
+
+xPLMessage::~xPLMessage() {
+ delete d;
+}
+
+
+void xPLMessage::addBodyItem( const QString &key, const QString &value ) {
+ d->body[key] = value;
+}
+
+void xPLMessage::addHeadItem( const QString &key, const QString &value ) {
+ if (key == "hop") {
+ d->hop = value.toInt();
+ } else if (key == "source") {
+ setSource(value);
+ } else if (key == "target") {
+ setTarget(value);
+ } else {
+ d->headExtra[key] = value;
+ }
+}
+
+QString xPLMessage::bodyItem( const QString &key ) const {
+ if (d->body.contains(key)) {
+ return d->body[key];
+ }
+ return "";
+}
+
+QString xPLMessage::headItem( const QString &key ) const {
+ if (d->headExtra.contains(key)) {
+ return d->headExtra[key];
+ }
+ return "";
+}
+
+QString xPLMessage::messageSchemeIdentifier() const {
+ return d->msi;
+}
+
+QString xPLMessage::source() const {
+ return d->source;
+}
+
+QString xPLMessage::target() const {
+ return d->target;
+}
+
+void xPLMessage::setMessageSchemeIdentifier( const QString &messageSchemeIdentifier ) {
+ d->msi = messageSchemeIdentifier;
+}
+
+void xPLMessage::setSource( const QString &value ) {
+ d->source = value;
+}
+
+void xPLMessage::setTarget( const QString &value ) {
+ d->target = value;
+}
+
+QString xPLMessage::toString() const {
+ QString message;
+
+ switch( d->identifier ) {
+ case xplcmnd:
+ message += "xpl-cmnd\n";
+ break;
+ case xplstat:
+ message += "xpl-stat\n";
+ break;
+ case xpltrig:
+ message += "xpl-trig\n";
+ break;
+ default:
+ return "";
+ }
+
+ message += "{\n";
+ message += QString("hop=%1\n").arg(d->hop);
+ message += QString("source=%1\n").arg(d->source);
+ message += QString("target=%1\n").arg(d->target);
+ for( QMap::const_iterator it = d->headExtra.begin(); it != d->headExtra.end(); ++it ) {
+ message += QString("%1=%2\n").arg(it.key()).arg(it.value());
+ }
+ message += "}\n";
+ message += d->msi + "\n";
+ message += "{\n";
+ for( QMap::const_iterator it = d->body.begin(); it != d->body.end(); ++it ) {
+ message += QString("%1=%2\n").arg(it.key()).arg(it.value());
+ }
+ message += "}\n";
+
+ return message;
+}
+
+xPLMessage *xPLMessage::createMessageFromString( const QString &message ) {
+ QStringList lines = message.split('\n', QString::SkipEmptyParts);
+ QStringList::const_iterator it = lines.begin();
+
+ Identifier i;
+
+ if (*it == "xpl-cmnd") {
+ i = xplcmnd;
+ } else if (*it == "xpl-stat") {
+ i = xplstat;
+ } else if (*it == "xpl-trig") {
+ i = xpltrig;
+ } else {
+ return 0;
+ }
+ ++it;
+
+ if (*it != "{") {
+ return 0;
+ }
+ ++it;
+
+ xPLMessage *msg = new xPLMessage(i);
+ for(; it != lines.end() && *it != "}"; ++it) {
+ msg->addHeadItem( (*it).section('=', 0, 0), (*it).section('=', 1) );
+ }
+ if (it == lines.end()) {
+ delete msg;
+ return 0;
+ }
+ ++it;
+
+ msg->setMessageSchemeIdentifier( *it );
+ ++it;
+
+ if (*it != "{") {
+ delete msg;
+ return 0;
+ }
+ ++it;
+
+ for(; it != lines.end() && *it != "}"; ++it) {
+ msg->addBodyItem( (*it).section('=', 0, 0), (*it).section('=', 1) );
+ }
+
+ return msg;
+}
+
diff --git a/xpl/qtxpl/xplmessage.h b/xpl/qtxpl/xplmessage.h
new file mode 100644
index 00000000..916d3b0a
--- /dev/null
+++ b/xpl/qtxpl/xplmessage.h
@@ -0,0 +1,57 @@
+#ifndef XPLMESSAGE_H
+#define XPLMESSAGE_H
+
+#ifdef _WINDOWS
+ #ifdef qtxpl_EXPORTS
+ #define QTXPL_EXPORT __declspec(dllexport)
+ #else
+ #define QTXPL_EXPORT __declspec(dllimport)
+ #endif
+#else
+ #define QTXPL_EXPORT
+#endif
+
+#include
+#include
+
+class QTXPL_EXPORT xPLMessage : public QObject
+{
+ Q_OBJECT
+
+ Q_ENUMS( Identifier );
+public:
+ enum Identifier { xplcmnd, xplstat, xpltrig };
+
+ xPLMessage( const xPLMessage & ); //Copy constructor
+ xPLMessage& operator=( xPLMessage ); //Copy assignment operator
+
+ xPLMessage(Identifier identifier);
+ xPLMessage();
+ virtual ~xPLMessage();
+
+ static xPLMessage *createMessageFromString( const QString &message );
+
+public slots:
+ void addBodyItem( const QString &key, const QString &value );
+ void addHeadItem( const QString &key, const QString &value );
+
+ QString bodyItem( const QString &key ) const;
+ QString headItem( const QString &key ) const;
+ QString messageSchemeIdentifier() const;
+ QString source() const;
+ QString target() const;
+
+ void setMessageSchemeIdentifier( const QString &msi );
+ void setSource( const QString &value );
+ void setTarget( const QString &value );
+
+ QString toString() const;
+
+private:
+ class xPLMessagePrivate;
+ xPLMessagePrivate *d;
+};
+
+Q_DECLARE_METATYPE(xPLMessage*)
+
+#endif // XPLMESSAGE_H
diff --git a/xpl/telldus-core-xpl/Makefile b/xpl/telldus-core-xpl/Makefile
new file mode 100644
index 00000000..00b6c1b0
--- /dev/null
+++ b/xpl/telldus-core-xpl/Makefile
@@ -0,0 +1,37 @@
+#
+# Makefile for xPLLib
+#
+
+#
+# For LINUX, use the following
+CCOPTS = -g -DLINUX -pedantic -Wall
+LIBS = -g -lm -lxPL -ltelldus-core
+
+LDOPTS = -O
+CC = cc $(CCOPTS)
+LD = cc $(LDOPTS)
+
+CMD_LIST = xPL_TelldusCore
+
+.c.o:
+ $(CC) -c $<
+
+.o:
+ $(LD) -o $@ $< $(LIBS)
+
+.c:
+ $(CC) -c $<
+ $(LD) -o $@ $< $(LIBS)
+
+
+all: ${CMD_LIST}
+
+clean:
+ rm -f *.o *.a core ${CMD_LIST}
+
+rebuild: clean all
+
+
+
+
+
diff --git a/xpl/telldus-core-xpl/xPL_TelldusCore.c b/xpl/telldus-core-xpl/xPL_TelldusCore.c
new file mode 100644
index 00000000..b4c08620
--- /dev/null
+++ b/xpl/telldus-core-xpl/xPL_TelldusCore.c
@@ -0,0 +1,399 @@
+
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define TELLDUS_VERSION "1.0"
+#define INSTANCE_MAX 16
+
+static xPL_ServicePtr telldusService = NULL;
+static Bool daemonMode = TRUE;
+char hostname[INSTANCE_MAX];
+
+void sendDevInfo(xPL_MessagePtr msg);
+void sendDevList(xPL_MessagePtr msg);
+void sendGatewayInfo();
+void sendNetInfo(xPL_MessagePtr msg);
+void sendNetList();
+
+void lightingCommandHandler(xPL_ServicePtr theService, xPL_MessagePtr theMessage, xPL_ObjectPtr userValue) {
+ Bool found = FALSE;
+ int deviceCount = 0, deviceId = 0, i, level = 0;
+
+ if (!xPL_doesMessageNamedValueExist(theMessage, "command")) {
+ return;
+ } else if (xPL_strcmpIgnoreCase(xPL_getMessageNamedValue(theMessage, "command"), "goto") != 0) {
+ return;
+ }
+
+ if (!xPL_doesMessageNamedValueExist(theMessage, "network")) {
+ return;
+ } else if (xPL_strcmpIgnoreCase(xPL_getMessageNamedValue(theMessage, "network"), "1") != 0) {
+ return;
+ }
+
+ if (!xPL_doesMessageNamedValueExist(theMessage, "device")) {
+ return;
+ } else {
+ /* Loop the devices to see it realy exists */
+ deviceCount = tdGetNumberOfDevices();
+
+ xPL_strToInt(xPL_getMessageNamedValue(theMessage, "device"), &deviceId);
+ for( i = 0; i < deviceCount; ++i ) {
+ if (tdGetDeviceId(i) == deviceId) {
+ found = TRUE;
+ break;
+ }
+ }
+ if (found == FALSE) {
+ return;
+ }
+ }
+
+ if (!xPL_doesMessageNamedValueExist(theMessage, "level")) {
+ return;
+ } else {
+ xPL_strToInt(xPL_getMessageNamedValue(theMessage, "level"), &level);
+ if (level < 0 || level > 100) {
+ return;
+ }
+ level = (float)level * 255.0 / 100.0;
+ }
+
+ if (level > 0 && level < 255) {
+ /* See if the device supports dim */
+ if (!(tdMethods(deviceId, TELLSTICK_DIM) & TELLSTICK_DIM)) {
+ /* Non dimmable device was dimmed */
+ return;
+ }
+ tdDim(deviceId, (unsigned char)level);
+ } else if (level == 255) {
+ /* See if the device supports dim */
+ if (!(tdMethods(deviceId, TELLSTICK_TURNON) & TELLSTICK_TURNON)) {
+ /* Non dimmable device was dimmed */
+ return;
+ }
+ tdTurnOn(deviceId);
+ } else if (level == 0) {
+ /* See if the device supports dim */
+ if (!(tdMethods(deviceId, TELLSTICK_TURNOFF) & TELLSTICK_TURNOFF)) {
+ /* Non dimmable device was dimmed */
+ return;
+ }
+ tdTurnOff(deviceId);
+ }
+
+}
+
+void lightingRequestHandler(xPL_ServicePtr theService, xPL_MessagePtr theMessage, xPL_ObjectPtr userValue) {
+
+ if (!xPL_doesMessageNamedValueExist(theMessage, "request")) {
+ return;
+ }
+
+ if (xPL_strcmpIgnoreCase(xPL_getMessageNamedValue(theMessage, "request"), "devinfo") == 0) {
+ sendDevInfo(theMessage);
+ } else if (xPL_strcmpIgnoreCase(xPL_getMessageNamedValue(theMessage, "request"), "devlist") == 0) {
+ sendDevList(theMessage);
+ } else if (xPL_strcmpIgnoreCase(xPL_getMessageNamedValue(theMessage, "request"), "gateinfo") == 0) {
+ sendGatewayInfo();
+ } else if (xPL_strcmpIgnoreCase(xPL_getMessageNamedValue(theMessage, "request"), "netinfo") == 0) {
+ sendNetInfo(theMessage);
+ } else if (xPL_strcmpIgnoreCase(xPL_getMessageNamedValue(theMessage, "request"), "netlist") == 0) {
+ sendNetList();
+ } else {
+/* fprintf(stdout, "Request: %s\n", xPL_getMessageNamedValue(theMessage, "request")); */
+ }
+}
+
+void shutdownHandler(int onSignal) {
+ xPL_setServiceEnabled(telldusService, FALSE);
+ xPL_releaseService(telldusService);
+ xPL_shutdown();
+ exit(0);
+}
+
+void sendDevInfo(xPL_MessagePtr msg) {
+ xPL_MessagePtr message = NULL;
+ Bool found = FALSE, deviceCount = tdGetNumberOfDevices();
+ int deviceId = 0, methods = 0, i = 0, lastSentCommand, level = 0;
+ char *name, buffer[12], *value;
+
+ xPL_strToInt(xPL_getMessageNamedValue(msg, "device"), &deviceId);
+ for( i = 0; i < deviceCount; ++i ) {
+ if (tdGetDeviceId(i) == deviceId) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ message = xPL_createBroadcastMessage(telldusService, xPL_MESSAGE_STATUS);
+ xPL_setSchema(message, "lighting", "devinfo");
+
+ xPL_setMessageNamedValue(message, "network", xPL_getMessageNamedValue(msg, "network"));
+ xPL_setMessageNamedValue(message, "device", xPL_getMessageNamedValue(msg, "device"));
+
+ if (xPL_strcmpIgnoreCase(xPL_getMessageNamedValue(msg, "network"), "1") == 0 && found == TRUE) {
+ lastSentCommand = tdLastSentCommand(deviceId, TELLSTICK_TURNON | TELLSTICK_TURNOFF | TELLSTICK_DIM);
+ if (lastSentCommand == TELLSTICK_TURNON) {
+ level = 100;
+ } else if (lastSentCommand == TELLSTICK_DIM) {
+ value = tdLastSentValue(deviceId);
+ level = atoi(value);
+ free(value);
+ level = round((float)level / 255.0 * 100.0);
+ if (level > 100) {
+ level = 100;
+ } else if (level < 0) {
+ level = 0;
+ }
+ } else {
+ level = 0;
+ }
+ methods = tdMethods(deviceId, TELLSTICK_TURNON | TELLSTICK_TURNOFF | TELLSTICK_DIM);
+ name = tdGetName(deviceId);
+ sprintf(buffer, "1,%s,0,%i", (methods & TELLSTICK_DIM ? "true" : "false"), level);
+ xPL_setMessageNamedValue(message, "status", "ok");
+ xPL_setMessageNamedValue(message, "name", name );
+ xPL_setMessageNamedValue(message, "report-on-manual", "false" );
+ xPL_setMessageNamedValue(message, "channel-count", "1" );
+ xPL_setMessageNamedValue(message, "primary-channel", "1" );
+ xPL_setMessageNamedValue(message, "channel", buffer );
+ xPL_setMessageNamedValue(message, "scene-count", "0" );
+
+ free(name);
+ } else {
+ xPL_setMessageNamedValue(message, "status", "not-found");
+ }
+
+ xPL_sendMessage(message);
+
+ xPL_releaseMessage(message);
+}
+
+void sendDevList(xPL_MessagePtr msg) {
+ int deviceCount = 0, i;
+ char deviceList[128];
+ xPL_MessagePtr message = NULL;
+
+ message = xPL_createBroadcastMessage(telldusService, xPL_MESSAGE_STATUS);
+ xPL_setSchema(message, "lighting", "devlist");
+
+ xPL_setMessageNamedValue(message, "network", xPL_getMessageNamedValue(msg, "network"));
+
+ if (xPL_strcmpIgnoreCase(xPL_getMessageNamedValue(msg, "network"), "1") == 0) {
+ xPL_setMessageNamedValue(message, "status", "ok");
+ deviceCount = tdGetNumberOfDevices();
+ for( i = 0; i < deviceCount; ++i ) {
+ if (i == 0) { /* First */
+ strcpy(deviceList, xPL_intToStr(tdGetDeviceId(i)));
+ } else {
+ strcat(deviceList, ",");
+ strcat(deviceList, xPL_intToStr(tdGetDeviceId(i)));
+ }
+ }
+
+ xPL_setMessageNamedValue(message, "device-count", xPL_intToStr(deviceCount) );
+ xPL_setMessageNamedValue(message, "device", deviceList );
+ } else {
+ xPL_setMessageNamedValue(message, "status", "not-found");
+ }
+
+ xPL_sendMessage(message);
+
+ xPL_releaseMessage(message);
+}
+
+void sendGatewayInfo() {
+ xPL_MessagePtr message = NULL;
+
+ message = xPL_createBroadcastMessage(telldusService, xPL_MESSAGE_STATUS);
+ xPL_setSchema(message, "lighting", "gateinfo");
+
+ xPL_setMessageNamedValue(message, "status", "ok");
+ xPL_setMessageNamedValue(message, "protocol", "TELLDUS");
+ xPL_setMessageNamedValue(message, "description", "xPL to Telldus TellStick gateway");
+ xPL_setMessageNamedValue(message, "version", TELLDUS_VERSION);
+ xPL_setMessageNamedValue(message, "author", "Telldus Technologies AB");
+ xPL_setMessageNamedValue(message, "info-url", "http://www.telldus.se");
+ xPL_setMessageNamedValue(message, "net-count", "1");
+ xPL_setMessageNamedValue(message, "preferred-net", "1");
+ xPL_setMessageNamedValue(message, "scenes-ok", "false");
+ xPL_setMessageNamedValue(message, "channels-ok", "false");
+ xPL_setMessageNamedValue(message, "fade-rate-ok", "false");
+
+ /* Broadcast the message */
+ xPL_sendMessage(message);
+
+ xPL_releaseMessage(message);
+}
+
+void sendNetInfo(xPL_MessagePtr msg) {
+ xPL_MessagePtr message = NULL;
+
+ message = xPL_createBroadcastMessage(telldusService, xPL_MESSAGE_STATUS);
+ xPL_setSchema(message, "lighting", "netinfo");
+
+ xPL_setMessageNamedValue(message, "network", xPL_getMessageNamedValue(msg, "network"));
+
+ if (xPL_strcmpIgnoreCase(xPL_getMessageNamedValue(msg, "network"), "1") == 0) {
+ xPL_setMessageNamedValue(message, "status", "ok");
+ xPL_setMessageNamedValue(message, "name", hostname );
+ xPL_setMessageNamedValue(message, "device-count", xPL_intToStr(tdGetNumberOfDevices()) );
+ xPL_setMessageNamedValue(message, "scene-count", "0" );
+ } else {
+ xPL_setMessageNamedValue(message, "status", "not-found");
+ }
+
+ xPL_sendMessage(message);
+
+ xPL_releaseMessage(message);
+}
+
+void sendNetList() {
+ xPL_MessagePtr message = NULL;
+
+ message = xPL_createBroadcastMessage(telldusService, xPL_MESSAGE_STATUS);
+ xPL_setSchema(message, "lighting", "netlist");
+
+ xPL_setMessageNamedValue(message, "status", "ok");
+ xPL_setMessageNamedValue(message, "network", "1");
+
+ /* Broadcast the message */
+ xPL_sendMessage(message);
+
+ xPL_releaseMessage(message);
+}
+
+void sendGatewayReadyMessage() {
+ xPL_MessagePtr gatewayReadyMessage = NULL;
+ /* Create a message to send */
+ gatewayReadyMessage = xPL_createBroadcastMessage(telldusService, xPL_MESSAGE_TRIGGER);
+ xPL_setSchema(gatewayReadyMessage, "lighting", "gateway");
+
+ /* Install the value and send the message */
+ xPL_setMessageNamedValue(gatewayReadyMessage, "report", "gateway-ready");
+
+ /* Broadcast the message */
+ xPL_sendMessage(gatewayReadyMessage);
+
+ xPL_releaseMessage(gatewayReadyMessage);
+}
+
+/* parseCmdLine will handles command line switches. Valid switches are: */
+/* -interface x - set interface to use */
+/* -xpldebug - set debugging and enable xPL debugging */
+/* -nodaemon - Dosn't disconnect from the console */
+static Bool parseCmdLine( int *argc, char *argv[]) {
+ int swptr;
+ int newcnt = 0;
+
+ /* Handle each item of the command line. If it starts with a '-', then */
+ /* process it as a switch. If not, then copy it to a new position in */
+ /* the argv list and up the new parm counter. */
+ for(swptr = 0; swptr < *argc; swptr++) {
+ /* If it doesn't begin with a '-', it's not a switch. */
+ if (argv[swptr][0] != '-') {
+ if (swptr != newcnt) argv[++newcnt] = argv[swptr];
+ } else {
+ /* Check for daemon mode */
+ if (!strcmp(argv[swptr],"-nodaemon")) {
+ daemonMode = FALSE;
+ continue;
+ }
+
+ /* Anything left is unknown */
+ fprintf(stderr, "Unknown switch `%s'", argv[swptr] );
+ return FALSE;
+ }
+ }
+
+ /* Set in place the new argument count and exit */
+ *argc = newcnt + 1;
+ return TRUE;
+}
+
+/* Print command usage info out */
+void printUsage(String ourName) {
+ fprintf(stderr, "%s - Telldus TellStick xPL interface\n", ourName);
+ fprintf(stderr, "Copyright (c) 2009, Telldus Technologies AB\n\n");
+ fprintf(stderr, "Usage: %s [-xpldebug] [-nodaemon] [-ip x] [-interface x]\n", ourName);
+ fprintf(stderr, " -xpldebug -- enable xPLLib debugging messagaes\n");
+ fprintf(stderr, " -nodaemon -- don't detach -- run from the console\n");
+ fprintf(stderr, " -interface x -- Use interface named x (i.e. eth0) as network interface\n");
+ fprintf(stderr, " -ip x -- Bind to specified IP address for xPL\n");
+}
+
+void startServer() {
+ /* Initialze lighting service */
+
+ /* Create a service for us */
+ if (gethostname(hostname, INSTANCE_MAX) != 0) {
+ fprintf(stderr, "Unable to retrieve the hostname");
+ exit(1);
+ }
+ telldusService = xPL_createService("telldus", "core", hostname);
+ xPL_setServiceVersion(telldusService, TELLDUS_VERSION);
+
+ /* Add a responder */
+ xPL_addServiceListener(telldusService, lightingRequestHandler, xPL_MESSAGE_COMMAND, "lighting", "request", NULL);
+ xPL_addServiceListener(telldusService, lightingCommandHandler, xPL_MESSAGE_COMMAND, "lighting", "basic", NULL);
+
+ /* Install signal traps for proper shutdown */
+ signal(SIGTERM, shutdownHandler);
+ signal(SIGINT, shutdownHandler);
+
+ /* Enable the service */
+ xPL_setServiceEnabled(telldusService, TRUE);
+ sendGatewayReadyMessage();
+
+ for (;;) {
+ /* Let XPL run */
+ xPL_processMessages(-1);
+ }
+}
+
+int main(int argc, String argv[]) {
+
+ /* Parse command line parms */
+ if (!xPL_parseCommonArgs(&argc, argv, FALSE)) {
+ exit(1);
+ }
+
+ /* Start xPL up */
+ if (!xPL_initialize(xPL_getParsedConnectionType())) {
+ fprintf(stderr, "Unable to start xPL");
+ exit(1);
+ }
+
+ /* Parse Hub command arguments */
+ if (!parseCmdLine(&argc, argv)) {
+ printUsage(argv[0]);
+ exit(1);
+ }
+
+ if (daemonMode) {
+ switch (fork()) {
+ case 0: /* child */
+ /* No io in child mode */
+ close(fileno(stdin));
+ close(fileno(stdout));
+ close(fileno(stderr));
+ setpgrp();
+
+ startServer();
+ break;
+ case -1: /* error */
+ fprintf(stderr, "Unable to spawn daemon, %s (%d)\n", strerror(errno), errno);
+ exit(1);
+ }
+ } else {
+ startServer();
+ }
+ return 0;
+}
+