Leyk lightener application for raspberry pi and piface module.
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

piface_function.py 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. #!/usr/bin/env python
  2. # -*- coding: UTF-8 -*-
  3. try:
  4. import pifacedigitalio
  5. except ImportError:
  6. pifacedigitalio = None
  7. import geo
  8. import logging
  9. import state_machine
  10. import task
  11. import random
  12. import time
  13. logger = logging.getLogger('APP')
  14. geo_position = geo.gps.coordinate(lat=49.976596, lon=9.1481443)
  15. class pi_face(object):
  16. LOG_PREFIX = 'PiFace:'
  17. PF_OUT_NAMES = ['Output 0', # 0
  18. 'Output 1', # 1
  19. 'Output 2', # 2
  20. 'Ploenlein', # 3
  21. 'Bakery', # 4
  22. 'Mill', # 5
  23. 'Reese House', # 6
  24. 'Bake House'] # 7
  25. def __init__(self):
  26. if pifacedigitalio is not None:
  27. pifacedigitalio.init()
  28. pi = pifacedigitalio.PiFaceDigital()
  29. self.__pf_outputs__ = pi.output_pins
  30. self.__pf_output_states__ = 8 * [False]
  31. #
  32. self._reload = 0
  33. self._task_10ms = task.periodic(0.01 - 0.007, self.task_10ms)
  34. #
  35. for i in range(0, 8):
  36. self.set_output(i, self.__pf_output_states__[i])
  37. def set_output(self, index, state, tries=5):
  38. tries = min(max(1, tries), 5)
  39. try_txt = ['1st', '2nd', '3rd', '4th', '5th']
  40. state = state is True
  41. if pifacedigitalio is not None:
  42. cnt = 0
  43. while (cnt < tries) and (self.__pf_outputs__[index].value != state):
  44. self.__pf_outputs__[index].value = 1 * state
  45. time.sleep(0.1)
  46. cnt += 1
  47. if self.__pf_outputs__[index].value != state:
  48. logger.warning('%s Control of output[%d] (%s) after %s try not successfull!', self.LOG_PREFIX, index, self.PF_OUT_NAMES[index], try_txt[cnt - 1])
  49. else:
  50. logger.info('%s Set output "%s" to %s.', self.LOG_PREFIX, self.PF_OUT_NAMES[index], repr(state))
  51. self.__pf_output_states__[index] = self.__pf_outputs__[index].value == 1
  52. else:
  53. logger.info('%s Set virtual output "%s" to %s.', self.LOG_PREFIX, self.PF_OUT_NAMES[index], repr(state))
  54. self.__pf_output_states__[index] = state
  55. def get_output(self, index):
  56. return self.__pf_output_states__[index]
  57. def get_ploenlein(self):
  58. return self.get_output(3)
  59. def set_bakery(self, state):
  60. self.set_output(4, state)
  61. def get_bakery(self):
  62. return self.get_output(4)
  63. def set_mill(self, state):
  64. self.set_output(5, state)
  65. def get_mill(self):
  66. return self.get_output(5)
  67. def set_ploenlein(self, state):
  68. self.set_output(3, state)
  69. def set_reese_house(self, state):
  70. self.set_output(6, state)
  71. def get_reese_house(self):
  72. return self.get_output(6)
  73. def set_bake_house(self, state):
  74. if state is True:
  75. self.set_output(7, state)
  76. self._task_10ms.run()
  77. elif state is False:
  78. self._task_10ms.stop()
  79. self.join()
  80. time.sleep(0.1)
  81. self.set_output(7, state)
  82. def get_bake_house(self):
  83. return not self._task_10ms._stopped
  84. def task_10ms(self, task_inst):
  85. if self._reload <= 0:
  86. if pifacedigitalio is not None:
  87. self.__pf_outputs__[7].turn_on()
  88. self._reload = random.choice(2 * [0] + 20 * [1] + 1 * [2])
  89. else:
  90. if pifacedigitalio is not None:
  91. self.__pf_outputs__[7].turn_off()
  92. self._reload -= 1
  93. def join(self):
  94. self._task_10ms.join()
  95. def stop(self):
  96. self._task_10ms.stop()
  97. self._task_10ms.join()
  98. for i in range(0, 8):
  99. self.control_output(i, False)
  100. pifacedigitalio.deinit()
  101. def __del__(self):
  102. self.stop()
  103. class state_machine_mode(state_machine.state_machine):
  104. LOG_PREFIX = 'LeykMode:'
  105. STATE_AUTOMATIC = 'automatic'
  106. STATE_MANUAL = 'manual'
  107. CONDITION_EXTERNAL_TRIGGER = 'external_trigger'
  108. TRANSITIONS = {
  109. STATE_AUTOMATIC: (
  110. (CONDITION_EXTERNAL_TRIGGER, 1, STATE_MANUAL),
  111. ),
  112. STATE_MANUAL: (
  113. (CONDITION_EXTERNAL_TRIGGER, 1, STATE_AUTOMATIC),
  114. ),
  115. }
  116. def __init__(self, **kwargs):
  117. state_machine.state_machine.__init__(self, self.STATE_AUTOMATIC, logging.INFO)
  118. self.__reset_triggers__()
  119. def __reset_triggers__(self):
  120. self.__to_automatic__ = False
  121. self.__to_manual__ = False
  122. def external_trigger(self):
  123. rv = False
  124. if self.this_state() == self.STATE_AUTOMATIC:
  125. rv = self.__to_manual__
  126. elif self.this_state() == self.STATE_MANUAL:
  127. rv = self.__to_automatic__
  128. self.__reset_triggers__()
  129. return rv
  130. def trigger_to_maual(self):
  131. if self.this_state() == self.STATE_AUTOMATIC:
  132. self.__to_manual__ = True
  133. return True
  134. return False
  135. def trigger_to_automatic(self):
  136. if self.this_state() == self.STATE_MANUAL:
  137. self.__to_automatic__ = True
  138. return True
  139. return False
  140. class state_machine_day_state(state_machine.state_machine):
  141. LOG_PREFIX = 'LeykState:'
  142. STATE_IDLE = 'idle'
  143. STATE_WAKE = 'wake'
  144. STATE_SUNRISE = 'sunrise'
  145. STATE_SUNSET = 'sunset'
  146. STATE_SLEEP = 'sleep'
  147. CONDITION_WAKE = 'condition_wake'
  148. CONDITION_SUNRISE = 'condition_sunrise'
  149. CONDITION_SUNSET = 'condition_sunset'
  150. CONDITION_SLEEP = 'condition_sleep'
  151. CONDITION_IDLE = 'condition_idle'
  152. TRANSITIONS = {
  153. STATE_IDLE: (
  154. (CONDITION_WAKE, 1, STATE_WAKE),
  155. (CONDITION_SUNRISE, 1, STATE_SUNRISE),
  156. (CONDITION_SUNSET, 1, STATE_SUNSET),
  157. (CONDITION_SLEEP, 1, STATE_SLEEP),
  158. ),
  159. STATE_WAKE: (
  160. (CONDITION_SUNRISE, 1, STATE_SUNRISE),
  161. (CONDITION_IDLE, 1, STATE_IDLE),
  162. ),
  163. STATE_SUNRISE: (
  164. (CONDITION_SUNSET, 1, STATE_SUNSET),
  165. (CONDITION_IDLE, 1, STATE_IDLE),
  166. ),
  167. STATE_SUNSET: (
  168. (CONDITION_SLEEP, 1, STATE_SLEEP),
  169. (CONDITION_IDLE, 1, STATE_IDLE),
  170. ),
  171. STATE_SLEEP: (
  172. (CONDITION_WAKE, 1, STATE_WAKE),
  173. (CONDITION_IDLE, 1, STATE_IDLE),
  174. ),
  175. }
  176. def __init__(self, **kwargs):
  177. state_machine.state_machine.__init__(self, self.STATE_IDLE, logging.INFO, **kwargs)
  178. def __current_state_calc__(self):
  179. def wake_time():
  180. tm = time.localtime()
  181. tm = list(tm)
  182. tm[3] = 6 # tm_hour
  183. tm[4] = 0 # tm_min
  184. tm[5] = 0 # tm_sec=0
  185. return time.mktime(time.struct_time(tm))
  186. def sunrise_time():
  187. return time.mktime(geo.sun.sunrise(geo_position)) + 30 * 60
  188. def sunset_time():
  189. return time.mktime(geo.sun.sunset(geo_position)) - 30 * 60
  190. def sleep_time():
  191. tm = time.localtime()
  192. tm = list(tm)
  193. tm[3] = 21 # tm_hour
  194. tm[4] = 15 # tm_min
  195. tm[5] = 0 # tm_sec=0
  196. return time.mktime(time.struct_time(tm))
  197. now = time.mktime(time.localtime())
  198. if now > sleep_time():
  199. return self.STATE_SLEEP
  200. elif now > sunset_time():
  201. return self.STATE_SUNSET
  202. elif now > sunrise_time():
  203. return self.STATE_SUNRISE
  204. elif now > wake_time():
  205. return self.STATE_WAKE
  206. else:
  207. return self.STATE_IDLE
  208. def condition_wake(self):
  209. if self.condition_idle():
  210. return False
  211. return self.__current_state_calc__() == self.STATE_WAKE
  212. def condition_sunrise(self):
  213. if self.condition_idle():
  214. return False
  215. return self.__current_state_calc__() == self.STATE_SUNRISE
  216. def condition_sunset(self):
  217. if self.condition_idle():
  218. return False
  219. return self.__current_state_calc__() == self.STATE_SUNSET
  220. def condition_sleep(self):
  221. if self.condition_idle():
  222. return False
  223. return self.__current_state_calc__() == self.STATE_SLEEP
  224. def condition_idle(self):
  225. return not self.sm_mode.this_state_is(self.sm_mode.STATE_AUTOMATIC)
  226. def report_sunset_sunrise(self):
  227. state_machine.logger.debug('Sunrise: %s - Sunset: %s;', time.strftime("%H:%M", geo.sun.sunrise(geo_position)), time.strftime("%H:%M", geo.sun.sunset(geo_position))
  228. )
  229. class leyk(object):
  230. LOG_PREFIX = 'Leyk:'
  231. def __init__(self):
  232. self.sm_mode = state_machine_mode()
  233. self.sm_day_state = state_machine_day_state(sm_mode=self.sm_mode)
  234. self.__pf__ = pi_face()
  235. self._queue = task.threaded_queue()
  236. self._queue.run()
  237. self._task_1s = task.periodic(1, self.task_1s)
  238. self._task_1s.run()
  239. self.sm_mode.register_state_change_callback(self.sm_mode.STATE_MANUAL, None, self._queue.clean_queue)
  240. self.sm_day_state.register_state_change_callback(None, None, self.clean_queue)
  241. self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_SLEEP, None, self.sm_day_state.report_sunset_sunrise)
  242. self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_SLEEP, None, self.fill_sleep_queue)
  243. self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_SUNRISE, None, self.sm_day_state.report_sunset_sunrise)
  244. self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_SUNRISE, None, self.fill_sunrise_queue)
  245. self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_SUNSET, None, self.sm_day_state.report_sunset_sunrise)
  246. self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_SUNSET, None, self.fill_sunset_queue)
  247. self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_WAKE, None, self.sm_day_state.report_sunset_sunrise)
  248. self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_WAKE, None, self.fill_wake_queue)
  249. def set_mode(self, mode):
  250. if mode == self.sm_mode.STATE_AUTOMATIC:
  251. return self.sm_mode.trigger_to_automatic()
  252. elif mode == self.sm_mode.STATE_MANUAL:
  253. return self.sm_mode.trigger_to_maual()
  254. else:
  255. return False
  256. def get_mode(self):
  257. return self.sm_mode.this_state()
  258. def get_state(self):
  259. return self.sm_day_state.this_state()
  260. def __queue_wrapper__(self, queue_inst, function, *args, **kwargs):
  261. function(*args, **kwargs)
  262. def set_ploenlein(self, state):
  263. if self.sm_mode.this_state_is(self.sm_mode.STATE_MANUAL):
  264. self.__pf__.set_ploenlein(state)
  265. return True
  266. else:
  267. return False
  268. def get_ploenlein(self):
  269. return self.__pf__.get_ploenlein()
  270. def set_bakery(self, state):
  271. if self.sm_mode.this_state_is(self.sm_mode.STATE_MANUAL):
  272. self.__pf__.set_bakery(state)
  273. return True
  274. else:
  275. return False
  276. def get_bakery(self):
  277. return self.__pf__.get_bakery()
  278. def set_bake_house(self, state):
  279. if self.sm_mode.this_state_is(self.sm_mode.STATE_MANUAL):
  280. self.__pf__.set_bake_house(state)
  281. return True
  282. else:
  283. return False
  284. def get_bake_house(self):
  285. return self.__pf__.get_bake_house()
  286. def set_mill(self, state):
  287. if self.sm_mode.this_state_is(self.sm_mode.STATE_MANUAL):
  288. self.__pf__.set_mill(state)
  289. return True
  290. else:
  291. return False
  292. def get_mill(self):
  293. return self.__pf__.get_mill()
  294. def set_reese_house(self, state):
  295. if self.sm_mode.this_state_is(self.sm_mode.STATE_MANUAL):
  296. self.__pf__.set_reese_house(state)
  297. return True
  298. else:
  299. return False
  300. def get_reese_house(self):
  301. return self.__pf__.get_reese_house()
  302. def wake_time(self):
  303. tm = time.localtime()
  304. tm = list(tm)
  305. tm[3] = 6 # tm_hour
  306. tm[4] = 0 # tm_min
  307. tm[5] = 0 # tm_sec=0
  308. return time.mktime(time.struct_time(tm))
  309. def sunrise_time(self):
  310. return time.mktime(geo.sun.sunrise(geo_position)) + 30 * 60
  311. def sunset_time(self):
  312. return time.mktime(geo.sun.sunset(geo_position)) - 30 * 60
  313. def sleep_time(self):
  314. tm = time.localtime()
  315. tm = list(tm)
  316. tm[3] = 21 # tm_hour
  317. tm[4] = 15 # tm_min
  318. tm[5] = 0 # tm_sec=0
  319. return time.mktime(time.struct_time(tm))
  320. def identify_current_state(self):
  321. now = time.mktime(time.localtime())
  322. if now > self.sleep_time():
  323. return self.ST_SLEEP
  324. elif now > self.sunset_time():
  325. return self.ST_SUNSET
  326. elif now > self.sunrise_time():
  327. return self.ST_SUNRISE
  328. elif now > self.wake_time():
  329. return self.ST_WAKE
  330. else:
  331. return self.ST_SLEEP
  332. def wait(self, queue_inst, delay):
  333. if delay > 0:
  334. logger.debug('%s Wait for %d seconds initiated. %d elements left in queue.', self.LOG_PREFIX, delay, queue_inst.qsize())
  335. cnt = 0
  336. while cnt < delay * 5:
  337. if queue_inst.qsize() == 0:
  338. logger.debug('%s Quit wait for %d seconds. %d elements left in queue.', self.LOG_PREFIX, delay, queue_inst.qsize())
  339. break
  340. time.sleep(0.2)
  341. cnt += 1
  342. def clean_queue(self):
  343. if self._queue.qsize() > 0:
  344. logger.info('Cleaning up remaining %d elements from queue', self._queue.qsize())
  345. self._queue.clean_queue()
  346. def fill_wake_queue(self):
  347. no_delay = self.sm_day_state.previous_state_was(self.sm_day_state.STATE_IDLE)
  348. # WAKE
  349. self._queue.enqueue(1, self.__queue_wrapper__, self.__pf__.set_bakery, True)
  350. self._queue.enqueue(2, self.wait, 0 if no_delay else 5 * 60)
  351. self._queue.enqueue(3, self.__queue_wrapper__, self.__pf__.set_bake_house, True)
  352. self._queue.enqueue(4, self.wait, 0 if no_delay else 10 * 60)
  353. self._queue.enqueue(5, self.__queue_wrapper__, self.__pf__.set_reese_house, True)
  354. self._queue.enqueue(6, self.wait, 0 if no_delay else 7 * 60)
  355. self._queue.enqueue(7, self.__queue_wrapper__, self.__pf__.set_ploenlein, True)
  356. self._queue.enqueue(8, self.wait, 0 if no_delay else 8 * 60)
  357. self._queue.enqueue(9, self.__queue_wrapper__, self.__pf__.set_mill, True)
  358. def fill_sunrise_queue(self):
  359. no_delay = self.sm_day_state.previous_state_was(self.sm_day_state.STATE_IDLE)
  360. # SUNRISE
  361. self._queue.enqueue(1, self.__queue_wrapper__, self.__pf__.set_bake_house, True)
  362. self._queue.enqueue(2, self.__queue_wrapper__, self.__pf__.set_ploenlein, False)
  363. self._queue.enqueue(3, self.wait, 0 if no_delay else 5 * 60)
  364. self._queue.enqueue(4, self.__queue_wrapper__, self.__pf__.set_mill, False)
  365. self._queue.enqueue(5, self.wait, 0 if no_delay else 8 * 60)
  366. self._queue.enqueue(6, self.__queue_wrapper__, self.__pf__.set_bakery, False)
  367. self._queue.enqueue(7, self.wait, 0 if no_delay else 12 * 60)
  368. self._queue.enqueue(8, self.__queue_wrapper__, self.__pf__.set_reese_house, False)
  369. def fill_sunset_queue(self):
  370. no_delay = self.sm_day_state.previous_state_was(self.sm_day_state.STATE_IDLE)
  371. # SUNSET
  372. self._queue.enqueue(1, self.__queue_wrapper__, self.__pf__.set_bake_house, True)
  373. self._queue.enqueue(2, self.__queue_wrapper__, self.__pf__.set_bakery, True)
  374. self._queue.enqueue(3, self.wait, 0 if no_delay else 10 * 60)
  375. self._queue.enqueue(4, self.__queue_wrapper__, self.__pf__.set_reese_house, True)
  376. self._queue.enqueue(5, self.wait, 0 if no_delay else 8 * 60)
  377. self._queue.enqueue(6, self.__queue_wrapper__, self.__pf__.set_mill, True)
  378. self._queue.enqueue(7, self.wait, 0 if no_delay else 7 * 60)
  379. self._queue.enqueue(8, self.__queue_wrapper__, self.__pf__.set_ploenlein, True)
  380. def fill_sleep_queue(self):
  381. no_delay = self.sm_day_state.previous_state_was(self.sm_day_state.STATE_IDLE)
  382. # SLEEP
  383. self._queue.enqueue(1, self.__queue_wrapper__, self.__pf__.set_bake_house, False)
  384. self._queue.enqueue(2, self.wait, 0 if no_delay else 5 * 60)
  385. self._queue.enqueue(3, self.__queue_wrapper__, self.__pf__.set_bakery, False)
  386. self._queue.enqueue(4, self.wait, 0 if no_delay else 9 * 60)
  387. self._queue.enqueue(5, self.__queue_wrapper__, self.__pf__.set_ploenlein, False)
  388. self._queue.enqueue(6, self.wait, 0 if no_delay else 9 * 60)
  389. self._queue.enqueue(7, self.__queue_wrapper__, self.__pf__.set_mill, False)
  390. self._queue.enqueue(8, self.wait, 0 if no_delay else 6 * 60)
  391. self._queue.enqueue(9, self.__queue_wrapper__, self.__pf__.set_reese_house, False)
  392. def task_1s(self, task_inst):
  393. self.sm_mode.work()
  394. self.sm_day_state.work()
  395. def join(self):
  396. self._task_1s.join()
  397. self._queue.join()
  398. def stop(self):
  399. self._task_1s.stop()
  400. self._queue.stop()
  401. def __del__(self):
  402. self.stop()