cleanup semicolons, whitespace and mutable default arguments

This commit is contained in:
Robin Gloster 2014-08-18 00:52:24 +02:00
parent a5c03b763a
commit 4144d60017
30 changed files with 1074 additions and 1068 deletions

View File

@ -3,7 +3,7 @@ SleekXMPP
SleekXMPP is an MIT licensed XMPP library for Python 2.6/3.1+, SleekXMPP is an MIT licensed XMPP library for Python 2.6/3.1+,
and is featured in examples in and is featured in examples in
`XMPP: The Definitive Guide <http://oreilly.com/catalog/9780596521271>`_ `XMPP: The Definitive Guide <http://oreilly.com/catalog/9780596521271>`_
by Kevin Smith, Remko Tronçon, and Peter Saint-Andre. If you've arrived by Kevin Smith, Remko Tronçon, and Peter Saint-Andre. If you've arrived
here from reading the Definitive Guide, please see the notes on updating here from reading the Definitive Guide, please see the notes on updating
the examples to the latest version of SleekXMPP. the examples to the latest version of SleekXMPP.
@ -52,7 +52,7 @@ The latest source code for SleekXMPP may be found on `Github
Installing DNSPython Installing DNSPython
--------------------- --------------------
If you are using Python3 and wish to use dnspython, you will have to checkout and If you are using Python3 and wish to use dnspython, you will have to checkout and
install the ``python3`` branch:: install the ``python3`` branch::
@ -144,7 +144,7 @@ SleekXMPP projects::
if __name__ == '__main__': if __name__ == '__main__':
# Ideally use optparse or argparse to get JID, # Ideally use optparse or argparse to get JID,
# password, and log level. # password, and log level.
logging.basicConfig(level=logging.DEBUG, logging.basicConfig(level=logging.DEBUG,
@ -158,15 +158,15 @@ SleekXMPP projects::
Credits Credits
------- -------
**Main Author:** Nathan Fritz **Main Author:** Nathan Fritz
`fritzy@netflint.net <xmpp:fritzy@netflint.net?message>`_, `fritzy@netflint.net <xmpp:fritzy@netflint.net?message>`_,
`@fritzy <http://twitter.com/fritzy>`_ `@fritzy <http://twitter.com/fritzy>`_
Nathan is also the author of XMPPHP and `Seesmic-AS3-XMPP Nathan is also the author of XMPPHP and `Seesmic-AS3-XMPP
<http://code.google.com/p/seesmic-as3-xmpp/>`_, and a former member of <http://code.google.com/p/seesmic-as3-xmpp/>`_, and a former member of
the XMPP Council. the XMPP Council.
**Co-Author:** Lance Stout **Co-Author:** Lance Stout
`lancestout@gmail.com <xmpp:lancestout@gmail.com?message>`_, `lancestout@gmail.com <xmpp:lancestout@gmail.com?message>`_,
`@lancestout <http://twitter.com/lancestout>`_ `@lancestout <http://twitter.com/lancestout>`_
**Contributors:** **Contributors:**

View File

@ -179,13 +179,13 @@ if __name__ == '__main__':
# node=opts.nodeid, # node=opts.nodeid,
# jid=xmpp.boundjid.full) # jid=xmpp.boundjid.full)
myDevice = TheDevice(opts.nodeid); myDevice = TheDevice(opts.nodeid)
# myDevice._add_field(name="Relay", typename="numeric", unit="Bool"); # myDevice._add_field(name="Relay", typename="numeric", unit="Bool");
myDevice._add_field(name="Temperature", typename="numeric", unit="C"); myDevice._add_field(name="Temperature", typename="numeric", unit="C")
myDevice._set_momentary_timestamp("2013-03-07T16:24:30") myDevice._set_momentary_timestamp("2013-03-07T16:24:30")
myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"}); myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"})
xmpp['xep_0323'].register_node(nodeId=opts.nodeid, device=myDevice, commTimeout=10); xmpp['xep_0323'].register_node(nodeId=opts.nodeid, device=myDevice, commTimeout=10)
xmpp.beClientOrServer(server=True) xmpp.beClientOrServer(server=True)
while not(xmpp.testForRelease()): while not(xmpp.testForRelease()):
xmpp.connect() xmpp.connect()

View File

@ -68,7 +68,7 @@ class RosterBrowser(sleekxmpp.ClientXMPP):
try: try:
self.get_roster() self.get_roster()
except IqError as err: except IqError as err:
print('Error: %' % err.iq['error']['condition']) print('Error: %s' % err.iq['error']['condition'])
except IqTimeout: except IqTimeout:
print('Error: Request timed out') print('Error: Request timed out')
self.send_presence() self.send_presence()

View File

@ -25,7 +25,6 @@ from sleekxmpp.exceptions import IqError, IqTimeout
from sleekxmpp.stanza import Message, Presence, Iq, StreamError from sleekxmpp.stanza import Message, Presence, Iq, StreamError
from sleekxmpp.stanza.roster import Roster from sleekxmpp.stanza.roster import Roster
from sleekxmpp.stanza.nick import Nick from sleekxmpp.stanza.nick import Nick
from sleekxmpp.stanza.htmlim import HTMLIM
from sleekxmpp.xmlstream import XMLStream, JID from sleekxmpp.xmlstream import XMLStream, JID
from sleekxmpp.xmlstream import ET, register_stanza_plugin from sleekxmpp.xmlstream import ET, register_stanza_plugin
@ -245,7 +244,7 @@ class BaseXMPP(XMLStream):
self.plugin[name].post_inited = True self.plugin[name].post_inited = True
return XMLStream.process(self, *args, **kwargs) return XMLStream.process(self, *args, **kwargs)
def register_plugin(self, plugin, pconfig={}, module=None): def register_plugin(self, plugin, pconfig=None, module=None):
"""Register and configure a plugin for use in this stream. """Register and configure a plugin for use in this stream.
:param plugin: The name of the plugin class. Plugin names must :param plugin: The name of the plugin class. Plugin names must

View File

@ -49,8 +49,13 @@ class ComponentXMPP(BaseXMPP):
Defaults to ``False``. Defaults to ``False``.
""" """
def __init__(self, jid, secret, host=None, port=None, def __init__(self, jid, secret, host=None, port=None, plugin_config=None, plugin_whitelist=None, use_jc_ns=False):
plugin_config={}, plugin_whitelist=[], use_jc_ns=False):
if not plugin_whitelist:
plugin_whitelist = []
if not plugin_config:
plugin_config = {}
if use_jc_ns: if use_jc_ns:
default_ns = 'jabber:client' default_ns = 'jabber:client'
else: else:

View File

@ -187,14 +187,14 @@ class FeatureMechanisms(BasePlugin):
except sasl.SASLCancelled: except sasl.SASLCancelled:
self.attempted_mechs.add(self.mech.name) self.attempted_mechs.add(self.mech.name)
self._send_auth() self._send_auth()
except sasl.SASLFailed:
self.attempted_mechs.add(self.mech.name)
self._send_auth()
except sasl.SASLMutualAuthFailed: except sasl.SASLMutualAuthFailed:
log.error("Mutual authentication failed! " + \ log.error("Mutual authentication failed! " + \
"A security breach is possible.") "A security breach is possible.")
self.attempted_mechs.add(self.mech.name) self.attempted_mechs.add(self.mech.name)
self.xmpp.disconnect() self.xmpp.disconnect()
except sasl.SASLFailed:
self.attempted_mechs.add(self.mech.name)
self._send_auth()
else: else:
resp.send(now=True) resp.send(now=True)
@ -207,13 +207,13 @@ class FeatureMechanisms(BasePlugin):
resp['value'] = self.mech.process(stanza['value']) resp['value'] = self.mech.process(stanza['value'])
except sasl.SASLCancelled: except sasl.SASLCancelled:
self.stanza.Abort(self.xmpp).send() self.stanza.Abort(self.xmpp).send()
except sasl.SASLFailed:
self.stanza.Abort(self.xmpp).send()
except sasl.SASLMutualAuthFailed: except sasl.SASLMutualAuthFailed:
log.error("Mutual authentication failed! " + \ log.error("Mutual authentication failed! " + \
"A security breach is possible.") "A security breach is possible.")
self.attempted_mechs.add(self.mech.name) self.attempted_mechs.add(self.mech.name)
self.xmpp.disconnect() self.xmpp.disconnect()
except sasl.SASLFailed:
self.stanza.Abort(self.xmpp).send()
else: else:
if resp.get_value() == '': if resp.get_value() == '':
resp.del_value() resp.del_value()

View File

@ -24,7 +24,7 @@ class GoogleAuth(ElementBase):
print('setting up google extension') print('setting up google extension')
def get_client_uses_full_bind_result(self): def get_client_uses_full_bind_result(self):
return self.parent()._get_attr(self.disovery_attr) == 'true' return self.parent()._get_attr(self.discovery_attr) == 'true'
def set_client_uses_full_bind_result(self, value): def set_client_uses_full_bind_result(self, value):
print('>>>', value) print('>>>', value)

View File

@ -52,7 +52,7 @@ class Item(ElementBase):
def get_source(self): def get_source(self):
return JID(self._get_attr('source', '')) return JID(self._get_attr('source', ''))
def set_source(self): def set_source(self, value):
self._set_attr('source', str(value)) self._set_attr('source', str(value))

View File

@ -6,8 +6,6 @@
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import logging
from sleekxmpp.stanza import Iq from sleekxmpp.stanza import Iq
from sleekxmpp.xmlstream.handler import Callback from sleekxmpp.xmlstream.handler import Callback
from sleekxmpp.xmlstream.matcher import StanzaPath from sleekxmpp.xmlstream.matcher import StanzaPath

View File

@ -151,7 +151,6 @@ class Form(ElementBase):
return fields return fields
def get_instructions(self): def get_instructions(self):
instructions = ''
instsXML = self.xml.findall('{%s}instructions' % self.namespace) instsXML = self.xml.findall('{%s}instructions' % self.namespace)
return "\n".join([instXML.text for instXML in instsXML]) return "\n".join([instXML.text for instXML in instsXML])

View File

@ -697,7 +697,8 @@ class Remote(object):
if(client.boundjid.bare in cls._sessions): if(client.boundjid.bare in cls._sessions):
raise RemoteException("There already is a session associated with these credentials!") raise RemoteException("There already is a session associated with these credentials!")
else: else:
cls._sessions[client.boundjid.bare] = client; cls._sessions[client.boundjid.bare] = client
def _session_close_callback(): def _session_close_callback():
with Remote._lock: with Remote._lock:
del cls._sessions[client.boundjid.bare] del cls._sessions[client.boundjid.bare]

View File

@ -61,7 +61,7 @@ class XEP_0009(BasePlugin):
iq.enable('rpc_query') iq.enable('rpc_query')
iq['rpc_query']['method_call']['method_name'] = pmethod iq['rpc_query']['method_call']['method_name'] = pmethod
iq['rpc_query']['method_call']['params'] = params iq['rpc_query']['method_call']['params'] = params
return iq; return iq
def make_iq_method_response(self, pid, pto, params): def make_iq_method_response(self, pid, pto, params):
iq = self.xmpp.makeIqResult(pid) iq = self.xmpp.makeIqResult(pid)
@ -93,7 +93,7 @@ class XEP_0009(BasePlugin):
def _item_not_found(self, iq): def _item_not_found(self, iq):
payload = iq.get_payload() payload = iq.get_payload()
iq.reply().error().set_payload(payload); iq.reply().error().set_payload(payload)
iq['error']['code'] = '404' iq['error']['code'] = '404'
iq['error']['type'] = 'cancel' iq['error']['type'] = 'cancel'
iq['error']['condition'] = 'item-not-found' iq['error']['condition'] = 'item-not-found'

View File

@ -324,7 +324,7 @@ class XEP_0030(BasePlugin):
callback -- Optional callback to execute when a reply is callback -- Optional callback to execute when a reply is
received instead of blocking and waiting for received instead of blocking and waiting for
the reply. the reply.
timeout_callback -- Optional callback to execute when no result timeout_callback -- Optional callback to execute when no result
has been received in timeout seconds. has been received in timeout seconds.
""" """
if local is None: if local is None:
@ -408,7 +408,7 @@ class XEP_0030(BasePlugin):
iterator -- If True, return a result set iterator using iterator -- If True, return a result set iterator using
the XEP-0059 plugin, if the plugin is loaded. the XEP-0059 plugin, if the plugin is loaded.
Otherwise the parameter is ignored. Otherwise the parameter is ignored.
timeout_callback -- Optional callback to execute when no result timeout_callback -- Optional callback to execute when no result
has been received in timeout seconds. has been received in timeout seconds.
""" """
if local or local is None and jid is None: if local or local is None and jid is None:
@ -604,7 +604,7 @@ class XEP_0030(BasePlugin):
""" """
self.api['del_features'](jid, node, None, kwargs) self.api['del_features'](jid, node, None, kwargs)
def _run_node_handler(self, htype, jid, node=None, ifrom=None, data={}): def _run_node_handler(self, htype, jid, node=None, ifrom=None, data=None):
""" """
Execute the most specific node handler for the given Execute the most specific node handler for the given
JID/node combination. JID/node combination.
@ -615,6 +615,9 @@ class XEP_0030(BasePlugin):
node -- The node requested. node -- The node requested.
data -- Optional, custom data to pass to the handler. data -- Optional, custom data to pass to the handler.
""" """
if not data:
data = {}
return self.api[htype](jid, node, ifrom, data) return self.api[htype](jid, node, ifrom, data)
def _handle_disco_info(self, iq): def _handle_disco_info(self, iq):

View File

@ -13,15 +13,18 @@ import logging
class Device(object): class Device(object):
""" """
Example implementation of a device readout object. Example implementation of a device readout object.
Is registered in the XEP_0323.register_node call Is registered in the XEP_0323.register_node call
The device object may be any custom implementation to support The device object may be any custom implementation to support
specific devices, but it must implement the functions: specific devices, but it must implement the functions:
has_field has_field
request_fields request_fields
""" """
def __init__(self, nodeId, fields={}): def __init__(self, nodeId, fields=None):
if not fields:
fields = {}
self.nodeId = nodeId self.nodeId = nodeId
self.fields = fields # see fields described below self.fields = fields # see fields described below
# {'type':'numeric', # {'type':'numeric',
@ -38,19 +41,19 @@ class Device(object):
Returns true if the supplied field name exists in this device. Returns true if the supplied field name exists in this device.
Arguments: Arguments:
field -- The field name field -- The field name
""" """
if field in self.fields.keys(): if field in self.fields.keys():
return True; return True
return False; return False
def refresh(self, fields): def refresh(self, fields):
""" """
override method to do the refresh work override method to do the refresh work
refresh values from hardware or other refresh values from hardware or other
""" """
pass pass
def request_fields(self, fields, flags, session, callback): def request_fields(self, fields, flags, session, callback):
""" """
@ -65,7 +68,7 @@ class Device(object):
Formatted as a dictionary like { "flag name": "flag value" ... } Formatted as a dictionary like { "flag name": "flag value" ... }
session -- Session id, only used in the callback as identifier session -- Session id, only used in the callback as identifier
callback -- Callback function to call when data is available. callback -- Callback function to call when data is available.
The callback function must support the following arguments: The callback function must support the following arguments:
session -- Session id, as supplied in the request_fields call session -- Session id, as supplied in the request_fields call
@ -73,11 +76,11 @@ class Device(object):
result -- The current result status of the readout. Valid values are: result -- The current result status of the readout. Valid values are:
"error" - Readout failed. "error" - Readout failed.
"fields" - Contains readout data. "fields" - Contains readout data.
"done" - Indicates that the readout is complete. May contain "done" - Indicates that the readout is complete. May contain
readout data. readout data.
timestamp_block -- [optional] Only applies when result != "error" timestamp_block -- [optional] Only applies when result != "error"
The readout data. Structured as a dictionary: The readout data. Structured as a dictionary:
{ {
timestamp: timestamp for this datablock, timestamp: timestamp for this datablock,
fields: list of field dictionary (one per readout field). fields: list of field dictionary (one per readout field).
readout field dictionary format: readout field dictionary format:
@ -89,10 +92,10 @@ class Device(object):
dataType: The datatype of the field. Only applies to type enum. dataType: The datatype of the field. Only applies to type enum.
flags: [optional] data classifier flags for the field, e.g. momentary flags: [optional] data classifier flags for the field, e.g. momentary
Formatted as a dictionary like { "flag name": "flag value" ... } Formatted as a dictionary like { "flag name": "flag value" ... }
} }
} }
error_msg -- [optional] Only applies when result == "error". error_msg -- [optional] Only applies when result == "error".
Error details when a request failed. Error details when a request failed.
""" """
logging.debug("request_fields called looking for fields %s",fields) logging.debug("request_fields called looking for fields %s",fields)
@ -101,10 +104,10 @@ class Device(object):
for f in fields: for f in fields:
if f not in self.fields.keys(): if f not in self.fields.keys():
self._send_reject(session, callback) self._send_reject(session, callback)
return False; return False
else: else:
# Request all fields # Request all fields
fields = self.fields.keys(); fields = self.fields.keys()
# Refresh data from device # Refresh data from device
@ -114,27 +117,27 @@ class Device(object):
if "momentary" in flags and flags['momentary'] == "true" or \ if "momentary" in flags and flags['momentary'] == "true" or \
"all" in flags and flags['all'] == "true": "all" in flags and flags['all'] == "true":
ts_block = {}; ts_block = {}
timestamp = ""; timestamp = ""
if len(self.momentary_timestamp) > 0: if len(self.momentary_timestamp) > 0:
timestamp = self.momentary_timestamp; timestamp = self.momentary_timestamp
else: else:
timestamp = self._get_timestamp(); timestamp = self._get_timestamp()
field_block = []; field_block = []
for f in self.momentary_data: for f in self.momentary_data:
if f in fields: if f in fields:
field_block.append({"name": f, field_block.append({"name": f,
"type": self.fields[f]["type"], "type": self.fields[f]["type"],
"unit": self.fields[f]["unit"], "unit": self.fields[f]["unit"],
"dataType": self.fields[f]["dataType"], "dataType": self.fields[f]["dataType"],
"value": self.momentary_data[f]["value"], "value": self.momentary_data[f]["value"],
"flags": self.momentary_data[f]["flags"]}); "flags": self.momentary_data[f]["flags"]})
ts_block["timestamp"] = timestamp; ts_block["timestamp"] = timestamp
ts_block["fields"] = field_block; ts_block["fields"] = field_block
callback(session, result="done", nodeId=self.nodeId, timestamp_block=ts_block); callback(session, result="done", nodeId=self.nodeId, timestamp_block=ts_block)
return return
from_flag = self._datetime_flag_parser(flags, 'from') from_flag = self._datetime_flag_parser(flags, 'from')
@ -142,36 +145,36 @@ class Device(object):
for ts in sorted(self.timestamp_data.keys()): for ts in sorted(self.timestamp_data.keys()):
tsdt = datetime.datetime.strptime(ts, "%Y-%m-%dT%H:%M:%S") tsdt = datetime.datetime.strptime(ts, "%Y-%m-%dT%H:%M:%S")
if not from_flag is None: if not from_flag is None:
if tsdt < from_flag: if tsdt < from_flag:
#print (str(tsdt) + " < " + str(from_flag)) #print (str(tsdt) + " < " + str(from_flag))
continue continue
if not to_flag is None: if not to_flag is None:
if tsdt > to_flag: if tsdt > to_flag:
#print (str(tsdt) + " > " + str(to_flag)) #print (str(tsdt) + " > " + str(to_flag))
continue continue
ts_block = {}; ts_block = {}
field_block = []; field_block = []
for f in self.timestamp_data[ts]: for f in self.timestamp_data[ts]:
if f in fields: if f in fields:
field_block.append({"name": f, field_block.append({"name": f,
"type": self.fields[f]["type"], "type": self.fields[f]["type"],
"unit": self.fields[f]["unit"], "unit": self.fields[f]["unit"],
"dataType": self.fields[f]["dataType"], "dataType": self.fields[f]["dataType"],
"value": self.timestamp_data[ts][f]["value"], "value": self.timestamp_data[ts][f]["value"],
"flags": self.timestamp_data[ts][f]["flags"]}); "flags": self.timestamp_data[ts][f]["flags"]})
ts_block["timestamp"] = ts; ts_block["timestamp"] = ts
ts_block["fields"] = field_block; ts_block["fields"] = field_block
callback(session, result="fields", nodeId=self.nodeId, timestamp_block=ts_block); callback(session, result="fields", nodeId=self.nodeId, timestamp_block=ts_block)
callback(session, result="done", nodeId=self.nodeId, timestamp_block=None); callback(session, result="done", nodeId=self.nodeId, timestamp_block=None)
def _datetime_flag_parser(self, flags, flagname): def _datetime_flag_parser(self, flags, flagname):
if not flagname in flags: if not flagname in flags:
return None return None
dt = None dt = None
try: try:
dt = datetime.datetime.strptime(flags[flagname], "%Y-%m-%dT%H:%M:%S") dt = datetime.datetime.strptime(flags[flagname], "%Y-%m-%dT%H:%M:%S")
@ -195,7 +198,7 @@ class Device(object):
session -- Session id, see definition in request_fields function session -- Session id, see definition in request_fields function
callback -- Callback function, see definition in request_fields function callback -- Callback function, see definition in request_fields function
""" """
callback(session, result="error", nodeId=self.nodeId, timestamp_block=None, error_msg="Reject"); callback(session, result="error", nodeId=self.nodeId, timestamp_block=None, error_msg="Reject")
def _add_field(self, name, typename, unit=None, dataType=None): def _add_field(self, name, typename, unit=None, dataType=None):
""" """
@ -207,7 +210,7 @@ class Device(object):
unit -- [optional] only applies to "numeric". Unit for the field. unit -- [optional] only applies to "numeric". Unit for the field.
dataType -- [optional] only applies to "enum". Datatype for the field. dataType -- [optional] only applies to "enum". Datatype for the field.
""" """
self.fields[name] = {"type": typename, "unit": unit, "dataType": dataType}; self.fields[name] = {"type": typename, "unit": unit, "dataType": dataType}
def _add_field_timestamp_data(self, name, timestamp, value, flags=None): def _add_field_timestamp_data(self, name, timestamp, value, flags=None):
""" """
@ -221,12 +224,12 @@ class Device(object):
Formatted as a dictionary like { "flag name": "flag value" ... } Formatted as a dictionary like { "flag name": "flag value" ... }
""" """
if not name in self.fields.keys(): if not name in self.fields.keys():
return False; return False
if not timestamp in self.timestamp_data: if not timestamp in self.timestamp_data:
self.timestamp_data[timestamp] = {}; self.timestamp_data[timestamp] = {}
self.timestamp_data[timestamp][name] = {"value": value, "flags": flags}; self.timestamp_data[timestamp][name] = {"value": value, "flags": flags}
return True; return True
def _add_field_momentary_data(self, name, value, flags=None): def _add_field_momentary_data(self, name, value, flags=None):
""" """
@ -239,17 +242,17 @@ class Device(object):
Formatted as a dictionary like { "flag name": "flag value" ... } Formatted as a dictionary like { "flag name": "flag value" ... }
""" """
if name not in self.fields: if name not in self.fields:
return False; return False
if flags is None: if flags is None:
flags = {}; flags = {}
flags["momentary"] = "true" flags["momentary"] = "true"
self.momentary_data[name] = {"value": value, "flags": flags}; self.momentary_data[name] = {"value": value, "flags": flags}
return True; return True
def _set_momentary_timestamp(self, timestamp): def _set_momentary_timestamp(self, timestamp):
""" """
This function is only for unit testing to produce predictable results. This function is only for unit testing to produce predictable results.
""" """
self.momentary_timestamp = timestamp; self.momentary_timestamp = timestamp

View File

