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

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