""" Slixmpp: The Slick XMPP Library Implementation of xeps for Internet of Things http://wiki.xmpp.org/web/Tech_pages/IoT_systems Copyright (C) 2013 Sustainable Innovation, Joachim.lindborg@sust.se, bjorn.westrom@consoden.se This file is part of Slixmpp. See the file LICENSE for copying permission. """ from slixmpp import Iq, Message from slixmpp.xmlstream import register_stanza_plugin, ElementBase, ET, JID from re import match class Control(ElementBase): """ Placeholder for the namespace, not used as a stanza """ namespace = 'urn:xmpp:iot:control' name = 'control' plugin_attrib = name interfaces = set(tuple()) class ControlSet(ElementBase): namespace = 'urn:xmpp:iot:control' name = 'set' plugin_attrib = name interfaces = {'nodes','datas'} def __init__(self, xml=None, parent=None): ElementBase.__init__(self, xml, parent) self._nodes = set() self._datas = set() def setup(self, xml=None): """ Populate the stanza object using an optional XML object. Overrides ElementBase.setup Caches item information. Arguments: xml -- Use an existing XML object for the stanza's values. """ ElementBase.setup(self, xml) self._nodes = {node['nodeId'] for node in self['nodes']} self._datas = {data['name'] for data in self['datas']} def add_node(self, nodeId, sourceId=None, cacheType=None): """ Add a new node element. Each item is required to have a nodeId, but may also specify a sourceId value and cacheType. Arguments: nodeId -- The ID for the node. sourceId -- [optional] identifying the data source controlling the device cacheType -- [optional] narrowing down the search to a specific kind of node """ if nodeId not in self._nodes: self._nodes.add((nodeId)) node = RequestNode(parent=self) node['nodeId'] = nodeId node['sourceId'] = sourceId node['cacheType'] = cacheType self.iterables.append(node) return node return None def del_node(self, nodeId): """ Remove a single node. Arguments: nodeId -- Node ID of the item to remove. """ if nodeId in self._nodes: nodes = [i for i in self.iterables if isinstance(i, RequestNode)] for node in nodes: if node['nodeId'] == nodeId: self.xml.remove(node.xml) self.iterables.remove(node) return True return False def get_nodes(self): """Return all nodes.""" nodes = [] for node in self['substanzas']: if isinstance(node, RequestNode): nodes.append(node) return nodes def set_nodes(self, nodes): """ Set or replace all nodes. The given nodes must be in a list or set where each item is a tuple of the form: (nodeId, sourceId, cacheType) Arguments: nodes -- A series of nodes in tuple format. """ self.del_nodes() for node in nodes: if isinstance(node, RequestNode): self.add_node(node['nodeId'], node['sourceId'], node['cacheType']) else: nodeId, sourceId, cacheType = node self.add_node(nodeId, sourceId, cacheType) def del_nodes(self): """Remove all nodes.""" self._nodes = set() nodes = [i for i in self.iterables if isinstance(i, RequestNode)] for node in nodes: self.xml.remove(node.xml) self.iterables.remove(node) def add_data(self, name, typename, value): """ Add a new data element. Arguments: name -- The name of the data element typename -- The type of data element (boolean, color, string, date, dateTime, double, duration, int, long, time) value -- The value of the data element """ if name not in self._datas: dataObj = None if typename == "boolean": dataObj = BooleanParameter(parent=self) elif typename == "color": dataObj = ColorParameter(parent=self) elif typename == "string": dataObj = StringParameter(parent=self) elif typename == "date": dataObj = DateParameter(parent=self) elif typename == "dateTime": dataObj = DateTimeParameter(parent=self) elif typename == "double": dataObj = DoubleParameter(parent=self) elif typename == "duration": dataObj = DurationParameter(parent=self) elif typename == "int": dataObj = IntParameter(parent=self) elif typename == "long": dataObj = LongParameter(parent=self) elif typename == "time": dataObj = TimeParameter(parent=self) dataObj['name'] = name dataObj['value'] = value self._datas.add(name) self.iterables.append(dataObj) return dataObj return None def del_data(self, name): """ Remove a single data element. Arguments: data_name -- The data element name to remove. """ if name in self._datas: datas = [i for i in self.iterables if isinstance(i, BaseParameter)] for data in datas: if data['name'] == name: self.xml.remove(data.xml) self.iterables.remove(data) return True return False def get_datas(self): """ Return all data elements. """ datas = [] for data in self['substanzas']: if isinstance(data, BaseParameter): datas.append(data) return datas def set_datas(self, datas): """ Set or replace all data elements. The given elements must be in a list or set where each item is a data element (numeric, string, boolean, dateTime, timeSpan or enum) Arguments: datas -- A series of data elements. """ self.del_datas() for data in datas: self.add_data(name=data['name'], typename=data._get_typename(), value=data['value']) def del_datas(self): """Remove all data elements.""" self._datas = set() datas = [i for i in self.iterables if isinstance(i, BaseParameter)] for data in datas: self.xml.remove(data.xml) self.iterables.remove(data) class RequestNode(ElementBase): """ Node element in a request """ namespace = 'urn:xmpp:iot:control' name = 'node' plugin_attrib = name interfaces = {'nodeId','sourceId','cacheType'} class ControlSetResponse(ElementBase): namespace = 'urn:xmpp:iot:control' name = 'setResponse' plugin_attrib = name interfaces = {'responseCode'} def __init__(self, xml=None, parent=None): ElementBase.__init__(self, xml, parent) self._nodes = set() self._datas = set() def setup(self, xml=None): """ Populate the stanza object using an optional XML object. Overrides ElementBase.setup Caches item information. Arguments: xml -- Use an existing XML object for the stanza's values. """ ElementBase.setup(self, xml) self._nodes = {node['nodeId'] for node in self['nodes']} self._datas = {data['name'] for data in self['datas']} def add_node(self, nodeId, sourceId=None, cacheType=None): """ Add a new node element. Each item is required to have a nodeId, but may also specify a sourceId value and cacheType. Arguments: nodeId -- The ID for the node. sourceId -- [optional] identifying the data source controlling the device cacheType -- [optional] narrowing down the search to a specific kind of node """ if nodeId not in self._nodes: self._nodes.add(nodeId) node = RequestNode(parent=self) node['nodeId'] = nodeId node['sourceId'] = sourceId node['cacheType'] = cacheType self.iterables.append(node) return node return None def del_node(self, nodeId): """ Remove a single node. Arguments: nodeId -- Node ID of the item to remove. """ if nodeId in self._nodes: nodes = [i for i in self.iterables if isinstance(i, RequestNode)] for node in nodes: if node['nodeId'] == nodeId: self.xml.remove(node.xml) self.iterables.remove(node) return True return False def get_nodes(self): """Return all nodes.""" nodes = [] for node in self['substanzas']: if isinstance(node, RequestNode): nodes.append(node) return nodes def set_nodes(self, nodes): """ Set or replace all nodes. The given nodes must be in a list or set where each item is a tuple of the form: (nodeId, sourceId, cacheType) Arguments: nodes -- A series of nodes in tuple format. """ self.del_nodes() for node in nodes: if isinstance(node, RequestNode): self.add_node(node['nodeId'], node['sourceId'], node['cacheType']) else: nodeId, sourceId, cacheType = node self.add_node(nodeId, sourceId, cacheType) def del_nodes(self): """Remove all nodes.""" self._nodes = set() nodes = [i for i in self.iterables if isinstance(i, RequestNode)] for node in nodes: self.xml.remove(node.xml) self.iterables.remove(node) def add_data(self, name): """ Add a new ResponseParameter element. Arguments: name -- Name of the parameter """ if name not in self._datas: self._datas.add(name) data = ResponseParameter(parent=self) data['name'] = name self.iterables.append(data) return data return None def del_data(self, name): """ Remove a single ResponseParameter element. Arguments: name -- The data element name to remove. """ if name in self._datas: datas = [i for i in self.iterables if isinstance(i, ResponseParameter)] for data in datas: if data['name'] == name: self.xml.remove(data.xml) self.iterables.remove(data) return True return False def get_datas(self): """ Return all ResponseParameter elements. """ datas = set() for data in self['substanzas']: if isinstance(data, ResponseParameter): datas.add(data) return datas def set_datas(self, datas): """ Set or replace all data elements. The given elements must be in a list or set of ResponseParameter elements Arguments: datas -- A series of data element names. """ self.del_datas() for data in datas: self.add_data(name=data['name']) def del_datas(self): """Remove all ResponseParameter elements.""" self._datas = set() datas = [i for i in self.iterables if isinstance(i, ResponseParameter)] for data in datas: self.xml.remove(data.xml) self.iterables.remove(data) class Error(ElementBase): namespace = 'urn:xmpp:iot:control' name = 'error' plugin_attrib = name interfaces = {'var','text'} def get_text(self): """Return then contents inside the XML tag.""" return self.xml.text def set_text(self, value): """Set then contents inside the XML tag. Arguments: value -- string """ self.xml.text = value return self def del_text(self): """Remove the contents inside the XML tag.""" self.xml.text = "" return self class ResponseParameter(ElementBase): """ Parameter element in ControlSetResponse. """ namespace = 'urn:xmpp:iot:control' name = 'parameter' plugin_attrib = name interfaces = {'name'} class BaseParameter(ElementBase): """ Parameter element in SetCommand. This is a base class, all instances of parameters added to SetCommand must be of types: BooleanParameter ColorParameter StringParameter DateParameter DateTimeParameter DoubleParameter DurationParameter IntParameter LongParameter TimeParameter """ namespace = 'urn:xmpp:iot:control' name = 'baseParameter' plugin_attrib = name interfaces = {'name','value'} def _get_typename(self): return self.name class BooleanParameter(BaseParameter): """ Field data of type boolean. Note that the value is expressed as a string. """ name = 'boolean' plugin_attrib = name class ColorParameter(BaseParameter): """ Field data of type color. Note that the value is expressed as a string. """ name = 'color' plugin_attrib = name class StringParameter(BaseParameter): """ Field data of type string. """ name = 'string' plugin_attrib = name class DateParameter(BaseParameter): """ Field data of type date. Note that the value is expressed as a string. """ name = 'date' plugin_attrib = name class DateTimeParameter(BaseParameter): """ Field data of type dateTime. Note that the value is expressed as a string. """ name = 'dateTime' plugin_attrib = name class DoubleParameter(BaseParameter): """ Field data of type double. Note that the value is expressed as a string. """ name = 'double' plugin_attrib = name class DurationParameter(BaseParameter): """ Field data of type duration. Note that the value is expressed as a string. """ name = 'duration' plugin_attrib = name class IntParameter(BaseParameter): """ Field data of type int. Note that the value is expressed as a string. """ name = 'int' plugin_attrib = name class LongParameter(BaseParameter): """ Field data of type long (64-bit int). Note that the value is expressed as a string. """ name = 'long' plugin_attrib = name class TimeParameter(BaseParameter): """ Field data of type time. Note that the value is expressed as a string. """ name = 'time' plugin_attrib = name register_stanza_plugin(Iq, ControlSet) register_stanza_plugin(Message, ControlSet) register_stanza_plugin(ControlSet, RequestNode, iterable=True) register_stanza_plugin(ControlSet, BooleanParameter, iterable=True) register_stanza_plugin(ControlSet, ColorParameter, iterable=True) register_stanza_plugin(ControlSet, StringParameter, iterable=True) register_stanza_plugin(ControlSet, DateParameter, iterable=True) register_stanza_plugin(ControlSet, DateTimeParameter, iterable=True) register_stanza_plugin(ControlSet, DoubleParameter, iterable=True) register_stanza_plugin(ControlSet, DurationParameter, iterable=True) register_stanza_plugin(ControlSet, IntParameter, iterable=True) register_stanza_plugin(ControlSet, LongParameter, iterable=True) register_stanza_plugin(ControlSet, TimeParameter, iterable=True) register_stanza_plugin(Iq, ControlSetResponse) register_stanza_plugin(ControlSetResponse, Error) register_stanza_plugin(ControlSetResponse, RequestNode, iterable=True) register_stanza_plugin(ControlSetResponse, ResponseParameter, iterable=True)