@ -29,12 +29,12 @@ log = logging.getLogger(__name__)
class XEP_0323(BasePlugin): class XEP_0323(BasePlugin):
""" """
XEP-0323: IoT Sensor Data XEP-0323: IoT Sensor Data
This XEP provides the underlying architecture, basic operations and data This XEP provides the underlying architecture, basic operations and data
structures for sensor data communication over XMPP networks. It includes structures for sensor data communication over XMPP networks. It includes
a hardware abstraction model, removing any technical detail implemented a hardware abstraction model, removing any technical detail implemented
in underlying technologies. in underlying technologies.
Also see <http://xmpp.org/extensions/xep-0323.html> Also see <http://xmpp.org/extensions/xep-0323.html>
@ -55,10 +55,10 @@ class XEP_0323(BasePlugin):
Sensordata Event:Rejected -- Received a reject from sensor for a request Sensordata Event:Rejected -- Received a reject from sensor for a request
Sensordata Event:Cancelled -- Received a cancel confirm from sensor Sensordata Event:Cancelled -- Received a cancel confirm from sensor
Sensordata Event:Fields -- Received fields from sensor for a request Sensordata Event:Fields -- Received fields from sensor for a request
This may be triggered multiple times since This may be triggered multiple times since
the sensor can split up its response in the sensor can split up its response in
multiple messages. multiple messages.
Sensordata Event:Failure -- Received a failure indication from sensor Sensordata Event:Failure -- Received a failure indication from sensor
for a request. Typically a comm timeout. for a request. Typically a comm timeout.
Attributes: Attributes:
@ -69,7 +69,7 @@ class XEP_0323(BasePlugin):
relevant to a request's session. This dictionary is used relevant to a request's session. This dictionary is used
both by the client and sensor side. On client side, seqnr both by the client and sensor side. On client side, seqnr
is used as key, while on sensor side, a session_id is used is used as key, while on sensor side, a session_id is used
as key. This ensures that the two will not collide, so as key. This ensures that the two will not collide, so
one instance can be both client and sensor. one instance can be both client and sensor.
Sensor side Sensor side
----------- -----------
@ -89,12 +89,12 @@ class XEP_0323(BasePlugin):
Sensor side Sensor side
----------- -----------
register_node -- Register a sensor as available from this XMPP register_node -- Register a sensor as available from this XMPP
instance. instance.
Client side Client side
----------- -----------
request_data -- Initiates a request for data from one or more request_data -- Initiates a request for data from one or more
sensors. Non-blocking, a callback function will sensors. Non-blocking, a callback function will
be called when data is available. be called when data is available.
@ -102,7 +102,7 @@ class XEP_0323(BasePlugin):
name = 'xep_0323' name = 'xep_0323'
description = 'XEP-0323 Internet of Things - Sensor Data' description = 'XEP-0323 Internet of Things - Sensor Data'
dependencies = set(['xep_0030']) dependencies = set(['xep_0030'])
stanza = stanza stanza = stanza
@ -155,11 +155,11 @@ class XEP_0323(BasePlugin):
self._handle_event_started)) self._handle_event_started))
# Server side dicts # Server side dicts
self.nodes = {}; self.nodes = {}
self.sessions = {}; self.sessions = {}
self.last_seqnr = 0; self.last_seqnr = 0
self.seqnr_lock = Lock(); self.seqnr_lock = Lock()
## For testning only ## For testning only
self.test_authenticated_from = "" self.test_authenticated_from = ""
@ -182,7 +182,7 @@ class XEP_0323(BasePlugin):
def plugin_end(self): def plugin_end(self):
""" Stop the XEP-0323 plugin """ """ Stop the XEP-0323 plugin """
self.sessions.clear(); self.sessions.clear()
self.xmpp.remove_handler('Sensordata Event:Req') self.xmpp.remove_handler('Sensordata Event:Req')
self.xmpp.remove_handler('Sensordata Event:Accepted') self.xmpp.remove_handler('Sensordata Event:Accepted')
self.xmpp.remove_handler('Sensordata Event:Rejected') self.xmpp.remove_handler('Sensordata Event:Rejected')
@ -198,9 +198,9 @@ class XEP_0323(BasePlugin):
def register_node(self, nodeId, device, commTimeout, sourceId=None, cacheType=None): def register_node(self, nodeId, device, commTimeout, sourceId=None, cacheType=None):
""" """
Register a sensor/device as available for serving of data through this XMPP Register a sensor/device as available for serving of data through this XMPP
instance. instance.
The device object may by any custom implementation to support The device object may by any custom implementation to support
specific devices, but it must implement the functions: specific devices, but it must implement the functions:
has_field has_field
request_fields request_fields
@ -212,25 +212,25 @@ class XEP_0323(BasePlugin):
commTimeout -- Time in seconds to wait between each callback from device during commTimeout -- Time in seconds to wait between each callback from device during
a data readout. Float. a data readout. Float.
sourceId -- [optional] identifying the data source controlling the device sourceId -- [optional] identifying the data source controlling the device
cacheType -- [optional] narrowing down the search to a specific kind of node cacheType -- [optional] narrowing down the search to a specific kind of node
""" """
self.nodes[nodeId] = {"device": device, self.nodes[nodeId] = {"device": device,
"commTimeout": commTimeout, "commTimeout": commTimeout,
"sourceId": sourceId, "sourceId": sourceId,
"cacheType": cacheType}; "cacheType": cacheType}
def _set_authenticated(self, auth=''): def _set_authenticated(self, auth=''):
""" Internal testing function """ """ Internal testing function """
self.test_authenticated_from = auth; self.test_authenticated_from = auth
def _handle_event_req(self, iq): def _handle_event_req(self, iq):
""" """
Event handler for reception of an Iq with req - this is a request. Event handler for reception of an Iq with req - this is a request.
Verifies that Verifies that
- all the requested nodes are available - all the requested nodes are available
- at least one of the requested fields is available from at least - at least one of the requested fields is available from at least
one of the nodes one of the nodes
If the request passes verification, an accept response is sent, and If the request passes verification, an accept response is sent, and
@ -238,42 +238,42 @@ class XEP_0323(BasePlugin):
If the verification fails, a reject message is sent. If the verification fails, a reject message is sent.
""" """
seqnr = iq['req']['seqnr']; seqnr = iq['req']['seqnr']
error_msg = ''; error_msg = ''
req_ok = True; req_ok = True
# Authentication # Authentication
if len(self.test_authenticated_from) > 0 and not iq['from'] == self.test_authenticated_from: if len(self.test_authenticated_from) > 0 and not iq['from'] == self.test_authenticated_from:
# Invalid authentication # Invalid authentication
req_ok = False; req_ok = False
error_msg = "Access denied"; error_msg = "Access denied"
# Nodes # Nodes
process_nodes = []; process_nodes = []
if len(iq['req']['nodes']) > 0: if len(iq['req']['nodes']) > 0:
for n in iq['req']['nodes']: for n in iq['req']['nodes']:
if not n['nodeId'] in self.nodes: if not n['nodeId'] in self.nodes:
req_ok = False; req_ok = False
error_msg = "Invalid nodeId " + n['nodeId']; error_msg = "Invalid nodeId " + n['nodeId']
process_nodes = [n['nodeId'] for n in iq['req']['nodes']]; process_nodes = [n['nodeId'] for n in iq['req']['nodes']]
else: else:
process_nodes = self.nodes.keys(); process_nodes = self.nodes.keys()
# Fields - if we just find one we are happy, otherwise we reject # Fields - if we just find one we are happy, otherwise we reject
process_fields = []; process_fields = []
if len(iq['req']['fields']) > 0: if len(iq['req']['fields']) > 0:
found = False found = False
for f in iq['req']['fields']: for f in iq['req']['fields']:
for node in self.nodes: for node in self.nodes:
if self.nodes[node]["device"].has_field(f['name']): if self.nodes[node]["device"].has_field(f['name']):
found = True; found = True
break; break
if not found: if not found:
req_ok = False; req_ok = False
error_msg = "Invalid field " + f['name']; error_msg = "Invalid field " + f['name']
process_fields = [f['name'] for n in iq['req']['fields']]; process_fields = [f['name'] for n in iq['req']['fields']]
req_flags = iq['req']._get_flags(); req_flags = iq['req']._get_flags()
request_delay_sec = None request_delay_sec = None
if 'when' in req_flags: if 'when' in req_flags:
@ -283,7 +283,7 @@ class XEP_0323(BasePlugin):
try: try:
dt = datetime.datetime.strptime(req_flags['when'], "%Y-%m-%dT%H:%M:%S") dt = datetime.datetime.strptime(req_flags['when'], "%Y-%m-%dT%H:%M:%S")
except ValueError: except ValueError:
req_ok = False; req_ok = False
error_msg = "Invalid datetime in 'when' flag, please use ISO format (i.e. 2013-04-05T15:00:03)." error_msg = "Invalid datetime in 'when' flag, please use ISO format (i.e. 2013-04-05T15:00:03)."
if not dt is None: if not dt is None:
@ -292,30 +292,30 @@ class XEP_0323(BasePlugin):
dtdiff = dt - dtnow dtdiff = dt - dtnow
request_delay_sec = dtdiff.seconds + dtdiff.days * 24 * 3600 request_delay_sec = dtdiff.seconds + dtdiff.days * 24 * 3600
if request_delay_sec <= 0: if request_delay_sec <= 0:
req_ok = False; req_ok = False
error_msg = "Invalid datetime in 'when' flag, cannot set a time in the past. Current time: " + dtnow.isoformat(); error_msg = "Invalid datetime in 'when' flag, cannot set a time in the past. Current time: " + dtnow.isoformat()
if req_ok: if req_ok:
session = self._new_session(); session = self._new_session()
self.sessions[session] = {"from": iq['from'], "to": iq['to'], "seqnr": seqnr}; self.sessions[session] = {"from": iq['from'], "to": iq['to'], "seqnr": seqnr}
self.sessions[session]["commTimers"] = {}; self.sessions[session]["commTimers"] = {}
self.sessions[session]["nodeDone"] = {}; self.sessions[session]["nodeDone"] = {}
#print("added session: " + str(self.sessions)) #print("added session: " + str(self.sessions))
iq.reply(); iq.reply()
iq['accepted']['seqnr'] = seqnr; iq['accepted']['seqnr'] = seqnr
if not request_delay_sec is None: if not request_delay_sec is None:
iq['accepted']['queued'] = "true" iq['accepted']['queued'] = "true"
iq.send(block=False); iq.send(block=False)
self.sessions[session]["node_list"] = process_nodes; self.sessions[session]["node_list"] = process_nodes
if not request_delay_sec is None: if not request_delay_sec is None:
# Delay request to requested time # Delay request to requested time
timer = Timer(request_delay_sec, self._event_delayed_req, args=(session, process_fields, req_flags)) timer = Timer(request_delay_sec, self._event_delayed_req, args=(session, process_fields, req_flags))
self.sessions[session]["commTimers"]["delaytimer"] = timer; self.sessions[session]["commTimers"]["delaytimer"] = timer
timer.start(); timer.start()
return return
if self.threaded: if self.threaded:
@ -324,19 +324,19 @@ class XEP_0323(BasePlugin):
tr_req.start() tr_req.start()
#print("started thread") #print("started thread")
else: else:
self._threaded_node_request(session, process_fields, req_flags); self._threaded_node_request(session, process_fields, req_flags)
else: else:
iq.reply(); iq.reply()
iq['type'] = 'error'; iq['type'] = 'error'
iq['rejected']['seqnr'] = seqnr; iq['rejected']['seqnr'] = seqnr
iq['rejected']['error'] = error_msg; iq['rejected']['error'] = error_msg
iq.send(block=False); iq.send(block=False)
def _threaded_node_request(self, session, process_fields, flags): def _threaded_node_request(self, session, process_fields, flags):
""" """
Helper function to handle the device readouts in a separate thread. Helper function to handle the device readouts in a separate thread.
Arguments: Arguments:
session -- The request session id session -- The request session id
process_fields -- The fields to request from the devices process_fields -- The fields to request from the devices
@ -344,41 +344,41 @@ class XEP_0323(BasePlugin):
Formatted as a dictionary like { "flag name": "flag value" ... } Formatted as a dictionary like { "flag name": "flag value" ... }
""" """
for node in self.sessions[session]["node_list"]: for node in self.sessions[session]["node_list"]:
self.sessions[session]["nodeDone"][node] = False; self.sessions[session]["nodeDone"][node] = False
for node in self.sessions[session]["node_list"]: for node in self.sessions[session]["node_list"]:
timer = TimerReset(self.nodes[node]['commTimeout'], self._event_comm_timeout, args=(session, node)); timer = TimerReset(self.nodes[node]['commTimeout'], self._event_comm_timeout, args=(session, node))
self.sessions[session]["commTimers"][node] = timer; self.sessions[session]["commTimers"][node] = timer
#print("Starting timer " + str(timer) + ", timeout: " + str(self.nodes[node]['commTimeout'])) #print("Starting timer " + str(timer) + ", timeout: " + str(self.nodes[node]['commTimeout']))
timer.start(); timer.start()
self.nodes[node]['device'].request_fields(process_fields, flags=flags, session=session, callback=self._device_field_request_callback); self.nodes[node]['device'].request_fields(process_fields, flags=flags, session=session, callback=self._device_field_request_callback)
def _event_comm_timeout(self, session, nodeId): def _event_comm_timeout(self, session, nodeId):
""" """
Triggered if any of the readout operations timeout. Triggered if any of the readout operations timeout.
Sends a failure message back to the client, stops communicating Sends a failure message back to the client, stops communicating
with the failing device. with the failing device.
Arguments: Arguments:
session -- The request session id session -- The request session id
nodeId -- The id of the device which timed out nodeId -- The id of the device which timed out
""" """
msg = self.xmpp.Message(); msg = self.xmpp.Message()
msg['from'] = self.sessions[session]['to']; msg['from'] = self.sessions[session]['to']
msg['to'] = self.sessions[session]['from']; msg['to'] = self.sessions[session]['from']
msg['failure']['seqnr'] = self.sessions[session]['seqnr']; msg['failure']['seqnr'] = self.sessions[session]['seqnr']
msg['failure']['error']['text'] = "Timeout"; msg['failure']['error']['text'] = "Timeout"
msg['failure']['error']['nodeId'] = nodeId; msg['failure']['error']['nodeId'] = nodeId
msg['failure']['error']['timestamp'] = datetime.datetime.now().replace(microsecond=0).isoformat(); msg['failure']['error']['timestamp'] = datetime.datetime.now().replace(microsecond=0).isoformat()
# Drop communication with this device and check if we are done # Drop communication with this device and check if we are done
self.sessions[session]["nodeDone"][nodeId] = True; self.sessions[session]["nodeDone"][nodeId] = True
if (self._all_nodes_done(session)): if (self._all_nodes_done(session)):
msg['failure']['done'] = 'true'; msg['failure']['done'] = 'true'
msg.send(); msg.send()
# The session is complete, delete it # The session is complete, delete it
#print("del session " + session + " due to timeout") #print("del session " + session + " due to timeout")
del self.sessions[session]; del self.sessions[session]
def _event_delayed_req(self, session, process_fields, req_flags): def _event_delayed_req(self, session, process_fields, req_flags):
""" """
@ -390,47 +390,47 @@ class XEP_0323(BasePlugin):
flags -- [optional] flags to pass to the devices, e.g. momentary flags -- [optional] flags to pass to the devices, e.g. momentary
Formatted as a dictionary like { "flag name": "flag value" ... } Formatted as a dictionary like { "flag name": "flag value" ... }
""" """
msg = self.xmpp.Message(); msg = self.xmpp.Message()
msg['from'] = self.sessions[session]['to']; msg['from'] = self.sessions[session]['to']
msg['to'] = self.sessions[session]['from']; msg['to'] = self.sessions[session]['from']
msg['started']['seqnr'] = self.sessions[session]['seqnr']; msg['started']['seqnr'] = self.sessions[session]['seqnr']
msg.send(); msg.send()
if self.threaded: if self.threaded:
tr_req = Thread(target=self._threaded_node_request, args=(session, process_fields, req_flags)) tr_req = Thread(target=self._threaded_node_request, args=(session, process_fields, req_flags))
tr_req.start() tr_req.start()
else: else:
self._threaded_node_request(session, process_fields, req_flags); self._threaded_node_request(session, process_fields, req_flags)
def _all_nodes_done(self, session): def _all_nodes_done(self, session):
""" """
Checks wheter all devices are done replying to the readout. Checks wheter all devices are done replying to the readout.
Arguments: Arguments:
session -- The request session id session -- The request session id
""" """
for n in self.sessions[session]["nodeDone"]: for n in self.sessions[session]["nodeDone"]:
if not self.sessions[session]["nodeDone"][n]: if not self.sessions[session]["nodeDone"][n]:
return False; return False
return True; return True
def _device_field_request_callback(self, session, nodeId, result, timestamp_block, error_msg=None): def _device_field_request_callback(self, session, nodeId, result, timestamp_block, error_msg=None):
""" """
Callback function called by the devices when they have any additional data. Callback function called by the devices when they have any additional data.
Composes a message with the data and sends it back to the client, and resets Composes a message with the data and sends it back to the client, and resets
the timeout timer for the device. the timeout timer for the device.
Arguments: Arguments:
session -- The request session id session -- The request session id
nodeId -- The device id which initiated the callback nodeId -- The device id which initiated the callback
result -- The current result status of the readout. Valid values are: result -- The current result status of the readout. Valid values are:
"error" - Readout failed. "error" - Readout failed.
"fields" - Contains readout data. "fields" - Contains readout data.
"done" - Indicates that the readout is complete. May contain "done" - Indicates that the readout is complete. May contain
readout data. readout data.
timestamp_block -- [optional] Only applies when result != "error" timestamp_block -- [optional] Only applies when result != "error"
The readout data. Structured as a dictionary: The readout data. Structured as a dictionary:
{ {
timestamp: timestamp for this datablock, timestamp: timestamp for this datablock,
fields: list of field dictionary (one per readout field). fields: list of field dictionary (one per readout field).
readout field dictionary format: readout field dictionary format:
@ -442,7 +442,7 @@ class XEP_0323(BasePlugin):
dataType: The datatype of the field. Only applies to type enum. dataType: The datatype of the field. Only applies to type enum.
flags: [optional] data classifier flags for the field, e.g. momentary flags: [optional] data classifier flags for the field, e.g. momentary
Formatted as a dictionary like { "flag name": "flag value" ... } Formatted as a dictionary like { "flag name": "flag value" ... }
} }
} }
error_msg -- [optional] Only applies when result == "error". error_msg -- [optional] Only applies when result == "error".
Error details when a request failed. Error details when a request failed.
@ -452,99 +452,99 @@ class XEP_0323(BasePlugin):
return return
if result == "error": if result == "error":
self.sessions[session]["commTimers"][nodeId].cancel(); self.sessions[session]["commTimers"][nodeId].cancel()
msg = self.xmpp.Message(); msg = self.xmpp.Message()
msg['from'] = self.sessions[session]['to']; msg['from'] = self.sessions[session]['to']
msg['to'] = self.sessions[session]['from']; msg['to'] = self.sessions[session]['from']
msg['failure']['seqnr'] = self.sessions[session]['seqnr']; msg['failure']['seqnr'] = self.sessions[session]['seqnr']
msg['failure']['error']['text'] = error_msg; msg['failure']['error']['text'] = error_msg
msg['failure']['error']['nodeId'] = nodeId; msg['failure']['error']['nodeId'] = nodeId
msg['failure']['error']['timestamp'] = datetime.datetime.now().replace(microsecond=0).isoformat(); msg['failure']['error']['timestamp'] = datetime.datetime.now().replace(microsecond=0).isoformat()
# Drop communication with this device and check if we are done # Drop communication with this device and check if we are done
self.sessions[session]["nodeDone"][nodeId] = True; self.sessions[session]["nodeDone"][nodeId] = True
if (self._all_nodes_done(session)): if (self._all_nodes_done(session)):
msg['failure']['done'] = 'true'; msg['failure']['done'] = 'true'
# The session is complete, delete it # The session is complete, delete it
# print("del session " + session + " due to error") # print("del session " + session + " due to error")
del self.sessions[session]; del self.sessions[session]
msg.send(); msg.send()
else: else:
msg = self.xmpp.Message(); msg = self.xmpp.Message()
msg['from'] = self.sessions[session]['to']; msg['from'] = self.sessions[session]['to']
msg['to'] = self.sessions[session]['from']; msg['to'] = self.sessions[session]['from']
msg['fields']['seqnr'] = self.sessions[session]['seqnr']; msg['fields']['seqnr'] = self.sessions[session]['seqnr']
if timestamp_block is not None and len(timestamp_block) > 0: if timestamp_block is not None and len(timestamp_block) > 0:
node = msg['fields'].add_node(nodeId); node = msg['fields'].add_node(nodeId)
ts = node.add_timestamp(timestamp_block["timestamp"]); ts = node.add_timestamp(timestamp_block["timestamp"])
for f in timestamp_block["fields"]: for f in timestamp_block["fields"]:
data = ts.add_data( typename=f['type'], data = ts.add_data( typename=f['type'],
name=f['name'], name=f['name'],
value=f['value'], value=f['value'],
unit=f['unit'], unit=f['unit'],
dataType=f['dataType'], dataType=f['dataType'],
flags=f['flags']); flags=f['flags'])
if result == "done": if result == "done":
self.sessions[session]["commTimers"][nodeId].cancel(); self.sessions[session]["commTimers"][nodeId].cancel()
self.sessions[session]["nodeDone"][nodeId] = True; self.sessions[session]["nodeDone"][nodeId] = True
msg['fields']['done'] = 'true'; msg['fields']['done'] = 'true'
if (self._all_nodes_done(session)): if (self._all_nodes_done(session)):
# The session is complete, delete it # The session is complete, delete it
# print("del session " + session + " due to complete") # print("del session " + session + " due to complete")
del self.sessions[session]; del self.sessions[session]
else: else:
# Restart comm timer # Restart comm timer
self.sessions[session]["commTimers"][nodeId].reset(); self.sessions[session]["commTimers"][nodeId].reset()
msg.send(); msg.send()
def _handle_event_cancel(self, iq): def _handle_event_cancel(self, iq):
""" Received Iq with cancel - this is a cancel request. """ Received Iq with cancel - this is a cancel request.
Delete the session and confirm. """ Delete the session and confirm. """
seqnr = iq['cancel']['seqnr']; seqnr = iq['cancel']['seqnr']
# Find the session # Find the session
for s in self.sessions: for s in self.sessions:
if self.sessions[s]['from'] == iq['from'] and self.sessions[s]['to'] == iq['to'] and self.sessions[s]['seqnr'] == seqnr: if self.sessions[s]['from'] == iq['from'] and self.sessions[s]['to'] == iq['to'] and self.sessions[s]['seqnr'] == seqnr:
# found it. Cancel all timers # found it. Cancel all timers
for n in self.sessions[s]["commTimers"]: for n in self.sessions[s]["commTimers"]:
self.sessions[s]["commTimers"][n].cancel(); self.sessions[s]["commTimers"][n].cancel()
# Confirm # Confirm
iq.reply(); iq.reply()
iq['type'] = 'result'; iq['type'] = 'result'
iq['cancelled']['seqnr'] = seqnr; iq['cancelled']['seqnr'] = seqnr
iq.send(block=False); iq.send(block=False)
# Delete session # Delete session
del self.sessions[s] del self.sessions[s]
return return
# Could not find session, send reject # Could not find session, send reject
iq.reply(); iq.reply()
iq['type'] = 'error'; iq['type'] = 'error'
iq['rejected']['seqnr'] = seqnr; iq['rejected']['seqnr'] = seqnr
iq['rejected']['error'] = "Cancel request received, no matching request is active."; iq['rejected']['error'] = "Cancel request received, no matching request is active."
iq.send(block=False); iq.send(block=False)
# ================================================================= # =================================================================
# Client side (data retriever) API # Client side (data retriever) API
def request_data(self, from_jid, to_jid, callback, nodeIds=None, fields=None, flags=None): def request_data(self, from_jid, to_jid, callback, nodeIds=None, fields=None, flags=None):
""" """
Called on the client side to initiade a data readout. Called on the client side to initiade a data readout.
Composes a message with the request and sends it to the device(s). Composes a message with the request and sends it to the device(s).
Does not block, the callback will be called when data is available. Does not block, the callback will be called when data is available.
Arguments: Arguments:
from_jid -- The jid of the requester from_jid -- The jid of the requester
to_jid -- The jid of the device(s) to_jid -- The jid of the device(s)
callback -- The callback function to call when data is availble. callback -- The callback function to call when data is availble.
The callback function must support the following arguments: The callback function must support the following arguments:
from_jid -- The jid of the responding device(s) from_jid -- The jid of the responding device(s)
@ -565,7 +565,7 @@ class XEP_0323(BasePlugin):
The timestamp of data in this callback. One callback will only The timestamp of data in this callback. One callback will only
contain data from one timestamp. contain data from one timestamp.
fields -- [optional] Mandatory when result == "fields". fields -- [optional] Mandatory when result == "fields".
List of field dictionaries representing the readout data. List of field dictionaries representing the readout data.
Dictionary format: Dictionary format:
{ {
typename: The field type (numeric, boolean, dateTime, timeSpan, string, enum) typename: The field type (numeric, boolean, dateTime, timeSpan, string, enum)
@ -575,11 +575,11 @@ class XEP_0323(BasePlugin):
dataType: The datatype of the field. Only applies to type enum. dataType: The datatype of the field. Only applies to type enum.
flags: [optional] data classifier flags for the field, e.g. momentary. flags: [optional] data classifier flags for the field, e.g. momentary.
Formatted as a dictionary like { "flag name": "flag value" ... } Formatted as a dictionary like { "flag name": "flag value" ... }
} }
error_msg -- [optional] Mandatory when result == "rejected" or "failure". error_msg -- [optional] Mandatory when result == "rejected" or "failure".
Details about why the request is rejected or failed. Details about why the request is rejected or failed.
"rejected" means that the request is stopped, but note that the "rejected" means that the request is stopped, but note that the
request will continue even after a "failure". "failure" only means request will continue even after a "failure". "failure" only means
that communication was stopped to that specific device, other that communication was stopped to that specific device, other
device(s) (if any) will continue their readout. device(s) (if any) will continue their readout.
@ -593,131 +593,131 @@ class XEP_0323(BasePlugin):
session -- Session identifier. Client can use this as a reference to cancel session -- Session identifier. Client can use this as a reference to cancel
the request. the request.
""" """
iq = self.xmpp.Iq(); iq = self.xmpp.Iq()
iq['from'] = from_jid; iq['from'] = from_jid
iq['to'] = to_jid; iq['to'] = to_jid
iq['type'] = "get"; iq['type'] = "get"
seqnr = self._get_new_seqnr(); seqnr = self._get_new_seqnr()
iq['id'] = seqnr; iq['id'] = seqnr
iq['req']['seqnr'] = seqnr; iq['req']['seqnr'] = seqnr
if nodeIds is not None: if nodeIds is not None:
for nodeId in nodeIds: for nodeId in nodeIds:
iq['req'].add_node(nodeId); iq['req'].add_node(nodeId)
if fields is not None: if fields is not None:
for field in fields: for field in fields:
iq['req'].add_field(field); iq['req'].add_field(field)
iq['req']._set_flags(flags); iq['req']._set_flags(flags)
self.sessions[seqnr] = {"from": iq['from'], "to": iq['to'], "seqnr": seqnr, "callback": callback}; self.sessions[seqnr] = {"from": iq['from'], "to": iq['to'], "seqnr": seqnr, "callback": callback}
iq.send(block=False); iq.send(block=False)
return seqnr; return seqnr
def cancel_request(self, session): def cancel_request(self, session):
""" """
Called on the client side to cancel a request for data readout. Called on the client side to cancel a request for data readout.
Composes a message with the cancellation and sends it to the device(s). Composes a message with the cancellation and sends it to the device(s).
Does not block, the callback will be called when cancellation is Does not block, the callback will be called when cancellation is
confirmed. confirmed.
Arguments: Arguments:
session -- The session id of the request to cancel session -- The session id of the request to cancel
""" """
seqnr = session seqnr = session
iq = self.xmpp.Iq(); iq = self.xmpp.Iq()
iq['from'] = self.sessions[seqnr]['from'] iq['from'] = self.sessions[seqnr]['from']
iq['to'] = self.sessions[seqnr]['to']; iq['to'] = self.sessions[seqnr]['to']
iq['type'] = "get"; iq['type'] = "get"
iq['id'] = seqnr; iq['id'] = seqnr
iq['cancel']['seqnr'] = seqnr; iq['cancel']['seqnr'] = seqnr
iq.send(block=False); iq.send(block=False)
def _get_new_seqnr(self): def _get_new_seqnr(self):
""" Returns a unique sequence number (unique across threads) """ """ Returns a unique sequence number (unique across threads) """
self.seqnr_lock.acquire(); self.seqnr_lock.acquire()
self.last_seqnr = self.last_seqnr + 1; self.last_seqnr += 1
self.seqnr_lock.release(); self.seqnr_lock.release()
return str(self.last_seqnr); return str(self.last_seqnr)
def _handle_event_accepted(self, iq): def _handle_event_accepted(self, iq):
""" Received Iq with accepted - request was accepted """ """ Received Iq with accepted - request was accepted """
seqnr = iq['accepted']['seqnr']; seqnr = iq['accepted']['seqnr']
result = "accepted" result = "accepted"
if iq['accepted']['queued'] == 'true': if iq['accepted']['queued'] == 'true':
result = "queued" result = "queued"
callback = self.sessions[seqnr]["callback"]; callback = self.sessions[seqnr]["callback"]
callback(from_jid=iq['from'], result=result); callback(from_jid=iq['from'], result=result)
def _handle_event_rejected(self, iq): def _handle_event_rejected(self, iq):
""" Received Iq with rejected - this is a reject. """ Received Iq with rejected - this is a reject.
Delete the session. """ Delete the session. """
seqnr = iq['rejected']['seqnr']; seqnr = iq['rejected']['seqnr']
callback = self.sessions[seqnr]["callback"]; callback = self.sessions[seqnr]["callback"]
callback(from_jid=iq['from'], result="rejected", error_msg=iq['rejected']['error']); callback(from_jid=iq['from'], result="rejected", error_msg=iq['rejected']['error'])
# Session terminated # Session terminated
del self.sessions[seqnr]; del self.sessions[seqnr]
def _handle_event_cancelled(self, iq): def _handle_event_cancelled(self, iq):
""" """
Received Iq with cancelled - this is a cancel confirm. Received Iq with cancelled - this is a cancel confirm.
Delete the session. Delete the session.
""" """
#print("Got cancelled") #print("Got cancelled")
seqnr = iq['cancelled']['seqnr']; seqnr = iq['cancelled']['seqnr']
callback = self.sessions[seqnr]["callback"]; callback = self.sessions[seqnr]["callback"]
callback(from_jid=iq['from'], result="cancelled"); callback(from_jid=iq['from'], result="cancelled")
# Session cancelled # Session cancelled
del self.sessions[seqnr]; del self.sessions[seqnr]
def _handle_event_fields(self, msg): def _handle_event_fields(self, msg):
""" """
Received Msg with fields - this is a data reponse to a request. Received Msg with fields - this is a data reponse to a request.
If this is the last data block, issue a "done" callback. If this is the last data block, issue a "done" callback.
""" """
seqnr = msg['fields']['seqnr']; seqnr = msg['fields']['seqnr']
callback = self.sessions[seqnr]["callback"]; callback = self.sessions[seqnr]["callback"]
for node in msg['fields']['nodes']: for node in msg['fields']['nodes']:
for ts in node['timestamps']: for ts in node['timestamps']:
fields = []; fields = []
for d in ts['datas']: for d in ts['datas']:
field_block = {}; field_block = {}
field_block["name"] = d['name']; field_block["name"] = d['name']
field_block["typename"] = d._get_typename(); field_block["typename"] = d._get_typename()
field_block["value"] = d['value']; field_block["value"] = d['value']
if not d['unit'] == "": field_block["unit"] = d['unit']; if not d['unit'] == "": field_block["unit"] = d['unit'];
if not d['dataType'] == "": field_block["dataType"] = d['dataType']; if not d['dataType'] == "": field_block["dataType"] = d['dataType'];
flags = d._get_flags(); flags = d._get_flags()
if not len(flags) == 0: if not len(flags) == 0:
field_block["flags"] = flags; field_block["flags"] = flags
fields.append(field_block); fields.append(field_block)
callback(from_jid=msg['from'], result="fields", nodeId=node['nodeId'], timestamp=ts['value'], fields=fields)
callback(from_jid=msg['from'], result="fields", nodeId=node['nodeId'], timestamp=ts['value'], fields=fields);
if msg['fields']['done'] == "true": if msg['fields']['done'] == "true":
callback(from_jid=msg['from'], result="done"); callback(from_jid=msg['from'], result="done")
# Session done # Session done
del self.sessions[seqnr]; del self.sessions[seqnr]
def _handle_event_failure(self, msg): def _handle_event_failure(self, msg):
"""
Received Msg with failure - our request failed
Delete the session.
""" """
seqnr = msg['failure']['seqnr']; Received Msg with failure - our request failed
callback = self.sessions[seqnr]["callback"]; Delete the session.
callback(from_jid=msg['from'], result="failure", nodeId=msg['failure']['error']['nodeId'], timestamp=msg['failure']['error']['timestamp'], error_msg=msg['failure']['error']['text']); """
seqnr = msg['failure']['seqnr']
callback = self.sessions[seqnr]["callback"]
callback(from_jid=msg['from'], result="failure", nodeId=msg['failure']['error']['nodeId'], timestamp=msg['failure']['error']['timestamp'], error_msg=msg['failure']['error']['text'])
# Session failed # Session failed
del self.sessions[seqnr]; del self.sessions[seqnr]
def _handle_event_started(self, msg): def _handle_event_started(self, msg):
"""
Received Msg with started - our request was queued and is now started.
""" """
seqnr = msg['started']['seqnr']; Received Msg with started - our request was queued and is now started.
callback = self.sessions[seqnr]["callback"]; """
callback(from_jid=msg['from'], result="started"); seqnr = msg['started']['seqnr']
callback = self.sessions[seqnr]["callback"]
callback(from_jid=msg['from'], result="started")

