summaryrefslogtreecommitdiff
path: root/sleekxmpp/plugins/xep_0323/stanza/sensordata.py
diff options
context:
space:
mode:
authorFlorent Le Coz <louiz@louiz.org>2014-07-17 14:19:04 +0200
committerFlorent Le Coz <louiz@louiz.org>2014-07-17 14:19:04 +0200
commit5ab77c745270d7d5c016c1dc7ef2a82533a4b16e (patch)
tree259377cc666f8b9c7954fc4e7b8f7a912bcfe101 /sleekxmpp/plugins/xep_0323/stanza/sensordata.py
parente5582694c07236e6830c20361840360a1dde37f3 (diff)
downloadslixmpp-5ab77c745270d7d5c016c1dc7ef2a82533a4b16e.tar.gz
slixmpp-5ab77c745270d7d5c016c1dc7ef2a82533a4b16e.tar.bz2
slixmpp-5ab77c745270d7d5c016c1dc7ef2a82533a4b16e.tar.xz
slixmpp-5ab77c745270d7d5c016c1dc7ef2a82533a4b16e.zip
Rename to slixmpp
Diffstat (limited to 'sleekxmpp/plugins/xep_0323/stanza/sensordata.py')
-rw-r--r--sleekxmpp/plugins/xep_0323/stanza/sensordata.py792
1 files changed, 0 insertions, 792 deletions
diff --git a/sleekxmpp/plugins/xep_0323/stanza/sensordata.py b/sleekxmpp/plugins/xep_0323/stanza/sensordata.py
deleted file mode 100644
index a11c3e94..00000000
--- a/sleekxmpp/plugins/xep_0323/stanza/sensordata.py
+++ /dev/null
@@ -1,792 +0,0 @@
-"""
- SleekXMPP: The Sleek XMPP Library
- Implementation of xeps for Internet of Things
- http://wiki.xmpp.org/web/Tech_pages/IoT_systems
- Copyright (C) 2013 Sustainable Innovation, Joachim.lindborg@sust.se, bjorn.westrom@consoden.se
- This file is part of SleekXMPP.
-
- See the file LICENSE for copying permission.
-"""
-
-from sleekxmpp import Iq, Message
-from sleekxmpp.xmlstream import register_stanza_plugin, ElementBase, ET, JID
-from re import match
-
-class Sensordata(ElementBase):
- """ Placeholder for the namespace, not used as a stanza """
- namespace = 'urn:xmpp:iot:sensordata'
- name = 'sensordata'
- plugin_attrib = name
- interfaces = set(tuple())
-
-class FieldTypes():
- """
- All field types are optional booleans that default to False
- """
- field_types = set([ 'momentary','peak','status','computed','identity','historicalSecond','historicalMinute','historicalHour', \
- 'historicalDay','historicalWeek','historicalMonth','historicalQuarter','historicalYear','historicalOther'])
-
-class FieldStatus():
- """
- All field statuses are optional booleans that default to False
- """
- field_status = set([ 'missing','automaticEstimate','manualEstimate','manualReadout','automaticReadout','timeOffset','warning','error', \
- 'signed','invoiced','endOfSeries','powerFailure','invoiceConfirmed'])
-
-class Request(ElementBase):
- namespace = 'urn:xmpp:iot:sensordata'
- name = 'req'
- plugin_attrib = name
- interfaces = set(['seqnr','nodes','fields','serviceToken','deviceToken','userToken','from','to','when','historical','all'])
- interfaces.update(FieldTypes.field_types);
- _flags = set(['serviceToken','deviceToken','userToken','from','to','when','historical','all']);
- _flags.update(FieldTypes.field_types);
-
- def __init__(self, xml=None, parent=None):
- ElementBase.__init__(self, xml, parent);
- self._nodes = set()
- self._fields = set()
-
- def setup(self, xml=None):
- """
- Populate the stanza object using an optional XML object.
-
- Overrides ElementBase.setup
-
- Caches item information.
-
- Arguments:
- xml -- Use an existing XML object for the stanza's values.
- """
- ElementBase.setup(self, xml)
- self._nodes = set([node['nodeId'] for node in self['nodes']])
- self._fields = set([field['name'] for field in self['fields']])
-
- def _get_flags(self):
- """
- Helper function for getting of flags. Returns all flags in
- dictionary format: { "flag name": "flag value" ... }
- """
- flags = {};
- for f in self._flags:
- if not self[f] == "":
- flags[f] = self[f];
- return flags;
-
- def _set_flags(self, flags):
- """
- Helper function for setting of flags.
-
- Arguments:
- flags -- Flags in dictionary format: { "flag name": "flag value" ... }
- """
- for f in self._flags:
- if flags is not None and f in flags:
- self[f] = flags[f];
- else:
- self[f] = None;
-
- def add_node(self, nodeId, sourceId=None, cacheType=None):
- """
- Add a new node element. Each item is required to have a
- nodeId, but may also specify a sourceId value and cacheType.
-
- Arguments:
- nodeId -- The ID for the node.
- sourceId -- [optional] identifying the data source controlling the device
- cacheType -- [optional] narrowing down the search to a specific kind of node
- """
- if nodeId not in self._nodes:
- self._nodes.add((nodeId))
- node = RequestNode(parent=self)
- node['nodeId'] = nodeId
- node['sourceId'] = sourceId
- node['cacheType'] = cacheType
- self.iterables.append(node)
- return node
- return None
-
- def del_node(self, nodeId):
- """
- Remove a single node.
-
- Arguments:
- nodeId -- Node ID of the item to remove.
- """
- if nodeId in self._nodes:
- nodes = [i for i in self.iterables if isinstance(i, RequestNode)]
- for node in nodes:
- if node['nodeId'] == nodeId:
- self.xml.remove(node.xml)
- self.iterables.remove(node)
- return True
- return False
-
- def get_nodes(self):
- """Return all nodes."""
- nodes = []
- for node in self['substanzas']:
- if isinstance(node, RequestNode):
- nodes.append(node)
- return nodes
-
- def set_nodes(self, nodes):
- """
- Set or replace all nodes. The given nodes must be in a
- list or set where each item is a tuple of the form:
- (nodeId, sourceId, cacheType)
-
- Arguments:
- nodes -- A series of nodes in tuple format.
- """
- self.del_nodes()
- for node in nodes:
- if isinstance(node, RequestNode):
- self.add_node(node['nodeId'], node['sourceId'], node['cacheType'])
- else:
- nodeId, sourceId, cacheType = node
- self.add_node(nodeId, sourceId, cacheType)
-
- def del_nodes(self):
- """Remove all nodes."""
- self._nodes = set()
- nodes = [i for i in self.iterables if isinstance(i, RequestNode)]
- for node in nodes:
- self.xml.remove(node.xml)
- self.iterables.remove(node)
-
-
- def add_field(self, name):
- """
- Add a new field element. Each item is required to have a
- name.
-
- Arguments:
- name -- The name of the field.
- """
- if name not in self._fields:
- self._fields.add((name))
- field = RequestField(parent=self)
- field['name'] = name
- self.iterables.append(field)
- return field
- return None
-
- def del_field(self, name):
- """
- Remove a single field.
-
- Arguments:
- name -- name of field to remove.
- """
- if name in self._fields:
- fields = [i for i in self.iterables if isinstance(i, RequestField)]
- for field in fields:
- if field['name'] == name:
- self.xml.remove(field.xml)
- self.iterables.remove(field)
- return True
- return False
-
- def get_fields(self):
- """Return all fields."""
- fields = []
- for field in self['substanzas']:
- if isinstance(field, RequestField):
- fields.append(field)
- return fields
-
- def set_fields(self, fields):
- """
- Set or replace all fields. The given fields must be in a
- list or set where each item is RequestField or string
-
- Arguments:
- fields -- A series of fields in RequestField or string format.
- """
- self.del_fields()
- for field in fields:
- if isinstance(field, RequestField):
- self.add_field(field['name'])
- else:
- self.add_field(field)
-
- def del_fields(self):
- """Remove all fields."""
- self._fields = set()
- fields = [i for i in self.iterables if isinstance(i, RequestField)]
- for field in fields:
- self.xml.remove(field.xml)
- self.iterables.remove(field)
-
-
-class RequestNode(ElementBase):
- """ Node element in a request """
- namespace = 'urn:xmpp:iot:sensordata'
- name = 'node'
- plugin_attrib = name
- interfaces = set(['nodeId','sourceId','cacheType'])
-
-class RequestField(ElementBase):
- """ Field element in a request """
- namespace = 'urn:xmpp:iot:sensordata'
- name = 'field'
- plugin_attrib = name
- interfaces = set(['name'])
-
-class Accepted(ElementBase):
- namespace = 'urn:xmpp:iot:sensordata'
- name = 'accepted'
- plugin_attrib = name
- interfaces = set(['seqnr','queued'])
-
-class Started(ElementBase):
- namespace = 'urn:xmpp:iot:sensordata'
- name = 'started'
- plugin_attrib = name
- interfaces = set(['seqnr'])
-
-class Failure(ElementBase):
- namespace = 'urn:xmpp:iot:sensordata'
- name = 'failure'
- plugin_attrib = name
- interfaces = set(['seqnr','done'])
-
-class Error(ElementBase):
- """ Error element in a request failure """
- namespace = 'urn:xmpp:iot:sensordata'
- name = 'error'
- plugin_attrib = name
- interfaces = set(['nodeId','timestamp','sourceId','cacheType','text'])
-
- def get_text(self):
- """Return then contents inside the XML tag."""
- return self.xml.text
-
- def set_text(self, value):
- """Set then contents inside the XML tag.
-
- :param value: string
- """
-
- self.xml.text = value;
- return self
-
- def del_text(self):
- """Remove the contents inside the XML tag."""
- self.xml.text = ""
- return self
-
-class Rejected(ElementBase):
- namespace = 'urn:xmpp:iot:sensordata'
- name = 'rejected'
- plugin_attrib = name
- interfaces = set(['seqnr','error'])
- sub_interfaces = set(['error'])
-
-class Fields(ElementBase):
- """ Fields element, top level in a response message with data """
- namespace = 'urn:xmpp:iot:sensordata'
- name = 'fields'
- plugin_attrib = name
- interfaces = set(['seqnr','done','nodes'])
-
- def __init__(self, xml=None, parent=None):
- ElementBase.__init__(self, xml, parent);
- self._nodes = set()
-
- def setup(self, xml=None):
- """
- Populate the stanza object using an optional XML object.
-
- Overrides ElementBase.setup
-
- Caches item information.
-
- Arguments:
- xml -- Use an existing XML object for the stanza's values.
- """
- ElementBase.setup(self, xml)
- self._nodes = set([node['nodeId'] for node in self['nodes']])
-
-
- def add_node(self, nodeId, sourceId=None, cacheType=None, substanzas=None):
- """
- Add a new node element. Each item is required to have a
- nodeId, but may also specify a sourceId value and cacheType.
-
- Arguments:
- nodeId -- The ID for the node.
- sourceId -- [optional] identifying the data source controlling the device
- cacheType -- [optional] narrowing down the search to a specific kind of node
- """
- if nodeId not in self._nodes:
- self._nodes.add((nodeId))
- node = FieldsNode(parent=self)
- node['nodeId'] = nodeId
- node['sourceId'] = sourceId
- node['cacheType'] = cacheType
- if substanzas is not None:
- node.set_timestamps(substanzas)
-
- self.iterables.append(node)
- return node
- return None
-
- def del_node(self, nodeId):
- """
- Remove a single node.
-
- Arguments:
- nodeId -- Node ID of the item to remove.
- """
- if nodeId in self._nodes:
- nodes = [i for i in self.iterables if isinstance(i, FieldsNode)]
- for node in nodes:
- if node['nodeId'] == nodeId:
- self.xml.remove(node.xml)
- self.iterables.remove(node)
- return True
- return False
-
- def get_nodes(self):
- """Return all nodes."""
- nodes = []
- for node in self['substanzas']:
- if isinstance(node, FieldsNode):
- nodes.append(node)
- return nodes
-
- def set_nodes(self, nodes):
- """
- Set or replace all nodes. The given nodes must be in a
- list or set where each item is a tuple of the form:
- (nodeId, sourceId, cacheType)
-
- Arguments:
- nodes -- A series of nodes in tuple format.
- """
- #print(str(id(self)) + " set_nodes: got " + str(nodes))
- self.del_nodes()
- for node in nodes:
- if isinstance(node, FieldsNode):
- self.add_node(node['nodeId'], node['sourceId'], node['cacheType'], substanzas=node['substanzas'])
- else:
- nodeId, sourceId, cacheType = node
- self.add_node(nodeId, sourceId, cacheType)
-
- def del_nodes(self):
- """Remove all nodes."""
- self._nodes = set()
- nodes = [i for i in self.iterables if isinstance(i, FieldsNode)]
- for node in nodes:
- self.xml.remove(node.xml)
- self.iterables.remove(node)
-
-
-class FieldsNode(ElementBase):
- """ Node element in response fields """
- namespace = 'urn:xmpp:iot:sensordata'
- name = 'node'
- plugin_attrib = name
- interfaces = set(['nodeId','sourceId','cacheType','timestamps'])
-
- def __init__(self, xml=None, parent=None):
- ElementBase.__init__(self, xml, parent);
- self._timestamps = set()
-
- def setup(self, xml=None):
- """
- Populate the stanza object using an optional XML object.
-
- Overrides ElementBase.setup
-
- Caches item information.
-
- Arguments:
- xml -- Use an existing XML object for the stanza's values.
- """
- ElementBase.setup(self, xml)
- self._timestamps = set([ts['value'] for ts in self['timestamps']])
-
- def add_timestamp(self, timestamp, substanzas=None):
- """
- Add a new timestamp element.
-
- Arguments:
- timestamp -- The timestamp in ISO format.
- """
- #print(str(id(self)) + " add_timestamp: " + str(timestamp))
-
- if timestamp not in self._timestamps:
- self._timestamps.add((timestamp))
- ts = Timestamp(parent=self)
- ts['value'] = timestamp
- if not substanzas is None:
- ts.set_datas(substanzas);
- #print("add_timestamp with substanzas: " + str(substanzas))
- self.iterables.append(ts)
- #print(str(id(self)) + " added_timestamp: " + str(id(ts)))
- return ts
- return None
-
- def del_timestamp(self, timestamp):
- """
- Remove a single timestamp.
-
- Arguments:
- timestamp -- timestamp (in ISO format) of the item to remove.
- """
- #print("del_timestamp: ")
- if timestamp in self._timestamps:
- timestamps = [i for i in self.iterables if isinstance(i, Timestamp)]
- for ts in timestamps:
- if ts['value'] == timestamp:
- self.xml.remove(ts.xml)
- self.iterables.remove(ts)
- return True
- return False
-
- def get_timestamps(self):
- """Return all timestamps."""
- #print(str(id(self)) + " get_timestamps: ")
- timestamps = []
- for timestamp in self['substanzas']:
- if isinstance(timestamp, Timestamp):
- timestamps.append(timestamp)
- return timestamps
-
- def set_timestamps(self, timestamps):
- """
- Set or replace all timestamps. The given timestamps must be in a
- list or set where each item is a timestamp
-
- Arguments:
- timestamps -- A series of timestamps.
- """
- #print(str(id(self)) + " set_timestamps: got " + str(timestamps))
- self.del_timestamps()
- for timestamp in timestamps:
- #print("set_timestamps: subset " + str(timestamp))
- #print("set_timestamps: subset.substanzas " + str(timestamp['substanzas']))
- if isinstance(timestamp, Timestamp):
- self.add_timestamp(timestamp['value'], substanzas=timestamp['substanzas'])
- else:
- #print("set_timestamps: got " + str(timestamp))
- self.add_timestamp(timestamp)
-
- def del_timestamps(self):
- """Remove all timestamps."""
- #print(str(id(self)) + " del_timestamps: ")
- self._timestamps = set()
- timestamps = [i for i in self.iterables if isinstance(i, Timestamp)]
- for timestamp in timestamps:
- self.xml.remove(timestamp.xml)
- self.iterables.remove(timestamp)
-
-class Field(ElementBase):
- """
- Field element in response Timestamp. This is a base class,
- all instances of fields added to Timestamp must be of types:
- DataNumeric
- DataString
- DataBoolean
- DataDateTime
- DataTimeSpan
- DataEnum
- """
- namespace = 'urn:xmpp:iot:sensordata'
- name = 'field'
- plugin_attrib = name
- interfaces = set(['name','module','stringIds']);
- interfaces.update(FieldTypes.field_types);
- interfaces.update(FieldStatus.field_status);
-
- _flags = set();
- _flags.update(FieldTypes.field_types);
- _flags.update(FieldStatus.field_status);
-
- def set_stringIds(self, value):
- """Verifies stringIds according to regexp from specification XMPP-0323.
-
- :param value: string
- """
-
- pattern = re.compile("^\d+([|]\w+([.]\w+)*([|][^,]*)?)?(,\d+([|]\w+([.]\w+)*([|][^,]*)?)?)*$")
- if pattern.match(value) is not None:
- self.xml.stringIds = value;
- else:
- # Bad content, add nothing
- pass
-
- return self
-
- def _get_flags(self):
- """
- Helper function for getting of flags. Returns all flags in
- dictionary format: { "flag name": "flag value" ... }
- """
- flags = {};
- for f in self._flags:
- if not self[f] == "":
- flags[f] = self[f];
- return flags;
-
- def _set_flags(self, flags):
- """
- Helper function for setting of flags.
-
- Arguments:
- flags -- Flags in dictionary format: { "flag name": "flag value" ... }
- """
- for f in self._flags:
- if flags is not None and f in flags:
- self[f] = flags[f];
- else:
- self[f] = None;
-
- def _get_typename(self):
- return "invalid type, use subclasses!";
-
-
-class Timestamp(ElementBase):
- """ Timestamp element in response Node """
- namespace = 'urn:xmpp:iot:sensordata'
- name = 'timestamp'
- plugin_attrib = name
- interfaces = set(['value','datas'])
-
- def __init__(self, xml=None, parent=None):
- ElementBase.__init__(self, xml, parent);
- self._datas = set()
-
- def setup(self, xml=None):
- """
- Populate the stanza object using an optional XML object.
-
- Overrides ElementBase.setup
-
- Caches item information.
-
- Arguments:
- xml -- Use an existing XML object for the stanza's values.
- """
- ElementBase.setup(self, xml)
- self._datas = set([data['name'] for data in self['datas']])
-
- def add_data(self, typename, name, value, module=None, stringIds=None, unit=None, dataType=None, flags=None):
- """
- Add a new data element.
-
- Arguments:
- typename -- The type of data element (numeric, string, boolean, dateTime, timeSpan or enum)
- value -- The value of the data element
- module -- [optional] language module to use for the data element
- stringIds -- [optional] The stringIds used to find associated text in the language module
- unit -- [optional] The unit. Only applicable for type numeric
- dataType -- [optional] The dataType. Only applicable for type enum
- """
- if name not in self._datas:
- dataObj = None;
- if typename == "numeric":
- dataObj = DataNumeric(parent=self);
- dataObj['unit'] = unit;
- elif typename == "string":
- dataObj = DataString(parent=self);
- elif typename == "boolean":
- dataObj = DataBoolean(parent=self);
- elif typename == "dateTime":
- dataObj = DataDateTime(parent=self);
- elif typename == "timeSpan":
- dataObj = DataTimeSpan(parent=self);
- elif typename == "enum":
- dataObj = DataEnum(parent=self);
- dataObj['dataType'] = dataType;
-
- dataObj['name'] = name;
- dataObj['value'] = value;
- dataObj['module'] = module;
- dataObj['stringIds'] = stringIds;
-
- if flags is not None:
- dataObj._set_flags(flags);
-
- self._datas.add(name)
- self.iterables.append(dataObj)
- return dataObj
- return None
-
- def del_data(self, name):
- """
- Remove a single data element.
-
- Arguments:
- data_name -- The data element name to remove.
- """
- if name in self._datas:
- datas = [i for i in self.iterables if isinstance(i, Field)]
- for data in datas:
- if data['name'] == name:
- self.xml.remove(data.xml)
- self.iterables.remove(data)
- return True
- return False
-
- def get_datas(self):
- """ Return all data elements. """
- datas = []
- for data in self['substanzas']:
- if isinstance(data, Field):
- datas.append(data)
- return datas
-
- def set_datas(self, datas):
- """
- Set or replace all data elements. The given elements must be in a
- list or set where each item is a data element (numeric, string, boolean, dateTime, timeSpan or enum)
-
- Arguments:
- datas -- A series of data elements.
- """
- self.del_datas()
- for data in datas:
- self.add_data(typename=data._get_typename(), name=data['name'], value=data['value'], module=data['module'], stringIds=data['stringIds'], unit=data['unit'], dataType=data['dataType'], flags=data._get_flags())
-
- def del_datas(self):
- """Remove all data elements."""
- self._datas = set()
- datas = [i for i in self.iterables if isinstance(i, Field)]
- for data in datas:
- self.xml.remove(data.xml)
- self.iterables.remove(data)
-
-class DataNumeric(Field):
- """
- Field data of type numeric.
- Note that the value is expressed as a string.
- """
- namespace = 'urn:xmpp:iot:sensordata'
- name = 'numeric'
- plugin_attrib = name
- interfaces = set(['value', 'unit']);
- interfaces.update(Field.interfaces);
-
- def _get_typename(self):
- return "numeric"
-
-class DataString(Field):
- """
- Field data of type string
- """
- namespace = 'urn:xmpp:iot:sensordata'
- name = 'string'
- plugin_attrib = name
- interfaces = set(['value']);
- interfaces.update(Field.interfaces);
-
- def _get_typename(self):
- return "string"
-
-class DataBoolean(Field):
- """
- Field data of type boolean.
- Note that the value is expressed as a string.
- """
- namespace = 'urn:xmpp:iot:sensordata'
- name = 'boolean'
- plugin_attrib = name
- interfaces = set(['value']);
- interfaces.update(Field.interfaces);
-
- def _get_typename(self):
- return "boolean"
-
-class DataDateTime(Field):
- """
- Field data of type dateTime.
- Note that the value is expressed as a string.
- """
- namespace = 'urn:xmpp:iot:sensordata'
- name = 'dateTime'
- plugin_attrib = name
- interfaces = set(['value']);
- interfaces.update(Field.interfaces);
-
- def _get_typename(self):
- return "dateTime"
-
-class DataTimeSpan(Field):
- """
- Field data of type timeSpan.
- Note that the value is expressed as a string.
- """
- namespace = 'urn:xmpp:iot:sensordata'
- name = 'timeSpan'
- plugin_attrib = name
- interfaces = set(['value']);
- interfaces.update(Field.interfaces);
-
- def _get_typename(self):
- return "timeSpan"
-
-class DataEnum(Field):
- """
- Field data of type enum.
- Note that the value is expressed as a string.
- """
- namespace = 'urn:xmpp:iot:sensordata'
- name = 'enum'
- plugin_attrib = name
- interfaces = set(['value', 'dataType']);
- interfaces.update(Field.interfaces);
-
- def _get_typename(self):
- return "enum"
-
-class Done(ElementBase):
- """ Done element used to signal that all data has been transferred """
- namespace = 'urn:xmpp:iot:sensordata'
- name = 'done'
- plugin_attrib = name
- interfaces = set(['seqnr'])
-
-class Cancel(ElementBase):
- """ Cancel element used to signal that a request shall be cancelled """
- namespace = 'urn:xmpp:iot:sensordata'
- name = 'cancel'
- plugin_attrib = name
- interfaces = set(['seqnr'])
-
-class Cancelled(ElementBase):
- """ Cancelled element used to signal that cancellation is confirmed """
- namespace = 'urn:xmpp:iot:sensordata'
- name = 'cancelled'
- plugin_attrib = name
- interfaces = set(['seqnr'])
-
-
-register_stanza_plugin(Iq, Request)
-register_stanza_plugin(Request, RequestNode, iterable=True)
-register_stanza_plugin(Request, RequestField, iterable=True)
-
-register_stanza_plugin(Iq, Accepted)
-register_stanza_plugin(Message, Failure)
-register_stanza_plugin(Failure, Error)
-
-register_stanza_plugin(Iq, Rejected)
-
-register_stanza_plugin(Message, Fields)
-register_stanza_plugin(Fields, FieldsNode, iterable=True)
-register_stanza_plugin(FieldsNode, Timestamp, iterable=True)
-register_stanza_plugin(Timestamp, Field, iterable=True)
-register_stanza_plugin(Timestamp, DataNumeric, iterable=True)
-register_stanza_plugin(Timestamp, DataString, iterable=True)
-register_stanza_plugin(Timestamp, DataBoolean, iterable=True)
-register_stanza_plugin(Timestamp, DataDateTime, iterable=True)
-register_stanza_plugin(Timestamp, DataTimeSpan, iterable=True)
-register_stanza_plugin(Timestamp, DataEnum, iterable=True)
-
-register_stanza_plugin(Message, Started)
-
-register_stanza_plugin(Iq, Cancel)
-register_stanza_plugin(Iq, Cancelled)