2 SleekXMPP: The Sleek XMPP Library
3 Implementation of xeps for Internet of Things
4 http://wiki.xmpp.org/web/Tech_pages/IoT_systems
5 Copyright (C) 2013 Sustainable Innovation, Joachim.lindborg@sust.se, bjorn.westrom@consoden.se
6 This file is part of SleekXMPP.
8 See the file LICENSE for copying permission.
16 Example implementation of a device readout object.
17 Is registered in the XEP_0323.register_node call
18 The device object may be any custom implementation to support
19 specific devices, but it must implement the functions:
24 def __init__(self
, nodeId
, fields
={}):
26 self
.fields
= fields
# see fields described below
31 self
.timestamp_data
= {}
32 self
.momentary_data
= {}
33 self
.momentary_timestamp
= ""
34 logging
.debug("Device object started nodeId %s",nodeId
)
36 def has_field(self
, field
):
38 Returns true if the supplied field name exists in this device.
41 field -- The field name
43 if field
in self
.fields
.keys():
47 def refresh(self
, fields
):
49 override method to do the refresh work
50 refresh values from hardware or other
55 def request_fields(self
, fields
, flags
, session
, callback
):
57 Starts a data readout. Verifies the requested fields,
58 refreshes the data (if needed) and calls the callback
63 fields -- List of field names to readout
64 flags -- [optional] data classifier flags for the field, e.g. momentary
65 Formatted as a dictionary like { "flag name": "flag value" ... }
66 session -- Session id, only used in the callback as identifier
67 callback -- Callback function to call when data is available.
69 The callback function must support the following arguments:
71 session -- Session id, as supplied in the request_fields call
72 nodeId -- Identifier for this device
73 result -- The current result status of the readout. Valid values are:
74 "error" - Readout failed.
75 "fields" - Contains readout data.
76 "done" - Indicates that the readout is complete. May contain
78 timestamp_block -- [optional] Only applies when result != "error"
79 The readout data. Structured as a dictionary:
81 timestamp: timestamp for this datablock,
82 fields: list of field dictionary (one per readout field).
83 readout field dictionary format:
85 type: The field type (numeric, boolean, dateTime, timeSpan, string, enum)
87 value: The field value
88 unit: The unit of the field. Only applies to type numeric.
89 dataType: The datatype of the field. Only applies to type enum.
90 flags: [optional] data classifier flags for the field, e.g. momentary
91 Formatted as a dictionary like { "flag name": "flag value" ... }
94 error_msg -- [optional] Only applies when result == "error".
95 Error details when a request failed.
98 logging
.debug("request_fields called looking for fields %s",fields
)
100 # Check availiability
102 if f
not in self
.fields
.keys():
103 self
._send
_reject
(session
, callback
)
107 fields
= self
.fields
.keys();
110 # Refresh data from device
112 logging
.debug("about to refresh device fields %s",fields
)
115 if "momentary" in flags
and flags
['momentary'] == "true" or \
116 "all" in flags
and flags
['all'] == "true":
120 if len(self
.momentary_timestamp
) > 0:
121 timestamp
= self
.momentary_timestamp
;
123 timestamp
= self
._get
_timestamp
();
126 for f
in self
.momentary_data
:
128 field_block
.append({"name": f
,
129 "type": self
.fields
[f
]["type"],
130 "unit": self
.fields
[f
]["unit"],
131 "dataType": self
.fields
[f
]["dataType"],
132 "value": self
.momentary_data
[f
]["value"],
133 "flags": self
.momentary_data
[f
]["flags"]});
134 ts_block
["timestamp"] = timestamp
;
135 ts_block
["fields"] = field_block
;
137 callback(session
, result
="done", nodeId
=self
.nodeId
, timestamp_block
=ts_block
);
140 from_flag
= self
._datetime
_flag
_parser
(flags
, 'from')
141 to_flag
= self
._datetime
_flag
_parser
(flags
, 'to')
143 for ts
in sorted(self
.timestamp_data
.keys()):
144 tsdt
= datetime
.datetime
.strptime(ts
, "%Y-%m-%dT%H:%M:%S")
145 if not from_flag
is None:
147 #print (str(tsdt) + " < " + str(from_flag))
149 if not to_flag
is None:
151 #print (str(tsdt) + " > " + str(to_flag))
157 for f
in self
.timestamp_data
[ts
]:
159 field_block
.append({"name": f
,
160 "type": self
.fields
[f
]["type"],
161 "unit": self
.fields
[f
]["unit"],
162 "dataType": self
.fields
[f
]["dataType"],
163 "value": self
.timestamp_data
[ts
][f
]["value"],
164 "flags": self
.timestamp_data
[ts
][f
]["flags"]});
166 ts_block
["timestamp"] = ts
;
167 ts_block
["fields"] = field_block
;
168 callback(session
, result
="fields", nodeId
=self
.nodeId
, timestamp_block
=ts_block
);
169 callback(session
, result
="done", nodeId
=self
.nodeId
, timestamp_block
=None);
171 def _datetime_flag_parser(self
, flags
, flagname
):
172 if not flagname
in flags
:
177 dt
= datetime
.datetime
.strptime(flags
[flagname
], "%Y-%m-%dT%H:%M:%S")
179 # Badly formatted datetime, ignore it
184 def _get_timestamp(self
):
186 Generates a properly formatted timestamp of current time
188 return datetime
.datetime
.now().replace(microsecond
=0).isoformat()
190 def _send_reject(self
, session
, callback
):
192 Sends a reject to the caller
195 session -- Session id, see definition in request_fields function
196 callback -- Callback function, see definition in request_fields function
198 callback(session
, result
="error", nodeId
=self
.nodeId
, timestamp_block
=None, error_msg
="Reject");
200 def _add_field(self
, name
, typename
, unit
=None, dataType
=None):
202 Adds a field to the device
205 name -- Name of the field
206 typename -- Type of the field (numeric, boolean, dateTime, timeSpan, string, enum)
207 unit -- [optional] only applies to "numeric". Unit for the field.
208 dataType -- [optional] only applies to "enum". Datatype for the field.
210 self
.fields
[name
] = {"type": typename
, "unit": unit
, "dataType": dataType
};
212 def _add_field_timestamp_data(self
, name
, timestamp
, value
, flags
=None):
214 Adds timestamped data to a field
217 name -- Name of the field
218 timestamp -- Timestamp for the data (string)
219 value -- Field value at the timestamp
220 flags -- [optional] data classifier flags for the field, e.g. momentary
221 Formatted as a dictionary like { "flag name": "flag value" ... }
223 if not name
in self
.fields
.keys():
225 if not timestamp
in self
.timestamp_data
:
226 self
.timestamp_data
[timestamp
] = {};
228 self
.timestamp_data
[timestamp
][name
] = {"value": value
, "flags": flags
};
231 def _add_field_momentary_data(self
, name
, value
, flags
=None):
233 Sets momentary data to a field
236 name -- Name of the field
237 value -- Field value at the timestamp
238 flags -- [optional] data classifier flags for the field, e.g. momentary
239 Formatted as a dictionary like { "flag name": "flag value" ... }
241 if name
not in self
.fields
:
246 flags
["momentary"] = "true"
247 self
.momentary_data
[name
] = {"value": value
, "flags": flags
};
250 def _set_momentary_timestamp(self
, timestamp
):
252 This function is only for unit testing to produce predictable results.
254 self
.momentary_timestamp
= timestamp
;