View File

@ -20,14 +20,14 @@ class Sensordata(ElementBase):
interfaces = set(tuple()) interfaces = set(tuple())
class FieldTypes(): class FieldTypes():
""" """
All field types are optional booleans that default to False All field types are optional booleans that default to False
""" """
field_types = set([ 'momentary','peak','status','computed','identity','historicalSecond','historicalMinute','historicalHour', \ field_types = set([ 'momentary','peak','status','computed','identity','historicalSecond','historicalMinute','historicalHour', \
'historicalDay','historicalWeek','historicalMonth','historicalQuarter','historicalYear','historicalOther']) 'historicalDay','historicalWeek','historicalMonth','historicalQuarter','historicalYear','historicalOther'])
class FieldStatus(): class FieldStatus():
""" """
All field statuses are optional booleans that default to False All field statuses are optional booleans that default to False
""" """
field_status = set([ 'missing','automaticEstimate','manualEstimate','manualReadout','automaticReadout','timeOffset','warning','error', \ field_status = set([ 'missing','automaticEstimate','manualEstimate','manualReadout','automaticReadout','timeOffset','warning','error', \
@ -38,12 +38,12 @@ class Request(ElementBase):
name = 'req' name = 'req'
plugin_attrib = name plugin_attrib = name
interfaces = set(['seqnr','nodes','fields','serviceToken','deviceToken','userToken','from','to','when','historical','all']) interfaces = set(['seqnr','nodes','fields','serviceToken','deviceToken','userToken','from','to','when','historical','all'])
interfaces.update(FieldTypes.field_types); interfaces.update(FieldTypes.field_types)
_flags = set(['serviceToken','deviceToken','userToken','from','to','when','historical','all']); _flags = set(['serviceToken','deviceToken','userToken','from','to','when','historical','all'])
_flags.update(FieldTypes.field_types); _flags.update(FieldTypes.field_types)
def __init__(self, xml=None, parent=None): def __init__(self, xml=None, parent=None):
ElementBase.__init__(self, xml, parent); ElementBase.__init__(self, xml, parent)
self._nodes = set() self._nodes = set()
self._fields = set() self._fields = set()
@ -64,27 +64,27 @@ class Request(ElementBase):
def _get_flags(self): def _get_flags(self):
""" """
Helper function for getting of flags. Returns all flags in Helper function for getting of flags. Returns all flags in
dictionary format: { "flag name": "flag value" ... } dictionary format: { "flag name": "flag value" ... }
""" """
flags = {}; flags = {}
for f in self._flags: for f in self._flags:
if not self[f] == "": if not self[f] == "":
flags[f] = self[f]; flags[f] = self[f]
return flags; return flags
def _set_flags(self, flags): def _set_flags(self, flags):
""" """
Helper function for setting of flags. Helper function for setting of flags.
Arguments: Arguments:
flags -- Flags in dictionary format: { "flag name": "flag value" ... } flags -- Flags in dictionary format: { "flag name": "flag value" ... }
""" """
for f in self._flags: for f in self._flags:
if flags is not None and f in flags: if flags is not None and f in flags:
self[f] = flags[f]; self[f] = flags[f]
else: else:
self[f] = None; self[f] = None
def add_node(self, nodeId, sourceId=None, cacheType=None): def add_node(self, nodeId, sourceId=None, cacheType=None):
""" """
@ -94,7 +94,7 @@ class Request(ElementBase):
Arguments: Arguments:
nodeId -- The ID for the node. nodeId -- The ID for the node.
sourceId -- [optional] identifying the data source controlling the device sourceId -- [optional] identifying the data source controlling the device
cacheType -- [optional] narrowing down the search to a specific kind of node cacheType -- [optional] narrowing down the search to a specific kind of node
""" """
if nodeId not in self._nodes: if nodeId not in self._nodes:
self._nodes.add((nodeId)) self._nodes.add((nodeId))
@ -269,7 +269,7 @@ class Error(ElementBase):
:param value: string :param value: string
""" """
self.xml.text = value; self.xml.text = value
return self return self
def del_text(self): def del_text(self):
@ -292,7 +292,7 @@ class Fields(ElementBase):
interfaces = set(['seqnr','done','nodes']) interfaces = set(['seqnr','done','nodes'])
def __init__(self, xml=None, parent=None): def __init__(self, xml=None, parent=None):
ElementBase.__init__(self, xml, parent); ElementBase.__init__(self, xml, parent)
self._nodes = set() self._nodes = set()
def setup(self, xml=None): def setup(self, xml=None):
@ -318,7 +318,7 @@ class Fields(ElementBase):
Arguments: Arguments:
nodeId -- The ID for the node. nodeId -- The ID for the node.
sourceId -- [optional] identifying the data source controlling the device sourceId -- [optional] identifying the data source controlling the device
cacheType -- [optional] narrowing down the search to a specific kind of node cacheType -- [optional] narrowing down the search to a specific kind of node
""" """
if nodeId not in self._nodes: if nodeId not in self._nodes:
self._nodes.add((nodeId)) self._nodes.add((nodeId))
@ -392,7 +392,7 @@ class FieldsNode(ElementBase):
interfaces = set(['nodeId','sourceId','cacheType','timestamps']) interfaces = set(['nodeId','sourceId','cacheType','timestamps'])
def __init__(self, xml=None, parent=None): def __init__(self, xml=None, parent=None):
ElementBase.__init__(self, xml, parent); ElementBase.__init__(self, xml, parent)
self._timestamps = set() self._timestamps = set()
def setup(self, xml=None): def setup(self, xml=None):
@ -411,7 +411,7 @@ class FieldsNode(ElementBase):
def add_timestamp(self, timestamp, substanzas=None): def add_timestamp(self, timestamp, substanzas=None):
""" """
Add a new timestamp element. Add a new timestamp element.
Arguments: Arguments:
timestamp -- The timestamp in ISO format. timestamp -- The timestamp in ISO format.
@ -423,7 +423,7 @@ class FieldsNode(ElementBase):
ts = Timestamp(parent=self) ts = Timestamp(parent=self)
ts['value'] = timestamp ts['value'] = timestamp
if not substanzas is None: if not substanzas is None:
ts.set_datas(substanzas); ts.set_datas(substanzas)
#print("add_timestamp with substanzas: " + str(substanzas)) #print("add_timestamp with substanzas: " + str(substanzas))
self.iterables.append(ts) self.iterables.append(ts)
#print(str(id(self)) + " added_timestamp: " + str(id(ts))) #print(str(id(self)) + " added_timestamp: " + str(id(ts)))
@ -485,7 +485,7 @@ class FieldsNode(ElementBase):
self.iterables.remove(timestamp) self.iterables.remove(timestamp)
class Field(ElementBase): class Field(ElementBase):
""" """
Field element in response Timestamp. This is a base class, Field element in response Timestamp. This is a base class,
all instances of fields added to Timestamp must be of types: all instances of fields added to Timestamp must be of types:
DataNumeric DataNumeric
@ -494,17 +494,17 @@ class Field(ElementBase):
DataDateTime DataDateTime
DataTimeSpan DataTimeSpan
DataEnum DataEnum
""" """
namespace = 'urn:xmpp:iot:sensordata' namespace = 'urn:xmpp:iot:sensordata'
name = 'field' name = 'field'
plugin_attrib = name plugin_attrib = name
interfaces = set(['name','module','stringIds']); interfaces = set(['name','module','stringIds'])
interfaces.update(FieldTypes.field_types); interfaces.update(FieldTypes.field_types)
interfaces.update(FieldStatus.field_status); interfaces.update(FieldStatus.field_status)
_flags = set(); _flags = set()
_flags.update(FieldTypes.field_types); _flags.update(FieldTypes.field_types)
_flags.update(FieldStatus.field_status); _flags.update(FieldStatus.field_status)
def set_stringIds(self, value): def set_stringIds(self, value):
"""Verifies stringIds according to regexp from specification XMPP-0323. """Verifies stringIds according to regexp from specification XMPP-0323.
@ -514,7 +514,7 @@ class Field(ElementBase):
pattern = re.compile("^\d+([|]\w+([.]\w+)*([|][^,]*)?)?(,\d+([|]\w+([.]\w+)*([|][^,]*)?)?)*$") pattern = re.compile("^\d+([|]\w+([.]\w+)*([|][^,]*)?)?(,\d+([|]\w+([.]\w+)*([|][^,]*)?)?)*$")
if pattern.match(value) is not None: if pattern.match(value) is not None:
self.xml.stringIds = value; self.xml.stringIds = value
else: else:
# Bad content, add nothing # Bad content, add nothing
pass pass
@ -523,30 +523,30 @@ class Field(ElementBase):
def _get_flags(self): def _get_flags(self):
""" """
Helper function for getting of flags. Returns all flags in Helper function for getting of flags. Returns all flags in
dictionary format: { "flag name": "flag value" ... } dictionary format: { "flag name": "flag value" ... }
""" """
flags = {}; flags = {}
for f in self._flags: for f in self._flags:
if not self[f] == "": if not self[f] == "":
flags[f] = self[f]; flags[f] = self[f]
return flags; return flags
def _set_flags(self, flags): def _set_flags(self, flags):
""" """
Helper function for setting of flags. Helper function for setting of flags.
Arguments: Arguments:
flags -- Flags in dictionary format: { "flag name": "flag value" ... } flags -- Flags in dictionary format: { "flag name": "flag value" ... }
""" """
for f in self._flags: for f in self._flags:
if flags is not None and f in flags: if flags is not None and f in flags:
self[f] = flags[f]; self[f] = flags[f]
else: else:
self[f] = None; self[f] = None
def _get_typename(self): def _get_typename(self):
return "invalid type, use subclasses!"; return "invalid type, use subclasses!"
class Timestamp(ElementBase): class Timestamp(ElementBase):
@ -557,7 +557,7 @@ class Timestamp(ElementBase):
interfaces = set(['value','datas']) interfaces = set(['value','datas'])
def __init__(self, xml=None, parent=None): def __init__(self, xml=None, parent=None):
ElementBase.__init__(self, xml, parent); ElementBase.__init__(self, xml, parent)
self._datas = set() self._datas = set()
def setup(self, xml=None): def setup(self, xml=None):
@ -576,7 +576,7 @@ class Timestamp(ElementBase):
def add_data(self, typename, name, value, module=None, stringIds=None, unit=None, dataType=None, flags=None): def add_data(self, typename, name, value, module=None, stringIds=None, unit=None, dataType=None, flags=None):
""" """
Add a new data element. Add a new data element.
Arguments: Arguments:
typename -- The type of data element (numeric, string, boolean, dateTime, timeSpan or enum) typename -- The type of data element (numeric, string, boolean, dateTime, timeSpan or enum)
@ -587,29 +587,29 @@ class Timestamp(ElementBase):
dataType -- [optional] The dataType. Only applicable for type enum dataType -- [optional] The dataType. Only applicable for type enum
""" """
if name not in self._datas: if name not in self._datas:
dataObj = None; dataObj = None
if typename == "numeric": if typename == "numeric":
dataObj = DataNumeric(parent=self); dataObj = DataNumeric(parent=self)
dataObj['unit'] = unit; dataObj['unit'] = unit
elif typename == "string": elif typename == "string":
dataObj = DataString(parent=self); dataObj = DataString(parent=self)
elif typename == "boolean": elif typename == "boolean":
dataObj = DataBoolean(parent=self); dataObj = DataBoolean(parent=self)
elif typename == "dateTime": elif typename == "dateTime":
dataObj = DataDateTime(parent=self); dataObj = DataDateTime(parent=self)
elif typename == "timeSpan": elif typename == "timeSpan":
dataObj = DataTimeSpan(parent=self); dataObj = DataTimeSpan(parent=self)
elif typename == "enum": elif typename == "enum":
dataObj = DataEnum(parent=self); dataObj = DataEnum(parent=self)
dataObj['dataType'] = dataType; dataObj['dataType'] = dataType
dataObj['name'] = name; dataObj['name'] = name
dataObj['value'] = value; dataObj['value'] = value
dataObj['module'] = module; dataObj['module'] = module
dataObj['stringIds'] = stringIds; dataObj['stringIds'] = stringIds
if flags is not None: if flags is not None:
dataObj._set_flags(flags); dataObj._set_flags(flags)
self._datas.add(name) self._datas.add(name)
self.iterables.append(dataObj) self.iterables.append(dataObj)
@ -661,87 +661,87 @@ class Timestamp(ElementBase):
self.iterables.remove(data) self.iterables.remove(data)
class DataNumeric(Field): class DataNumeric(Field):
""" """
Field data of type numeric. Field data of type numeric.
Note that the value is expressed as a string. Note that the value is expressed as a string.
""" """
namespace = 'urn:xmpp:iot:sensordata' namespace = 'urn:xmpp:iot:sensordata'
name = 'numeric' name = 'numeric'
plugin_attrib = name plugin_attrib = name
interfaces = set(['value', 'unit']); interfaces = set(['value', 'unit'])
interfaces.update(Field.interfaces); interfaces.update(Field.interfaces)
def _get_typename(self): def _get_typename(self):
return "numeric" return "numeric"
class DataString(Field): class DataString(Field):
""" """
Field data of type string Field data of type string
""" """
namespace = 'urn:xmpp:iot:sensordata' namespace = 'urn:xmpp:iot:sensordata'
name = 'string' name = 'string'
plugin_attrib = name plugin_attrib = name
interfaces = set(['value']); interfaces = set(['value'])
interfaces.update(Field.interfaces); interfaces.update(Field.interfaces)
def _get_typename(self): def _get_typename(self):
return "string" return "string"
class DataBoolean(Field): class DataBoolean(Field):
""" """
Field data of type boolean. Field data of type boolean.
Note that the value is expressed as a string. Note that the value is expressed as a string.
""" """
namespace = 'urn:xmpp:iot:sensordata' namespace = 'urn:xmpp:iot:sensordata'
name = 'boolean' name = 'boolean'
plugin_attrib = name plugin_attrib = name
interfaces = set(['value']); interfaces = set(['value'])
interfaces.update(Field.interfaces); interfaces.update(Field.interfaces)
def _get_typename(self): def _get_typename(self):
return "boolean" return "boolean"
class DataDateTime(Field): class DataDateTime(Field):
""" """
Field data of type dateTime. Field data of type dateTime.
Note that the value is expressed as a string. Note that the value is expressed as a string.
""" """
namespace = 'urn:xmpp:iot:sensordata' namespace = 'urn:xmpp:iot:sensordata'
name = 'dateTime' name = 'dateTime'
plugin_attrib = name plugin_attrib = name
interfaces = set(['value']); interfaces = set(['value'])
interfaces.update(Field.interfaces); interfaces.update(Field.interfaces)
def _get_typename(self): def _get_typename(self):
return "dateTime" return "dateTime"
class DataTimeSpan(Field): class DataTimeSpan(Field):
""" """
Field data of type timeSpan. Field data of type timeSpan.
Note that the value is expressed as a string. Note that the value is expressed as a string.
""" """
namespace = 'urn:xmpp:iot:sensordata' namespace = 'urn:xmpp:iot:sensordata'
name = 'timeSpan' name = 'timeSpan'
plugin_attrib = name plugin_attrib = name
interfaces = set(['value']); interfaces = set(['value'])
interfaces.update(Field.interfaces); interfaces.update(Field.interfaces)
def _get_typename(self): def _get_typename(self):
return "timeSpan" return "timeSpan"
class DataEnum(Field): class DataEnum(Field):
""" """
Field data of type enum. Field data of type enum.
Note that the value is expressed as a string. Note that the value is expressed as a string.
""" """
namespace = 'urn:xmpp:iot:sensordata' namespace = 'urn:xmpp:iot:sensordata'
name = 'enum' name = 'enum'
plugin_attrib = name plugin_attrib = name
interfaces = set(['value', 'dataType']); interfaces = set(['value', 'dataType'])
interfaces.update(Field.interfaces); interfaces.update(Field.interfaces)
def _get_typename(self): def _get_typename(self):
return "enum" return "enum"
class Done(ElementBase): class Done(ElementBase):
""" Done element used to signal that all data has been transferred """ """ Done element used to signal that all data has been transferred """

View File

@ -23,7 +23,7 @@ class _TimerReset(Thread):
t.cancel() # stop the timer's action if it's still waiting t.cancel() # stop the timer's action if it's still waiting
""" """
def __init__(self, interval, function, args=[], kwargs={}): def __init__(self, interval, function, *args, **kwargs):
Thread.__init__(self) Thread.__init__(self)
self.interval = interval self.interval = interval
self.function = function self.function = function

View File

@ -12,7 +12,6 @@ import logging
import time import time
from threading import Thread, Timer, Lock from threading import Thread, Timer, Lock
from sleekxmpp.xmlstream import JID
from sleekxmpp.xmlstream.handler import Callback from sleekxmpp.xmlstream.handler import Callback
from sleekxmpp.xmlstream.matcher import StanzaPath from sleekxmpp.xmlstream.matcher import StanzaPath
from sleekxmpp.plugins.base import BasePlugin from sleekxmpp.plugins.base import BasePlugin
@ -26,16 +25,16 @@ log = logging.getLogger(__name__)
class XEP_0325(BasePlugin): class XEP_0325(BasePlugin):
""" """
XEP-0325: IoT Control XEP-0325: IoT Control
Actuators are devices in sensor networks that can be controlled through Actuators are devices in sensor networks that can be controlled through
the network and act with the outside world. In sensor networks and the network and act with the outside world. In sensor networks and
Internet of Things applications, actuators make it possible to automate Internet of Things applications, actuators make it possible to automate
real-world processes. real-world processes.
This plugin implements a mechanism whereby actuators can be controlled This plugin implements a mechanism whereby actuators can be controlled
in XMPP-based sensor networks, making it possible to integrate sensors in XMPP-based sensor networks, making it possible to integrate sensors
and actuators of different brands, makes and models into larger and actuators of different brands, makes and models into larger
Internet of Things applications. Internet of Things applications.
Also see <http://xmpp.org/extensions/xep-0325.html> Also see <http://xmpp.org/extensions/xep-0325.html>
@ -52,9 +51,9 @@ class XEP_0325(BasePlugin):
Client side Client side
----------- -----------
Control Event:SetResponse -- Received a response to a Control Event:SetResponse -- Received a response to a
control request, type result control request, type result
Control Event:SetResponseError -- Received a response to a Control Event:SetResponseError -- Received a response to a
control request, type error control request, type error
Attributes: Attributes:
@ -65,7 +64,7 @@ class XEP_0325(BasePlugin):
relevant to a request's session. This dictionary is used relevant to a request's session. This dictionary is used
both by the client and sensor side. On client side, seqnr both by the client and sensor side. On client side, seqnr
is used as key, while on sensor side, a session_id is used is used as key, while on sensor side, a session_id is used
as key. This ensures that the two will not collide, so as key. This ensures that the two will not collide, so
one instance can be both client and sensor. one instance can be both client and sensor.
Sensor side Sensor side
----------- -----------
@ -85,15 +84,15 @@ class XEP_0325(BasePlugin):
Sensor side Sensor side
----------- -----------
register_node -- Register a sensor as available from this XMPP register_node -- Register a sensor as available from this XMPP
instance. instance.
Client side Client side
----------- -----------
set_request -- Initiates a control request to modify data in set_request -- Initiates a control request to modify data in
sensor(s). Non-blocking, a callback function will sensor(s). Non-blocking, a callback function will
be called when the sensor has responded. be called when the sensor has responded.
set_command -- Initiates a control command to modify data in set_command -- Initiates a control command to modify data in
sensor(s). Non-blocking. The sensor(s) will not sensor(s). Non-blocking. The sensor(s) will not
respond regardless of the result of the command, respond regardless of the result of the command,
so no callback is made. so no callback is made.
@ -102,7 +101,7 @@ class XEP_0325(BasePlugin):
name = 'xep_0325' name = 'xep_0325'
description = 'XEP-0325 Internet of Things - Control' description = 'XEP-0325 Internet of Things - Control'
dependencies = set(['xep_0030']) dependencies = set(['xep_0030'])
stanza = stanza stanza = stanza
@ -135,11 +134,11 @@ class XEP_0325(BasePlugin):
self._handle_set_response)) self._handle_set_response))
# Server side dicts # Server side dicts
self.nodes = {}; self.nodes = {}
self.sessions = {}; self.sessions = {}
self.last_seqnr = 0; self.last_seqnr = 0
self.seqnr_lock = Lock(); self.seqnr_lock = Lock()
## For testning only ## For testning only
self.test_authenticated_from = "" self.test_authenticated_from = ""
@ -156,13 +155,13 @@ class XEP_0325(BasePlugin):
def plugin_end(self): def plugin_end(self):
""" Stop the XEP-0325 plugin """ """ Stop the XEP-0325 plugin """
self.sessions.clear(); self.sessions.clear()
self.xmpp.remove_handler('Control Event:DirectSet') self.xmpp.remove_handler('Control Event:DirectSet')
self.xmpp.remove_handler('Control Event:SetReq') self.xmpp.remove_handler('Control Event:SetReq')
self.xmpp.remove_handler('Control Event:SetResponse') self.xmpp.remove_handler('Control Event:SetResponse')
self.xmpp.remove_handler('Control Event:SetResponseError') self.xmpp.remove_handler('Control Event:SetResponseError')
self.xmpp['xep_0030'].del_feature(feature=Control.namespace) self.xmpp['xep_0030'].del_feature(feature=Control.namespace)
self.xmpp['xep_0030'].set_items(node=Control.namespace, items=tuple()); self.xmpp['xep_0030'].set_items(node=Control.namespace, items=tuple())
# ================================================================= # =================================================================
@ -170,10 +169,10 @@ class XEP_0325(BasePlugin):
def register_node(self, nodeId, device, commTimeout, sourceId=None, cacheType=None): def register_node(self, nodeId, device, commTimeout, sourceId=None, cacheType=None):
""" """
Register a sensor/device as available for control requests/commands Register a sensor/device as available for control requests/commands
through this XMPP instance. through this XMPP instance.
The device object may by any custom implementation to support The device object may by any custom implementation to support
specific devices, but it must implement the functions: specific devices, but it must implement the functions:
has_control_field has_control_field
set_control_fields set_control_fields
@ -185,30 +184,30 @@ class XEP_0325(BasePlugin):
commTimeout -- Time in seconds to wait between each callback from device during commTimeout -- Time in seconds to wait between each callback from device during
a data readout. Float. a data readout. Float.
sourceId -- [optional] identifying the data source controlling the device sourceId -- [optional] identifying the data source controlling the device
cacheType -- [optional] narrowing down the search to a specific kind of node cacheType -- [optional] narrowing down the search to a specific kind of node
""" """
self.nodes[nodeId] = {"device": device, self.nodes[nodeId] = {"device": device,
"commTimeout": commTimeout, "commTimeout": commTimeout,
"sourceId": sourceId, "sourceId": sourceId,
"cacheType": cacheType}; "cacheType": cacheType}
def _set_authenticated(self, auth=''): def _set_authenticated(self, auth=''):
""" Internal testing function """ """ Internal testing function """
self.test_authenticated_from = auth; self.test_authenticated_from = auth
def _get_new_seqnr(self): def _get_new_seqnr(self):
""" Returns a unique sequence number (unique across threads) """ """ Returns a unique sequence number (unique across threads) """
self.seqnr_lock.acquire(); self.seqnr_lock.acquire()
self.last_seqnr = self.last_seqnr + 1; self.last_seqnr += 1
self.seqnr_lock.release(); self.seqnr_lock.release()
return str(self.last_seqnr); return str(self.last_seqnr)
def _handle_set_req(self, iq): def _handle_set_req(self, iq):
""" """
Event handler for reception of an Iq with set req - this is a Event handler for reception of an Iq with set req - this is a
control request. control request.
Verifies that Verifies that
- all the requested nodes are available - all the requested nodes are available
(if no nodes are specified in the request, assume all nodes) (if no nodes are specified in the request, assume all nodes)
- all the control fields are available from all requested nodes - all the control fields are available from all requested nodes
@ -216,80 +215,79 @@ class XEP_0325(BasePlugin):
If the request passes verification, the control request is passed If the request passes verification, the control request is passed
to the devices (in a separate thread). to the devices (in a separate thread).
If the verification fails, a setResponse with error indication If the verification fails, a setResponse with error indication
is sent. is sent.
""" """
error_msg = ''; error_msg = ''
req_ok = True; req_ok = True
missing_node = None; missing_node = None
missing_field = None; missing_field = None
# Authentication # Authentication
if len(self.test_authenticated_from) > 0 and not iq['from'] == self.test_authenticated_from: if len(self.test_authenticated_from) > 0 and not iq['from'] == self.test_authenticated_from:
# Invalid authentication # Invalid authentication
req_ok = False; req_ok = False
error_msg = "Access denied"; error_msg = "Access denied"
# Nodes # Nodes
process_nodes = [];
if len(iq['set']['nodes']) > 0: if len(iq['set']['nodes']) > 0:
for n in iq['set']['nodes']: for n in iq['set']['nodes']:
if not n['nodeId'] in self.nodes: if not n['nodeId'] in self.nodes:
req_ok = False; req_ok = False
missing_node = n['nodeId']; missing_node = n['nodeId']
error_msg = "Invalid nodeId " + n['nodeId']; error_msg = "Invalid nodeId " + n['nodeId']
process_nodes = [n['nodeId'] for n in iq['set']['nodes']]; process_nodes = [n['nodeId'] for n in iq['set']['nodes']]
else: else:
process_nodes = self.nodes.keys(); process_nodes = self.nodes.keys()
# Fields - for control we need to find all in all devices, otherwise we reject # Fields - for control we need to find all in all devices, otherwise we reject
process_fields = []; process_fields = []
if len(iq['set']['datas']) > 0: if len(iq['set']['datas']) > 0:
for f in iq['set']['datas']: for f in iq['set']['datas']:
for node in self.nodes: for node in self.nodes:
if not self.nodes[node]["device"].has_control_field(f['name'], f._get_typename()): if not self.nodes[node]["device"].has_control_field(f['name'], f._get_typename()):
req_ok = False; req_ok = False
missing_field = f['name']; missing_field = f['name']
error_msg = "Invalid field " + f['name']; error_msg = "Invalid field " + f['name']
break; break
process_fields = [(f['name'], f._get_typename(), f['value']) for f in iq['set']['datas']]; process_fields = [(f['name'], f._get_typename(), f['value']) for f in iq['set']['datas']]
if req_ok: if req_ok:
session = self._new_session(); session = self._new_session()
self.sessions[session] = {"from": iq['from'], "to": iq['to'], "seqnr": iq['id']}; self.sessions[session] = {"from": iq['from'], "to": iq['to'], "seqnr": iq['id']}
self.sessions[session]["commTimers"] = {}; self.sessions[session]["commTimers"] = {}
self.sessions[session]["nodeDone"] = {}; self.sessions[session]["nodeDone"] = {}
# Flag that a reply is exected when we are done # Flag that a reply is exected when we are done
self.sessions[session]["reply"] = True; self.sessions[session]["reply"] = True
self.sessions[session]["node_list"] = process_nodes; self.sessions[session]["node_list"] = process_nodes
if self.threaded: if self.threaded:
#print("starting thread") #print("starting thread")
tr_req = Thread(target=self._threaded_node_request, args=(session, process_fields)) tr_req = Thread(target=self._threaded_node_request, args=(session, process_fields))
tr_req.start() tr_req.start()
#print("started thread") #print("started thread")
else: else:
self._threaded_node_request(session, process_fields); self._threaded_node_request(session, process_fields)
else: else:
iq.reply(); iq.reply()
iq['type'] = 'error'; iq['type'] = 'error'
iq['setResponse']['responseCode'] = "NotFound"; iq['setResponse']['responseCode'] = "NotFound"
if missing_node is not None: if missing_node is not None:
iq['setResponse'].add_node(missing_node); iq['setResponse'].add_node(missing_node)
if missing_field is not None: if missing_field is not None:
iq['setResponse'].add_data(missing_field); iq['setResponse'].add_data(missing_field)
iq['setResponse']['error']['var'] = "Output"; iq['setResponse']['error']['var'] = "Output"
iq['setResponse']['error']['text'] = error_msg; iq['setResponse']['error']['text'] = error_msg
iq.send(block=False); iq.send(block=False)
def _handle_direct_set(self, msg): def _handle_direct_set(self, msg):
""" """
Event handler for reception of a Message with set command - this is a Event handler for reception of a Message with set command - this is a
direct control command. direct control command.
Verifies that Verifies that
- all the requested nodes are available - all the requested nodes are available
(if no nodes are specified in the request, assume all nodes) (if no nodes are specified in the request, assume all nodes)
- all the control fields are available from all requested nodes - all the control fields are available from all requested nodes
@ -299,73 +297,72 @@ class XEP_0325(BasePlugin):
to the devices (in a separate thread). to the devices (in a separate thread).
If the verification fails, do nothing. If the verification fails, do nothing.
""" """
req_ok = True; req_ok = True
# Nodes # Nodes
process_nodes = [];
if len(msg['set']['nodes']) > 0: if len(msg['set']['nodes']) > 0:
for n in msg['set']['nodes']: for n in msg['set']['nodes']:
if not n['nodeId'] in self.nodes: if not n['nodeId'] in self.nodes:
req_ok = False; req_ok = False
error_msg = "Invalid nodeId " + n['nodeId']; error_msg = "Invalid nodeId " + n['nodeId']
process_nodes = [n['nodeId'] for n in msg['set']['nodes']]; process_nodes = [n['nodeId'] for n in msg['set']['nodes']]
else: else:
process_nodes = self.nodes.keys(); process_nodes = self.nodes.keys()
# Fields - for control we need to find all in all devices, otherwise we reject # Fields - for control we need to find all in all devices, otherwise we reject
process_fields = []; process_fields = []
if len(msg['set']['datas']) > 0: if len(msg['set']['datas']) > 0:
for f in msg['set']['datas']: for f in msg['set']['datas']:
for node in self.nodes: for node in self.nodes:
if not self.nodes[node]["device"].has_control_field(f['name'], f._get_typename()): if not self.nodes[node]["device"].has_control_field(f['name'], f._get_typename()):
req_ok = False; req_ok = False
missing_field = f['name']; missing_field = f['name']
error_msg = "Invalid field " + f['name']; error_msg = "Invalid field " + f['name']
break; break
process_fields = [(f['name'], f._get_typename(), f['value']) for f in msg['set']['datas']]; process_fields = [(f['name'], f._get_typename(), f['value']) for f in msg['set']['datas']]
if req_ok: if req_ok:
session = self._new_session(); session = self._new_session()
self.sessions[session] = {"from": msg['from'], "to": msg['to']}; self.sessions[session] = {"from": msg['from'], "to": msg['to']}
self.sessions[session]["commTimers"] = {}; self.sessions[session]["commTimers"] = {}
self.sessions[session]["nodeDone"] = {}; self.sessions[session]["nodeDone"] = {}
self.sessions[session]["reply"] = False; self.sessions[session]["reply"] = False
self.sessions[session]["node_list"] = process_nodes; self.sessions[session]["node_list"] = process_nodes
if self.threaded: if self.threaded:
#print("starting thread") #print("starting thread")
tr_req = Thread(target=self._threaded_node_request, args=(session, process_fields)) tr_req = Thread(target=self._threaded_node_request, args=(session, process_fields))
tr_req.start() tr_req.start()
#print("started thread") #print("started thread")
else: else:
self._threaded_node_request(session, process_fields); self._threaded_node_request(session, process_fields)
def _threaded_node_request(self, session, process_fields): def _threaded_node_request(self, session, process_fields):
""" """
Helper function to handle the device control in a separate thread. Helper function to handle the device control in a separate thread.
Arguments: Arguments:
session -- The request session id session -- The request session id
process_fields -- The fields to set in the devices. List of tuple format: process_fields -- The fields to set in the devices. List of tuple format:
(name, datatype, value) (name, datatype, value)
""" """
for node in self.sessions[session]["node_list"]: for node in self.sessions[session]["node_list"]:
self.sessions[session]["nodeDone"][node] = False; self.sessions[session]["nodeDone"][node] = False
for node in self.sessions[session]["node_list"]: for node in self.sessions[session]["node_list"]:
timer = Timer(self.nodes[node]['commTimeout'], self._event_comm_timeout, args=(session, node)); timer = Timer(self.nodes[node]['commTimeout'], self._event_comm_timeout, args=(session, node))
self.sessions[session]["commTimers"][node] = timer; self.sessions[session]["commTimers"][node] = timer
timer.start(); timer.start()
self.nodes[node]['device'].set_control_fields(process_fields, session=session, callback=self._device_set_command_callback); self.nodes[node]['device'].set_control_fields(process_fields, session=session, callback=self._device_set_command_callback)
def _event_comm_timeout(self, session, nodeId): def _event_comm_timeout(self, session, nodeId):
""" """
Triggered if any of the control operations timeout. Triggered if any of the control operations timeout.
Stop communicating with the failing device. Stop communicating with the failing device.
If the control command was an Iq request, sends a failure If the control command was an Iq request, sends a failure
message back to the client. message back to the client.
Arguments: Arguments:
session -- The request session id session -- The request session id
nodeId -- The id of the device which timed out nodeId -- The id of the device which timed out
@ -373,51 +370,51 @@ class XEP_0325(BasePlugin):
if self.sessions[session]["reply"]: if self.sessions[session]["reply"]:
# Reply is exected when we are done # Reply is exected when we are done
iq = self.xmpp.Iq(); iq = self.xmpp.Iq()
iq['from'] = self.sessions[session]['to']; iq['from'] = self.sessions[session]['to']
iq['to'] = self.sessions[session]['from']; iq['to'] = self.sessions[session]['from']
iq['type'] = "error"; iq['type'] = "error"
iq['id'] = self.sessions[session]['seqnr']; iq['id'] = self.sessions[session]['seqnr']
iq['setResponse']['responseCode'] = "OtherError"; iq['setResponse']['responseCode'] = "OtherError"
iq['setResponse'].add_node(nodeId); iq['setResponse'].add_node(nodeId)
iq['setResponse']['error']['var'] = "Output"; iq['setResponse']['error']['var'] = "Output"
iq['setResponse']['error']['text'] = "Timeout."; iq['setResponse']['error']['text'] = "Timeout."
iq.send(block=False); iq.send(block=False)
## TODO - should we send one timeout per node?? ## TODO - should we send one timeout per node??
# Drop communication with this device and check if we are done # Drop communication with this device and check if we are done
self.sessions[session]["nodeDone"][nodeId] = True; self.sessions[session]["nodeDone"][nodeId] = True
if (self._all_nodes_done(session)): if (self._all_nodes_done(session)):
# The session is complete, delete it # The session is complete, delete it
del self.sessions[session]; del self.sessions[session]
def _all_nodes_done(self, session): def _all_nodes_done(self, session):
""" """
Checks wheter all devices are done replying to the control command. Checks wheter all devices are done replying to the control command.
Arguments: Arguments:
session -- The request session id session -- The request session id
""" """
for n in self.sessions[session]["nodeDone"]: for n in self.sessions[session]["nodeDone"]:
if not self.sessions[session]["nodeDone"][n]: if not self.sessions[session]["nodeDone"][n]:
return False; return False
return True; return True
def _device_set_command_callback(self, session, nodeId, result, error_field=None, error_msg=None): def _device_set_command_callback(self, session, nodeId, result, error_field=None, error_msg=None):
""" """
Callback function called by the devices when the control command is Callback function called by the devices when the control command is
complete or failed. complete or failed.
If needed, composes a message with the result and sends it back to the If needed, composes a message with the result and sends it back to the
client. client.
Arguments: Arguments:
session -- The request session id session -- The request session id
nodeId -- The device id which initiated the callback nodeId -- The device id which initiated the callback
result -- The current result status of the control command. Valid values are: result -- The current result status of the control command. Valid values are:
"error" - Set fields failed. "error" - Set fields failed.
"ok" - All fields were set. "ok" - All fields were set.
error_field -- [optional] Only applies when result == "error" error_field -- [optional] Only applies when result == "error"
The field name that failed (usually means it is missing) The field name that failed (usually means it is missing)
error_msg -- [optional] Only applies when result == "error". error_msg -- [optional] Only applies when result == "error".
Error details when a request failed. Error details when a request failed.
@ -428,62 +425,62 @@ class XEP_0325(BasePlugin):
return return
if result == "error": if result == "error":
self.sessions[session]["commTimers"][nodeId].cancel(); self.sessions[session]["commTimers"][nodeId].cancel()
if self.sessions[session]["reply"]: if self.sessions[session]["reply"]:
# Reply is exected when we are done # Reply is exected when we are done
iq = self.xmpp.Iq(); iq = self.xmpp.Iq()
iq['from'] = self.sessions[session]['to']; iq['from'] = self.sessions[session]['to']
iq['to'] = self.sessions[session]['from']; iq['to'] = self.sessions[session]['from']
iq['type'] = "error"; iq['type'] = "error"
iq['id'] = self.sessions[session]['seqnr']; iq['id'] = self.sessions[session]['seqnr']
iq['setResponse']['responseCode'] = "OtherError"; iq['setResponse']['responseCode'] = "OtherError"
iq['setResponse'].add_node(nodeId); iq['setResponse'].add_node(nodeId)
if error_field is not None: if error_field is not None:
iq['setResponse'].add_data(error_field); iq['setResponse'].add_data(error_field)
iq['setResponse']['error']['var'] = error_field; iq['setResponse']['error']['var'] = error_field
iq['setResponse']['error']['text'] = error_msg; iq['setResponse']['error']['text'] = error_msg
iq.send(block=False); iq.send(block=False)
# Drop communication with this device and check if we are done # Drop communication with this device and check if we are done
self.sessions[session]["nodeDone"][nodeId] = True; self.sessions[session]["nodeDone"][nodeId] = True
if (self._all_nodes_done(session)): if (self._all_nodes_done(session)):
# The session is complete, delete it # The session is complete, delete it
del self.sessions[session]; del self.sessions[session]
else: else:
self.sessions[session]["commTimers"][nodeId].cancel(); self.sessions[session]["commTimers"][nodeId].cancel()
self.sessions[session]["nodeDone"][nodeId] = True; self.sessions[session]["nodeDone"][nodeId] = True
if (self._all_nodes_done(session)): if (self._all_nodes_done(session)):
if self.sessions[session]["reply"]: if self.sessions[session]["reply"]:
# Reply is exected when we are done # Reply is exected when we are done
iq = self.xmpp.Iq(); iq = self.xmpp.Iq()
iq['from'] = self.sessions[session]['to']; iq['from'] = self.sessions[session]['to']
iq['to'] = self.sessions[session]['from']; iq['to'] = self.sessions[session]['from']
iq['type'] = "result"; iq['type'] = "result"
iq['id'] = self.sessions[session]['seqnr']; iq['id'] = self.sessions[session]['seqnr']
iq['setResponse']['responseCode'] = "OK"; iq['setResponse']['responseCode'] = "OK"
iq.send(block=False); iq.send(block=False)
# The session is complete, delete it # The session is complete, delete it
del self.sessions[session]; del self.sessions[session]
# ================================================================= # =================================================================
# Client side (data controller) API # Client side (data controller) API
def set_request(self, from_jid, to_jid, callback, fields, nodeIds=None): def set_request(self, from_jid, to_jid, callback, fields, nodeIds=None):
""" """
Called on the client side to initiade a control request. Called on the client side to initiade a control request.
Composes a message with the request and sends it to the device(s). Composes a message with the request and sends it to the device(s).
Does not block, the callback will be called when the device(s) Does not block, the callback will be called when the device(s)
has responded. has responded.
Arguments: Arguments:
from_jid -- The jid of the requester from_jid -- The jid of the requester
to_jid -- The jid of the device(s) to_jid -- The jid of the device(s)
callback -- The callback function to call when data is availble. callback -- The callback function to call when data is availble.
The callback function must support the following arguments: The callback function must support the following arguments:
from_jid -- The jid of the responding device(s) from_jid -- The jid of the responding device(s)
@ -494,46 +491,46 @@ class XEP_0325(BasePlugin):
"Locked" - Field(s) is locked and cannot "Locked" - Field(s) is locked and cannot
be changed at the moment. be changed at the moment.
"NotImplemented" - Request feature not implemented. "NotImplemented" - Request feature not implemented.
"FormError" - Error while setting with "FormError" - Error while setting with
a form (not implemented). a form (not implemented).
"OtherError" - Indicates other types of "OtherError" - Indicates other types of
errors, such as timeout. errors, such as timeout.
Details in the error_msg. Details in the error_msg.
nodeId -- [optional] Only applicable when result == "error"
List of node Ids of failing device(s).
fields -- [optional] Only applicable when result == "error" nodeId -- [optional] Only applicable when result == "error"
List of node Ids of failing device(s).
fields -- [optional] Only applicable when result == "error"
List of fields that failed.[optional] Mandatory when result == "rejected" or "failure". List of fields that failed.[optional] Mandatory when result == "rejected" or "failure".
error_msg -- Details about why the request failed. error_msg -- Details about why the request failed.
fields -- Fields to set. List of tuple format: (name, typename, value). fields -- Fields to set. List of tuple format: (name, typename, value).
nodeIds -- [optional] Limits the request to the node Ids in this list. nodeIds -- [optional] Limits the request to the node Ids in this list.
""" """
iq = self.xmpp.Iq(); iq = self.xmpp.Iq()
iq['from'] = from_jid; iq['from'] = from_jid
iq['to'] = to_jid; iq['to'] = to_jid
seqnr = self._get_new_seqnr(); seqnr = self._get_new_seqnr()
iq['id'] = seqnr; iq['id'] = seqnr
iq['type'] = "set"; iq['type'] = "set"
if nodeIds is not None: if nodeIds is not None:
for nodeId in nodeIds: for nodeId in nodeIds:
iq['set'].add_node(nodeId); iq['set'].add_node(nodeId)
if fields is not None: if fields is not None:
for name, typename, value in fields: for name, typename, value in fields:
iq['set'].add_data(name=name, typename=typename, value=value); iq['set'].add_data(name=name, typename=typename, value=value)
self.sessions[seqnr] = {"from": iq['from'], "to": iq['to'], "callback": callback}; self.sessions[seqnr] = {"from": iq['from'], "to": iq['to'], "callback": callback}
iq.send(block=False); iq.send(block=False)
def set_command(self, from_jid, to_jid, fields, nodeIds=None): def set_command(self, from_jid, to_jid, fields, nodeIds=None):
""" """
Called on the client side to initiade a control command. Called on the client side to initiade a control command.
Composes a message with the set commandand sends it to the device(s). Composes a message with the set commandand sends it to the device(s).
Does not block. Device(s) will not respond, regardless of result. Does not block. Device(s) will not respond, regardless of result.
Arguments: Arguments:
from_jid -- The jid of the requester from_jid -- The jid of the requester
to_jid -- The jid of the device(s) to_jid -- The jid of the device(s)
@ -541,34 +538,32 @@ class XEP_0325(BasePlugin):
fields -- Fields to set. List of tuple format: (name, typename, value). fields -- Fields to set. List of tuple format: (name, typename, value).
nodeIds -- [optional] Limits the request to the node Ids in this list. nodeIds -- [optional] Limits the request to the node Ids in this list.
""" """
msg = self.xmpp.Message(); msg = self.xmpp.Message()
msg['from'] = from_jid; msg['from'] = from_jid
msg['to'] = to_jid; msg['to'] = to_jid
msg['type'] = "set"; msg['type'] = "set"
if nodeIds is not None: if nodeIds is not None:
for nodeId in nodeIds: for nodeId in nodeIds:
msg['set'].add_node(nodeId); msg['set'].add_node(nodeId)
if fields is not None: if fields is not None:
for name, typename, value in fields: for name, typename, value in fields:
msg['set'].add_data(name, typename, value); msg['set'].add_data(name, typename, value)
# We won't get any reply, so don't create a session # We won't get any reply, so don't create a session
msg.send(); msg.send()
def _handle_set_response(self, iq): def _handle_set_response(self, iq):
""" Received response from device(s) """ """ Received response from device(s) """
#print("ooh") #print("ooh")
seqnr = iq['id']; seqnr = iq['id']
from_jid = str(iq['from']); from_jid = str(iq['from'])
result = iq['setResponse']['responseCode']; result = iq['setResponse']['responseCode']
nodeIds = [n['name'] for n in iq['setResponse']['nodes']]; nodeIds = [n['name'] for n in iq['setResponse']['nodes']]
fields = [f['name'] for f in iq['setResponse']['datas']]; fields = [f['name'] for f in iq['setResponse']['datas']]
error_msg = None; error_msg = None
if not iq['setResponse'].find('error') is None and not iq['setResponse']['error']['text'] == "": if not iq['setResponse'].find('error') is None and not iq['setResponse']['error']['text'] == "":
error_msg = iq['setResponse']['error']['text']; error_msg = iq['setResponse']['error']['text']
callback = self.sessions[seqnr]["callback"]; callback = self.sessions[seqnr]["callback"]
callback(from_jid=from_jid, result=result, nodeIds=nodeIds, fields=fields, error_msg=error_msg); callback(from_jid=from_jid, result=result, nodeIds=nodeIds, fields=fields, error_msg=error_msg)

