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; +} +