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

453 行
15KB

  1. import pprint
  2. import sqlite3
  3. from flask import make_response, g
  4. import datetime
  5. from datetime import datetime
  6. from flask.views import MethodView
  7. from flask_classy import FlaskView, route
  8. from flask_sqlalchemy import SQLAlchemy
  9. from time import localtime, strftime
  10. from functools import wraps, update_wrapper
  11. from props import *
  12. from hardware import *
  13. import time
  14. import uuid
  15. class NotificationAPI(object):
  16. pass
  17. class ActorAPI(object):
  18. def init_actors(self):
  19. self.app.logger.info("Init Actors")
  20. t = self.cache.get("actor_types")
  21. for key, value in t.iteritems():
  22. value.get("class").init_global()
  23. for key in self.cache.get("actors"):
  24. self.init_actor(key)
  25. def init_actor(self, id):
  26. try:
  27. value = self.cache.get("actors").get(int(id))
  28. cfg = value.config.copy()
  29. cfg.update(dict(api=self, id=id, name=value.name))
  30. cfg.update(dict(api=self, id=id, name=value.name))
  31. clazz = self.cache.get("actor_types").get(value.type).get("class")
  32. value.instance = clazz(**cfg)
  33. value.instance.init()
  34. value.state = 0
  35. value.power = 100
  36. except Exception as e:
  37. self.notify("Actor Error", "Failed to setup actor %s. Please check the configuraiton" % value.name,
  38. type="danger", timeout=None)
  39. self.app.logger.error("Initializing of Actor %s failed" % id)
  40. def switch_actor_on(self, id, power=None):
  41. actor = self.cache.get("actors").get(id)
  42. if actor.state == 1:
  43. return
  44. actor.instance.on(power=power)
  45. actor.state = 1
  46. if power is not None:
  47. actor.power = power
  48. self.emit("SWITCH_ACTOR", actor)
  49. def actor_power(self, id, power=100):
  50. actor = self.cache.get("actors").get(id)
  51. actor.instance.set_power(power=power)
  52. actor.power = power
  53. self.emit("SWITCH_ACTOR", actor)
  54. def switch_actor_off(self, id):
  55. actor = self.cache.get("actors").get(id)
  56. if actor.state == 0:
  57. return
  58. actor.instance.off()
  59. actor.state = 0
  60. self.emit("SWITCH_ACTOR", actor)
  61. class SensorAPI(object):
  62. def init_sensors(self):
  63. '''
  64. Initialize all sensors
  65. :return:
  66. '''
  67. self.app.logger.info("Init Sensors")
  68. t = self.cache.get("sensor_types")
  69. for key, value in t.iteritems():
  70. value.get("class").init_global()
  71. for key in self.cache.get("sensors"):
  72. self.init_sensor(key)
  73. def stop_sensor(self, id):
  74. try:
  75. self.cache.get("sensors").get(id).instance.stop()
  76. except Exception as e:
  77. self.app.logger.info("Stop Sensor Error")
  78. pass
  79. def init_sensor(self, id):
  80. '''
  81. initialize sensor by id
  82. :param id:
  83. :return:
  84. '''
  85. def start_active_sensor(instance):
  86. '''
  87. start active sensors as background job
  88. :param instance:
  89. :return:
  90. '''
  91. instance.execute()
  92. try:
  93. if id in self.cache.get("sensor_instances"):
  94. self.cache.get("sensor_instances").get(id).stop()
  95. value = self.cache.get("sensors").get(id)
  96. cfg = value.config.copy()
  97. cfg.update(dict(api=self, id=id, name=value.name))
  98. clazz = self.cache.get("sensor_types").get(value.type).get("class")
  99. value.instance = clazz(**cfg)
  100. value.instance.init()
  101. if isinstance(value.instance, SensorPassive):
  102. # Passive Sensors
  103. value.mode = "P"
  104. else:
  105. # Active Sensors
  106. value.mode = "A"
  107. t = self.socketio.start_background_task(target=start_active_sensor, instance=value.instance)
  108. except Exception as e:
  109. self.notify("Sensor Error", "Failed to setup Sensor %s. Please check the configuraiton" % value.name, type="danger", timeout=None)
  110. self.app.logger.error("Initializing of Sensor %s failed" % id)
  111. def receive_sensor_value(self, id, value):
  112. self.emit("SENSOR_UPDATE", self.cache.get("sensors")[id])
  113. self.save_to_file(id, value)
  114. def save_to_file(self, id, value, prefix="sensor"):
  115. filename = "./logs/%s_%s.log" % (prefix, str(id))
  116. formatted_time = strftime("%Y-%m-%d %H:%M:%S", localtime())
  117. msg = str(formatted_time) + "," +str(value) + "\n"
  118. with open(filename, "a") as file:
  119. file.write(msg)
  120. def log_action(self, text):
  121. filename = "./logs/action.log"
  122. formatted_time = strftime("%Y-%m-%d %H:%M:%S", localtime())
  123. with open(filename, "a") as file:
  124. file.write("%s,%s\n" % (formatted_time, text))
  125. def shutdown_sensor(self, id):
  126. self.cache.get("sensors")[id].stop()
  127. def get_sensor_value(self, id):
  128. try:
  129. id = int(id)
  130. return int(self.cache.get("sensors")[id].instance.last_value)
  131. except Exception as e:
  132. return None
  133. class CacheAPI(object):
  134. def get_sensor(self, id):
  135. try:
  136. return self.cache["sensors"][id]
  137. except:
  138. return None
  139. def get_actor(self, id):
  140. try:
  141. return self.cache["actors"][id]
  142. except:
  143. return None
  144. class CraftBeerPi(ActorAPI, SensorAPI):
  145. cache = {
  146. "init": {},
  147. "config": {},
  148. "actor_types": {},
  149. "sensor_types": {},
  150. "sensors": {},
  151. "sensor_instances": {},
  152. "init": [],
  153. "background":[],
  154. "step_types": {},
  155. "controller_types": {},
  156. "messages": [],
  157. "plugins": {},
  158. "fermentation_controller_types": {},
  159. "fermenter_task": {}
  160. }
  161. buzzer = None
  162. eventbus = {}
  163. # constructor
  164. def __init__(self, app, socketio):
  165. self.app = app
  166. self.socketio = socketio
  167. def emit(self, key, data):
  168. self.socketio.emit(key, data, namespace='/brew')
  169. def notify(self, headline, message, type="success", timeout=5000):
  170. self.beep()
  171. msg = {"id": str(uuid.uuid1()), "type": type, "headline": headline, "message": message, "timeout": timeout}
  172. if timeout is None:
  173. self.cache["messages"].append(msg)
  174. self.emit("NOTIFY", msg)
  175. def beep(self):
  176. if self.buzzer is not None:
  177. self.buzzer.beep()
  178. def add_cache_callback(self, key, method):
  179. method.callback = True
  180. self.cache[key] = method
  181. def get_config_parameter(self, key, default):
  182. cfg = self.cache.get("config").get(key)
  183. if cfg is None:
  184. return default
  185. else:
  186. return cfg.value
  187. def clear_cache(self, key, is_array=False):
  188. if is_array:
  189. self.cache[key] = []
  190. else:
  191. self.cache[key] = {}
  192. # helper method for parsing props
  193. def __parseProps(self, key, cls):
  194. name = cls.__name__
  195. self.cache[key][name] = {"name": name, "class": cls, "properties": []}
  196. tmpObj = cls()
  197. members = [attr for attr in dir(tmpObj) if not callable(getattr(tmpObj, attr)) and not attr.startswith("__")]
  198. for m in members:
  199. if isinstance(tmpObj.__getattribute__(m), Property.Number):
  200. t = tmpObj.__getattribute__(m)
  201. self.cache[key][name]["properties"].append(
  202. {"name": m, "label": t.label, "type": "number", "configurable": t.configurable})
  203. elif isinstance(tmpObj.__getattribute__(m), Property.Text):
  204. t = tmpObj.__getattribute__(m)
  205. self.cache[key][name]["properties"].append(
  206. {"name": m, "label": t.label, "type": "text", "configurable": t.configurable})
  207. elif isinstance(tmpObj.__getattribute__(m), Property.Select):
  208. t = tmpObj.__getattribute__(m)
  209. self.cache[key][name]["properties"].append(
  210. {"name": m, "label": t.label, "type": "select", "configurable": True, "options": t.options})
  211. return cls
  212. def actor(self, cls):
  213. return self.__parseProps("actor_types", cls)
  214. def sensor(self, cls):
  215. return self.__parseProps("sensor_types", cls)
  216. def controller(self, cls):
  217. return self.__parseProps("controller_types", cls)
  218. def fermentation_controller(self, cls):
  219. return self.__parseProps("fermentation_controller_types", cls)
  220. def get_controller(self, name):
  221. return self.cache["controller_types"].get(name)
  222. def get_fermentation_controller(self, name):
  223. return self.cache["fermentation_controller_types"].get(name)
  224. # Step action
  225. def action(self,label):
  226. def real_decorator(func):
  227. func.action = True
  228. func.label = label
  229. return func
  230. return real_decorator
  231. # step decorator
  232. def step(self, cls):
  233. key = "step_types"
  234. name = cls.__name__
  235. self.cache[key][name] = {"name": name, "class": cls, "properties": [], "actions": []}
  236. tmpObj = cls()
  237. members = [attr for attr in dir(tmpObj) if not callable(getattr(tmpObj, attr)) and not attr.startswith("__")]
  238. for m in members:
  239. if isinstance(tmpObj.__getattribute__(m), StepProperty.Number):
  240. t = tmpObj.__getattribute__(m)
  241. self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "number", "configurable": t.configurable})
  242. elif isinstance(tmpObj.__getattribute__(m), StepProperty.Text):
  243. t = tmpObj.__getattribute__(m)
  244. self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "text", "configurable": t.configurable})
  245. elif isinstance(tmpObj.__getattribute__(m), StepProperty.Select):
  246. t = tmpObj.__getattribute__(m)
  247. self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "select", "options": t.options})
  248. elif isinstance(tmpObj.__getattribute__(m), StepProperty.Actor):
  249. t = tmpObj.__getattribute__(m)
  250. self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "actor", "configurable": t.configurable})
  251. elif isinstance(tmpObj.__getattribute__(m), StepProperty.Sensor):
  252. t = tmpObj.__getattribute__(m)
  253. self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "sensor", "configurable": t.configurable})
  254. elif isinstance(tmpObj.__getattribute__(m), StepProperty.Kettle):
  255. t = tmpObj.__getattribute__(m)
  256. self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "kettle", "configurable": t.configurable})
  257. for name, method in cls.__dict__.iteritems():
  258. if hasattr(method, "action"):
  259. label = method.__getattribute__("label")
  260. self.cache[key][cls.__name__]["actions"].append({"method": name, "label": label})
  261. return cls
  262. # Event Bus
  263. def event(self, name, async=False):
  264. def real_decorator(function):
  265. if self.eventbus.get(name) is None:
  266. self.eventbus[name] = []
  267. self.eventbus[name].append({"function": function, "async": async})
  268. def wrapper(*args, **kwargs):
  269. return function(*args, **kwargs)
  270. return wrapper
  271. return real_decorator
  272. def emit_message(self, message):
  273. self.emit_event(name="MESSAGE", message=message)
  274. def emit_event(self, name, **kwargs):
  275. for i in self.eventbus.get(name, []):
  276. if i["async"] is False:
  277. i["function"](**kwargs)
  278. else:
  279. t = self.socketio.start_background_task(target=i["function"], **kwargs)
  280. # initializer decorator
  281. def initalizer(self, order=0):
  282. def real_decorator(function):
  283. self.cache["init"].append({"function": function, "order": order})
  284. def wrapper(*args, **kwargs):
  285. return function(*args, **kwargs)
  286. return wrapper
  287. return real_decorator
  288. def try_catch(self, errorResult="ERROR"):
  289. def real_decorator(function):
  290. def wrapper(*args, **kwargs):
  291. try:
  292. return function(*args, **kwargs)
  293. except:
  294. self.app.logger.error("Exception in function %s. Return default %s" % (function.__name__, errorResult))
  295. return errorResult
  296. return wrapper
  297. return real_decorator
  298. def nocache(self, view):
  299. @wraps(view)
  300. def no_cache(*args, **kwargs):
  301. response = make_response(view(*args, **kwargs))
  302. response.headers['Last-Modified'] = datetime.now()
  303. response.headers[
  304. 'Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0'
  305. response.headers['Pragma'] = 'no-cache'
  306. response.headers['Expires'] = '-1'
  307. return response
  308. return update_wrapper(no_cache, view)
  309. def init_kettle(self, id):
  310. try:
  311. value = self.cache.get("kettle").get(id)
  312. value["state"] = False
  313. except:
  314. self.notify("Kettle Setup Faild", "Please check %s configuration" % value.name, type="danger", timeout=None)
  315. self.app.logger.error("Initializing of Kettle %s failed" % id)
  316. def run_init(self):
  317. '''
  318. call all initialziers after startup
  319. :return:
  320. '''
  321. self.app.logger.info("Invoke Init")
  322. self.cache["init"] = sorted(self.cache["init"], key=lambda k: k['order'])
  323. for i in self.cache.get("init"):
  324. self.app.logger.info("-> %s " % i.get("function").__name__)
  325. i.get("function")(self)
  326. def backgroundtask(self, key, interval, config_parameter=None):
  327. '''
  328. Background Task Decorator
  329. :param key:
  330. :param interval:
  331. :param config_parameter:
  332. :return:
  333. '''
  334. def real_decorator(function):
  335. self.cache["background"].append({"function": function, "key": key, "interval": interval, "config_parameter": config_parameter})
  336. def wrapper(*args, **kwargs):
  337. return function(*args, **kwargs)
  338. return wrapper
  339. return real_decorator
  340. def run_background_processes(self):
  341. '''
  342. call all background task after startup
  343. :return:
  344. '''
  345. self.app.logger.info("Start Background")
  346. def job(interval, method):
  347. while True:
  348. try:
  349. method()
  350. except Exception as e:
  351. self.app.logger.error("Exception" + method.__name__ + ": " + str(e))
  352. self.socketio.sleep(interval)
  353. for value in self.cache.get("background"):
  354. t = self.socketio.start_background_task(target=job, interval=value.get("interval"), method=value.get("function"))