View File

@ -13,16 +13,16 @@ import datetime
class Device(object): class Device(object):
""" """
Example implementation of a device control object. Example implementation of a device control object.
The device object may by any custom implementation to support The device object may by any custom implementation to support
specific devices, but it must implement the functions: specific devices, but it must implement the functions:
has_control_field has_control_field
set_control_fields set_control_fields
""" """
def __init__(self, nodeId): def __init__(self, nodeId):
self.nodeId = nodeId; self.nodeId = nodeId
self.control_fields = {}; self.control_fields = {}
def has_control_field(self, field, typename): def has_control_field(self, field, typename):
""" """
@ -30,12 +30,12 @@ class Device(object):
and the type matches for control in this device. and the type matches for control in this device.
Arguments: Arguments:
field -- The field name field -- The field name
typename -- The expected type typename -- The expected type
""" """
if field in self.control_fields and self.control_fields[field]["type"] == typename: if field in self.control_fields and self.control_fields[field]["type"] == typename:
return True; return True
return False; return False
def set_control_fields(self, fields, session, callback): def set_control_fields(self, fields, session, callback):
""" """
@ -43,22 +43,22 @@ class Device(object):
sets the data and (if needed) and calls the callback. sets the data and (if needed) and calls the callback.
Arguments: Arguments:
fields -- List of control fields in tuple format: fields -- List of control fields in tuple format:
(name, typename, value) (name, typename, value)
session -- Session id, only used in the callback as identifier session -- Session id, only used in the callback as identifier
callback -- Callback function to call when control set is complete. callback -- Callback function to call when control set is complete.
The callback function must support the following arguments: The callback function must support the following arguments:
session -- Session id, as supplied in the session -- Session id, as supplied in the
request_fields call request_fields call
nodeId -- Identifier for this device nodeId -- Identifier for this device
result -- The current result status of the readout. result -- The current result status of the readout.
Valid values are: Valid values are:
"error" - Set fields failed. "error" - Set fields failed.
"ok" - All fields were set. "ok" - All fields were set.
error_field -- [optional] Only applies when result == "error" error_field -- [optional] Only applies when result == "error"
The field name that failed The field name that failed
(usually means it is missing) (usually means it is missing)
error_msg -- [optional] Only applies when result == "error". error_msg -- [optional] Only applies when result == "error".
Error details when a request failed. Error details when a request failed.
@ -69,12 +69,12 @@ class Device(object):
for name, typename, value in fields: for name, typename, value in fields:
if not self.has_control_field(name, typename): if not self.has_control_field(name, typename):
self._send_control_reject(session, name, "NotFound", callback) self._send_control_reject(session, name, "NotFound", callback)
return False; return False
for name, typename, value in fields: for name, typename, value in fields:
self._set_field_value(name, value) self._set_field_value(name, value)
callback(session, result="ok", nodeId=self.nodeId); callback(session, result="ok", nodeId=self.nodeId)
return True return True
def _send_control_reject(self, session, field, message, callback): def _send_control_reject(self, session, field, message, callback):
@ -82,12 +82,12 @@ class Device(object):
Sends a reject to the caller Sends a reject to the caller
Arguments: Arguments:
session -- Session id, see definition in session -- Session id, see definition in
set_control_fields function set_control_fields function
callback -- Callback function, see definition in callback -- Callback function, see definition in
set_control_fields function set_control_fields function
""" """
callback(session, result="error", nodeId=self.nodeId, error_field=field, error_msg=message); callback(session, result="error", nodeId=self.nodeId, error_field=field, error_msg=message)
def _add_control_field(self, name, typename, value): def _add_control_field(self, name, typename, value):
""" """
@ -95,12 +95,12 @@ class Device(object):
Arguments: Arguments:
name -- Name of the field name -- Name of the field
typename -- Type of the field, one of: typename -- Type of the field, one of:
(boolean, color, string, date, dateTime, (boolean, color, string, date, dateTime,
double, duration, int, long, time) double, duration, int, long, time)
value -- Field value value -- Field value
""" """
self.control_fields[name] = {"type": typename, "value": value}; self.control_fields[name] = {"type": typename, "value": value}
def _set_field_value(self, name, value): def _set_field_value(self, name, value):
""" """
@ -111,7 +111,7 @@ class Device(object):
value -- New value for the field value -- New value for the field
""" """
if name in self.control_fields: if name in self.control_fields:
self.control_fields[name]["value"] = value; self.control_fields[name]["value"] = value
def _get_field_value(self, name): def _get_field_value(self, name):
""" """
@ -121,5 +121,5 @@ class Device(object):
name -- Name of the field name -- Name of the field
""" """
if name in self.control_fields: if name in self.control_fields:
return self.control_fields[name]["value"]; return self.control_fields[name]["value"]
return None; return None

