選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

320 行
11KB

  1. import time
  2. from flask import request
  3. from flask_classy import route
  4. from modules import DBModel, cbpi, get_db
  5. from modules.core.baseview import BaseView
  6. class Fermenter(DBModel):
  7. __fields__ = ["name", "brewname", "sensor", "sensor2", "sensor3", "heater", "cooler", "logic", "config", "target_temp"]
  8. __table_name__ = "fermenter"
  9. __json_fields__ = ["config"]
  10. class FermenterStep(DBModel):
  11. __fields__ = ["name", "days", "hours", "minutes", "temp", "direction", "order", "state", "start", "end", "timer_start", "fermenter_id"]
  12. __table_name__ = "fermenter_step"
  13. @classmethod
  14. def get_by_fermenter_id(cls, id):
  15. cur = get_db().cursor()
  16. cur.execute("SELECT * FROM %s WHERE fermenter_id = ?" % cls.__table_name__,(id,))
  17. result = []
  18. for r in cur.fetchall():
  19. result.append(cls(r))
  20. return result
  21. @classmethod
  22. def get_max_order(cls,id):
  23. cur = get_db().cursor()
  24. cur.execute("SELECT max(fermenter_step.'order') as 'order' FROM %s WHERE fermenter_id = ?" % cls.__table_name__, (id,))
  25. r = cur.fetchone()
  26. return r.get("order")
  27. @classmethod
  28. def update_state(cls, id, state):
  29. cur = get_db().cursor()
  30. cur.execute("UPDATE %s SET state = ? WHERE id =?" % cls.__table_name__, (state, id))
  31. get_db().commit()
  32. @classmethod
  33. def update_timer(cls, id, timer):
  34. cur = get_db().cursor()
  35. cur.execute("UPDATE %s SET timer_start = ? WHERE id =?" % cls.__table_name__, (timer, id))
  36. get_db().commit()
  37. @classmethod
  38. def get_by_state(cls, state):
  39. cur = get_db().cursor()
  40. cur.execute("SELECT * FROM %s WHERE state = ?" % cls.__table_name__, state)
  41. r = cur.fetchone()
  42. if r is not None:
  43. return cls(r)
  44. else:
  45. return None
  46. @classmethod
  47. def reset_all_steps(cls,id):
  48. cur = get_db().cursor()
  49. cur.execute("UPDATE %s SET state = 'I', start = NULL, end = NULL, timer_start = NULL WHERE fermenter_id = ?" % cls.__table_name__, (id,))
  50. get_db().commit()
  51. class FermenterView(BaseView):
  52. model = Fermenter
  53. cache_key = "fermenter"
  54. def _post_post_callback(self, m):
  55. m.state = False
  56. m.steps = []
  57. def _pre_put_callback(self, m):
  58. m.state = False
  59. try:
  60. m.instance.stop()
  61. except:
  62. pass
  63. def _post_put_callback(self, m):
  64. m.state = False
  65. self.reset(int(m.id))
  66. @route('/<int:id>/targettemp/<temp>', methods=['POST'])
  67. def postTargetTemp(self, id, temp):
  68. if temp is None or not temp:
  69. return ('', 500)
  70. id = int(id)
  71. temp = float(temp)
  72. cbpi.cache.get(self.cache_key)[id].target_temp = float(temp)
  73. self.model.update(**self.api.cache.get(self.cache_key)[id].__dict__)
  74. cbpi.emit("UPDATE_FERMENTER_TARGET_TEMP", {"id": id, "target_temp": temp})
  75. return ('', 204)
  76. @route('/<int:id>/brewname', methods=['POST'])
  77. def postBrewName(self, id):
  78. data = request.json
  79. brewname = data.get("brewname")
  80. cbpi.cache.get(self.cache_key)[id].brewname = brewname
  81. self.model.update(**self.api.cache.get(self.cache_key)[id].__dict__)
  82. cbpi.emit("UPDATE_FERMENTER_BREWNAME", {"id": id, "brewname": brewname})
  83. return ('', 204)
  84. @classmethod
  85. def post_init_callback(cls, obj):
  86. obj.steps = FermenterStep.get_by_fermenter_id(obj.id)
  87. obj.state = False
  88. @route('/<int:id>/step', methods=['POST'])
  89. def postStep(self, id):
  90. data = request.json
  91. order_max = FermenterStep.get_max_order(id)
  92. order = order_max + 1 if order_max is not None else 1
  93. data["order"] = order
  94. data["days"] = 0 if data["days"] == "" else data["days"]
  95. data["hours"] = 0 if data["hours"] == "" else data["hours"]
  96. data["minutes"] = 0 if data["minutes"] == "" else data["minutes"]
  97. data["temp"] = 0 if data["temp"] == "" else data["temp"]
  98. data["state"] = "I"
  99. data["name"] = "NO NAME" if data["name"] == "" else data["name"]
  100. f = FermenterStep.insert(**data)
  101. cbpi.cache.get(self.cache_key)[id].steps.append(f)
  102. cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key)[id])
  103. return ('', 204)
  104. @route('/<int:id>/step/<int:stepid>', methods=["PUT"])
  105. def putStep(self, id, stepid):
  106. data = request.json
  107. # Select modal
  108. data["id"] = stepid
  109. data["fermenter_id"] = id
  110. data["days"] = 0 if data["days"] == "" else data["days"]
  111. data["hours"] = 0 if data["hours"] == "" else data["hours"]
  112. data["minutes"] = 0 if data["minutes"] == "" else data["minutes"]
  113. for s in cbpi.cache.get(self.cache_key)[id].steps:
  114. if s.id == stepid:
  115. s.__dict__.update(**data)
  116. FermenterStep.update(**s.__dict__)
  117. break
  118. cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key)[id])
  119. return ('', 204)
  120. @route('/<int:id>/step/<int:stepid>', methods=["DELETE"])
  121. def deleteStep(self, id, stepid):
  122. for idx, s in enumerate(cbpi.cache.get(self.cache_key)[id].steps):
  123. if s.id == stepid:
  124. del cbpi.cache.get(self.cache_key)[id].steps[idx]
  125. FermenterStep.delete(s.id)
  126. break
  127. cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key)[id])
  128. return ('', 204)
  129. @route('/<int:id>/start', methods=['POST'])
  130. def start_fermentation(self, id):
  131. active = None
  132. for idx, s in enumerate(cbpi.cache.get(self.cache_key)[id].steps):
  133. if s.state == 'A':
  134. active = s
  135. break
  136. inactive = None
  137. for idx, s in enumerate(cbpi.cache.get(self.cache_key)[id].steps):
  138. if s.state == 'I':
  139. inactive = s
  140. break
  141. if active is not None:
  142. active.state = 'D'
  143. active.end = time.time()
  144. FermenterStep.update(**active.__dict__)
  145. del cbpi.cache["fermenter_task"][id]
  146. if inactive is not None:
  147. fermenter = self.get_fermenter(inactive.fermenter_id)
  148. current_temp = cbpi.get_sensor_value(int(fermenter.sensor))
  149. inactive.state = 'A'
  150. inactive.start = time.time()
  151. inactive.direction = "C" if current_temp >= inactive.temp else "H"
  152. FermenterStep.update(**inactive.__dict__)
  153. self.postTargetTemp(id, inactive.temp)
  154. cbpi.cache["fermenter_task"][id] = inactive
  155. cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key)[id])
  156. return ('', 204)
  157. @route('/<int:id>/reset', methods=["POST"])
  158. def reset(self, id):
  159. FermenterStep.reset_all_steps(id)
  160. cbpi.cache[self.cache_key][id].steps = FermenterStep.get_by_fermenter_id(id)
  161. if id in cbpi.cache["fermenter_task"]:
  162. del cbpi.cache["fermenter_task"][id]
  163. cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key)[id])
  164. return ('', 204)
  165. @route('/<int:id>/automatic', methods=['POST'])
  166. def toggle(self, id):
  167. fermenter = cbpi.cache.get(self.cache_key)[id]
  168. try:
  169. print((fermenter.state))
  170. if fermenter.state is False:
  171. # Start controller
  172. if fermenter.logic is not None:
  173. cfg = fermenter.config.copy()
  174. cfg.update(
  175. dict(api=cbpi, fermenter_id=fermenter.id, heater=fermenter.heater, sensor=fermenter.sensor))
  176. instance = cbpi.get_fermentation_controller(fermenter.logic).get("class")(**cfg)
  177. instance.init()
  178. fermenter.instance = instance
  179. def run(instance):
  180. instance.run()
  181. fermenter.state = not fermenter.state
  182. cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key).get(id))
  183. t = cbpi.socketio.start_background_task(target=run, instance=instance)
  184. fermenter.state = not fermenter.state
  185. cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key).get(id))
  186. else:
  187. # Stop controller
  188. fermenter.instance.stop()
  189. fermenter.state = not fermenter.state
  190. cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key).get(id))
  191. except Exception as e:
  192. print(e)
  193. cbpi.notify("Toogle Fementer Controller failed", "Pleae check the %s configuration" % fermenter.name,
  194. type="danger", timeout=None)
  195. return ('', 500)
  196. return ('', 204)
  197. def get_fermenter(self, id):
  198. return cbpi.cache["fermenter"].get(id)
  199. def target_temp_reached(self,id, step):
  200. timestamp = time.time()
  201. days = step.days * 24 * 60 * 60
  202. hours = step.hours * 60 * 60
  203. minutes = step.minutes * 60
  204. target_time = days + hours + minutes + timestamp
  205. FermenterStep.update_timer(step.id, target_time)
  206. step.timer_start = target_time
  207. cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key)[id])
  208. def check_step(self):
  209. for key, value in cbpi.cache["fermenter_task"].items():
  210. try:
  211. fermenter = self.get_fermenter(key)
  212. current_temp = current_temp = cbpi.get_sensor_value(int(fermenter.sensor))
  213. if value.timer_start is None:
  214. if value.direction == "H" :
  215. if current_temp >= value.temp:
  216. self.target_temp_reached(key,value)
  217. else:
  218. if current_temp <= value.temp:
  219. self.target_temp_reached(key, value)
  220. else:
  221. if time.time() >= value.timer_start:
  222. self.start_fermentation(key)
  223. else:
  224. pass
  225. except Exception as e:
  226. pass
  227. @cbpi.backgroundtask(key="read_target_temps_fermenter", interval=5)
  228. def read_target_temps(api):
  229. """
  230. background process that reads all passive sensors in interval of 1 second
  231. :return: None
  232. """
  233. result = {}
  234. for key, value in cbpi.cache.get("fermenter").items():
  235. cbpi.save_to_file(key, value.target_temp, prefix="fermenter")
  236. instance = FermenterView()
  237. @cbpi.backgroundtask(key="fermentation_task", interval=1)
  238. def execute_fermentation_step(api):
  239. with cbpi.app.app_context():
  240. instance.check_step()
  241. def init_active_steps():
  242. '''
  243. active_steps = FermenterStep.query.filter_by(state='A')
  244. for a in active_steps:
  245. db.session.expunge(a)
  246. cbpi.cache["fermenter_task"][a.fermenter_id] = a
  247. '''
  248. @cbpi.initalizer(order=1)
  249. def init(cbpi):
  250. FermenterView.register(cbpi.app, route_base='/api/fermenter')
  251. FermenterView.init_cache()