summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--htdocs/webiopi.js37
-rw-r--r--python/webiopi/devices/sensor/__init__.py13
-rw-r--r--python/webiopi/devices/sensor/icmp.py131
3 files changed, 181 insertions, 0 deletions
diff --git a/htdocs/webiopi.js b/htdocs/webiopi.js
index c7eae66..04029d7 100644
--- a/htdocs/webiopi.js
+++ b/htdocs/webiopi.js
@@ -665,6 +665,10 @@ WebIOPi.prototype.newDevice = function(type, name) {
665 if (type == "Distance") { 665 if (type == "Distance") {
666 return new Distance(name); 666 return new Distance(name);
667 } 667 }
668
669 if (type == "Duration") {
670 return new Duration(name);
671 }
668 672
669 if (type == "PiFaceDigital") { 673 if (type == "PiFaceDigital") {
670 return new PiFaceDigital(name); 674 return new PiFaceDigital(name);
@@ -1347,6 +1351,39 @@ Distance.prototype.refreshUI = function() {
1347 }); 1351 });
1348} 1352}
1349 1353
1354function Duration(name) {
1355 this.name = name;
1356 this.url = "/devices/" + name + "/sensor";
1357 this.refreshTime = 1000;
1358}
1359
1360Duration.prototype.toString = function() {
1361 return this.name + ": Duration";
1362}
1363
1364Duration.prototype.getMillimeter = function(callback) {
1365 $.get(this.url + "/duration/ms", function(data) {
1366 callback(this.name, data);
1367 });
1368}
1369
1370Duration.prototype.refreshUI = function() {
1371 var dist = this;
1372 var element = this.element;
1373
1374 if ((element != undefined) && (element.header == undefined)) {
1375 element.header = $("<h3>" + this + "</h3>");
1376 element.append(element.header);
1377 }
1378
1379 this.getMillimeter(function(name, data){
1380 if (element != undefined) {
1381 element.header.text(dist + ": " + data + "ms");
1382 }
1383 setTimeout(function(){dist.refreshUI()}, dist.refreshTime);
1384 });
1385}
1386
1350function PiFaceDigital(name) { 1387function PiFaceDigital(name) {
1351 this.name = name; 1388 this.name = name;
1352 this.url = "/devices/" + name + "/digital"; 1389 this.url = "/devices/" + name + "/digital";
diff --git a/python/webiopi/devices/sensor/__init__.py b/python/webiopi/devices/sensor/__init__.py
index 598a82f..f7879ec 100644
--- a/python/webiopi/devices/sensor/__init__.py
+++ b/python/webiopi/devices/sensor/__init__.py
@@ -169,9 +169,22 @@ class Distance():
169 def getYard(self): 169 def getYard(self):
170 return self.getInch() / 36 170 return self.getInch() / 36
171 171
172class Duration():
173 def __family__(self):
174 return "Duration"
175
176 def __getMilliseconds__(self):
177 raise NotImplementedError
178
179 @request("GET", "sensor/duration/ms")
180 @response("%.02f")
181 def getMilliseconds(self):
182 return self.__getMilliseconds__()
183
172DRIVERS = {} 184DRIVERS = {}
173DRIVERS["bmp085"] = ["BMP085"] 185DRIVERS["bmp085"] = ["BMP085"]
174DRIVERS["onewiretemp"] = ["DS1822", "DS1825", "DS18B20", "DS18S20", "DS28EA00"] 186DRIVERS["onewiretemp"] = ["DS1822", "DS1825", "DS18B20", "DS18S20", "DS28EA00"]
175DRIVERS["tmpXXX"] = ["TMP75", "TMP102", "TMP275"] 187DRIVERS["tmpXXX"] = ["TMP75", "TMP102", "TMP275"]
176DRIVERS["tslXXXX"] = ["TSL2561", "TSL2561CS", "TSL2561T", "TSL4531", "TSL45311", "TSL45313", "TSL45315", "TSL45317"] 188DRIVERS["tslXXXX"] = ["TSL2561", "TSL2561CS", "TSL2561T", "TSL4531", "TSL45311", "TSL45313", "TSL45315", "TSL45317"]
177DRIVERS["vcnl4000"] = ["VCNL4000"] 189DRIVERS["vcnl4000"] = ["VCNL4000"]
190DRIVERS["icmp"] = ["ICMP"]
diff --git a/python/webiopi/devices/sensor/icmp.py b/python/webiopi/devices/sensor/icmp.py
new file mode 100644
index 0000000..8a4065d
--- /dev/null
+++ b/python/webiopi/devices/sensor/icmp.py
@@ -0,0 +1,131 @@
1# Copyright 2013 Manuel Mausz
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15from webiopi.devices.sensor import Duration
16from webiopi.utils.logger import debug
17
18import socket, struct, select, random
19from time import time
20
21ICMP_ECHO_REQUEST = 8
22ICMP_CODE = socket.getprotobyname('icmp')
23ERROR_DESCR = {
24 1: 'ERROR: ICMP messages can only be sent from processes running as root.',
25 10013: 'ERROR: ICMP messages can only be sent by users or processes with administrator rights.'
26}
27
28class ICMP(Duration):
29 def __init__(self, ip='127.0.0.1'):
30 self.ip = ip
31
32 def __str__(self):
33 return "ICMP(ip=%s)" % self.ip
34
35 def __family__(self):
36 return Duration.__family__(self)
37
38 def __getMilliseconds__(self):
39 return self.echo(self.ip, 1) * 1000
40
41 def close(self):
42 pass
43
44 def __checksum__(self, source_string):
45 sum = 0
46 count_to = (len(source_string) / 2) * 2
47 count = 0
48 while count < count_to:
49 this_val = ord(source_string[count + 1])*256+ord(source_string[count])
50 sum = sum + this_val
51 sum = sum & 0xffffffff # Necessary?
52 count = count + 2
53 if count_to < len(source_string):
54 sum = sum + ord(source_string[len(source_string) - 1])
55 sum = sum & 0xffffffff # Necessary?
56 sum = (sum >> 16) + (sum & 0xffff)
57 sum = sum + (sum >> 16)
58 answer = ~sum
59 answer = answer & 0xffff
60
61 answer = answer >> 8 | (answer << 8 & 0xff00)
62 return answer
63
64
65 def __create_packet__(self, id):
66 """Creates a new echo request packet based on the given "id"."""
67 # Builds Dummy Header
68 # Header is type (8), code (8), checksum (16), id (16), sequence (16)
69 header = struct.pack('bbHHh', ICMP_ECHO_REQUEST, 0, 0, id, 1)
70 data = 192 * 'Q'
71
72 # Builds Real Header
73 header = struct.pack('bbHHh', ICMP_ECHO_REQUEST, 0, socket.htons(self.__checksum__(header + data)), id, 1)
74 return header + data
75
76
77 def __response_handler__(self, sock, packet_id, time_sent, timeout):
78 """Handles packet response, returning either the delay or timing out (returns "None")."""
79 while True:
80 ready = select.select([sock], [], [], timeout)
81 if ready[0] == []: # Timeout
82 return
83
84 time_received = time()
85 rec_packet, addr = sock.recvfrom(1024)
86 icmp_header = rec_packet[20:28]
87 type, code, checksum, rec_id, sequence = struct.unpack('bbHHh', icmp_header)
88
89 if rec_id == packet_id:
90 return time_received - time_sent
91
92 timeout -= time_received - time_sent
93 if timeout <= 0:
94 return
95
96 def echo(self, dest_addr, timeout=1):
97 """
98 Sends one ICMP packet to the given destination address (dest_addr)
99 which can be either an ip or a hostname.
100
101 "timeout" can be any integer or float except for negatives and zero.
102
103 Returns either the delay (in seconds), or "None" on timeout or an
104 invalid address, respectively.
105
106 """
107
108 try:
109 sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, ICMP_CODE)
110 except socket.error, (error_number, msg):
111 if error_number in ERROR_DESCR:
112 # Operation not permitted
113 raise socket.error(''.join((msg, ERROR_DESCR[error_number])))
114 raise # Raises the original error
115
116 try:
117 socket.gethostbyname(dest_addr)
118 except socket.gaierror:
119 return
120
121 packet_id = int((id(timeout) * random.random()) % 65535)
122 packet = self.__create_packet__(packet_id)
123 while packet:
124 # The icmp protocol does not use a port, but the function
125 # below expects it, so we just give it a dummy port.
126 sent = sock.sendto(packet, (dest_addr, 1))
127 packet = packet[sent:]
128
129 delay = self.__response_handler__(sock, packet_id, time(), timeout)
130 sock.close()
131 return delay