View File

@ -26,7 +26,7 @@ class ControlSet(ElementBase):
interfaces = set(['nodes','datas']) interfaces = set(['nodes','datas'])
def __init__(self, xml=None, parent=None): def __init__(self, xml=None, parent=None):
ElementBase.__init__(self, xml, parent); ElementBase.__init__(self, xml, parent)
self._nodes = set() self._nodes = set()
self._datas = set() self._datas = set()
@ -53,7 +53,7 @@ class ControlSet(ElementBase):
Arguments: Arguments:
nodeId -- The ID for the node. nodeId -- The ID for the node.
sourceId -- [optional] identifying the data source controlling the device sourceId -- [optional] identifying the data source controlling the device
cacheType -- [optional] narrowing down the search to a specific kind of node cacheType -- [optional] narrowing down the search to a specific kind of node
""" """
if nodeId not in self._nodes: if nodeId not in self._nodes:
self._nodes.add((nodeId)) self._nodes.add((nodeId))
@ -117,40 +117,40 @@ class ControlSet(ElementBase):
def add_data(self, name, typename, value): def add_data(self, name, typename, value):
""" """
Add a new data element. Add a new data element.
Arguments: Arguments:
name -- The name of the data element name -- The name of the data element
typename -- The type of data element typename -- The type of data element
(boolean, color, string, date, dateTime, (boolean, color, string, date, dateTime,
double, duration, int, long, time) double, duration, int, long, time)
value -- The value of the data element value -- The value of the data element
""" """
if name not in self._datas: if name not in self._datas:
dataObj = None; dataObj = None
if typename == "boolean": if typename == "boolean":
dataObj = BooleanParameter(parent=self); dataObj = BooleanParameter(parent=self)
elif typename == "color": elif typename == "color":
dataObj = ColorParameter(parent=self); dataObj = ColorParameter(parent=self)
elif typename == "string": elif typename == "string":
dataObj = StringParameter(parent=self); dataObj = StringParameter(parent=self)
elif typename == "date": elif typename == "date":
dataObj = DateParameter(parent=self); dataObj = DateParameter(parent=self)
elif typename == "dateTime": elif typename == "dateTime":
dataObj = DateTimeParameter(parent=self); dataObj = DateTimeParameter(parent=self)
elif typename == "double": elif typename == "double":
dataObj = DoubleParameter(parent=self); dataObj = DoubleParameter(parent=self)
elif typename == "duration": elif typename == "duration":
dataObj = DurationParameter(parent=self); dataObj = DurationParameter(parent=self)
elif typename == "int": elif typename == "int":
dataObj = IntParameter(parent=self); dataObj = IntParameter(parent=self)
elif typename == "long": elif typename == "long":
dataObj = LongParameter(parent=self); dataObj = LongParameter(parent=self)
elif typename == "time": elif typename == "time":
dataObj = TimeParameter(parent=self); dataObj = TimeParameter(parent=self)
dataObj['name'] = name; dataObj['name'] = name
dataObj['value'] = value; dataObj['value'] = value
self._datas.add(name) self._datas.add(name)
self.iterables.append(dataObj) self.iterables.append(dataObj)
@ -217,7 +217,7 @@ class ControlSetResponse(ElementBase):
interfaces = set(['responseCode']) interfaces = set(['responseCode'])
def __init__(self, xml=None, parent=None): def __init__(self, xml=None, parent=None):
ElementBase.__init__(self, xml, parent); ElementBase.__init__(self, xml, parent)
self._nodes = set() self._nodes = set()
self._datas = set() self._datas = set()
@ -244,7 +244,7 @@ class ControlSetResponse(ElementBase):
Arguments: Arguments:
nodeId -- The ID for the node. nodeId -- The ID for the node.
sourceId -- [optional] identifying the data source controlling the device sourceId -- [optional] identifying the data source controlling the device
cacheType -- [optional] narrowing down the search to a specific kind of node cacheType -- [optional] narrowing down the search to a specific kind of node
""" """
if nodeId not in self._nodes: if nodeId not in self._nodes:
self._nodes.add(nodeId) self._nodes.add(nodeId)
@ -308,7 +308,7 @@ class ControlSetResponse(ElementBase):
def add_data(self, name): def add_data(self, name):
""" """
Add a new ResponseParameter element. Add a new ResponseParameter element.
Arguments: Arguments:
name -- Name of the parameter name -- Name of the parameter
@ -316,7 +316,7 @@ class ControlSetResponse(ElementBase):
if name not in self._datas: if name not in self._datas:
self._datas.add(name) self._datas.add(name)
data = ResponseParameter(parent=self) data = ResponseParameter(parent=self)
data['name'] = name; data['name'] = name
self.iterables.append(data) self.iterables.append(data)
return data return data
return None return None
@ -383,26 +383,26 @@ class Error(ElementBase):
value -- string value -- string
""" """
self.xml.text = value; self.xml.text = value
return self return self
def del_text(self): def del_text(self):
"""Remove the contents inside the XML tag.""" """Remove the contents inside the XML tag."""
self.xml.text = "" self.xml.text = ""
return self return self
class ResponseParameter(ElementBase): class ResponseParameter(ElementBase):
""" """
Parameter element in ControlSetResponse. Parameter element in ControlSetResponse.
""" """
namespace = 'urn:xmpp:iot:control' namespace = 'urn:xmpp:iot:control'
name = 'parameter' name = 'parameter'
plugin_attrib = name plugin_attrib = name
interfaces = set(['name']); interfaces = set(['name'])
class BaseParameter(ElementBase): class BaseParameter(ElementBase):
""" """
Parameter element in SetCommand. This is a base class, Parameter element in SetCommand. This is a base class,
all instances of parameters added to SetCommand must be of types: all instances of parameters added to SetCommand must be of types:
BooleanParameter BooleanParameter
@ -415,90 +415,91 @@ class BaseParameter(ElementBase):
IntParameter IntParameter
LongParameter LongParameter
TimeParameter TimeParameter
""" """
namespace = 'urn:xmpp:iot:control' namespace = 'urn:xmpp:iot:control'
name = 'baseParameter' name = 'baseParameter'
plugin_attrib = name plugin_attrib = name
interfaces = set(['name','value']); interfaces = set(['name','value'])
def _get_typename(self): def _get_typename(self):
return self.name; return self.name
class BooleanParameter(BaseParameter): class BooleanParameter(BaseParameter):
""" """
Field data of type boolean. Field data of type boolean.
Note that the value is expressed as a string. Note that the value is expressed as a string.
""" """
name = 'boolean' name = 'boolean'
plugin_attrib = name plugin_attrib = name
class ColorParameter(BaseParameter): class ColorParameter(BaseParameter):
""" """
Field data of type color. Field data of type color.
Note that the value is expressed as a string. Note that the value is expressed as a string.
""" """
name = 'color' name = 'color'
plugin_attrib = name plugin_attrib = name
class StringParameter(BaseParameter): class StringParameter(BaseParameter):
""" """
Field data of type string. Field data of type string.
""" """
name = 'string' name = 'string'
plugin_attrib = name plugin_attrib = name
class DateParameter(BaseParameter): class DateParameter(BaseParameter):
""" """
Field data of type date. Field data of type date.
Note that the value is expressed as a string. Note that the value is expressed as a string.
""" """
name = 'date' name = 'date'
plugin_attrib = name plugin_attrib = name
class DateTimeParameter(BaseParameter): class DateTimeParameter(BaseParameter):
""" """
Field data of type dateTime. Field data of type dateTime.
Note that the value is expressed as a string. Note that the value is expressed as a string.
""" """
name = 'dateTime' name = 'dateTime'
plugin_attrib = name plugin_attrib = name
class DoubleParameter(BaseParameter): class DoubleParameter(BaseParameter):
""" """
Field data of type double. Field data of type double.
Note that the value is expressed as a string. Note that the value is expressed as a string.
""" """
name = 'double' name = 'double'
plugin_attrib = name plugin_attrib = name
class DurationParameter(BaseParameter): class DurationParameter(BaseParameter):
""" """
Field data of type duration. Field data of type duration.
Note that the value is expressed as a string. Note that the value is expressed as a string.
""" """
name = 'duration' name = 'duration'
plugin_attrib = name plugin_attrib = name
class IntParameter(BaseParameter): class IntParameter(BaseParameter):
""" """
Field data of type int. Field data of type int.
Note that the value is expressed as a string. Note that the value is expressed as a string.
""" """
name = 'int' name = 'int'
plugin_attrib = name plugin_attrib = name
class LongParameter(BaseParameter): class LongParameter(BaseParameter):
""" """
Field data of type long (64-bit int). Field data of type long (64-bit int).
Note that the value is expressed as a string. Note that the value is expressed as a string.
""" """
name = 'long' name = 'long'
plugin_attrib = name plugin_attrib = name
class TimeParameter(BaseParameter): class TimeParameter(BaseParameter):
""" """
Field data of type time. Field data of type time.
Note that the value is expressed as a string. Note that the value is expressed as a string.
""" """
name = 'time' name = 'time'
plugin_attrib = name plugin_attrib = name

View File

@ -237,8 +237,7 @@ class RosterNode(object):
if not self.xmpp.is_component: if not self.xmpp.is_component:
return self.update(jid, subscription='remove') return self.update(jid, subscription='remove')
def update(self, jid, name=None, subscription=None, groups=[], def update(self, jid, name=None, subscription=None, groups=None, block=True, timeout=None, callback=None):
block=True, timeout=None, callback=None):
""" """
Update a JID's subscription information. Update a JID's subscription information.
@ -258,6 +257,9 @@ class RosterNode(object):
Will be executed when the roster is received. Will be executed when the roster is received.
Implies block=False. Implies block=False.
""" """
if not groups:
groups = []
self[jid]['name'] = name self[jid]['name'] = name
self[jid]['groups'] = groups self[jid]['groups'] = groups
self[jid].save() self[jid].save()

View File

@ -288,11 +288,8 @@ class SleekTest(unittest.TestCase):
if self.xmpp: if self.xmpp:
self.xmpp.socket.disconnect_error() self.xmpp.socket.disconnect_error()
def stream_start(self, mode='client', skip=True, header=None, def stream_start(self, mode='client', skip=True, header=None, socket='mock', jid='tester@localhost',
socket='mock', jid='tester@localhost', password='test', server='localhost', port=5222, sasl_mech=None, plugins=None, plugin_config=None):
password='test', server='localhost',
port=5222, sasl_mech=None,
plugins=None, plugin_config={}):
""" """
Initialize an XMPP client or component using a dummy XML stream. Initialize an XMPP client or component using a dummy XML stream.
@ -315,6 +312,9 @@ class SleekTest(unittest.TestCase):
plugins -- List of plugins to register. By default, all plugins plugins -- List of plugins to register. By default, all plugins
are loaded. are loaded.
""" """
if not plugin_config:
plugin_config = {}
if mode == 'client': if mode == 'client':
self.xmpp = ClientXMPP(jid, password, self.xmpp = ClientXMPP(jid, password,
sasl_mech=sasl_mech, sasl_mech=sasl_mech,
@ -425,8 +425,7 @@ class SleekTest(unittest.TestCase):
parts.append('xmlns="%s"' % default_ns) parts.append('xmlns="%s"' % default_ns)
return header % ' '.join(parts) return header % ' '.join(parts)
def recv(self, data, defaults=[], method='exact', def recv(self, data, defaults=None, method='exact', use_values=True, timeout=1):
use_values=True, timeout=1):
""" """
Pass data to the dummy XMPP client as if it came from an XMPP server. Pass data to the dummy XMPP client as if it came from an XMPP server.
@ -447,6 +446,9 @@ class SleekTest(unittest.TestCase):
timeout -- Time to wait in seconds for data to be received by timeout -- Time to wait in seconds for data to be received by
a live connection. a live connection.
""" """
if not defaults:
defaults = []
if self.xmpp.socket.is_live: if self.xmpp.socket.is_live:
# we are working with a live connection, so we should # we are working with a live connection, so we should
# verify what has been received instead of simulating # verify what has been received instead of simulating

View File

@ -213,7 +213,7 @@ class socksocket(socket.socket):
# Resolve locally # Resolve locally
ipaddr = socket.inet_aton(socket.gethostbyname(destaddr)) ipaddr = socket.inet_aton(socket.gethostbyname(destaddr))
req = req + chr(0x01).encode() + ipaddr req = req + chr(0x01).encode() + ipaddr
req = req + struct.pack(">H", destport) req += struct.pack(">H", destport)
self.sendall(req) self.sendall(req)
# Get the response # Get the response
resp = self.__recvall(4) resp = self.__recvall(4)
@ -282,7 +282,7 @@ class socksocket(socket.socket):
# The username parameter is considered userid for SOCKS4 # The username parameter is considered userid for SOCKS4
if self.__proxy[4] != None: if self.__proxy[4] != None:
req = req + self.__proxy[4] req = req + self.__proxy[4]
req = req + chr(0x00).encode() req += chr(0x00).encode()
# DNS name if remote resolving is required # DNS name if remote resolving is required
# NOTE: This is actually an extension to the SOCKS4 protocol # NOTE: This is actually an extension to the SOCKS4 protocol
# called SOCKS4A and may not be supported in all cases. # called SOCKS4A and may not be supported in all cases.

View File

@ -34,7 +34,7 @@ class StateMachine(object):
self.lock.release() self.lock.release()
def transition(self, from_state, to_state, wait=0.0, func=None, args=[], kwargs={}): def transition(self, from_state, to_state, wait=0.0, func=None, *args, **kwargs):
''' '''
Transition from the given `from_state` to the given `to_state`. Transition from the given `from_state` to the given `to_state`.
This method will return `True` if the state machine is now in `to_state`. It This method will return `True` if the state machine is now in `to_state`. It
@ -70,7 +70,7 @@ class StateMachine(object):
func=func, args=args, kwargs=kwargs) func=func, args=args, kwargs=kwargs)
def transition_any(self, from_states, to_state, wait=0.0, func=None, args=[], kwargs={}): def transition_any(self, from_states, to_state, wait=0.0, func=None, *args, **kwargs):
''' '''
Transition from any of the given `from_states` to the given `to_state`. Transition from any of the given `from_states` to the given `to_state`.
''' '''

View File

@ -853,7 +853,7 @@ class XMLStream(object):
if sys.version_info >= (2, 7): if sys.version_info >= (2, 7):
ssl_args['ciphers'] = self.ciphers ssl_args['ciphers'] = self.ciphers
ssl_socket = ssl.wrap_socket(self.socket, **ssl_args); ssl_socket = ssl.wrap_socket(self.socket, **ssl_args)
if hasattr(self.socket, 'socket'): if hasattr(self.socket, 'socket'):
# We are using a testing socket, so preserve the top # We are using a testing socket, so preserve the top
@ -1148,7 +1148,7 @@ class XMLStream(object):
""" """
return len(self.__event_handlers.get(name, [])) return len(self.__event_handlers.get(name, []))
def event(self, name, data={}, direct=False): def event(self, name, data=None, direct=False):
"""Manually trigger a custom event. """Manually trigger a custom event.
:param name: The name of the event to trigger. :param name: The name of the event to trigger.
@ -1159,6 +1159,9 @@ class XMLStream(object):
event queue. All event handlers will run in the event queue. All event handlers will run in the
same thread. same thread.
""" """
if not data:
data = {}
log.debug("Event triggered: " + name) log.debug("Event triggered: " + name)
handlers = self.__event_handlers.get(name, []) handlers = self.__event_handlers.get(name, [])
@ -1318,9 +1321,6 @@ class XMLStream(object):
try: try:
sent += self.socket.send(data[sent:]) sent += self.socket.send(data[sent:])
count += 1 count += 1
except Socket.error as serr:
if serr.errno != errno.EINTR:
raise
except ssl.SSLError as serr: except ssl.SSLError as serr:
if tries >= self.ssl_retry_max: if tries >= self.ssl_retry_max:
log.debug('SSL error: max retries reached') log.debug('SSL error: max retries reached')
@ -1335,6 +1335,9 @@ class XMLStream(object):
if not self.stop.is_set(): if not self.stop.is_set():
time.sleep(self.ssl_retry_delay) time.sleep(self.ssl_retry_delay)
tries += 1 tries += 1
except Socket.error as serr:
if serr.errno != errno.EINTR:
raise
if count > 1: if count > 1:
log.debug('SENT: %d chunks', count) log.debug('SENT: %d chunks', count)
except (Socket.error, ssl.SSLError) as serr: except (Socket.error, ssl.SSLError) as serr:
@ -1744,9 +1747,6 @@ class XMLStream(object):
try: try:
sent += self.socket.send(enc_data[sent:]) sent += self.socket.send(enc_data[sent:])
count += 1 count += 1
except Socket.error as serr:
if serr.errno != errno.EINTR:
raise
except ssl.SSLError as serr: except ssl.SSLError as serr:
if tries >= self.ssl_retry_max: if tries >= self.ssl_retry_max:
log.debug('SSL error: max retries reached') log.debug('SSL error: max retries reached')
@ -1759,6 +1759,9 @@ class XMLStream(object):
if not self.stop.is_set(): if not self.stop.is_set():
time.sleep(self.ssl_retry_delay) time.sleep(self.ssl_retry_delay)
tries += 1 tries += 1
except Socket.error as serr:
if serr.errno != errno.EINTR:
raise
if count > 1: if count > 1:
log.debug('SENT: %d chunks', count) log.debug('SENT: %d chunks', count)
self.send_queue.task_done() self.send_queue.task_done()

View File

@ -385,7 +385,7 @@ class TestElementBase(SleekTest):
interfaces = set(('bar', 'baz')) interfaces = set(('bar', 'baz'))
def setBar(self, value): def setBar(self, value):
self._set_sub_text("path/to/only/bar", value); self._set_sub_text("path/to/only/bar", value)
def getBar(self): def getBar(self):
return self._get_sub_text("path/to/only/bar") return self._get_sub_text("path/to/only/bar")
@ -394,7 +394,7 @@ class TestElementBase(SleekTest):
self._del_sub("path/to/only/bar") self._del_sub("path/to/only/bar")
def setBaz(self, value): def setBaz(self, value):
self._set_sub_text("path/to/just/baz", value); self._set_sub_text("path/to/just/baz", value)
def getBaz(self): def getBaz(self):
return self._get_sub_text("path/to/just/baz") return self._get_sub_text("path/to/just/baz")

View File

