2013-05-17 10:18:00 +00:00
|
|
|
"""
|
|
|
|
SleekXMPP: The Sleek XMPP Library
|
|
|
|
Implementation of xeps for Internet of Things
|
|
|
|
http://wiki.xmpp.org/web/Tech_pages/IoT_systems
|
2013-08-30 00:29:52 +00:00
|
|
|
Copyright (C) 2013 Sustainable Innovation, Joachim.lindborg@sust.se, bjorn.westrom@consoden.se
|
2013-05-17 10:18:00 +00:00
|
|
|
This file is part of SleekXMPP.
|
|
|
|
|
|
|
|
See the file LICENSE for copying permission.
|
|
|
|
"""
|
|
|
|
|
|
|
|
from sleekxmpp import Iq, Message
|
|
|
|
from sleekxmpp.xmlstream import register_stanza_plugin, ElementBase, ET, JID
|
2013-08-30 00:29:52 +00:00
|
|
|
from re import match
|
2013-05-17 10:18:00 +00:00
|
|
|
|
|
|
|
class Sensordata(ElementBase):
|
2013-08-30 00:29:52 +00:00
|
|
|
""" Placeholder for the namespace, not used as a stanza """
|
|
|
|
namespace = 'urn:xmpp:iot:sensordata'
|
2013-05-17 10:18:00 +00:00
|
|
|
name = 'sensordata'
|
|
|
|
plugin_attrib = name
|
|
|
|
interfaces = set(tuple())
|
|
|
|
|
2013-08-30 00:29:52 +00:00
|
|
|
class FieldTypes():
|
|
|
|
"""
|
|
|
|
All field types are optional booleans that default to False
|
|
|
|
"""
|
|
|
|
field_types = set([ 'momentary','peak','status','computed','identity','historicalSecond','historicalMinute','historicalHour', \
|
|
|
|
'historicalDay','historicalWeek','historicalMonth','historicalQuarter','historicalYear','historicalOther'])
|
|
|
|
|
|
|
|
class FieldStatus():
|
|
|
|
"""
|
|
|
|
All field statuses are optional booleans that default to False
|
|
|
|
"""
|
|
|
|
field_status = set([ 'missing','automaticEstimate','manualEstimate','manualReadout','automaticReadout','timeOffset','warning','error', \
|
|
|
|
'signed','invoiced','endOfSeries','powerFailure','invoiceConfirmed'])
|
|
|
|
|
2013-05-17 10:18:00 +00:00
|
|
|
class Request(ElementBase):
|
2013-08-30 00:29:52 +00:00
|
|
|
namespace = 'urn:xmpp:iot:sensordata'
|
2013-05-17 10:18:00 +00:00
|
|
|
name = 'req'
|
|
|
|
plugin_attrib = name
|
2013-08-30 00:29:52 +00:00
|
|
|
interfaces = set(['seqnr','nodes','fields','serviceToken','deviceToken','userToken','from','to','when','historical','all'])
|
|
|
|
interfaces.update(FieldTypes.field_types);
|
|
|
|
_flags = set(['serviceToken','deviceToken','userToken','from','to','when','historical','all']);
|
|
|
|
_flags.update(FieldTypes.field_types);
|
|
|
|
|
|
|
|
def __init__(self, xml=None, parent=None):
|
|
|
|
ElementBase.__init__(self, xml, parent);
|
|
|
|
self._nodes = set()
|
|
|
|
self._fields = 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 = set([node['nodeId'] for node in self['nodes']])
|
|
|
|
self._fields = set([field['name'] for field in self['fields']])
|
|
|
|
|
|
|
|
def _get_flags(self):
|
|
|
|
"""
|
|
|
|
Helper function for getting of flags. Returns all flags in
|
|
|
|
dictionary format: { "flag name": "flag value" ... }
|
|
|
|
"""
|
|
|
|
flags = {};
|
|
|
|
for f in self._flags:
|
|
|
|
if not self[f] == "":
|
|
|
|
flags[f] = self[f];
|
|
|
|
return flags;
|
|
|
|
|
|
|
|
def _set_flags(self, flags):
|
|
|
|
"""
|
|
|
|
Helper function for setting of flags.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
flags -- Flags in dictionary format: { "flag name": "flag value" ... }
|
|
|
|
"""
|
|
|
|
for f in self._flags:
|
|
|
|
if flags is not None and f in flags:
|
|
|
|
self[f] = flags[f];
|
|
|
|
else:
|
|
|
|
self[f] = None;
|
|
|
|
|
|
|
|
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 = set()
|
|
|
|
for node in self['substanzas']:
|
|
|
|
if isinstance(node, RequestNode):
|
|
|
|
nodes.add(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_field(self, name):
|
|
|
|
"""
|
|
|
|
Add a new field element. Each item is required to have a
|
|
|
|
name.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
name -- The name of the field.
|
|
|
|
"""
|
|
|
|
if name not in self._fields:
|
|
|
|
self._fields.add((name))
|
|
|
|
field = RequestField(parent=self)
|
|
|
|
field['name'] = name
|
|
|
|
self.iterables.append(field)
|
|
|
|
return field
|
|
|
|
return None
|
|
|
|
|
|
|
|
def del_field(self, name):
|
|
|
|
"""
|
|
|
|
Remove a single field.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
name -- name of field to remove.
|
|
|
|
"""
|
|
|
|
if name in self._fields:
|
|
|
|
fields = [i for i in self.iterables if isinstance(i, RequestField)]
|
|
|
|
for field in fields:
|
|
|
|
if field['name'] == name:
|
|
|
|
self.xml.remove(field.xml)
|
|
|
|
self.iterables.remove(field)
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
def get_fields(self):
|
|
|
|
"""Return all fields."""
|
|
|
|
fields = set()
|
|
|
|
for field in self['substanzas']:
|
|
|
|
if isinstance(field, RequestField):
|
|
|
|
fields.add(field)
|
|
|
|
return fields
|
|
|
|
|
|
|
|
def set_fields(self, fields):
|
|
|
|
"""
|
|
|
|
Set or replace all fields. The given fields must be in a
|
|
|
|
list or set where each item is RequestField or string
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
fields -- A series of fields in RequestField or string format.
|
|
|
|
"""
|
|
|
|
self.del_fields()
|
|
|
|
for field in fields:
|
|
|
|
if isinstance(field, RequestField):
|
|
|
|
self.add_field(field['name'])
|
|
|
|
else:
|
|
|
|
self.add_field(field)
|
|
|
|
|
|
|
|
def del_fields(self):
|
|
|
|
"""Remove all fields."""
|
|
|
|
self._fields = set()
|
|
|
|
fields = [i for i in self.iterables if isinstance(i, RequestField)]
|
|
|
|
for field in fields:
|
|
|
|
self.xml.remove(field.xml)
|
|
|
|
self.iterables.remove(field)
|
|
|
|
|
|
|
|
|
|
|
|
class RequestNode(ElementBase):
|
|
|
|
""" Node element in a request """
|
|
|
|
namespace = 'urn:xmpp:iot:sensordata'
|
|
|
|
name = 'node'
|
|
|
|
plugin_attrib = name
|
|
|
|
interfaces = set(['nodeId','sourceId','cacheType'])
|
2013-05-17 10:18:00 +00:00
|
|
|
|
2013-08-30 00:29:52 +00:00
|
|
|
class RequestField(ElementBase):
|
|
|
|
""" Field element in a request """
|
|
|
|
namespace = 'urn:xmpp:iot:sensordata'
|
|
|
|
name = 'field'
|
|
|
|
plugin_attrib = name
|
|
|
|
interfaces = set(['name'])
|
2013-05-17 10:18:00 +00:00
|
|
|
|
|
|
|
class Accepted(ElementBase):
|
2013-08-30 00:29:52 +00:00
|
|
|
namespace = 'urn:xmpp:iot:sensordata'
|
2013-05-17 10:18:00 +00:00
|
|
|
name = 'accepted'
|
|
|
|
plugin_attrib = name
|
2013-08-30 00:29:52 +00:00
|
|
|
interfaces = set(['seqnr','queued'])
|
2013-05-17 10:18:00 +00:00
|
|
|
|
2013-08-30 00:29:52 +00:00
|
|
|
class Started(ElementBase):
|
|
|
|
namespace = 'urn:xmpp:iot:sensordata'
|
|
|
|
name = 'started'
|
|
|
|
plugin_attrib = name
|
|
|
|
interfaces = set(['seqnr'])
|
2013-05-17 10:18:00 +00:00
|
|
|
|
|
|
|
class Failure(ElementBase):
|
2013-08-30 00:29:52 +00:00
|
|
|
namespace = 'urn:xmpp:iot:sensordata'
|
2013-05-17 10:18:00 +00:00
|
|
|
name = 'failure'
|
|
|
|
plugin_attrib = name
|
2013-08-30 00:29:52 +00:00
|
|
|
interfaces = set(['seqnr','done'])
|
|
|
|
|
|
|
|
class Error(ElementBase):
|
|
|
|
""" Error element in a request failure """
|
|
|
|
namespace = 'urn:xmpp:iot:sensordata'
|
|
|
|
name = 'error'
|
|
|
|
plugin_attrib = name
|
|
|
|
interfaces = set(['nodeId','timestamp','sourceId','cacheType','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.
|
|
|
|
|
|
|
|
:param 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 Rejected(ElementBase):
|
|
|
|
namespace = 'urn:xmpp:iot:sensordata'
|
|
|
|
name = 'rejected'
|
|
|
|
plugin_attrib = name
|
|
|
|
interfaces = set(['seqnr','error'])
|
|
|
|
sub_interfaces = set(['error'])
|
|
|
|
|
|
|
|
class Fields(ElementBase):
|
|
|
|
""" Fields element, top level in a response message with data """
|
|
|
|
namespace = 'urn:xmpp:iot:sensordata'
|
|
|
|
name = 'fields'
|
|
|
|
plugin_attrib = name
|
|
|
|
interfaces = set(['seqnr','done','nodes'])
|
|
|
|
|
|
|
|
def __init__(self, xml=None, parent=None):
|
|
|
|
ElementBase.__init__(self, xml, parent);
|
|
|
|
self._nodes = 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 = set([node['nodeId'] for node in self['nodes']])
|
|
|
|
|
|
|
|
|
|
|
|
def add_node(self, nodeId, sourceId=None, cacheType=None, substanzas=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 = FieldsNode(parent=self)
|
|
|
|
node['nodeId'] = nodeId
|
|
|
|
node['sourceId'] = sourceId
|
|
|
|
node['cacheType'] = cacheType
|
|
|
|
if substanzas is not None:
|
|
|
|
node.set_timestamps(substanzas)
|
|
|
|
|
|
|
|
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, FieldsNode)]
|
|
|
|
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 = set()
|
|
|
|
for node in self['substanzas']:
|
|
|
|
if isinstance(node, FieldsNode):
|
|
|
|
nodes.add(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.
|
|
|
|
"""
|
|
|
|
#print(str(id(self)) + " set_nodes: got " + str(nodes))
|
|
|
|
self.del_nodes()
|
|
|
|
for node in nodes:
|
|
|
|
if isinstance(node, FieldsNode):
|
|
|
|
self.add_node(node['nodeId'], node['sourceId'], node['cacheType'], substanzas=node['substanzas'])
|
|
|
|
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, FieldsNode)]
|
|
|
|
for node in nodes:
|
|
|
|
self.xml.remove(node.xml)
|
|
|
|
self.iterables.remove(node)
|
|
|
|
|
|
|
|
|
|
|
|
class FieldsNode(ElementBase):
|
|
|
|
""" Node element in response fields """
|
|
|
|
namespace = 'urn:xmpp:iot:sensordata'
|
|
|
|
name = 'node'
|
|
|
|
plugin_attrib = name
|
|
|
|
interfaces = set(['nodeId','sourceId','cacheType','timestamps'])
|
|
|
|
|
|
|
|
def __init__(self, xml=None, parent=None):
|
|
|
|
ElementBase.__init__(self, xml, parent);
|
|
|
|
self._timestamps = 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._timestamps = set([ts['value'] for ts in self['timestamps']])
|
|
|
|
|
|
|
|
def add_timestamp(self, timestamp, substanzas=None):
|
|
|
|
"""
|
|
|
|
Add a new timestamp element.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
timestamp -- The timestamp in ISO format.
|
|
|
|
"""
|
|
|
|
#print(str(id(self)) + " add_timestamp: " + str(timestamp))
|
|
|
|
|
|
|
|
if timestamp not in self._timestamps:
|
|
|
|
self._timestamps.add((timestamp))
|
|
|
|
ts = Timestamp(parent=self)
|
|
|
|
ts['value'] = timestamp
|
|
|
|
if not substanzas is None:
|
|
|
|
ts.set_datas(substanzas);
|
|
|
|
#print("add_timestamp with substanzas: " + str(substanzas))
|
|
|
|
self.iterables.append(ts)
|
|
|
|
#print(str(id(self)) + " added_timestamp: " + str(id(ts)))
|
|
|
|
return ts
|
|
|
|
return None
|
|
|
|
|
|
|
|
def del_timestamp(self, timestamp):
|
|
|
|
"""
|
|
|
|
Remove a single timestamp.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
timestamp -- timestamp (in ISO format) of the item to remove.
|
|
|
|
"""
|
|
|
|
#print("del_timestamp: ")
|
|
|
|
if timestamp in self._timestamps:
|
|
|
|
timestamps = [i for i in self.iterables if isinstance(i, Timestamp)]
|
|
|
|
for ts in timestamps:
|
|
|
|
if ts['value'] == timestamp:
|
|
|
|
self.xml.remove(ts.xml)
|
|
|
|
self.iterables.remove(ts)
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
def get_timestamps(self):
|
|
|
|
"""Return all timestamps."""
|
|
|
|
#print(str(id(self)) + " get_timestamps: ")
|
|
|
|
timestamps = set()
|
|
|
|
for timestamp in self['substanzas']:
|
|
|
|
if isinstance(timestamp, Timestamp):
|
|
|
|
timestamps.add(timestamp)
|
|
|
|
return timestamps
|
|
|
|
|
|
|
|
def set_timestamps(self, timestamps):
|
|
|
|
"""
|
|
|
|
Set or replace all timestamps. The given timestamps must be in a
|
|
|
|
list or set where each item is a timestamp
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
timestamps -- A series of timestamps.
|
|
|
|
"""
|
|
|
|
#print(str(id(self)) + " set_timestamps: got " + str(timestamps))
|
|
|
|
self.del_timestamps()
|
|
|
|
for timestamp in timestamps:
|
|
|
|
#print("set_timestamps: subset " + str(timestamp))
|
|
|
|
#print("set_timestamps: subset.substanzas " + str(timestamp['substanzas']))
|
|
|
|
if isinstance(timestamp, Timestamp):
|
|
|
|
self.add_timestamp(timestamp['value'], substanzas=timestamp['substanzas'])
|
|
|
|
else:
|
|
|
|
#print("set_timestamps: got " + str(timestamp))
|
|
|
|
self.add_timestamp(timestamp)
|
|
|
|
|
|
|
|
def del_timestamps(self):
|
|
|
|
"""Remove all timestamps."""
|
|
|
|
#print(str(id(self)) + " del_timestamps: ")
|
|
|
|
self._timestamps = set()
|
|
|
|
timestamps = [i for i in self.iterables if isinstance(i, Timestamp)]
|
|
|
|
for timestamp in timestamps:
|
|
|
|
self.xml.remove(timestamp.xml)
|
|
|
|
self.iterables.remove(timestamp)
|
|
|
|
|
|
|
|
class Field(ElementBase):
|
|
|
|
"""
|
|
|
|
Field element in response Timestamp. This is a base class,
|
|
|
|
all instances of fields added to Timestamp must be of types:
|
|
|
|
DataNumeric
|
|
|
|
DataString
|
|
|
|
DataBoolean
|
|
|
|
DataDateTime
|
|
|
|
DataTimeSpan
|
|
|
|
DataEnum
|
|
|
|
"""
|
|
|
|
namespace = 'urn:xmpp:iot:sensordata'
|
|
|
|
name = 'field'
|
|
|
|
plugin_attrib = name
|
|
|
|
interfaces = set(['name','module','stringIds']);
|
|
|
|
interfaces.update(FieldTypes.field_types);
|
|
|
|
interfaces.update(FieldStatus.field_status);
|
|
|
|
|
|
|
|
_flags = set();
|
|
|
|
_flags.update(FieldTypes.field_types);
|
|
|
|
_flags.update(FieldStatus.field_status);
|
|
|
|
|
|
|
|
def set_stringIds(self, value):
|
|
|
|
"""Verifies stringIds according to regexp from specification XMPP-0323.
|
|
|
|
|
|
|
|
:param value: string
|
|
|
|
"""
|
|
|
|
|
|
|
|
pattern = re.compile("^\d+([|]\w+([.]\w+)*([|][^,]*)?)?(,\d+([|]\w+([.]\w+)*([|][^,]*)?)?)*$")
|
|
|
|
if pattern.match(value) is not None:
|
|
|
|
self.xml.stringIds = value;
|
|
|
|
else:
|
|
|
|
# Bad content, add nothing
|
|
|
|
pass
|
|
|
|
|
|
|
|
return self
|
|
|
|
|
|
|
|
def _get_flags(self):
|
|
|
|
"""
|
|
|
|
Helper function for getting of flags. Returns all flags in
|
|
|
|
dictionary format: { "flag name": "flag value" ... }
|
|
|
|
"""
|
|
|
|
flags = {};
|
|
|
|
for f in self._flags:
|
|
|
|
if not self[f] == "":
|
|
|
|
flags[f] = self[f];
|
|
|
|
return flags;
|
|
|
|
|
|
|
|
def _set_flags(self, flags):
|
|
|
|
"""
|
|
|
|
Helper function for setting of flags.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
flags -- Flags in dictionary format: { "flag name": "flag value" ... }
|
|
|
|
"""
|
|
|
|
for f in self._flags:
|
|
|
|
if flags is not None and f in flags:
|
|
|
|
self[f] = flags[f];
|
|
|
|
else:
|
|
|
|
self[f] = None;
|
|
|
|
|
|
|
|
def _get_typename(self):
|
|
|
|
return "invalid type, use subclasses!";
|
|
|
|
|
|
|
|
|
|
|
|
class Timestamp(ElementBase):
|
|
|
|
""" Timestamp element in response Node """
|
|
|
|
namespace = 'urn:xmpp:iot:sensordata'
|
|
|
|
name = 'timestamp'
|
|
|
|
plugin_attrib = name
|
|
|
|
interfaces = set(['value','datas'])
|
|
|
|
|
|
|
|
def __init__(self, xml=None, parent=None):
|
|
|
|
ElementBase.__init__(self, xml, parent);
|
|
|
|
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._datas = set([data['name'] for data in self['datas']])
|
|
|
|
|
|
|
|
def add_data(self, typename, name, value, module=None, stringIds=None, unit=None, dataType=None, flags=None):
|
|
|
|
"""
|
|
|
|
Add a new data element.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
typename -- The type of data element (numeric, string, boolean, dateTime, timeSpan or enum)
|
|
|
|
value -- The value of the data element
|
|
|
|
module -- [optional] language module to use for the data element
|
|
|
|
stringIds -- [optional] The stringIds used to find associated text in the language module
|
|
|
|
unit -- [optional] The unit. Only applicable for type numeric
|
|
|
|
dataType -- [optional] The dataType. Only applicable for type enum
|
|
|
|
"""
|
|
|
|
if name not in self._datas:
|
|
|
|
dataObj = None;
|
|
|
|
if typename == "numeric":
|
|
|
|
dataObj = DataNumeric(parent=self);
|
|
|
|
dataObj['unit'] = unit;
|
|
|
|
elif typename == "string":
|
|
|
|
dataObj = DataString(parent=self);
|
|
|
|
elif typename == "boolean":
|
|
|
|
dataObj = DataBoolean(parent=self);
|
|
|
|
elif typename == "dateTime":
|
|
|
|
dataObj = DataDateTime(parent=self);
|
|
|
|
elif typename == "timeSpan":
|
|
|
|
dataObj = DataTimeSpan(parent=self);
|
|
|
|
elif typename == "enum":
|
|
|
|
dataObj = DataEnum(parent=self);
|
|
|
|
dataObj['dataType'] = dataType;
|
|
|
|
|
|
|
|
dataObj['name'] = name;
|
|
|
|
dataObj['value'] = value;
|
|
|
|
dataObj['module'] = module;
|
|
|
|
dataObj['stringIds'] = stringIds;
|
|
|
|
|
|
|
|
if flags is not None:
|
|
|
|
dataObj._set_flags(flags);
|
|
|
|
|
|
|
|
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, Field)]
|
|
|
|
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 = set()
|
|
|
|
for data in self['substanzas']:
|
|
|
|
if isinstance(data, Field):
|
|
|
|
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 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(typename=data._get_typename(), name=data['name'], value=data['value'], module=data['module'], stringIds=data['stringIds'], unit=data['unit'], dataType=data['dataType'], flags=data._get_flags())
|
|
|
|
|
|
|
|
def del_datas(self):
|
|
|
|
"""Remove all data elements."""
|
|
|
|
self._datas = set()
|
|
|
|
datas = [i for i in self.iterables if isinstance(i, Field)]
|
|
|
|
for data in datas:
|
|
|
|
self.xml.remove(data.xml)
|
|
|
|
self.iterables.remove(data)
|
|
|
|
|
|
|
|
class DataNumeric(Field):
|
|
|
|
"""
|
|
|
|
Field data of type numeric.
|
|
|
|
Note that the value is expressed as a string.
|
|
|
|
"""
|
|
|
|
namespace = 'urn:xmpp:iot:sensordata'
|
|
|
|
name = 'numeric'
|
|
|
|
plugin_attrib = name
|
|
|
|
interfaces = set(['value', 'unit']);
|
|
|
|
interfaces.update(Field.interfaces);
|
|
|
|
|
|
|
|
def _get_typename(self):
|
|
|
|
return "numeric"
|
|
|
|
|
|
|
|
class DataString(Field):
|
|
|
|
"""
|
|
|
|
Field data of type string
|
|
|
|
"""
|
|
|
|
namespace = 'urn:xmpp:iot:sensordata'
|
|
|
|
name = 'string'
|
|
|
|
plugin_attrib = name
|
|
|
|
interfaces = set(['value']);
|
|
|
|
interfaces.update(Field.interfaces);
|
|
|
|
|
|
|
|
def _get_typename(self):
|
|
|
|
return "string"
|
|
|
|
|
|
|
|
class DataBoolean(Field):
|
|
|
|
"""
|
|
|
|
Field data of type boolean.
|
|
|
|
Note that the value is expressed as a string.
|
|
|
|
"""
|
|
|
|
namespace = 'urn:xmpp:iot:sensordata'
|
|
|
|
name = 'boolean'
|
|
|
|
plugin_attrib = name
|
|
|
|
interfaces = set(['value']);
|
|
|
|
interfaces.update(Field.interfaces);
|
|
|
|
|
|
|
|
def _get_typename(self):
|
|
|
|
return "boolean"
|
|
|
|
|
|
|
|
class DataDateTime(Field):
|
|
|
|
"""
|
|
|
|
Field data of type dateTime.
|
|
|
|
Note that the value is expressed as a string.
|
|
|
|
"""
|
|
|
|
namespace = 'urn:xmpp:iot:sensordata'
|
|
|
|
name = 'dateTime'
|
|
|
|
plugin_attrib = name
|
|
|
|
interfaces = set(['value']);
|
|
|
|
interfaces.update(Field.interfaces);
|
|
|
|
|
|
|
|
def _get_typename(self):
|
|
|
|
return "dateTime"
|
|
|
|
|
|
|
|
class DataTimeSpan(Field):
|
|
|
|
"""
|
|
|
|
Field data of type timeSpan.
|
|
|
|
Note that the value is expressed as a string.
|
|
|
|
"""
|
|
|
|
namespace = 'urn:xmpp:iot:sensordata'
|
|
|
|
name = 'timeSpan'
|
|
|
|
plugin_attrib = name
|
|
|
|
interfaces = set(['value']);
|
|
|
|
interfaces.update(Field.interfaces);
|
|
|
|
|
|
|
|
def _get_typename(self):
|
|
|
|
return "timeSpan"
|
|
|
|
|
|
|
|
class DataEnum(Field):
|
|
|
|
"""
|
|
|
|
Field data of type enum.
|
|
|
|
Note that the value is expressed as a string.
|
|
|
|
"""
|
|
|
|
namespace = 'urn:xmpp:iot:sensordata'
|
|
|
|
name = 'enum'
|
|
|
|
plugin_attrib = name
|
|
|
|
interfaces = set(['value', 'dataType']);
|
|
|
|
interfaces.update(Field.interfaces);
|
|
|
|
|
|
|
|
def _get_typename(self):
|
|
|
|
return "enum"
|
|
|
|
|
|
|
|
class Done(ElementBase):
|
|
|
|
""" Done element used to signal that all data has been transferred """
|
|
|
|
namespace = 'urn:xmpp:iot:sensordata'
|
|
|
|
name = 'done'
|
|
|
|
plugin_attrib = name
|
|
|
|
interfaces = set(['seqnr'])
|
|
|
|
|
|
|
|
class Cancel(ElementBase):
|
|
|
|
""" Cancel element used to signal that a request shall be cancelled """
|
|
|
|
namespace = 'urn:xmpp:iot:sensordata'
|
|
|
|
name = 'cancel'
|
|
|
|
plugin_attrib = name
|
|
|
|
interfaces = set(['seqnr'])
|
|
|
|
|
|
|
|
class Cancelled(ElementBase):
|
|
|
|
""" Cancelled element used to signal that cancellation is confirmed """
|
|
|
|
namespace = 'urn:xmpp:iot:sensordata'
|
|
|
|
name = 'cancelled'
|
|
|
|
plugin_attrib = name
|
|
|
|
interfaces = set(['seqnr'])
|
|
|
|
|
|
|
|
|
|
|
|
register_stanza_plugin(Iq, Request)
|
|
|
|
register_stanza_plugin(Request, RequestNode, iterable=True)
|
|
|
|
register_stanza_plugin(Request, RequestField, iterable=True)
|
|
|
|
|
|
|
|
register_stanza_plugin(Iq, Accepted)
|
|
|
|
register_stanza_plugin(Message, Failure)
|
|
|
|
register_stanza_plugin(Failure, Error)
|
|
|
|
|
|
|
|
register_stanza_plugin(Iq, Rejected)
|
|
|
|
|
|
|
|
register_stanza_plugin(Message, Fields)
|
|
|
|
register_stanza_plugin(Fields, FieldsNode, iterable=True)
|
|
|
|
register_stanza_plugin(FieldsNode, Timestamp, iterable=True)
|
|
|
|
register_stanza_plugin(Timestamp, Field, iterable=True)
|
|
|
|
register_stanza_plugin(Timestamp, DataNumeric, iterable=True)
|
|
|
|
register_stanza_plugin(Timestamp, DataString, iterable=True)
|
|
|
|
register_stanza_plugin(Timestamp, DataBoolean, iterable=True)
|
|
|
|
register_stanza_plugin(Timestamp, DataDateTime, iterable=True)
|
|
|
|
register_stanza_plugin(Timestamp, DataTimeSpan, iterable=True)
|
|
|
|
register_stanza_plugin(Timestamp, DataEnum, iterable=True)
|
|
|
|
|
|
|
|
register_stanza_plugin(Message, Started)
|
|
|
|
|
|
|
|
register_stanza_plugin(Iq, Cancel)
|
|
|
|
register_stanza_plugin(Iq, Cancelled)
|