1
2
3 """
4 Copyright (c) 2015 Alan Yorinks All rights reserved.
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public
8 License as published by the Free Software Foundation; either
9 version 3 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 """
20
21 import json
22 import asyncio
23 import datetime
24 import argparse
25
26 from autobahn.asyncio.websocket import WebSocketServerProtocol, \
27 WebSocketServerFactory
28
29 from pymata_aio.pymata_core import PymataCore
30 from pymata_aio.constants import Constants
31
32
33 -class PymataIOT(WebSocketServerProtocol):
34 """
35 This class implements the PyMata Websocket interface. JSON command messages are received via Websocket connection,
36 decoded and mapped to an associated PyMata method to be executed
37 operation.
38
39 The methods below are not intended to be called directly. The JSON message format is documented as part of the
40 method description. All JSON reply messages (to be handled by the client) will also be documented for
41 within the description.
42
43 usage: pymata_iot.py [-h] [-host HOSTNAME] [-port PORT] [-wait WAIT]
44 [-comport COM] [-sleep SLEEP]
45
46 optional arguments:
47 -h, --help show this help message and exit
48 -host HOSTNAME Server name or IP address
49 -port PORT Server port number
50 -wait WAIT Arduino wait time
51 -comport COM Arduino COM port
52 -sleep SLEEP sleep tune in ms.
53 """
54 parser = argparse.ArgumentParser()
55 parser.add_argument("-host", dest="hostname", default="localhost", help="Server name or IP address")
56 parser.add_argument("-port", dest="port", default="9000", help="Server port number")
57 parser.add_argument("-wait", dest="wait", default="2", help="Arduino wait time")
58 parser.add_argument("-comport", dest="com", default="None", help="Arduino COM port")
59 parser.add_argument("-sleep", dest="sleep", default=".001", help="sleep tune in ms.")
60 args = parser.parse_args()
61
62 ip_addr = args.hostname
63 ip_port = args.port
64
65 if args.com == 'None':
66 comport = None
67 else:
68 comport = args.com
69
70 core = PymataCore(int(args.wait), float(args.sleep), comport)
71 core.start()
72
74 """
75 This is the "constructor" for PymataIOT. It sets up a translation dictionary using incoming JSON commands
76 and maps them to command methods.
77 @return: No Return.
78 """
79
80 self.command_map = {
81 "analog_read": self.analog_read,
82 "analog_write": self.analog_write,
83 "digital_read": self.digital_read,
84 "digital_write": self.digital_write,
85 "disable_analog_reporting": self.disable_analog_reporting,
86 "disable_digital_reporting": self.disable_digital_reporting,
87 "enable_analog_reporting": self.disable_analog_reporting,
88 "enable_digital_reporting": self.disable_digital_reporting,
89 "encoder_config": self.encoder_config,
90 "encoder_read": self.encoder_read,
91 "get_analog_latch_data": self.get_analog_latch_data,
92 "get_analog_map": self.get_analog_map,
93 "get_capability_report": self.get_capability_report,
94 "get_digital_latch_data": self.get_digital_latch_data,
95 "get_firmware_version": self.get_firmware_version,
96 "get_pin_state": self.get_pinstate_report,
97 "get_protocol_version": self.get_protocol_version,
98 "get_pymata_version": self.get_pymata_version,
99 "i2c_config": self.i2c_config,
100 "i2c_read_data": self.i2c_read_data,
101 "i2c_read_request": self.i2c_read_request,
102 "i2c_write_request": self.i2c_write_request,
103 "play_tone": self.play_tone,
104 "set_analog_latch": self.set_analog_latch,
105 "set_digital_latch": self.set_digital_latch,
106 "set_pin_mode": self.set_pin_mode,
107 "set_sampling_interval": self.set_sampling_interval,
108 "sonar_config": self.sonar_config,
109 "sonar_read": self.sonar_read,
110 "servo_config": self.servo_config,
111 "stepper_config": self.stepper_config,
112 "stepper_step": self.stepper_step
113 }
114
115 @asyncio.coroutine
117 """
118 This method reads and returns the last reported value for an analog pin.
119 Normally not used since analog pin updates will be provided automatically
120 as they occur with the analog_message_reply being sent to the client after set_pin_mode is called.
121 (see enable_analog_reporting for message format).
122
123 @param command: {"method": "analog_read", "params": [ANALOG_PIN]}
124 @return: {"method": "analog_read_reply", "params": [PIN, ANALOG_DATA_VALUE]}
125 """
126 pin = int(command[0])
127 data_val = yield from self.core.analog_read(pin)
128 reply = json.dumps({"method": "analog_read_reply", "params": [pin, data_val]})
129 self.sendMessage(reply.encode('utf8'))
130
131 @asyncio.coroutine
133 """
134 This method writes a value to an analog pin.
135
136 It is used to set the output of a PWM pin or the angle of a Servo.
137 @param command: {"method": "analog_write", "params": [PIN, WRITE_VALUE]}
138 @return: No return message.
139 """
140 pin = int(command[0])
141 value = int(command[1])
142 yield from self.core.analog_write(pin, value)
143
144 @asyncio.coroutine
146 """
147 This method reads and returns the last reported value for a digital pin.
148 Normally not used since digital pin updates will be provided automatically
149 as they occur with the digital_message_reply being sent to the client after set_pin_mode is called..
150 (see enable_digital_reporting for message format)
151
152 @param command: {"method": "digital_read", "params": [PIN]}
153 @return: {"method": "digital_read_reply", "params": [PIN, DIGITAL_DATA_VALUE]}
154 """
155 pin = int(command[0])
156 data_val = yield from self.core.digital_read(pin)
157 reply = json.dumps({"method": "digital_read_reply", "params": [pin, data_val]})
158 self.sendMessage(reply.encode('utf8'))
159
160 @asyncio.coroutine
162 """
163 This method writes a zero or one to a digital pin.
164 @param command: {"method": "digital_write", "params": [PIN, DIGITAL_DATA_VALUE]}
165 @return: No return message..
166 """
167 pin = int(command[0])
168 value = int(command[1])
169 yield from self.core.digital_write(pin, value)
170
171 @asyncio.coroutine
173 """
174 Disable Firmata reporting for an analog pin.
175 @param command: {"method": "disable_analog_reporting", "params": [PIN]}
176 @return: No return message..
177 """
178 pin = int(command[0])
179 yield from self.core.disable_analog_reporting(pin)
180
181 @asyncio.coroutine
183 """
184 Disable Firmata reporting for a digital pin.
185 @param command: {"method": "disable_digital_reporting", "params": [PIN]}
186 @return: No return message.
187 """
188 pin = int(command[0])
189 yield from self.core.disable_digital_reporting(pin)
190
191 @asyncio.coroutine
193 """
194 Enable Firmata reporting for an analog pin.
195 @param command: {"method": "enable_analog_reporting", "params": [PIN]}
196 @return: {"method": "analog_message_reply", "params": [PIN, ANALOG_DATA_VALUE]}
197 """
198 pin = int(command[0])
199 yield from self.core.enable_analog_reporting(pin)
200
201 @asyncio.coroutine
203 """
204 Enable Firmata reporting for a digital pin.
205
206 @param command: {"method": "enable_digital_reporting", "params": [PIN]}
207 @return: {"method": "digital_message_reply", "params": [PIN, DIGITAL_DATA_VALUE]}
208 """
209 pin = int(command[0])
210 yield from self.core.enable_digital_reporting(pin)
211
212 @asyncio.coroutine
214 """
215 Configure 2 pins for FirmataPlus encoder operation.
216
217 @param command: {"method": "encoder_config", "params": [PIN_A, PIN_B]}
218 @return: {"method": "encoder_data_reply", "params": [ENCODER_DATA]}
219 """
220 pin_a = int(command[0])
221 pin_b = int(command[1])
222 yield from self.core.encoder_config(pin_a, pin_b, self.encoder_callback)
223
224 @asyncio.coroutine
226 """
227 This is a polling method to read the last cached FirmataPlus encoder value.
228 Normally not used. See encoder config for the asynchronous report message format.
229
230 @param command: {"method": "encoder_read", "params": [PIN_A]}
231 @return: {"method": "encoder_read_reply", "params": [PIN_A, ENCODER_VALUE]}
232 """
233 pin = int(command[0])
234 val = yield from self.core.encoder_read(pin)
235 reply = json.dumps({"method": "encoder_read_reply", "params": [pin, val]})
236 self.sendMessage(reply.encode('utf8'))
237
238 @asyncio.coroutine
240 """
241 This method retrieves a latch table entry for an analog pin.
242
243 See constants.py for definition of reply message parameters.
244
245 @param command: {"method": "get_analog_latch_data", "params": [ANALOG_PIN]}
246 @return: {"method": "get_analog_latch_data_reply", "params": [ANALOG_PIN, LATCHED_STATE, THRESHOLD_TYPE,\
247 THRESHOLD_TARGET, DATA_VALUE, TIME_STAMP ]}
248 """
249 pin = int(command[0])
250 data_val = yield from self.core.get_analog_latch_data(pin)
251 if data_val:
252 data_val = data_val[0:-1]
253 reply = json.dumps({"method": "get_analog_latch_data_reply", "params": [pin, data_val]})
254 self.sendMessage(reply.encode('utf8'))
255
256 @asyncio.coroutine
258 """
259 This method retrieves the Firmata analog map.
260
261 Refer to: http://firmata.org/wiki/Protocol#Analog_Mapping_Query to interpret the reply
262
263 The command JSON format is: {"method":"get_analog_map","params":["null"]}
264 @return: {"method": "analog_map_reply", "params": [ANALOG_MAP]}
265 """
266 value = yield from self.core.get_analog_map()
267 if value:
268 reply = json.dumps({"method": "analog_map_reply", "params": value})
269 else:
270 reply = json.dumps({"method": "analog_map_reply", "params": "None"})
271 self.sendMessage(reply.encode('utf8'))
272
273 @asyncio.coroutine
275 """
276 This method retrieves the Firmata capability report.
277
278 Refer to http://firmata.org/wiki/Protocol#Capability_Query
279
280 The command format is: {"method":"get_capability_report","params":["null"]}
281
282 @return: {"method": "capability_report_reply", "params": [RAW_CAPABILITY_REPORT]}
283 """
284 value = yield from self.core.get_capability_report()
285 asyncio.sleep(.1)
286 if value:
287 reply = json.dumps({"method": "capability_report_reply", "params": value})
288 else:
289 reply = json.dumps({"method": "capability_report_reply", "params": "None"})
290 self.sendMessage(reply.encode('utf8'))
291
292 @asyncio.coroutine
294 """
295 This method retrieves a latch table entry for a digital pin.
296
297 See constants.py for definition of reply message parameters.
298
299 @param command: {"method": "get_digital_latch_data", "params": [ANALOG_PIN]}
300 @return: {"method": "get_digital_latch_data_reply", "params": [DIGITAL_PIN, LATCHED_STATE, THRESHOLD_TYPE,\
301 THRESHOLD_TARGET, DATA_VALUE, TIME_STAMP ]}
302 """
303 pin = int(command[0])
304 data_val = yield from self.core.get_digital_latch_data(pin)
305 if data_val:
306 data_val = data_val[0:-1]
307 reply = json.dumps({"method": "get_digital_latch_data_reply", "params": [pin, data_val]})
308 self.sendMessage(reply.encode('utf8'))
309
310 @asyncio.coroutine
312 """
313 This method retrieves the Firmata firmware version.
314
315 See: http://firmata.org/wiki/Protocol#Query_Firmware_Name_and_Version
316
317
318 JSON command: {"method": "get_firmware_version", "params": ["null"]}
319
320 @return: {"method": "firmware_version_reply", "params": [FIRMWARE_VERSION]}
321 """
322 value = yield from self.core.get_firmware_version()
323 if value:
324 reply = json.dumps({"method": "firmware_version_reply", "params": value})
325 else:
326 reply = json.dumps({"method": "firmware_version_reply", "params": "Unknown"})
327 self.sendMessage(reply.encode('utf8'))
328
329 @asyncio.coroutine
331 """
332 This method retrieves a Firmata pin_state report for a pin..
333
334 See: http://firmata.org/wiki/Protocol#Pin_State_Query
335
336 @param command: {"method": "get_pin_state", "params": [PIN]}
337 @return: {"method": "get_pin_state_reply", "params": [PIN_NUMBER, PIN_MODE, PIN_STATE]}
338 """
339 pin = int(command[0])
340 value = yield from self.core.get_pin_state(pin)
341 if value:
342 reply = json.dumps({"method": "pin_state_reply", "params": value})
343 else:
344 reply = json.dumps({"method": "pin_state_reply", "params": "Unknown"})
345 self.sendMessage(reply.encode('utf8'))
346
347 @asyncio.coroutine
349 """
350 This method retrieves the Firmata protocol version.
351
352 JSON command: {"method": "get_protocol_version", "params": ["null"]}
353
354 @return: {"method": "protocol_version_reply", "params": [PROTOCOL_VERSION]}
355 """
356 value = yield from self.core.get_protocol_version()
357 if value:
358 reply = json.dumps({"method": "protocol_version_reply", "params": value})
359 else:
360 reply = json.dumps({"method": "protocol_version_reply", "params": "Unknown"})
361 self.sendMessage(reply.encode('utf8'))
362
363 @asyncio.coroutine
365 """
366 This method retrieves the PyMata release version number.
367
368 JSON command: {"method": "get_pymata_version", "params": ["null"]}
369
370 @return: {"method": "pymata_version_reply", "params":[PYMATA_VERSION]}
371 """
372 value = yield from self.core.get_pymata_version()
373 if value:
374 reply = json.dumps({"method": "pymata_version_reply", "params": value})
375 else:
376 reply = json.dumps({"method": "pymata_version_reply", "params": "Unknown"})
377 self.sendMessage(reply.encode('utf8'))
378
379 @asyncio.coroutine
381 """
382 This method initializes the I2c and sets the optional read delay (in microseconds).
383
384 It must be called before doing any other i2c operations for a given device.
385 @param command: {"method": "i2c_config", "params": [DELAY]}
386 @return: No Return message.
387 """
388 delay = int(command[0])
389 yield from self.core.i2c_config(delay)
390
391 @asyncio.coroutine
393 """
394 This method retrieves the last value read for an i2c device identified by address.
395 This is a polling implementation and i2c_read_request and i2c_read_request_reply may be
396 a better alternative.
397 @param command: {"method": "i2c_read_data", "params": [I2C_ADDRESS ]}
398 @return:{"method": "i2c_read_data_reply", "params": i2c_data}
399 """
400 address = int(command[0])
401 i2c_data = yield from self.core.i2c_read_data(address)
402 reply = json.dumps({"method": "i2c_read_data_reply", "params": i2c_data})
403 self.sendMessage(reply.encode('utf8'))
404
405 @asyncio.coroutine
407 """
408 This method sends an I2C read request to Firmata. It is qualified by a single shot, continuous
409 read, or stop reading command.
410 Special Note: for the read type supply one of the following strings:
411
412 "I2C_READ"
413
414 "I2C_READ | I2C_END_TX_MASK"
415
416 "I2C_READ_CONTINUOUSLY"
417
418 "I2C_READ_CONTINUOUSLY | I2C_END_TX_MASK"
419
420 @param command: {"method": "i2c_read_request", "params": [I2C_ADDRESS, I2C_REGISTER,
421 NUMBER_OF_BYTES, I2C_READ_TYPE ]}
422 @return: {"method": "i2c_read_request_reply", "params": [DATA]}
423 """
424 device_address = int(command[0])
425 register = int(command[1])
426 number_of_bytes = int(command[2])
427
428 if command[3] == "I2C_READ_CONTINUOUS":
429 read_type = Constants.I2C_READ_CONTINUOUSLY
430 elif command[3] == "I2C_READ":
431 read_type = Constants.I2C_READ
432 else:
433 read_type = Constants.I2C_STOP_READING
434 yield from self.core.i2c_read_request(device_address, register, number_of_bytes, read_type,
435 self.i2c_read_request_callback)
436
437 @asyncio.coroutine
439 """
440 This method performs an I2C write at a given I2C address,
441 @param command: {"method": "i2c_write_request", "params": [I2C_DEVICE_ADDRESS, [DATA_TO_WRITE]]}
442 @return:No return message.
443 """
444 device_address = int(command[0])
445 params = command[1]
446 params = [int(i) for i in params]
447 yield from self.core.i2c_write_request(device_address, *params)
448
449 @asyncio.coroutine
451 """
452 This method controls a piezo device to play a tone. It is a FirmataPlus feature.
453 Tone command is TONE_TONE to play, TONE_NO_TONE to stop playing.
454 @param command: {"method": "play_tone", "params": [PIN, TONE_COMMAND, FREQUENCY(Hz), DURATION(MS)]}
455 @return:No return message.
456 """
457 pin = int(command[0])
458 if command[1] == "TONE_TONE":
459 tone_command = Constants.TONE_TONE
460 else:
461 tone_command = Constants.TONE_NO_TONE
462 frequency = int(command[2])
463 duration = int(command[3])
464 yield from self.core.play_tone(pin, tone_command, frequency, duration)
465
466 @asyncio.coroutine
468 """
469 This method sets the an analog latch for a given analog pin, providing the threshold type, and
470 latching threshold.
471 @param command: {"method": "set_analog_latch", "params": [PIN, THRESHOLD_TYPE, THRESHOLD_VALUE]}
472 @return:{"method": "analog_latch_data_reply", "params": [PIN, DATA_VALUE_LATCHED, TIMESTAMP_STRING]}
473 """
474 pin = int(command[0])
475 threshold_type = int(command[1])
476 threshold_value = int(command[2])
477 yield from self.core.set_analog_latch(pin, threshold_type, threshold_value, self.analog_latch_callback)
478
479 @asyncio.coroutine
481 """
482 This method sets the a digital latch for a given digital pin, the threshold type, and latching threshold.
483 @param command:{"method": "set_digital_latch", "params": [PIN, THRESHOLD (0 or 1)]}
484 @return:{"method": digital_latch_data_reply", "params": [PIN, DATA_VALUE_LATCHED, TIMESTAMP_STRING]}
485 """
486 pin = int(command[0])
487 threshold_value = int(command[1])
488 yield from self.core.set_digital_latch(pin, threshold_value, self.digital_latch_callback)
489
490 @asyncio.coroutine
492 """
493 This method sets the pin mode for the selected pin. It handles: Input, Analog(Input) PWM, and OUTPUT. Servo
494 is handled by servo_config().
495 @param command: {"method": "set_pin_mode", "params": [PIN, MODE]}
496 @return:No return message.
497 """
498 pin = int(command[0])
499 mode = int(command[1])
500 if mode == Constants.INPUT:
501 cb = self.digital_callback
502 elif mode == Constants.ANALOG:
503 cb = self.analog_callback
504 else:
505 cb = None
506
507 yield from self.core.set_pin_mode(pin, mode, cb)
508
510 """
511 This method sets the Firmata sampling interval in ms.
512 @param command:{"method": "set_sampling_interval", "params": [INTERVAL]}
513 @return:No return message.
514 """
515 sample_interval = int(command[0])
516 yield from self.core.set_sampling_interval(sample_interval)
517
518 @asyncio.coroutine
520 """
521 This method configures 2 pins to support HC-SR04 Ping devices.
522 This is a FirmataPlus feature.
523 @param command: {"method": "sonar_config", "params": [TRIGGER_PIN, ECHO_PIN, PING_INTERVAL(default=50),
524 MAX_DISTANCE(default= 200 cm]}
525 @return:{"method": "sonar_data_reply", "params": [DISTANCE_IN_CM]}
526 """
527 trigger = int(command[0])
528 echo = int(command[1])
529 interval = int(command[2])
530 max_dist = int(command[3])
531 yield from self.core.sonar_config(trigger, echo, self.sonar_callback, interval, max_dist)
532
533 @asyncio.coroutine
535 """
536 This method retrieves the last sonar data value that was cached.
537 This is a polling method. After sonar config, sonar_data_reply messages will be sent automatically.
538 @param command: {"method": "sonar_read", "params": [TRIGGER_PIN]}
539 @return:{"method": "sonar_read_reply", "params": [TRIGGER_PIN, DATA_VALUE]}
540 """
541 pin = int(command[0])
542 val = yield from self.core.sonar_data_retrieve(pin)
543
544 reply = json.dumps({"method": "sonar_read_reply", "params": [pin, val]})
545 self.sendMessage(reply.encode('utf8'))
546
547 @asyncio.coroutine
549 """
550 This method configures a pin for servo operation. The servo angle is set by using analog_write().
551 @param command: {"method": "servo_config", "params": [PIN, MINIMUM_PULSE(ms), MAXIMUM_PULSE(ms)]}
552 @return:No message returned.
553 """
554 pin = int(command[0])
555 min_pulse = int(command[1])
556 max_pulse = int(command[2])
557 yield from self.core.servo_config(pin, min_pulse, max_pulse)
558
559 @asyncio.coroutine
561 """
562 This method configures 4 pins for stepper motor operation.
563 This is a FirmataPlus feature.
564 @param command: {"method": "stepper_config", "params": [STEPS_PER_REVOLUTION, [PIN1, PIN2, PIN3, PIN4]]}
565 @return:No message returned.
566 """
567 steps_per_revs = int(command[0])
568 pins = command[1]
569 pin1 = int(pins[0])
570 pin2 = int(pins[1])
571 pin3 = int(pins[2])
572 pin4 = int(pins[3])
573 yield from self.core.stepper_config(steps_per_revs, [pin1, pin2, pin3, pin4])
574
575 @asyncio.coroutine
577 """
578 This method activates a stepper motor motion.
579 This is a FirmataPlus feature.
580 @param command: {"method": "stepper_step", "params": [SPEED, NUMBER_OF_STEPS]}
581 @return:No message returned.
582 """
583 speed = int(command[0])
584 num_steps = int(command[1])
585 yield from self.core.stepper_step(speed, num_steps)
586
587 - def onClose(self, was_clean, code, reason):
588 """
589 Websocket management message.
590 This message is received when the client closes the connection. A console status message is printed and
591 and the interface is shutdown before exiting.
592 @param was_clean: Autobahn provided flag
593 @param code:Autobahn provided flag
594 @param reason:Autobahn provided flag
595 @return:Console message is generated.
596 """
597 print("WebSocket connection closed: {0}".format(reason))
598 yield from self.core.shutdown()
599
601 """
602 WebSocket management method.
603 This method issues a console status message for Websocket connection establishment.
604 @param request: Websocket request
605 @return: No return value.
606 """
607 print("Client connecting: {0}".format(request.peer))
608
610 """
611 Websocket management method.
612 This method receives JSON messages from the Websocket client. All messages are assumed to be text.
613 If a binary message is sent, a console status message is generated.
614
615 The JSON command is interpreted and translated to a method call to handle the command request.
616 @param payload: JSON command
617 @param is_binary: True if message is binary. Assumed to always be False
618 @return: No value is returned.
619 """
620 if is_binary:
621 print("Binary message received: {0} bytes".format(len(payload)))
622 print('Expected text and not binary')
623 else:
624 cmd_dict = json.loads(payload.decode('utf8'))
625 client_cmd = cmd_dict.get("method")
626
627 if client_cmd in self.command_map:
628 cmd = self.command_map.get(client_cmd)
629 params = cmd_dict.get("params")
630 if params[0] != "null":
631 yield from cmd(params)
632 else:
633 yield from cmd()
634
636 """
637 WebSocket management method.
638 This method issues a console status message for the opening of a Websocket connection. It sends a Firmata
639 reset command to the Arduino.
640 @return: No return value.
641 """
642 print("WebSocket connection open.")
643 yield from self.core.send_reset()
644
646 """
647 This method handles the analog message received from pymata_core
648 @param data: analog callback message
649 @return:{"method": "analog_message_reply", "params": [PIN, DATA_VALUE}
650 """
651 reply = json.dumps({"method": "analog_message_reply", "params": [data[0], data[1]]})
652 self.sendMessage(reply.encode('utf8'))
653
655 """
656 This method handles analog_latch data received from pymata_core
657 @param data: analog latch callback message
658 @return:{"method": "analog_latch_data_reply", "params": [ANALOG_PIN, VALUE_AT_TRIGGER, TIME_STAMP_STRING]}
659 """
660 ts = data[2]
661 st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
662 reply = json.dumps({"method": "analog_latch_data_reply", "params": [data[0], data[1], st]})
663 self.sendMessage(reply.encode('utf8'))
664
666 """
667 This method handles the digital message received from pymata_core
668 @param data: digital callback message
669 @return:{"method": "digital_message_reply", "params": [PIN, DATA_VALUE]}
670 """
671 reply = json.dumps({"method": "digital_message_reply", "params": [data[0], data[1]]})
672 self.sendMessage(reply.encode('utf8'))
673
674
676 """
677 This method handles the digital latch data message received from pymata_core
678 @param data: digital latch callback message
679 @return:s{"method": "digital_latch_data_reply", "params": [PIN, DATA_VALUE_AT_TRIGGER, TIME_STAMP_STRING]}
680 """
681 ts = data[2]
682 st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
683 reply = json.dumps({"method": "digital_latch_data_reply", "params": [data[0], data[1], st]})
684 self.sendMessage(reply.encode('utf8'))
685
687 """
688 This method handles the encoder data message received from pymata_core
689 @param data: encoder data callback message
690 @return:{"method": "encoder_data_reply", "params": [ENCODER VALUE]}
691 """
692 reply = json.dumps({"method": "encoder_data_reply", "params": data})
693 self.sendMessage(reply.encode('utf8'))
694
696 """
697 This method handles the i2c read data message received from pymata_core.
698 @param data: i2c read data callback message
699 @return:{"method": "i2c_read_request_reply", "params": [DATA_VALUE]}
700 """
701 reply = json.dumps({"method": "i2c_read_request_reply", "params": data})
702 self.sendMessage(reply.encode('utf8'))
703
705 """
706 This method handles the i2c cached read data received from pymata_core.
707 @param data: i2c read cached data callback message
708 @return:{"method": "i2c_read_data_reply", "params": [DATA_VALUE]}
709 """
710 reply = json.dumps({"method": "i2c_read_data_reply", "params": data})
711 self.sendMessage(reply.encode('utf8'))
712
714 """
715 This method handles sonar data received from pymata_core.
716 @param data: sonar data callback message
717 @return:{"method": "sonar_data_reply", "params": [DATA_VALUE]}
718 """
719 reply = json.dumps({"method": "sonar_data_reply", "params": data})
720
721 self.sendMessage(reply.encode('utf8'))
722
723
724 if __name__ == '__main__':
725
726
727 ws_string = 'ws://' + PymataIOT.ip_addr + ':' + PymataIOT.ip_port
728 print('Websocket server operating on: ' + ws_string)
729 factory = WebSocketServerFactory(ws_string, debug=False)
730 factory.protocol = PymataIOT
731
732 loop = asyncio.get_event_loop()
733
734 coro = loop.create_server(factory, '0.0.0.0', int(PymataIOT.ip_port))
735 server = loop.run_until_complete(coro)
736
737 try:
738 loop.run_forever()
739 except KeyboardInterrupt:
740 pass
741 finally:
742 server.close()
743 loop.close()
744