@ -6,7 +6,7 @@ import sleekxmpp.plugins.xep_0323 as xep_0323
namespace='sn' namespace='sn'
class TestSensorDataStanzas(SleekTest): class TestSensorDataStanzas(SleekTest):
def setUp(self): def setUp(self):
pass pass
@ -59,8 +59,8 @@ class TestSensorDataStanzas(SleekTest):
iq['req']['momentary'] = 'true' iq['req']['momentary'] = 'true'
iq['req'].add_node("Device02", "Source02", "CacheType"); iq['req'].add_node("Device02", "Source02", "CacheType")
iq['req'].add_node("Device44"); iq['req'].add_node("Device44")
self.check(iq,""" self.check(iq,"""
<iq type='get' <iq type='get'
@ -75,7 +75,7 @@ class TestSensorDataStanzas(SleekTest):
""" """
) )
iq['req'].del_node("Device02"); iq['req'].del_node("Device02")
self.check(iq,""" self.check(iq,"""
<iq type='get' <iq type='get'
@ -89,7 +89,7 @@ class TestSensorDataStanzas(SleekTest):
""" """
) )
iq['req'].del_nodes(); iq['req'].del_nodes()
self.check(iq,""" self.check(iq,"""
<iq type='get' <iq type='get'
@ -115,8 +115,8 @@ class TestSensorDataStanzas(SleekTest):
iq['req']['momentary'] = 'true' iq['req']['momentary'] = 'true'
iq['req'].add_field("Top temperature"); iq['req'].add_field("Top temperature")
iq['req'].add_field("Bottom temperature"); iq['req'].add_field("Bottom temperature")
self.check(iq,""" self.check(iq,"""
<iq type='get' <iq type='get'
@ -171,7 +171,7 @@ class TestSensorDataStanzas(SleekTest):
iq['accepted']['seqnr'] = '2' iq['accepted']['seqnr'] = '2'
self.check(iq,""" self.check(iq,"""
<iq type='result' <iq type='result'
from='device@clayster.com' from='device@clayster.com'
to='master@clayster.com/amr' to='master@clayster.com/amr'
id='2'> id='2'>
@ -193,7 +193,7 @@ class TestSensorDataStanzas(SleekTest):
iq['rejected']['error'] = 'Access denied.' iq['rejected']['error'] = 'Access denied.'
self.check(iq,""" self.check(iq,"""
<iq type='error' <iq type='error'
from='device@clayster.com' from='device@clayster.com'
to='master@clayster.com/amr' to='master@clayster.com/amr'
id='4'> id='4'>
@ -237,12 +237,12 @@ class TestSensorDataStanzas(SleekTest):
msg['to'] = 'master@clayster.com/amr' msg['to'] = 'master@clayster.com/amr'
msg['fields']['seqnr'] = '1' msg['fields']['seqnr'] = '1'
node = msg['fields'].add_node("Device02"); node = msg['fields'].add_node("Device02")
ts = node.add_timestamp("2013-03-07T16:24:30"); ts = node.add_timestamp("2013-03-07T16:24:30")
data = ts.add_data(typename="numeric", name="Temperature", value="-12.42", unit='K'); data = ts.add_data(typename="numeric", name="Temperature", value="-12.42", unit='K')
data['momentary'] = 'true'; data['momentary'] = 'true'
data['automaticReadout'] = 'true'; data['automaticReadout'] = 'true'
self.check(msg,""" self.check(msg,"""
<message from='device@clayster.com' <message from='device@clayster.com'
@ -250,7 +250,7 @@ class TestSensorDataStanzas(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='1'> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='1'>
<node nodeId='Device02'> <node nodeId='Device02'>
<timestamp value='2013-03-07T16:24:30'> <timestamp value='2013-03-07T16:24:30'>
<numeric name='Temperature' momentary='true' automaticReadout='true' value='-12.42' unit='K'/> <numeric name='Temperature' momentary='true' automaticReadout='true' value='-12.42' unit='K'/>
</timestamp> </timestamp>
</node> </node>
</fields> </fields>
@ -258,10 +258,9 @@ class TestSensorDataStanzas(SleekTest):
""" """
) )
node = msg['fields'].add_node("EmptyDevice"); node = msg['fields'].add_node("EmptyDevice")
node = msg['fields'].add_node("Device04"); node = msg['fields'].add_node("Device04")
ts = node.add_timestamp("EmptyTimestamp"); ts = node.add_timestamp("EmptyTimestamp")
self.check(msg,""" self.check(msg,"""
<message from='device@clayster.com' <message from='device@clayster.com'
@ -269,7 +268,7 @@ class TestSensorDataStanzas(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='1'> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='1'>
<node nodeId='Device02'> <node nodeId='Device02'>
<timestamp value='2013-03-07T16:24:30'> <timestamp value='2013-03-07T16:24:30'>
<numeric name='Temperature' momentary='true' automaticReadout='true' value='-12.42' unit='K'/> <numeric name='Temperature' momentary='true' automaticReadout='true' value='-12.42' unit='K'/>
</timestamp> </timestamp>
</node> </node>
<node nodeId='EmptyDevice'/> <node nodeId='EmptyDevice'/>
@ -281,32 +280,32 @@ class TestSensorDataStanzas(SleekTest):
""" """
) )
node = msg['fields'].add_node("Device77"); node = msg['fields'].add_node("Device77")
ts = node.add_timestamp("2013-05-03T12:00:01"); ts = node.add_timestamp("2013-05-03T12:00:01")
data = ts.add_data(typename="numeric", name="Temperature", value="-12.42", unit='K'); data = ts.add_data(typename="numeric", name="Temperature", value="-12.42", unit='K')
data['historicalDay'] = 'true'; data['historicalDay'] = 'true'
data = ts.add_data(typename="numeric", name="Speed", value="312.42", unit='km/h'); data = ts.add_data(typename="numeric", name="Speed", value="312.42", unit='km/h')
data['historicalWeek'] = 'false'; data['historicalWeek'] = 'false'
data = ts.add_data(typename="string", name="Temperature name", value="Bottom oil"); data = ts.add_data(typename="string", name="Temperature name", value="Bottom oil")
data['historicalMonth'] = 'true'; data['historicalMonth'] = 'true'
data = ts.add_data(typename="string", name="Speed name", value="Top speed"); data = ts.add_data(typename="string", name="Speed name", value="Top speed")
data['historicalQuarter'] = 'false'; data['historicalQuarter'] = 'false'
data = ts.add_data(typename="dateTime", name="T1", value="1979-01-01T00:00:00"); data = ts.add_data(typename="dateTime", name="T1", value="1979-01-01T00:00:00")
data['historicalYear'] = 'true'; data['historicalYear'] = 'true'
data = ts.add_data(typename="dateTime", name="T2", value="2000-01-01T01:02:03"); data = ts.add_data(typename="dateTime", name="T2", value="2000-01-01T01:02:03")
data['historicalOther'] = 'false'; data['historicalOther'] = 'false'
data = ts.add_data(typename="timeSpan", name="TS1", value="P5Y"); data = ts.add_data(typename="timeSpan", name="TS1", value="P5Y")
data['missing'] = 'true'; data['missing'] = 'true'
data = ts.add_data(typename="timeSpan", name="TS2", value="PT2M1S"); data = ts.add_data(typename="timeSpan", name="TS2", value="PT2M1S")
data['manualEstimate'] = 'false'; data['manualEstimate'] = 'false'
data = ts.add_data(typename="enum", name="top color", value="red", dataType="string"); data = ts.add_data(typename="enum", name="top color", value="red", dataType="string")
data['invoiced'] = 'true'; data['invoiced'] = 'true'
data = ts.add_data(typename="enum", name="bottom color", value="black", dataType="string"); data = ts.add_data(typename="enum", name="bottom color", value="black", dataType="string")
data['powerFailure'] = 'false'; data['powerFailure'] = 'false'
data = ts.add_data(typename="boolean", name="Temperature real", value="false"); data = ts.add_data(typename="boolean", name="Temperature real", value="false")
data['historicalDay'] = 'true'; data['historicalDay'] = 'true'
data = ts.add_data(typename="boolean", name="Speed real", value="true"); data = ts.add_data(typename="boolean", name="Speed real", value="true")
data['historicalWeek'] = 'false'; data['historicalWeek'] = 'false'
self.check(msg,""" self.check(msg,"""
<message from='device@clayster.com' <message from='device@clayster.com'
@ -314,7 +313,7 @@ class TestSensorDataStanzas(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='1'> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='1'>
<node nodeId='Device02'> <node nodeId='Device02'>
<timestamp value='2013-03-07T16:24:30'> <timestamp value='2013-03-07T16:24:30'>
<numeric name='Temperature' momentary='true' automaticReadout='true' value='-12.42' unit='K'/> <numeric name='Temperature' momentary='true' automaticReadout='true' value='-12.42' unit='K'/>
</timestamp> </timestamp>
</node> </node>
<node nodeId='EmptyDevice'/> <node nodeId='EmptyDevice'/>
@ -323,18 +322,18 @@ class TestSensorDataStanzas(SleekTest):
</node> </node>
<node nodeId='Device77'> <node nodeId='Device77'>
<timestamp value='2013-05-03T12:00:01'> <timestamp value='2013-05-03T12:00:01'>
<numeric name='Temperature' historicalDay='true' value='-12.42' unit='K'/> <numeric name='Temperature' historicalDay='true' value='-12.42' unit='K'/>
<numeric name='Speed' historicalWeek='false' value='312.42' unit='km/h'/> <numeric name='Speed' historicalWeek='false' value='312.42' unit='km/h'/>
<string name='Temperature name' historicalMonth='true' value='Bottom oil'/> <string name='Temperature name' historicalMonth='true' value='Bottom oil'/>
<string name='Speed name' historicalQuarter='false' value='Top speed'/> <string name='Speed name' historicalQuarter='false' value='Top speed'/>
<dateTime name='T1' historicalYear='true' value='1979-01-01T00:00:00'/> <dateTime name='T1' historicalYear='true' value='1979-01-01T00:00:00'/>
<dateTime name='T2' historicalOther='false' value='2000-01-01T01:02:03'/> <dateTime name='T2' historicalOther='false' value='2000-01-01T01:02:03'/>
<timeSpan name='TS1' missing='true' value='P5Y'/> <timeSpan name='TS1' missing='true' value='P5Y'/>
<timeSpan name='TS2' manualEstimate='false' value='PT2M1S'/> <timeSpan name='TS2' manualEstimate='false' value='PT2M1S'/>
<enum name='top color' invoiced='true' value='red' dataType='string'/> <enum name='top color' invoiced='true' value='red' dataType='string'/>
<enum name='bottom color' powerFailure='false' value='black' dataType='string'/> <enum name='bottom color' powerFailure='false' value='black' dataType='string'/>
<boolean name='Temperature real' historicalDay='true' value='false'/> <boolean name='Temperature real' historicalDay='true' value='false'/>
<boolean name='Speed real' historicalWeek='false' value='true'/> <boolean name='Speed real' historicalWeek='false' value='true'/>
</timestamp> </timestamp>
</node> </node>
</fields> </fields>
@ -342,21 +341,19 @@ class TestSensorDataStanzas(SleekTest):
""" """
) )
def testTimestamp(self): def testTimestamp(self):
msg = self.Message(); msg = self.Message()
msg['from'] = 'device@clayster.com' msg['from'] = 'device@clayster.com'
msg['to'] = 'master@clayster.com/amr' msg['to'] = 'master@clayster.com/amr'
msg['fields']['seqnr'] = '1' msg['fields']['seqnr'] = '1'
node = msg['fields'].add_node("Device02"); node = msg['fields'].add_node("Device02")
node = msg['fields'].add_node("Device03"); node = msg['fields'].add_node("Device03")
ts = node.add_timestamp("2013-03-07T16:24:30");
ts = node.add_timestamp("2013-03-07T16:24:31");
ts = node.add_timestamp("2013-03-07T16:24:30")
ts = node.add_timestamp("2013-03-07T16:24:31")
self.check(msg,""" self.check(msg,"""
<message from='device@clayster.com' <message from='device@clayster.com'
@ -386,8 +383,8 @@ class TestSensorDataStanzas(SleekTest):
self.check(msg,emptyStringIdXML) self.check(msg,emptyStringIdXML)
msg['fields']['stringIds'] = "1" msg['fields']['stringIds'] = "1"
self.check(msg,emptyStringIdXML) self.check(msg,emptyStringIdXML)
suite = unittest.TestLoader().loadTestsFromTestCase(TestSensorDataStanzas) suite = unittest.TestLoader().loadTestsFromTestCase(TestSensorDataStanzas)

View File

@ -15,7 +15,7 @@ import sleekxmpp.plugins.xep_0325 as xep_0325
namespace='sn' namespace='sn'
class TestControlStanzas(SleekTest): class TestControlStanzas(SleekTest):
def setUp(self): def setUp(self):
pass pass
@ -29,8 +29,8 @@ class TestControlStanzas(SleekTest):
iq['from'] = 'master@clayster.com/amr' iq['from'] = 'master@clayster.com/amr'
iq['to'] = 'device@clayster.com' iq['to'] = 'device@clayster.com'
iq['id'] = '1' iq['id'] = '1'
iq['set'].add_node("Device02", "Source02", "MyCacheType"); iq['set'].add_node("Device02", "Source02", "MyCacheType")
iq['set'].add_node("Device15"); iq['set'].add_node("Device15")
iq['set'].add_data("Tjohej", "boolean", "true") iq['set'].add_data("Tjohej", "boolean", "true")
self.check(iq,""" self.check(iq,"""
@ -47,7 +47,7 @@ class TestControlStanzas(SleekTest):
""" """
) )
iq['set'].del_node("Device02"); iq['set'].del_node("Device02")
self.check(iq,""" self.check(iq,"""
<iq type='set' <iq type='set'
@ -62,7 +62,7 @@ class TestControlStanzas(SleekTest):
""" """
) )
iq['set'].del_nodes(); iq['set'].del_nodes()
self.check(iq,""" self.check(iq,"""
<iq type='set' <iq type='set'
@ -84,12 +84,12 @@ class TestControlStanzas(SleekTest):
msg = self.Message() msg = self.Message()
msg['from'] = 'master@clayster.com/amr' msg['from'] = 'master@clayster.com/amr'
msg['to'] = 'device@clayster.com' msg['to'] = 'device@clayster.com'
msg['set'].add_node("Device02"); msg['set'].add_node("Device02")
msg['set'].add_node("Device15"); msg['set'].add_node("Device15")
msg['set'].add_data("Tjohej", "boolean", "true") msg['set'].add_data("Tjohej", "boolean", "true")
self.check(msg,""" self.check(msg,"""
<message <message
from='master@clayster.com/amr' from='master@clayster.com/amr'
to='device@clayster.com'> to='device@clayster.com'>
<set xmlns='urn:xmpp:iot:control'> <set xmlns='urn:xmpp:iot:control'>
@ -111,7 +111,7 @@ class TestControlStanzas(SleekTest):
iq['from'] = 'master@clayster.com/amr' iq['from'] = 'master@clayster.com/amr'
iq['to'] = 'device@clayster.com' iq['to'] = 'device@clayster.com'
iq['id'] = '8' iq['id'] = '8'
iq['setResponse']['responseCode'] = "OK"; iq['setResponse']['responseCode'] = "OK"
self.check(iq,""" self.check(iq,"""
<iq type='result' <iq type='result'
@ -128,10 +128,9 @@ class TestControlStanzas(SleekTest):
iq['from'] = 'master@clayster.com/amr' iq['from'] = 'master@clayster.com/amr'
iq['to'] = 'device@clayster.com' iq['to'] = 'device@clayster.com'
iq['id'] = '9' iq['id'] = '9'
iq['setResponse']['responseCode'] = "OtherError"; iq['setResponse']['responseCode'] = "OtherError"
iq['setResponse']['error']['var'] = "Output"; iq['setResponse']['error']['var'] = "Output"
iq['setResponse']['error']['text'] = "Test of other error.!"; iq['setResponse']['error']['text'] = "Test of other error.!"
self.check(iq,""" self.check(iq,"""
<iq type='error' <iq type='error'
@ -150,11 +149,10 @@ class TestControlStanzas(SleekTest):
iq['from'] = 'master@clayster.com/amr' iq['from'] = 'master@clayster.com/amr'
iq['to'] = 'device@clayster.com' iq['to'] = 'device@clayster.com'
iq['id'] = '9' iq['id'] = '9'
iq['setResponse']['responseCode'] = "NotFound"; iq['setResponse']['responseCode'] = "NotFound"
iq['setResponse'].add_node("Device17", "Source09"); iq['setResponse'].add_node("Device17", "Source09")
iq['setResponse'].add_node("Device18", "Source09"); iq['setResponse'].add_node("Device18", "Source09")
iq['setResponse'].add_data("Tjohopp"); iq['setResponse'].add_data("Tjohopp")
self.check(iq,""" self.check(iq,"""
<iq type='error' <iq type='error'
@ -179,38 +177,38 @@ class TestControlStanzas(SleekTest):
iq['from'] = 'master@clayster.com/amr' iq['from'] = 'master@clayster.com/amr'
iq['to'] = 'device@clayster.com' iq['to'] = 'device@clayster.com'
iq['id'] = '1' iq['id'] = '1'
iq['set'].add_node("Device02", "Source02", "MyCacheType"); iq['set'].add_node("Device02", "Source02", "MyCacheType")
iq['set'].add_node("Device15"); iq['set'].add_node("Device15")
iq['set'].add_data("Tjohej", "boolean", "true"); iq['set'].add_data("Tjohej", "boolean", "true")
iq['set'].add_data("Tjohej2", "boolean", "false"); iq['set'].add_data("Tjohej2", "boolean", "false")
iq['set'].add_data("TjohejC", "color", "FF00FF"); iq['set'].add_data("TjohejC", "color", "FF00FF")
iq['set'].add_data("TjohejC2", "color", "00FF00"); iq['set'].add_data("TjohejC2", "color", "00FF00")
iq['set'].add_data("TjohejS", "string", "String1"); iq['set'].add_data("TjohejS", "string", "String1")
iq['set'].add_data("TjohejS2", "string", "String2"); iq['set'].add_data("TjohejS2", "string", "String2")
iq['set'].add_data("TjohejDate", "date", "2012-01-01"); iq['set'].add_data("TjohejDate", "date", "2012-01-01")
iq['set'].add_data("TjohejDate2", "date", "1900-12-03"); iq['set'].add_data("TjohejDate2", "date", "1900-12-03")
iq['set'].add_data("TjohejDateT4", "dateTime", "1900-12-03 12:30"); iq['set'].add_data("TjohejDateT4", "dateTime", "1900-12-03 12:30")
iq['set'].add_data("TjohejDateT2", "dateTime", "1900-12-03 11:22"); iq['set'].add_data("TjohejDateT2", "dateTime", "1900-12-03 11:22")
iq['set'].add_data("TjohejDouble2", "double", "200.22"); iq['set'].add_data("TjohejDouble2", "double", "200.22")
iq['set'].add_data("TjohejDouble3", "double", "-12232131.3333"); iq['set'].add_data("TjohejDouble3", "double", "-12232131.3333")
iq['set'].add_data("TjohejDur", "duration", "P5Y"); iq['set'].add_data("TjohejDur", "duration", "P5Y")
iq['set'].add_data("TjohejDur2", "duration", "PT2M1S"); iq['set'].add_data("TjohejDur2", "duration", "PT2M1S")
iq['set'].add_data("TjohejInt", "int", "1"); iq['set'].add_data("TjohejInt", "int", "1")
iq['set'].add_data("TjohejInt2", "int", "-42"); iq['set'].add_data("TjohejInt2", "int", "-42")
iq['set'].add_data("TjohejLong", "long", "123456789098"); iq['set'].add_data("TjohejLong", "long", "123456789098")
iq['set'].add_data("TjohejLong2", "long", "-90983243827489374"); iq['set'].add_data("TjohejLong2", "long", "-90983243827489374")
iq['set'].add_data("TjohejTime", "time", "23:59"); iq['set'].add_data("TjohejTime", "time", "23:59")
iq['set'].add_data("TjohejTime2", "time", "12:00"); iq['set'].add_data("TjohejTime2", "time", "12:00")
self.check(iq,""" self.check(iq,"""
<iq type='set' <iq type='set'
@ -244,5 +242,5 @@ class TestControlStanzas(SleekTest):
</iq> </iq>
""" """
) )
suite = unittest.TestLoader().loadTestsFromTestCase(TestControlStanzas) suite = unittest.TestLoader().loadTestsFromTestCase(TestControlStanzas)

View File

@ -19,7 +19,7 @@ class TestStreamSensorData(SleekTest):
pass pass
def _time_now(self): def _time_now(self):
return datetime.datetime.now().replace(microsecond=0).isoformat(); return datetime.datetime.now().replace(microsecond=0).isoformat()
def tearDown(self): def tearDown(self):
self.stream_close() self.stream_close()
@ -29,12 +29,12 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030', plugins=['xep_0030',
'xep_0323']) 'xep_0323'])
myDevice = Device("Device22"); myDevice = Device("Device22")
myDevice._add_field(name="Temperature", typename="numeric", unit="°C"); myDevice._add_field(name="Temperature", typename="numeric", unit="°C")
myDevice._set_momentary_timestamp("2013-03-07T16:24:30") myDevice._set_momentary_timestamp("2013-03-07T16:24:30")
myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"}); myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"})
self.xmpp['xep_0323'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5); self.xmpp['xep_0323'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5)
self.recv(""" self.recv("""
<iq type='get' <iq type='get'
@ -46,7 +46,7 @@ class TestStreamSensorData(SleekTest):
""") """)
self.send(""" self.send("""
<iq type='result' <iq type='result'
from='device@clayster.com' from='device@clayster.com'
to='master@clayster.com/amr' to='master@clayster.com/amr'
id='1'> id='1'>
@ -60,11 +60,11 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='1' done='true'> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='1' done='true'>
<node nodeId='Device22'> <node nodeId='Device22'>
<timestamp value='2013-03-07T16:24:30'> <timestamp value='2013-03-07T16:24:30'>
<numeric name='Temperature' momentary='true' automaticReadout='true' value='23.4' unit='°C'/> <numeric name='Temperature' momentary='true' automaticReadout='true' value='23.4' unit='°C'/>
</timestamp> </timestamp>
</node> </node>
</fields> </fields>
</message> </message>
""") """)
def testRequestRejectAuth(self): def testRequestRejectAuth(self):
@ -73,7 +73,7 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030', plugins=['xep_0030',
'xep_0323']) 'xep_0323'])
self.xmpp['xep_0323']._set_authenticated("darth@deathstar.com"); self.xmpp['xep_0323']._set_authenticated("darth@deathstar.com")
self.recv(""" self.recv("""
<iq type='get' <iq type='get'
@ -85,7 +85,7 @@ class TestStreamSensorData(SleekTest):
""") """)
self.send(""" self.send("""
<iq type='error' <iq type='error'
from='device@clayster.com' from='device@clayster.com'
to='master@clayster.com/amr' to='master@clayster.com/amr'
id='4'> id='4'>
@ -101,8 +101,8 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030', plugins=['xep_0030',
'xep_0323']) 'xep_0323'])
myDevice = Device("Device44"); myDevice = Device("Device44")
self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5); self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5)
print("."), print("."),
@ -118,7 +118,7 @@ class TestStreamSensorData(SleekTest):
""") """)
self.send(""" self.send("""
<iq type='error' <iq type='error'
from='device@clayster.com' from='device@clayster.com'
to='master@clayster.com/amr' to='master@clayster.com/amr'
id='77'> id='77'>
@ -142,7 +142,7 @@ class TestStreamSensorData(SleekTest):
""") """)
self.send(""" self.send("""
<iq type='result' <iq type='result'
from='device@clayster.com' from='device@clayster.com'
to='master@clayster.com/amr' to='master@clayster.com/amr'
id='8'> id='8'>
@ -157,11 +157,11 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030', plugins=['xep_0030',
'xep_0323']) 'xep_0323'])
myDevice = Device("Device44"); myDevice = Device("Device44")
myDevice._add_field(name='Voltage', typename="numeric", unit="V"); myDevice._add_field(name='Voltage', typename="numeric", unit="V")
myDevice._add_field_timestamp_data(name="Voltage", value="230.4", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"}); myDevice._add_field_timestamp_data(name="Voltage", value="230.4", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"})
self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5); self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5)
print("."), print("."),
@ -177,7 +177,7 @@ class TestStreamSensorData(SleekTest):
""") """)
self.send(""" self.send("""
<iq type='error' <iq type='error'
from='device@clayster.com' from='device@clayster.com'
to='master@clayster.com/amr' to='master@clayster.com/amr'
id='7'> id='7'>
@ -201,7 +201,7 @@ class TestStreamSensorData(SleekTest):
""") """)
self.send(""" self.send("""
<iq type='result' <iq type='result'
from='device@clayster.com' from='device@clayster.com'
to='master@clayster.com/amr' to='master@clayster.com/amr'
id='8'> id='8'>
@ -215,11 +215,11 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='7'> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='7'>
<node nodeId='Device44'> <node nodeId='Device44'>
<timestamp value='2000-01-01T00:01:02'> <timestamp value='2000-01-01T00:01:02'>
<numeric name='Voltage' invoiced='true' value='230.4' unit='V'/> <numeric name='Voltage' invoiced='true' value='230.4' unit='V'/>
</timestamp> </timestamp>
</node> </node>
</fields> </fields>
</message> </message>
""") """)
self.send(""" self.send("""
@ -227,7 +227,7 @@ class TestStreamSensorData(SleekTest):
to='master@clayster.com/amr'> to='master@clayster.com/amr'>
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='7' done='true'> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='7' done='true'>
</fields> </fields>
</message> </message>
""") """)
def testRequestMultiTimestampSingleField(self): def testRequestMultiTimestampSingleField(self):
@ -236,15 +236,15 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030', plugins=['xep_0030',
'xep_0323']) 'xep_0323'])
myDevice = Device("Device44"); myDevice = Device("Device44")
myDevice._add_field(name='Voltage', typename="numeric", unit="V"); myDevice._add_field(name='Voltage', typename="numeric", unit="V")
myDevice._add_field_timestamp_data(name="Voltage", value="230.4", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"}); myDevice._add_field_timestamp_data(name="Voltage", value="230.4", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"})
myDevice._add_field(name='Current', typename="numeric", unit="A"); myDevice._add_field(name='Current', typename="numeric", unit="A")
myDevice._add_field(name='Height', typename="string"); myDevice._add_field(name='Height', typename="string")
myDevice._add_field_timestamp_data(name="Voltage", value="230.6", timestamp="2000-01-01T01:01:02"); myDevice._add_field_timestamp_data(name="Voltage", value="230.6", timestamp="2000-01-01T01:01:02")
myDevice._add_field_timestamp_data(name="Height", value="115 m", timestamp="2000-01-01T01:01:02", flags={"invoiced": "true"}); myDevice._add_field_timestamp_data(name="Height", value="115 m", timestamp="2000-01-01T01:01:02", flags={"invoiced": "true"})
self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5); self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5)
print("."), print("."),
@ -260,7 +260,7 @@ class TestStreamSensorData(SleekTest):
""") """)
self.send(""" self.send("""
<iq type='result' <iq type='result'
from='device@clayster.com' from='device@clayster.com'
to='master@clayster.com/amr' to='master@clayster.com/amr'
id='8'> id='8'>
@ -274,11 +274,11 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='7'> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='7'>
<node nodeId='Device44'> <node nodeId='Device44'>
<timestamp value='2000-01-01T00:01:02'> <timestamp value='2000-01-01T00:01:02'>
<numeric name='Voltage' invoiced='true' value='230.4' unit='V'/> <numeric name='Voltage' invoiced='true' value='230.4' unit='V'/>
</timestamp> </timestamp>
</node> </node>
</fields> </fields>
</message> </message>
""") """)
self.send(""" self.send("""
@ -287,11 +287,11 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='7'> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='7'>
<node nodeId='Device44'> <node nodeId='Device44'>
<timestamp value='2000-01-01T01:01:02'> <timestamp value='2000-01-01T01:01:02'>
<numeric name='Voltage' value='230.6' unit='V'/> <numeric name='Voltage' value='230.6' unit='V'/>
</timestamp> </timestamp>
</node> </node>
</fields> </fields>
</message> </message>
""") """)
self.send(""" self.send("""
@ -299,7 +299,7 @@ class TestStreamSensorData(SleekTest):
to='master@clayster.com/amr'> to='master@clayster.com/amr'>
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='7' done='true'> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='7' done='true'>
</fields> </fields>
</message> </message>
""") """)
def testRequestMultiTimestampAllFields(self): def testRequestMultiTimestampAllFields(self):
@ -308,15 +308,15 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030', plugins=['xep_0030',
'xep_0323']) 'xep_0323'])
myDevice = Device("Device44"); myDevice = Device("Device44")
myDevice._add_field(name='Voltage', typename="numeric", unit="V"); myDevice._add_field(name='Voltage', typename="numeric", unit="V")
myDevice._add_field_timestamp_data(name="Voltage", value="230.4", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"}); myDevice._add_field_timestamp_data(name="Voltage", value="230.4", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"})
myDevice._add_field(name='Current', typename="numeric", unit="A"); myDevice._add_field(name='Current', typename="numeric", unit="A")
myDevice._add_field(name='Height', typename="string"); myDevice._add_field(name='Height', typename="string")
myDevice._add_field_timestamp_data(name="Voltage", value="230.6", timestamp="2000-01-01T01:01:02"); myDevice._add_field_timestamp_data(name="Voltage", value="230.6", timestamp="2000-01-01T01:01:02")
myDevice._add_field_timestamp_data(name="Height", value="115 m", timestamp="2000-01-01T01:01:02", flags={"invoiced": "true"}); myDevice._add_field_timestamp_data(name="Height", value="115 m", timestamp="2000-01-01T01:01:02", flags={"invoiced": "true"})
self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5); self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5)
print("."), print("."),
@ -330,7 +330,7 @@ class TestStreamSensorData(SleekTest):
""") """)
self.send(""" self.send("""
<iq type='result' <iq type='result'
from='device@clayster.com' from='device@clayster.com'
to='master@clayster.com/amr' to='master@clayster.com/amr'
id='8'> id='8'>
@ -344,11 +344,11 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='7'> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='7'>
<node nodeId='Device44'> <node nodeId='Device44'>
<timestamp value='2000-01-01T00:01:02'> <timestamp value='2000-01-01T00:01:02'>
<numeric name='Voltage' invoiced='true' value='230.4' unit='V'/> <numeric name='Voltage' invoiced='true' value='230.4' unit='V'/>
</timestamp> </timestamp>
</node> </node>
</fields> </fields>
</message> </message>
""") """)
self.send(""" self.send("""
@ -357,12 +357,12 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='7'> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='7'>
<node nodeId='Device44'> <node nodeId='Device44'>
<timestamp value='2000-01-01T01:01:02'> <timestamp value='2000-01-01T01:01:02'>
<numeric name='Voltage' value='230.6' unit='V'/> <numeric name='Voltage' value='230.6' unit='V'/>
<string name='Height' invoiced='true' value='115 m'/> <string name='Height' invoiced='true' value='115 m'/>
</timestamp> </timestamp>
</node> </node>
</fields> </fields>
</message> </message>
""") """)
self.send(""" self.send("""
@ -370,7 +370,7 @@ class TestStreamSensorData(SleekTest):
to='master@clayster.com/amr'> to='master@clayster.com/amr'>
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='7' done='true'> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='7' done='true'>
</fields> </fields>
</message> </message>
""") """)
def testRequestAPI(self): def testRequestAPI(self):
@ -379,7 +379,7 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030', plugins=['xep_0030',
'xep_0323']) 'xep_0323'])
self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", callback=None); self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", callback=None)
self.send(""" self.send("""
<iq type='get' <iq type='get'
@ -390,7 +390,7 @@ class TestStreamSensorData(SleekTest):
</iq> </iq>
""") """)
self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33', 'Device22'], callback=None); self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33', 'Device22'], callback=None)
self.send(""" self.send("""
<iq type='get' <iq type='get'
@ -404,7 +404,7 @@ class TestStreamSensorData(SleekTest):
</iq> </iq>
""") """)
self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", fields=['Temperature', 'Voltage'], callback=None); self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", fields=['Temperature', 'Voltage'], callback=None)
self.send(""" self.send("""
<iq type='get' <iq type='get'
@ -424,13 +424,13 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030', plugins=['xep_0030',
'xep_0323']) 'xep_0323'])
results = []; results = []
def my_callback(from_jid, result, nodeId=None, timestamp=None, fields=None, error_msg=None): def my_callback(from_jid, result, nodeId=None, timestamp=None, fields=None, error_msg=None):
if (result == "rejected") and (error_msg == "Invalid device Device22"): if (result == "rejected") and (error_msg == "Invalid device Device22"):
results.append("rejected"); results.append("rejected")
self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33', 'Device22'], callback=my_callback); self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33', 'Device22'], callback=my_callback)
self.send(""" self.send("""
<iq type='get' <iq type='get'
@ -445,20 +445,20 @@ class TestStreamSensorData(SleekTest):
""") """)
self.recv(""" self.recv("""
<iq type='error' <iq type='error'
from='you@google.com' from='you@google.com'
to='tester@localhost' to='tester@localhost'
id='1'> id='1'>
<rejected xmlns='urn:xmpp:iot:sensordata' seqnr='1'> <rejected xmlns='urn:xmpp:iot:sensordata' seqnr='1'>
<error>Invalid device Device22</error> <error>Invalid device Device22</error>
</rejected> </rejected>
</iq> </iq>
""") """)
time.sleep(.1) time.sleep(.1)
self.failUnless(results == ["rejected"], self.failUnless(results == ["rejected"],
"Rejected callback was not properly executed"); "Rejected callback was not properly executed")
def testRequestAcceptedAPI(self): def testRequestAcceptedAPI(self):
@ -466,12 +466,12 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030', plugins=['xep_0030',
'xep_0323']) 'xep_0323'])
results = []; results = []
def my_callback(from_jid, result, nodeId=None, timestamp=None, fields=None, error_msg=None): def my_callback(from_jid, result, nodeId=None, timestamp=None, fields=None, error_msg=None):
results.append(result); results.append(result)
self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33', 'Device22'], callback=my_callback); self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33', 'Device22'], callback=my_callback)
self.send(""" self.send("""
<iq type='get' <iq type='get'
@ -486,18 +486,18 @@ class TestStreamSensorData(SleekTest):
""") """)
self.recv(""" self.recv("""
<iq type='result' <iq type='result'
from='you@google.com' from='you@google.com'
to='tester@localhost' to='tester@localhost'
id='1'> id='1'>
<accepted xmlns='urn:xmpp:iot:sensordata' seqnr='1'/> <accepted xmlns='urn:xmpp:iot:sensordata' seqnr='1'/>
</iq> </iq>
""") """)
time.sleep(.1) time.sleep(.1)
self.failUnless(results == ["accepted"], self.failUnless(results == ["accepted"],
"Accepted callback was not properly executed"); "Accepted callback was not properly executed")
def testRequestFieldsAPI(self): def testRequestFieldsAPI(self):
@ -505,25 +505,25 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030', plugins=['xep_0030',
'xep_0323']) 'xep_0323'])
results = []; results = []
callback_data = {}; callback_data = {}
def my_callback(from_jid, result, nodeId=None, timestamp=None, fields=None, error_msg=None): def my_callback(from_jid, result, nodeId=None, timestamp=None, fields=None, error_msg=None):
results.append(result); results.append(result)
if result == "fields": if result == "fields":
callback_data["nodeId"] = nodeId; callback_data["nodeId"] = nodeId
callback_data["timestamp"] = timestamp; callback_data["timestamp"] = timestamp
callback_data["error_msg"] = error_msg; callback_data["error_msg"] = error_msg
for f in fields: for f in fields:
callback_data["field_" + f['name']] = f; callback_data["field_" + f['name']] = f
t1= threading.Thread(name="request_data", t1= threading.Thread(name="request_data",
target=self.xmpp['xep_0323'].request_data, target=self.xmpp['xep_0323'].request_data,
kwargs={"from_jid": "tester@localhost", kwargs={"from_jid": "tester@localhost",
"to_jid": "you@google.com", "to_jid": "you@google.com",
"nodeIds": ['Device33'], "nodeIds": ['Device33'],
"callback": my_callback}); "callback": my_callback})
t1.start(); t1.start()
#self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33'], callback=my_callback); #self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33'], callback=my_callback);
self.send(""" self.send("""
@ -538,12 +538,12 @@ class TestStreamSensorData(SleekTest):
""") """)
self.recv(""" self.recv("""
<iq type='result' <iq type='result'
from='you@google.com' from='you@google.com'
to='tester@localhost' to='tester@localhost'
id='1'> id='1'>
<accepted xmlns='urn:xmpp:iot:sensordata' seqnr='1'/> <accepted xmlns='urn:xmpp:iot:sensordata' seqnr='1'/>
</iq> </iq>
""") """)
self.recv(""" self.recv("""
@ -552,42 +552,42 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='1'> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='1'>
<node nodeId='Device33'> <node nodeId='Device33'>
<timestamp value='2000-01-01T00:01:02'> <timestamp value='2000-01-01T00:01:02'>
<numeric name='Voltage' invoiced='true' value='230.4' unit='V'/> <numeric name='Voltage' invoiced='true' value='230.4' unit='V'/>
<boolean name='TestBool' value='true'/> <boolean name='TestBool' value='true'/>
</timestamp> </timestamp>
</node> </node>
</fields> </fields>
</message> </message>
""") """)
self.recv(""" self.recv("""
<message from='you@google.com' <message from='you@google.com'
to='tester@localhost'> to='tester@localhost'>
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='1' done='true'/> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='1' done='true'/>
</message> </message>
""") """)
t1.join(); t1.join()
time.sleep(.5) time.sleep(.5)
self.failUnlessEqual(results, ["accepted","fields","done"]); self.failUnlessEqual(results, ["accepted","fields","done"])
# self.assertIn("nodeId", callback_data); # self.assertIn("nodeId", callback_data);
self.assertTrue("nodeId" in callback_data) self.assertTrue("nodeId" in callback_data)
self.failUnlessEqual(callback_data["nodeId"], "Device33"); self.failUnlessEqual(callback_data["nodeId"], "Device33")
# self.assertIn("timestamp", callback_data); # self.assertIn("timestamp", callback_data);
self.assertTrue("timestamp" in callback_data); self.assertTrue("timestamp" in callback_data)
self.failUnlessEqual(callback_data["timestamp"], "2000-01-01T00:01:02"); self.failUnlessEqual(callback_data["timestamp"], "2000-01-01T00:01:02")
#self.assertIn("field_Voltage", callback_data); #self.assertIn("field_Voltage", callback_data);
self.assertTrue("field_Voltage" in callback_data); self.assertTrue("field_Voltage" in callback_data)
self.failUnlessEqual(callback_data["field_Voltage"], {"name": "Voltage", "value": "230.4", "typename": "numeric", "unit": "V", "flags": {"invoiced": "true"}}); self.failUnlessEqual(callback_data["field_Voltage"], {"name": "Voltage", "value": "230.4", "typename": "numeric", "unit": "V", "flags": {"invoiced": "true"}})
#self.assertIn("field_TestBool", callback_data); #self.assertIn("field_TestBool", callback_data);
self.assertTrue("field_TestBool" in callback_data); self.assertTrue("field_TestBool" in callback_data)
self.failUnlessEqual(callback_data["field_TestBool"], {"name": "TestBool", "value": "true", "typename": "boolean" }); self.failUnlessEqual(callback_data["field_TestBool"], {"name": "TestBool", "value": "true", "typename": "boolean" })
def testServiceDiscoveryClient(self): def testServiceDiscoveryClient(self):
self.stream_start(mode='client', self.stream_start(mode='client',
plugins=['xep_0030', plugins=['xep_0030',
'xep_0323']); 'xep_0323'])
self.recv(""" self.recv("""
<iq type='get' <iq type='get'
@ -605,14 +605,14 @@ class TestStreamSensorData(SleekTest):
<query xmlns='http://jabber.org/protocol/disco#info'> <query xmlns='http://jabber.org/protocol/disco#info'>
<identity category='client' type='bot'/> <identity category='client' type='bot'/>
<feature var='urn:xmpp:iot:sensordata'/> <feature var='urn:xmpp:iot:sensordata'/>
</query> </query>
</iq> </iq>
""") """)
def testServiceDiscoveryComponent(self): def testServiceDiscoveryComponent(self):
self.stream_start(mode='component', self.stream_start(mode='component',
plugins=['xep_0030', plugins=['xep_0030',
'xep_0323']); 'xep_0323'])
self.recv(""" self.recv("""
<iq type='get' <iq type='get'
@ -631,8 +631,8 @@ class TestStreamSensorData(SleekTest):
<query xmlns='http://jabber.org/protocol/disco#info'> <query xmlns='http://jabber.org/protocol/disco#info'>
<identity category='component' type='generic'/> <identity category='component' type='generic'/>
<feature var='urn:xmpp:iot:sensordata'/> <feature var='urn:xmpp:iot:sensordata'/>
</query> </query>
</iq> </iq>
""") """)
def testRequestTimeout(self): def testRequestTimeout(self):
@ -641,23 +641,23 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030', plugins=['xep_0030',
'xep_0323']) 'xep_0323'])
results = []; results = []
callback_data = {}; callback_data = {}
def my_callback(from_jid, result, nodeId=None, timestamp=None, error_msg=None): def my_callback(from_jid, result, nodeId=None, timestamp=None, error_msg=None):
results.append(result); results.append(result)
if result == "failure": if result == "failure":
callback_data["nodeId"] = nodeId; callback_data["nodeId"] = nodeId
callback_data["timestamp"] = timestamp; callback_data["timestamp"] = timestamp
callback_data["error_msg"] = error_msg; callback_data["error_msg"] = error_msg
t1= threading.Thread(name="request_data", t1= threading.Thread(name="request_data",
target=self.xmpp['xep_0323'].request_data, target=self.xmpp['xep_0323'].request_data,
kwargs={"from_jid": "tester@localhost", kwargs={"from_jid": "tester@localhost",
"to_jid": "you@google.com", "to_jid": "you@google.com",
"nodeIds": ['Device33'], "nodeIds": ['Device33'],
"callback": my_callback}); "callback": my_callback})
t1.start(); t1.start()
self.send(""" self.send("""
<iq type='get' <iq type='get'
@ -671,12 +671,12 @@ class TestStreamSensorData(SleekTest):
""") """)
self.recv(""" self.recv("""
<iq type='result' <iq type='result'
from='you@google.com' from='you@google.com'
to='tester@localhost' to='tester@localhost'
id='1'> id='1'>
<accepted xmlns='urn:xmpp:iot:sensordata' seqnr='1'/> <accepted xmlns='urn:xmpp:iot:sensordata' seqnr='1'/>
</iq> </iq>
""") """)
self.recv(""" self.recv("""
@ -688,31 +688,31 @@ class TestStreamSensorData(SleekTest):
</message> </message>
""") """)
t1.join(); t1.join()
time.sleep(.5) time.sleep(.5)
self.failUnlessEqual(results, ["accepted","failure"]); self.failUnlessEqual(results, ["accepted","failure"])
# self.assertIn("nodeId", callback_data); # self.assertIn("nodeId", callback_data);
self.assertTrue("nodeId" in callback_data); self.assertTrue("nodeId" in callback_data)
self.failUnlessEqual(callback_data["nodeId"], "Device33"); self.failUnlessEqual(callback_data["nodeId"], "Device33")
# self.assertIn("timestamp", callback_data); # self.assertIn("timestamp", callback_data);
self.assertTrue("timestamp" in callback_data); self.assertTrue("timestamp" in callback_data)
self.failUnlessEqual(callback_data["timestamp"], "2013-03-07T17:13:30"); self.failUnlessEqual(callback_data["timestamp"], "2013-03-07T17:13:30")
# self.assertIn("error_msg", callback_data); # self.assertIn("error_msg", callback_data);
self.assertTrue("error_msg" in callback_data); self.assertTrue("error_msg" in callback_data)
self.failUnlessEqual(callback_data["error_msg"], "Timeout."); self.failUnlessEqual(callback_data["error_msg"], "Timeout.")
def testDelayedRequest(self): def testDelayedRequest(self):
self.stream_start(mode='component', self.stream_start(mode='component',
plugins=['xep_0030', plugins=['xep_0030',
'xep_0323']) 'xep_0323'])
myDevice = Device("Device22"); myDevice = Device("Device22")
myDevice._add_field(name="Temperature", typename="numeric", unit="°C"); myDevice._add_field(name="Temperature", typename="numeric", unit="°C")
myDevice._set_momentary_timestamp("2013-03-07T16:24:30") myDevice._set_momentary_timestamp("2013-03-07T16:24:30")
myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"}); myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"})
self.xmpp['xep_0323'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5); self.xmpp['xep_0323'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5)
dtnow = datetime.datetime.now() dtnow = datetime.datetime.now()
ts_2sec = datetime.timedelta(0,2) ts_2sec = datetime.timedelta(0,2)
@ -729,7 +729,7 @@ class TestStreamSensorData(SleekTest):
""") """)
self.send(""" self.send("""
<iq type='result' <iq type='result'
from='device@clayster.com' from='device@clayster.com'
to='master@clayster.com/amr' to='master@clayster.com/amr'
id='1'> id='1'>
@ -743,7 +743,7 @@ class TestStreamSensorData(SleekTest):
<message from='device@clayster.com' <message from='device@clayster.com'
to='master@clayster.com/amr'> to='master@clayster.com/amr'>
<started xmlns='urn:xmpp:iot:sensordata' seqnr='1' /> <started xmlns='urn:xmpp:iot:sensordata' seqnr='1' />
</message> </message>
""") """)
self.send(""" self.send("""
@ -752,11 +752,11 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='1' done='true'> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='1' done='true'>
<node nodeId='Device22'> <node nodeId='Device22'>
<timestamp value='2013-03-07T16:24:30'> <timestamp value='2013-03-07T16:24:30'>
<numeric name='Temperature' momentary='true' automaticReadout='true' value='23.4' unit='°C'/> <numeric name='Temperature' momentary='true' automaticReadout='true' value='23.4' unit='°C'/>
</timestamp> </timestamp>
</node> </node>
</fields> </fields>
</message> </message>
""") """)
def testDelayedRequestFail(self): def testDelayedRequestFail(self):
@ -764,12 +764,12 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030', plugins=['xep_0030',
'xep_0323']) 'xep_0323'])
myDevice = Device("Device22"); myDevice = Device("Device22")
myDevice._add_field(name="Temperature", typename="numeric", unit="°C"); myDevice._add_field(name="Temperature", typename="numeric", unit="°C")
myDevice._set_momentary_timestamp("2013-03-07T16:24:30") myDevice._set_momentary_timestamp("2013-03-07T16:24:30")
myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"}); myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"})
self.xmpp['xep_0323'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5); self.xmpp['xep_0323'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5)
dtnow = datetime.datetime.now() dtnow = datetime.datetime.now()
ts_2sec = datetime.timedelta(0,2) ts_2sec = datetime.timedelta(0,2)
@ -792,7 +792,7 @@ class TestStreamSensorData(SleekTest):
xml_stanza['rejected']['error'] = error_text xml_stanza['rejected']['error'] = error_text
self._filtered_stanza_check(""" self._filtered_stanza_check("""
<iq type='error' <iq type='error'
from='device@clayster.com' from='device@clayster.com'
to='master@clayster.com/amr' to='master@clayster.com/amr'
id='1'> id='1'>
@ -825,13 +825,13 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030', plugins=['xep_0030',
'xep_0323']) 'xep_0323'])
myDevice = Device("Device44"); myDevice = Device("Device44")
myDevice._add_field(name='Voltage', typename="numeric", unit="V"); myDevice._add_field(name='Voltage', typename="numeric", unit="V")
myDevice._add_field_timestamp_data(name="Voltage", value="230.1", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"}); myDevice._add_field_timestamp_data(name="Voltage", value="230.1", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"})
myDevice._add_field_timestamp_data(name="Voltage", value="230.2", timestamp="2000-02-01T00:01:02", flags={"invoiced": "true"}); myDevice._add_field_timestamp_data(name="Voltage", value="230.2", timestamp="2000-02-01T00:01:02", flags={"invoiced": "true"})
myDevice._add_field_timestamp_data(name="Voltage", value="230.3", timestamp="2000-03-01T00:01:02", flags={"invoiced": "true"}); myDevice._add_field_timestamp_data(name="Voltage", value="230.3", timestamp="2000-03-01T00:01:02", flags={"invoiced": "true"})
self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5); self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5)
print("."), print("."),
@ -847,7 +847,7 @@ class TestStreamSensorData(SleekTest):
""") """)
self.send(""" self.send("""
<iq type='result' <iq type='result'
from='device@clayster.com' from='device@clayster.com'
to='master@clayster.com/amr' to='master@clayster.com/amr'
id='6'> id='6'>
@ -861,11 +861,11 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='6'> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='6'>
<node nodeId='Device44'> <node nodeId='Device44'>
<timestamp value='2000-02-01T00:01:02'> <timestamp value='2000-02-01T00:01:02'>
<numeric name='Voltage' invoiced='true' value='230.2' unit='V'/> <numeric name='Voltage' invoiced='true' value='230.2' unit='V'/>
</timestamp> </timestamp>
</node> </node>
</fields> </fields>
</message> </message>
""") """)
self.send(""" self.send("""
@ -874,11 +874,11 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='6'> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='6'>
<node nodeId='Device44'> <node nodeId='Device44'>
<timestamp value='2000-03-01T00:01:02'> <timestamp value='2000-03-01T00:01:02'>
<numeric name='Voltage' invoiced='true' value='230.3' unit='V'/> <numeric name='Voltage' invoiced='true' value='230.3' unit='V'/>
</timestamp> </timestamp>
</node> </node>
</fields> </fields>
</message> </message>
""") """)
self.send(""" self.send("""
@ -886,7 +886,7 @@ class TestStreamSensorData(SleekTest):
to='master@clayster.com/amr'> to='master@clayster.com/amr'>
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='6' done='true'> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='6' done='true'>
</fields> </fields>
</message> </message>
""") """)
def testRequestFieldTo(self): def testRequestFieldTo(self):
@ -895,13 +895,13 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030', plugins=['xep_0030',
'xep_0323']) 'xep_0323'])
myDevice = Device("Device44"); myDevice = Device("Device44")
myDevice._add_field(name='Voltage', typename="numeric", unit="V"); myDevice._add_field(name='Voltage', typename="numeric", unit="V")
myDevice._add_field_timestamp_data(name="Voltage", value="230.1", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"}); myDevice._add_field_timestamp_data(name="Voltage", value="230.1", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"})
myDevice._add_field_timestamp_data(name="Voltage", value="230.2", timestamp="2000-02-01T00:01:02", flags={"invoiced": "true"}); myDevice._add_field_timestamp_data(name="Voltage", value="230.2", timestamp="2000-02-01T00:01:02", flags={"invoiced": "true"})
myDevice._add_field_timestamp_data(name="Voltage", value="230.3", timestamp="2000-03-01T00:01:02", flags={"invoiced": "true"}); myDevice._add_field_timestamp_data(name="Voltage", value="230.3", timestamp="2000-03-01T00:01:02", flags={"invoiced": "true"})
self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5); self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5)
print("."), print("."),
@ -917,7 +917,7 @@ class TestStreamSensorData(SleekTest):
""") """)
self.send(""" self.send("""
<iq type='result' <iq type='result'
from='device@clayster.com' from='device@clayster.com'
to='master@clayster.com/amr' to='master@clayster.com/amr'
id='6'> id='6'>
@ -931,11 +931,11 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='6'> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='6'>
<node nodeId='Device44'> <node nodeId='Device44'>
<timestamp value='2000-01-01T00:01:02'> <timestamp value='2000-01-01T00:01:02'>
<numeric name='Voltage' invoiced='true' value='230.1' unit='V'/> <numeric name='Voltage' invoiced='true' value='230.1' unit='V'/>
</timestamp> </timestamp>
</node> </node>
</fields> </fields>
</message> </message>
""") """)
self.send(""" self.send("""
@ -944,11 +944,11 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='6'> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='6'>
<node nodeId='Device44'> <node nodeId='Device44'>
<timestamp value='2000-02-01T00:01:02'> <timestamp value='2000-02-01T00:01:02'>
<numeric name='Voltage' invoiced='true' value='230.2' unit='V'/> <numeric name='Voltage' invoiced='true' value='230.2' unit='V'/>
</timestamp> </timestamp>
</node> </node>
</fields> </fields>
</message> </message>
""") """)
self.send(""" self.send("""
@ -956,7 +956,7 @@ class TestStreamSensorData(SleekTest):
to='master@clayster.com/amr'> to='master@clayster.com/amr'>
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='6' done='true'> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='6' done='true'>
</fields> </fields>
</message> </message>
""") """)
def testRequestFieldFromTo(self): def testRequestFieldFromTo(self):
@ -965,13 +965,13 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030', plugins=['xep_0030',
'xep_0323']) 'xep_0323'])
myDevice = Device("Device44"); myDevice = Device("Device44")
myDevice._add_field(name='Voltage', typename="numeric", unit="V"); myDevice._add_field(name='Voltage', typename="numeric", unit="V")
myDevice._add_field_timestamp_data(name="Voltage", value="230.1", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"}); myDevice._add_field_timestamp_data(name="Voltage", value="230.1", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"})
myDevice._add_field_timestamp_data(name="Voltage", value="230.2", timestamp="2000-02-01T00:01:02", flags={"invoiced": "true"}); myDevice._add_field_timestamp_data(name="Voltage", value="230.2", timestamp="2000-02-01T00:01:02", flags={"invoiced": "true"})
myDevice._add_field_timestamp_data(name="Voltage", value="230.3", timestamp="2000-03-01T00:01:02", flags={"invoiced": "true"}); myDevice._add_field_timestamp_data(name="Voltage", value="230.3", timestamp="2000-03-01T00:01:02", flags={"invoiced": "true"})
self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5); self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5)
print("."), print("."),
@ -987,7 +987,7 @@ class TestStreamSensorData(SleekTest):
""") """)
self.send(""" self.send("""
<iq type='result' <iq type='result'
from='device@clayster.com' from='device@clayster.com'
to='master@clayster.com/amr' to='master@clayster.com/amr'
id='6'> id='6'>
@ -1001,11 +1001,11 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='6'> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='6'>
<node nodeId='Device44'> <node nodeId='Device44'>
<timestamp value='2000-02-01T00:01:02'> <timestamp value='2000-02-01T00:01:02'>
<numeric name='Voltage' invoiced='true' value='230.2' unit='V'/> <numeric name='Voltage' invoiced='true' value='230.2' unit='V'/>
</timestamp> </timestamp>
</node> </node>
</fields> </fields>
</message> </message>
""") """)
self.send(""" self.send("""
@ -1013,7 +1013,7 @@ class TestStreamSensorData(SleekTest):
to='master@clayster.com/amr'> to='master@clayster.com/amr'>
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='6' done='true'> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='6' done='true'>
</fields> </fields>
</message> </message>
""") """)
def testDelayedRequestClient(self): def testDelayedRequestClient(self):
@ -1021,25 +1021,25 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030', plugins=['xep_0030',
'xep_0323']) 'xep_0323'])
results = []; results = []
callback_data = {}; callback_data = {}
def my_callback(from_jid, result, nodeId=None, timestamp=None, fields=None, error_msg=None): def my_callback(from_jid, result, nodeId=None, timestamp=None, fields=None, error_msg=None):
results.append(result); results.append(result)
if result == "fields": if result == "fields":
callback_data["nodeId"] = nodeId; callback_data["nodeId"] = nodeId
callback_data["timestamp"] = timestamp; callback_data["timestamp"] = timestamp
callback_data["error_msg"] = error_msg; callback_data["error_msg"] = error_msg
for f in fields: for f in fields:
callback_data["field_" + f['name']] = f; callback_data["field_" + f['name']] = f
t1= threading.Thread(name="request_data", t1= threading.Thread(name="request_data",
target=self.xmpp['xep_0323'].request_data, target=self.xmpp['xep_0323'].request_data,
kwargs={"from_jid": "tester@localhost", kwargs={"from_jid": "tester@localhost",
"to_jid": "you@google.com", "to_jid": "you@google.com",
"nodeIds": ['Device33'], "nodeIds": ['Device33'],
"callback": my_callback}); "callback": my_callback})
t1.start(); t1.start()
#self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33'], callback=my_callback); #self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33'], callback=my_callback);
self.send(""" self.send("""
@ -1054,20 +1054,20 @@ class TestStreamSensorData(SleekTest):
""") """)
self.recv(""" self.recv("""
<iq type='result' <iq type='result'
from='you@google.com' from='you@google.com'
to='tester@localhost' to='tester@localhost'
id='1'> id='1'>
<accepted xmlns='urn:xmpp:iot:sensordata' seqnr='1' queued='true'/> <accepted xmlns='urn:xmpp:iot:sensordata' seqnr='1' queued='true'/>
</iq> </iq>
""") """)
self.recv(""" self.recv("""
<message from='device@clayster.com' <message from='device@clayster.com'
to='master@clayster.com/amr'> to='master@clayster.com/amr'>
<started xmlns='urn:xmpp:iot:sensordata' seqnr='1' /> <started xmlns='urn:xmpp:iot:sensordata' seqnr='1' />
</message> </message>
""") """)
self.recv(""" self.recv("""
<message from='you@google.com' <message from='you@google.com'
@ -1075,37 +1075,37 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='1'> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='1'>
<node nodeId='Device33'> <node nodeId='Device33'>
<timestamp value='2000-01-01T00:01:02'> <timestamp value='2000-01-01T00:01:02'>
<numeric name='Voltage' invoiced='true' value='230.4' unit='V'/> <numeric name='Voltage' invoiced='true' value='230.4' unit='V'/>
<boolean name='TestBool' value='true'/> <boolean name='TestBool' value='true'/>
</timestamp> </timestamp>
</node> </node>
</fields> </fields>
</message> </message>
""") """)
self.recv(""" self.recv("""
<message from='you@google.com' <message from='you@google.com'
to='tester@localhost'> to='tester@localhost'>
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='1' done='true'/> <fields xmlns='urn:xmpp:iot:sensordata' seqnr='1' done='true'/>
</message> </message>
""") """)
t1.join(); t1.join()
time.sleep(.5) time.sleep(.5)
self.failUnlessEqual(results, ["queued","started","fields","done"]); self.failUnlessEqual(results, ["queued","started","fields","done"])
# self.assertIn("nodeId", callback_data); # self.assertIn("nodeId", callback_data);
self.assertTrue("nodeId" in callback_data); self.assertTrue("nodeId" in callback_data)
self.failUnlessEqual(callback_data["nodeId"], "Device33"); self.failUnlessEqual(callback_data["nodeId"], "Device33")
# self.assertIn("timestamp", callback_data); # self.assertIn("timestamp", callback_data);
self.assertTrue("timestamp" in callback_data); self.assertTrue("timestamp" in callback_data)
self.failUnlessEqual(callback_data["timestamp"], "2000-01-01T00:01:02"); self.failUnlessEqual(callback_data["timestamp"], "2000-01-01T00:01:02")
# self.assertIn("field_Voltage", callback_data); # self.assertIn("field_Voltage", callback_data);
self.assertTrue("field_Voltage" in callback_data); self.assertTrue("field_Voltage" in callback_data)
self.failUnlessEqual(callback_data["field_Voltage"], {"name": "Voltage", "value": "230.4", "typename": "numeric", "unit": "V", "flags": {"invoiced": "true"}}); self.failUnlessEqual(callback_data["field_Voltage"], {"name": "Voltage", "value": "230.4", "typename": "numeric", "unit": "V", "flags": {"invoiced": "true"}})
# self.assertIn("field_TestBool", callback_data); # self.assertIn("field_TestBool", callback_data);
self.assertTrue("field_TestBool" in callback_data); self.assertTrue("field_TestBool" in callback_data)
self.failUnlessEqual(callback_data["field_TestBool"], {"name": "TestBool", "value": "true", "typename": "boolean" }); self.failUnlessEqual(callback_data["field_TestBool"], {"name": "TestBool", "value": "true", "typename": "boolean" })
def testRequestFieldsCancelAPI(self): def testRequestFieldsCancelAPI(self):
@ -1114,12 +1114,12 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030', plugins=['xep_0030',
'xep_0323']) 'xep_0323'])
results = []; results = []
def my_callback(from_jid, result, nodeId=None, timestamp=None, fields=None, error_msg=None): def my_callback(from_jid, result, nodeId=None, timestamp=None, fields=None, error_msg=None):
results.append(result); results.append(result)
session = self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33'], callback=my_callback); session = self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33'], callback=my_callback)
self.send(""" self.send("""
<iq type='get' <iq type='get'
@ -1133,15 +1133,15 @@ class TestStreamSensorData(SleekTest):
""") """)
self.recv(""" self.recv("""
<iq type='result' <iq type='result'
from='you@google.com' from='you@google.com'
to='tester@localhost' to='tester@localhost'
id='1'> id='1'>
<accepted xmlns='urn:xmpp:iot:sensordata' seqnr='1'/> <accepted xmlns='urn:xmpp:iot:sensordata' seqnr='1'/>
</iq> </iq>
""") """)
self.xmpp['xep_0323'].cancel_request(session=session); self.xmpp['xep_0323'].cancel_request(session=session)
self.send(""" self.send("""
<iq type='get' <iq type='get'
@ -1163,19 +1163,19 @@ class TestStreamSensorData(SleekTest):
time.sleep(.5) time.sleep(.5)
self.failUnlessEqual(results, ["accepted","cancelled"]); self.failUnlessEqual(results, ["accepted","cancelled"])
def testDelayedRequestCancel(self): def testDelayedRequestCancel(self):
self.stream_start(mode='component', self.stream_start(mode='component',
plugins=['xep_0030', plugins=['xep_0030',
'xep_0323']) 'xep_0323'])
myDevice = Device("Device22"); myDevice = Device("Device22")
myDevice._add_field(name="Temperature", typename="numeric", unit="°C"); myDevice._add_field(name="Temperature", typename="numeric", unit="°C")
myDevice._set_momentary_timestamp("2013-03-07T16:24:30") myDevice._set_momentary_timestamp("2013-03-07T16:24:30")
myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"}); myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"})
self.xmpp['xep_0323'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5); self.xmpp['xep_0323'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5)
dtnow = datetime.datetime.now() dtnow = datetime.datetime.now()
ts_2sec = datetime.timedelta(0,2) ts_2sec = datetime.timedelta(0,2)
@ -1192,7 +1192,7 @@ class TestStreamSensorData(SleekTest):
""") """)
self.send(""" self.send("""
<iq type='result' <iq type='result'
from='device@clayster.com' from='device@clayster.com'
to='master@clayster.com/amr' to='master@clayster.com/amr'
id='1'> id='1'>

View File

@ -28,7 +28,7 @@ class TestStreamControl(SleekTest):
pass pass
def _time_now(self): def _time_now(self):
return datetime.datetime.now().replace(microsecond=0).isoformat(); return datetime.datetime.now().replace(microsecond=0).isoformat()
def tearDown(self): def tearDown(self):
self.stream_close() self.stream_close()
@ -38,10 +38,10 @@ class TestStreamControl(SleekTest):
plugins=['xep_0030', plugins=['xep_0030',
'xep_0325']) 'xep_0325'])
myDevice = Device("Device22"); myDevice = Device("Device22")
myDevice._add_control_field(name="Temperature", typename="int", value="15"); myDevice._add_control_field(name="Temperature", typename="int", value="15")
self.xmpp['xep_0325'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5); self.xmpp['xep_0325'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5)
self.recv(""" self.recv("""
<iq type='set' <iq type='set'
@ -60,26 +60,26 @@ class TestStreamControl(SleekTest):
to='master@clayster.com/amr' to='master@clayster.com/amr'
id='1'> id='1'>
<setResponse xmlns='urn:xmpp:iot:control' responseCode="OK" /> <setResponse xmlns='urn:xmpp:iot:control' responseCode="OK" />
</iq> </iq>
""") """)
self.assertEqual(myDevice._get_field_value("Temperature"), "17"); self.assertEqual(myDevice._get_field_value("Temperature"), "17")
def testRequestSetMulti(self): def testRequestSetMulti(self):
self.stream_start(mode='component', self.stream_start(mode='component',
plugins=['xep_0030', plugins=['xep_0030',
'xep_0325']) 'xep_0325'])
myDevice = Device("Device22"); myDevice = Device("Device22")
myDevice._add_control_field(name="Temperature", typename="int", value="15"); myDevice._add_control_field(name="Temperature", typename="int", value="15")
myDevice._add_control_field(name="Startup", typename="date", value="2013-01-03"); myDevice._add_control_field(name="Startup", typename="date", value="2013-01-03")
myDevice2 = Device("Device23"); myDevice2 = Device("Device23")
myDevice2._add_control_field(name="Temperature", typename="int", value="19"); myDevice2._add_control_field(name="Temperature", typename="int", value="19")
myDevice2._add_control_field(name="Startup", typename="date", value="2013-01-09"); myDevice2._add_control_field(name="Startup", typename="date", value="2013-01-09")
self.xmpp['xep_0325'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5); self.xmpp['xep_0325'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5)
self.xmpp['xep_0325'].register_node(nodeId="Device23", device=myDevice2, commTimeout=0.5); self.xmpp['xep_0325'].register_node(nodeId="Device23", device=myDevice2, commTimeout=0.5)
self.recv(""" self.recv("""
<iq type='set' <iq type='set'
@ -99,11 +99,11 @@ class TestStreamControl(SleekTest):
to='master@clayster.com/amr' to='master@clayster.com/amr'
id='1'> id='1'>
<setResponse xmlns='urn:xmpp:iot:control' responseCode="OK" /> <setResponse xmlns='urn:xmpp:iot:control' responseCode="OK" />
</iq> </iq>
""") """)
self.assertEqual(myDevice._get_field_value("Temperature"), "17"); self.assertEqual(myDevice._get_field_value("Temperature"), "17")
self.assertEqual(myDevice2._get_field_value("Temperature"), "19"); self.assertEqual(myDevice2._get_field_value("Temperature"), "19")
self.recv(""" self.recv("""
<iq type='set' <iq type='set'
@ -125,23 +125,23 @@ class TestStreamControl(SleekTest):
to='master@clayster.com/amr' to='master@clayster.com/amr'
id='2'> id='2'>
<setResponse xmlns='urn:xmpp:iot:control' responseCode="OK" /> <setResponse xmlns='urn:xmpp:iot:control' responseCode="OK" />
</iq> </iq>
""") """)
self.assertEqual(myDevice._get_field_value("Temperature"), "20"); self.assertEqual(myDevice._get_field_value("Temperature"), "20")
self.assertEqual(myDevice2._get_field_value("Temperature"), "20"); self.assertEqual(myDevice2._get_field_value("Temperature"), "20")
self.assertEqual(myDevice._get_field_value("Startup"), "2013-02-01"); self.assertEqual(myDevice._get_field_value("Startup"), "2013-02-01")
self.assertEqual(myDevice2._get_field_value("Startup"), "2013-02-01"); self.assertEqual(myDevice2._get_field_value("Startup"), "2013-02-01")
def testRequestSetFail(self): def testRequestSetFail(self):
self.stream_start(mode='component', self.stream_start(mode='component',
plugins=['xep_0030', plugins=['xep_0030',
'xep_0325']) 'xep_0325'])
myDevice = Device("Device23"); myDevice = Device("Device23")
myDevice._add_control_field(name="Temperature", typename="int", value="15"); myDevice._add_control_field(name="Temperature", typename="int", value="15")
self.xmpp['xep_0325'].register_node(nodeId="Device23", device=myDevice, commTimeout=0.5); self.xmpp['xep_0325'].register_node(nodeId="Device23", device=myDevice, commTimeout=0.5)
self.recv(""" self.recv("""
<iq type='set' <iq type='set'
@ -163,24 +163,24 @@ class TestStreamControl(SleekTest):
<parameter name='Voltage' /> <parameter name='Voltage' />
<error var='Output'>Invalid field Voltage</error> <error var='Output'>Invalid field Voltage</error>
</setResponse> </setResponse>
</iq> </iq>
""") """)
self.assertEqual(myDevice._get_field_value("Temperature"), "15"); self.assertEqual(myDevice._get_field_value("Temperature"), "15")
self.assertFalse(myDevice.has_control_field("Voltage", "int")); self.assertFalse(myDevice.has_control_field("Voltage", "int"))
def testDirectSetOk(self): def testDirectSetOk(self):
self.stream_start(mode='component', self.stream_start(mode='component',
plugins=['xep_0030', plugins=['xep_0030',
'xep_0325']) 'xep_0325'])
myDevice = Device("Device22"); myDevice = Device("Device22")
myDevice._add_control_field(name="Temperature", typename="int", value="15"); myDevice._add_control_field(name="Temperature", typename="int", value="15")
self.xmpp['xep_0325'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5); self.xmpp['xep_0325'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5)
self.recv(""" self.recv("""
<message <message
from='master@clayster.com/amr' from='master@clayster.com/amr'
to='device@clayster.com'> to='device@clayster.com'>
<set xmlns='urn:xmpp:iot:control'> <set xmlns='urn:xmpp:iot:control'>
@ -191,20 +191,20 @@ class TestStreamControl(SleekTest):
time.sleep(.5) time.sleep(.5)
self.assertEqual(myDevice._get_field_value("Temperature"), "17"); self.assertEqual(myDevice._get_field_value("Temperature"), "17")
def testDirectSetFail(self): def testDirectSetFail(self):
self.stream_start(mode='component', self.stream_start(mode='component',
plugins=['xep_0030', plugins=['xep_0030',
'xep_0325']) 'xep_0325'])
myDevice = Device("Device22"); myDevice = Device("Device22")
myDevice._add_control_field(name="Temperature", typename="int", value="15"); myDevice._add_control_field(name="Temperature", typename="int", value="15")
self.xmpp['xep_0325'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5); self.xmpp['xep_0325'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5)
self.recv(""" self.recv("""
<message <message
from='master@clayster.com/amr' from='master@clayster.com/amr'
to='device@clayster.com'> to='device@clayster.com'>
<set xmlns='urn:xmpp:iot:control'> <set xmlns='urn:xmpp:iot:control'>
@ -215,8 +215,8 @@ class TestStreamControl(SleekTest):
time.sleep(.5) time.sleep(.5)
self.assertEqual(myDevice._get_field_value("Temperature"), "15"); self.assertEqual(myDevice._get_field_value("Temperature"), "15")
self.assertFalse(myDevice.has_control_field("Voltage", "int")); self.assertFalse(myDevice.has_control_field("Voltage", "int"))
def testRequestSetOkAPI(self): def testRequestSetOkAPI(self):
@ -225,16 +225,16 @@ class TestStreamControl(SleekTest):
plugins=['xep_0030', plugins=['xep_0030',
'xep_0325']) 'xep_0325'])
results = []; results = []
def my_callback(from_jid, result, nodeIds=None, fields=None, error_msg=None): def my_callback(from_jid, result, nodeIds=None, fields=None, error_msg=None):
results.append(result); results.append(result)
fields = [] fields = []
fields.append(("Temperature", "double", "20.5")) fields.append(("Temperature", "double", "20.5"))
fields.append(("TemperatureAlarmSetting", "string", "High")) fields.append(("TemperatureAlarmSetting", "string", "High"))
self.xmpp['xep_0325'].set_request(from_jid="tester@localhost", to_jid="you@google.com", fields=fields, nodeIds=['Device33', 'Device22'], callback=my_callback); self.xmpp['xep_0325'].set_request(from_jid="tester@localhost", to_jid="you@google.com", fields=fields, nodeIds=['Device33', 'Device22'], callback=my_callback)
self.send(""" self.send("""
<iq type='set' <iq type='set'
@ -256,12 +256,12 @@ class TestStreamControl(SleekTest):
to='tester@localhost' to='tester@localhost'
id='1'> id='1'>
<setResponse xmlns='urn:xmpp:iot:control' responseCode="OK" /> <setResponse xmlns='urn:xmpp:iot:control' responseCode="OK" />
</iq> </iq>
""") """)
time.sleep(.5) time.sleep(.5)
self.assertEqual(results, ["OK"]); self.assertEqual(results, ["OK"])
def testRequestSetErrorAPI(self): def testRequestSetErrorAPI(self):
@ -269,16 +269,16 @@ class TestStreamControl(SleekTest):
plugins=['xep_0030', plugins=['xep_0030',
'xep_0325']) 'xep_0325'])
results = []; results = []
def my_callback(from_jid, result, nodeIds=None, fields=None, error_msg=None): def my_callback(from_jid, result, nodeIds=None, fields=None, error_msg=None):
results.append(result); results.append(result)
fields = [] fields = []
fields.append(("Temperature", "double", "20.5")) fields.append(("Temperature", "double", "20.5"))
fields.append(("TemperatureAlarmSetting", "string", "High")) fields.append(("TemperatureAlarmSetting", "string", "High"))
self.xmpp['xep_0325'].set_request(from_jid="tester@localhost", to_jid="you@google.com", fields=fields, nodeIds=['Device33', 'Device22'], callback=my_callback); self.xmpp['xep_0325'].set_request(from_jid="tester@localhost", to_jid="you@google.com", fields=fields, nodeIds=['Device33', 'Device22'], callback=my_callback)
self.send(""" self.send("""
<iq type='set' <iq type='set'
@ -302,17 +302,17 @@ class TestStreamControl(SleekTest):
<setResponse xmlns='urn:xmpp:iot:control' responseCode="OtherError" > <setResponse xmlns='urn:xmpp:iot:control' responseCode="OtherError" >
<error var='Temperature'>Sensor error</error> <error var='Temperature'>Sensor error</error>
</setResponse> </setResponse>
</iq> </iq>
""") """)
time.sleep(.5) time.sleep(.5)
self.assertEqual(results, ["OtherError"]); self.assertEqual(results, ["OtherError"])
def testServiceDiscoveryClient(self): def testServiceDiscoveryClient(self):
self.stream_start(mode='client', self.stream_start(mode='client',
plugins=['xep_0030', plugins=['xep_0030',
'xep_0325']); 'xep_0325'])
self.recv(""" self.recv("""
<iq type='get' <iq type='get'
@ -330,14 +330,14 @@ class TestStreamControl(SleekTest):
<query xmlns='http://jabber.org/protocol/disco#info'> <query xmlns='http://jabber.org/protocol/disco#info'>
<identity category='client' type='bot'/> <identity category='client' type='bot'/>
<feature var='urn:xmpp:iot:control'/> <feature var='urn:xmpp:iot:control'/>
</query> </query>
</iq> </iq>
""") """)
def testServiceDiscoveryComponent(self): def testServiceDiscoveryComponent(self):
self.stream_start(mode='component', self.stream_start(mode='component',
plugins=['xep_0030', plugins=['xep_0030',
'xep_0325']); 'xep_0325'])
self.recv(""" self.recv("""
<iq type='get' <iq type='get'
@ -356,8 +356,8 @@ class TestStreamControl(SleekTest):
<query xmlns='http://jabber.org/protocol/disco#info'> <query xmlns='http://jabber.org/protocol/disco#info'>
<identity category='component' type='generic'/> <identity category='component' type='generic'/>
<feature var='urn:xmpp:iot:control'/> <feature var='urn:xmpp:iot:control'/>
</query> </query>
</iq> </iq>
""") """)