您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

495 行
17KB

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