Yet Another WebIOPi+
 All Classes Namespaces Files Functions Variables Macros Pages
__init__.py
Go to the documentation of this file.
1 # Copyright 2014 Andreas Riegg - t-h-i-n-x.net
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 # Changelog
16 #
17 # 1.0 2014-06-04 Initial release.
18 # 1.1 2014-08-17 Added public NON-REST methods that work with
19 # naive Python datetime objects and restructured code
20 # 1.2 2014-09-23 Added driver lookup for class OsClock
21 #
22 # Usage remarks
23 #
24 # - Date formatting is handled according to ISO8601:2004 (aka ISO calendar).
25 # - Public NON-REST methods have real Python datetime objects as parameters
26 # - All datetime objects are handeled in naive mode as (most) RTC chips do
27 # not handle timezione infos. It's up to the user to decide what time gets
28 # stored in the chips. It's recommended to use UTC.
29 # - Setting date via REST is only possible as full date string to be able
30 # to check calendar consistency
31 # - Setting time via REST is handled the same way for consistency and
32 # simplification
33 #
34 
35 from webiopi.decorators.rest import request, response
36 from webiopi.utils.types import toint, M_JSON
37 from datetime import datetime, date, time
38 
39 
40 class Clock():
41 
42  def __init__(self):
43  return
44 
45 #---------- Abstraction framework contracts ----------
46 
47  def __family__(self):
48  return "Clock"
49 
50 
51 #---------- Clock abstraction REST implementation ----------
52 
53  @request("GET", "clock/*")
54  @response(contentType=M_JSON)
55  def clockWildcard(self):
56  return {"isotime": self.getTimeString(),
57  "isodate": self.getDateString(),
58  "isodow": self.getDow()}
59  # {
60  # "isotime" = "12:34:56",
61  # "isodate" = "2014-05-13",
62  # "isodow" = 3
63  # }
64 
65  @request("GET", "clock/datetime")
66  @response("%s")
67  def getDateTimeString(self):
68  return self.__getDateTimeString__()
69 
70  @request("GET", "clock/date")
71  @response("%s")
72  def getDateString(self):
73  return self.__getDateString__()
74 
75  @request("POST", "clock/date/%(value)s")
76  @response("%s")
77  def setDateString(self, value):
78  year, month, day = self.String2DateValues(value)
79  newDate = date(year, month, day)
80  self.setDate(newDate)
81  return self.getDateString()
82 
83  @request("GET", "clock/time")
84  @response("%s")
85  def getTimeString(self):
86  return self.__getTimeString__()
87 
88  @request("POST", "clock/time/%(value)s")
89  @response("%s")
90  def setTimeString(self, value):
91  hour, minute, second = self.String2TimeValues(value)
92  newTime = time(hour, minute, second)
93  self.setTime(newTime)
94  return self.getTimeString()
95 
96  @request("GET", "clock/second")
97  @response("%d")
98  def getSec(self):
99  return self.__getSec__()
100 
101  @request("GET", "clock/minute")
102  @response("%d")
103  def getMin(self):
104  return self.__getMin__()
105 
106  @request("GET", "clock/hour")
107  @response("%d")
108  def getHrs(self):
109  return self.__getHrs__()
110 
111  @request("GET", "clock/dow")
112  @response("%d")
113  def getDow(self):
114  return self.__getDow__()
115 
116  @request("POST", "clock/dow/%(value)d")
117  @response("%d")
118  def setDow(self, value):
119  self.checkDow(value)
120  self.__setDow__(value)
121  return self.getDow()
122 
123  @request("GET", "clock/day")
124  @response("%d")
125  def getDay(self):
126  return self.__getDay__()
127 
128  @request("GET", "clock/month")
129  @response("%d")
130  def getMon(self):
131  return self.__getMon__()
132 
133  @request("GET", "clock/year")
134  @response("%d")
135  def getYrs(self):
136  return self.__getYrs__()
137 
138 
139 #---------- Clock abstraction NON-REST implementation ----------
140 
141  def getDateTime(self):
142  return self.__getDateTime__()
143 
144  def setDateTime(self, aDatetime):
145  return self.__setDateTime__(aDatetime)
146 
147  def getDate(self):
148  return self.__getDate__()
149 
150  def setDate(self, aDate):
151  return self.__setDate__(aDate)
152 
153  def getTime(self):
154  return self.__getTime__()
155 
156  def setTime(self, aTime):
157  return self.__setTime__(aTime)
158 
159 
160 #---------- Clock abstraction contracts ----------
161 
162  def __getSec__(self):
163  raise NotImplementedError
164 
165  def __setSec__(self, value):
166  raise NotImplementedError
167 
168  def __getMin__(self):
169  raise NotImplementedError
170 
171  def __setMin__(self, value):
172  raise NotImplementedError
173 
174  def __getHrs__(self):
175  raise NotImplementedError
176 
177  def __setHrs__(self, value):
178  raise NotImplementedError
179 
180  def __getDay__(self):
181  raise NotImplementedError
182 
183  def __setDay__(self, value):
184  raise NotImplementedError
185 
186  def __getMon__(self):
187  raise NotImplementedError
188 
189  def __setMon__(self, value):
190  raise NotImplementedError
191 
192  def __getYrs__(self):
193  raise NotImplementedError
194 
195  def __setYrs__(self, value):
196  raise NotImplementedError
197 
198  def __getDow__(self):
199  raise NotImplementedError
200 
201  def __setDow__(self, value):
202  raise NotImplementedError
203 
204 
205 #---------- Clock default implementations ----------
206 # Rely on reading and writing the base digits, all or some may be reimplemented
207 # in subclasses for performance improvements by e.g. sequential register access.
208 # If all methods here that have the __set prefix are reimplemented without using
209 # any of the __set digit primitives (e.g. __setYrs__()), then these __set methods
210 # are not mandatory to be reimplemented in order to avoid the NotImplementedError
211 # as they will never be called (but __setDow__() IS still needed).
212 
213  def __getDateTime__(self):
214  return datetime(self.__getYrs__(),
215  self.__getMon__(),
216  self.__getDay__(),
217  self.__getHrs__(),
218  self.__getMin__(),
219  self.__getSec__())
220 
221  def __setDateTime__(self, aDatetime):
222  self.checkYear(aDatetime.year)
223  self.__setYrs__(aDatetime.year)
224  self.__setMon__(aDatetime.month)
225  self.__setDay__(aDatetime.day)
226  self.__setHrs__(aDatetime.hour)
227  self.__setMin__(aDatetime.minute)
228  self.__setSec__(aDatetime.second)
229 
230  def __getDate__(self):
231  return date(self.__getYrs__(),
232  self.__getMon__(),
233  self.__getDay__())
234 
235  def __setDate__(self, aDate):
236  self.checkYear(aDate.year)
237  self.__setYrs__(aDate.year)
238  self.__setMon__(aDate.month)
239  self.__setDay__(aDate.day)
240 
241  def __getTime__(self):
242  return time(self.__getHrs__(),
243  self.__getMin__(),
244  self.__getSec__())
245 
246  def __setTime__(self, aTime):
247  self.__setHrs__(aTime.hour)
248  self.__setMin__(aTime.minute)
249  self.__setSec__(aTime.second)
250 
251 
252 #---------- Clock abstraction helpers ----------
253 
255  theDateTime = self.__getDateTime__()
256  return self.Strings2DateTimeString(
257  self.DateValues2String(theDateTime.year, theDateTime.month, theDateTime.day),
258  self.TimeValues2String(theDateTime.hour, theDateTime.minute, theDateTime.second))
259 
260  def __getDateString__(self):
261  theDate = self.__getDate__()
262  return self.DateValues2String(theDate.year, theDate.month, theDate.day)
263 
264  def __getTimeString__(self):
265  theTime = self.__getTime__()
266  return self.TimeValues2String(theTime.hour, theTime.minute, theTime.second)
267 
268 
269 #---------- BCD Conversions ----------
270 
271  def BcdBits2Int(self, bits):
272  if (bits < 0):
273  raise NotImplementedError
274  elif (bits <= 0xFF):
275  # most used case, make it faster
276  return (
277  (((bits >> 4) & 0x0F) * 10) +
278  (bits & 0x0F)
279  )
280  elif (bits <= 0xFFFFFFFF):
281  # 32 bits, being 8 digits, should be enough ...
282  return (
283  (((bits >> 28) & 0x0F) * 10000000) +
284  (((bits >> 24) & 0x0F) * 1000000) +
285  (((bits >> 20) & 0x0F) * 100000) +
286  (((bits >> 16) & 0x0F) * 10000) +
287  (((bits >> 12) & 0x0F) * 1000) +
288  (((bits >> 8) & 0x0F) * 100) +
289  (((bits >> 4) & 0x0F) * 10) +
290  (bits & 0x0F)
291  )
292  else:
293  raise NotImplementedError
294 
295  def Int2BcdBits(self, value):
296  valueString = "%d" % value
297  bcdBits = 0
298  digits = len(valueString)
299  for i in range(digits):
300  bcdBits += int(valueString[i]) << (4 * (digits - 1 - i))
301  return bcdBits
302 
303 
304 #---------- Value checks ----------
305 
306  def checkYear(self, year):
307  # Most RTC chips handle leap years only correct for 21st century
308  # so limit allowed year value to this range
309  if not year in range(2000,2100):
310  raise ValueError("year [%d] out of range [%d..%d]" % (year, 2000, 2099))
311 
312  def checkDow(self, dow):
313  if not dow in range(1,8):
314  raise ValueError("dow [%d] out of range [%d..%d]" % (dow, 1, 7))
315 
316 
317 #---------- Clock values formatting ----------
318 
319  def DateValues2String(self, year, month, day):
320  #"2014-05-13"
321  return "%04d-%02d-%02d" % (year, month, day)
322 
323  def TimeValues2String(self, hour, minute, second):
324  #"12:34:56"
325  return "%02d:%02d:%02d" % (hour, minute, second)
326 
327  def Strings2DateTimeString(self, dateString, timeString):
328  #"2014-05-13T12:34:56"
329  return "%sT%s" % (dateString, timeString)
330 
331  def String2DateValues(self, string):
332  values = string.split('-')
333  year = toint(values[0])
334  month = toint(values[1])
335  day = toint(values[2])
336  return (year, month, day)
337 
338  def String2TimeValues(self, string):
339  values = string.split(':')
340  hour = toint(values[0])
341  minute = toint(values[1])
342  if len(values) > 2:
343  second = toint(values[2])
344  else:
345  second = 0
346  return (hour, minute, second)
347 
348 
349 #---------- Driver lookup ----------
350 
351 DRIVERS = {}
352 DRIVERS["dsrtc"] = ["DS1307", "DS1337", "DS1338", "DS3231"]
353 DRIVERS["mcprtc"] = ["MCP7940"]
354 DRIVERS["osrtc"] = ["OsClock"]
tuple response
Definition: coap-client.py:9