Yet Another WebIOPi+
 All Classes Namespaces Files Functions Variables Macros Pages
rest.py
Go to the documentation of this file.
1 # Copyright 2012-2013 Eric Ptak - trouch.com
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 
15 from webiopi.utils import types
16 from webiopi.utils import logger
17 from webiopi.utils.types import M_JSON, M_PLAIN
18 from webiopi.utils.version import BOARD_REVISION, VERSION_STRING, MAPPING
19 from webiopi.devices import manager
20 from webiopi.devices import instance
21 from webiopi.devices.bus import BUSLIST
22 
23 try:
24  import _webiopi.GPIO as GPIO
25 except:
26  pass
27 
28 MACROS = {}
29 
30 class RESTHandler():
31  def __init__(self):
32  self.device_mapping = True
33  self.export = []
34  self.routes = {}
35  self.macros = {}
36 
37 
38  def addMacro(self, macro):
39  self.macros[macro.__name__] = macro
40 
41  def addRoute(self, source, destination):
42  if source[0] == "/":
43  source = source[1:]
44  if destination[0] == "/":
45  destination = destination[1:]
46  self.routes[source] = destination
47  logger.info("Added Route /%s => /%s" % (source, destination))
48 
49  def findRoute(self, path):
50  for source in self.routes:
51  if path.startswith(source):
52  route = path.replace(source, self.routes[source])
53  logger.info("Routing /%s => /%s" % (path, route))
54  return route
55  return path
56 
57  def extract(self, fmtArray, pathArray, args):
58  if len(fmtArray) != len(pathArray):
59  return False
60  if len(fmtArray) == 0:
61  return True
62  fmt = fmtArray[0]
63  path = pathArray[0]
64  if fmt == path:
65  return self.extract(fmtArray[1:], pathArray[1:], args)
66  if fmt.startswith("%"):
67 
68  fmt = fmt[1:]
69  t = 's'
70  if fmt[0] == '(':
71  if fmt[-1] == ')':
72  name = fmt[1:-1]
73  elif fmt[-2] == ')':
74  name = fmt[1:-2]
75  t = fmt[-1]
76  else:
77  raise Exception("Missing closing brace")
78  else:
79  name = fmt
80 
81  if t == 's':
82  args[name] = path
83  elif t == 'b':
84  args[name] = types.str2bool(path)
85  elif t == 'd':
86  args[name] = types.toint(path)
87  elif t == 'x':
88  args[name] = int(path, 16)
89  elif t == 'f':
90  args[name] = float(path)
91  else:
92  raise Exception("Unknown format type : %s" % t)
93 
94  return self.extract(fmtArray[1:], pathArray[1:], args)
95 
96  return False
97 
98  def getDeviceRoute(self, method, path):
99  pathArray = path.split("/")
100  deviceName = pathArray[0]
101  device = instance.DEVICES[deviceName]
102  if device == None:
103  return (None, deviceName + " Not Found")
104  pathArray = pathArray[1:]
105  funcs = device["functions"][method]
106  functionName = "/".join(pathArray)
107  if functionName in funcs:
108  return (funcs[functionName], {})
109 
110  for fname in funcs:
111  func = funcs[fname]
112  funcPathArray = func.path.split("/")
113  args = {}
114  if self.extract(funcPathArray, pathArray, args):
115  return (func, args)
116 
117  return (None, functionName + " Not Found")
118 
119  def callDeviceFunction(self, method, path, data=None):
120  (func, args) = self.getDeviceRoute(method, path)
121  if func == None:
122  return (404, args, M_PLAIN)
123 
124  if func.data != None:
125  args[func.data] = data
126 
127  result = func(**args)
128  response = None
129  contentType = None
130  if result != None:
131  if hasattr(func, "contentType"):
132  contentType = func.contentType
133  if contentType == M_JSON:
134  response = types.jsonDumps(result)
135  else:
136  response = func.format % result
137  else:
138  response = result
139 
140  return (200, response, contentType)
141 
142  def do_GET(self, relativePath, compact=False):
143  relativePath = self.findRoute(relativePath)
144 
145  # JSON full state
146  if relativePath == "*":
147  return (200, self.getJSON(compact), M_JSON)
148 
149  # RPi header map
150  elif relativePath == "map":
151  json = "%s" % MAPPING
152  json = json.replace("'", '"')
153  return (200, json, M_JSON)
154 
155  # server version
156  elif relativePath == "version":
157  return (200, VERSION_STRING, M_PLAIN)
158 
159  # board revision
160  elif relativePath == "revision":
161  revision = "%s" % BOARD_REVISION
162  return (200, revision, M_PLAIN)
163 
164  # Single GPIO getter
165  elif relativePath.startswith("GPIO/"):
166  return self.callDeviceFunction("GET", relativePath)
167 
168  elif relativePath == "devices/*":
169  return (200, manager.getDevicesJSON(compact), M_JSON)
170 
171  elif relativePath.startswith("devices/"):
172  if not self.device_mapping:
173  return (404, None, None)
174  path = relativePath.replace("devices/", "")
175  return self.callDeviceFunction("GET", path)
176 
177  else:
178  return (0, None, None)
179 
180  def do_POST(self, relativePath, data, compact=False):
181  relativePath = self.findRoute(relativePath)
182 
183  if relativePath.startswith("GPIO/"):
184  return self.callDeviceFunction("POST", relativePath)
185 
186  elif relativePath.startswith("macros/"):
187  paths = relativePath.split("/")
188  mname = paths[1]
189  if len(paths) > 2:
190  value = paths[2]
191  else:
192  value = ""
193 
194  if mname in self.macros:
195  macro = self.macros[mname]
196 
197  if ',' in value:
198  args = value.split(',')
199  result = macro(*args)
200  elif len(value) > 0:
201  result = macro(value)
202  else:
203  result = macro()
204 
205  response = ""
206  if result:
207  response = "%s" % result
208  return (200, response, M_PLAIN)
209 
210  else:
211  return (404, mname + " Not Found", M_PLAIN)
212 
213  elif relativePath.startswith("devices/"):
214  if not self.device_mapping:
215  return (404, None, None)
216  path = relativePath.replace("devices/", "")
217  return self.callDeviceFunction("POST", path, data)
218 
219  else: # path unknowns
220  return (0, None, None)
221 
222  def getJSON(self, compact=False):
223  if compact:
224  f = 'f'
225  v = 'v'
226  else:
227  f = 'function'
228  v = 'value'
229 
230  json = {}
231  for (bus, value) in BUSLIST.items():
232  json[bus] = int(value["enabled"])
233 
234  gpios = {}
235  if len(self.export) > 0:
236  export = self.export
237  else:
238  export = range(GPIO.GPIO_COUNT)
239 
240  for gpio in export:
241  gpios[gpio] = {}
242  if compact:
243  gpios[gpio][f] = GPIO.getFunction(gpio)
244  else:
245  gpios[gpio][f] = GPIO.getFunctionString(gpio)
246  gpios[gpio][v] = int(GPIO.input(gpio))
247 
248  if GPIO.getFunction(gpio) == GPIO.PWM:
249  (pwmType, value) = GPIO.getPulse(gpio).split(':')
250  gpios[gpio][pwmType] = value
251 
252  json['GPIO'] = gpios
253  return types.jsonDumps(json)
254