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

338 行
11KB

  1. import json
  2. import logging
  3. import os
  4. import sqlite3
  5. import uuid
  6. from datetime import datetime
  7. from functools import wraps, update_wrapper
  8. from importlib import import_module
  9. from time import localtime, strftime
  10. from flask import Flask, redirect, json, g, make_response
  11. from flask_socketio import SocketIO
  12. from baseapi import *
  13. from db import DBModel
  14. from modules.core.basetypes import Sensor, Actor
  15. from modules.database.dbmodel import Kettle
  16. class ComplexEncoder(json.JSONEncoder):
  17. def default(self, obj):
  18. try:
  19. if isinstance(obj, DBModel):
  20. return obj.__dict__
  21. elif isinstance(obj, Actor):
  22. return {"state": obj.value}
  23. elif isinstance(obj, Sensor):
  24. return {"value": obj.value, "unit": obj.unit}
  25. elif hasattr(obj, "callback"):
  26. return obj()
  27. else:
  28. return None
  29. return None
  30. except TypeError as e:
  31. pass
  32. return None
  33. class Addon(object):
  34. def __init__(self, cbpi):
  35. self.step = StepAPI(cbpi)
  36. self.actor = ActorAPI(cbpi)
  37. self.sensor = SensorAPI(cbpi)
  38. self.kettle = KettleAPI(cbpi)
  39. self.fermenter = FermenterAPI(cbpi)
  40. self.core = CoreAPI(cbpi)
  41. def init(self):
  42. self.core.init()
  43. self.step.init()
  44. self.actor.init()
  45. self.sensor.init()
  46. # self.kettle.init()
  47. # self.fermenter.init()
  48. class ActorCore(object):
  49. key = "actor_types"
  50. def __init__(self, cbpi):
  51. self.cbpi = cbpi
  52. self.cbpi.cache["actors"] = {}
  53. self.cbpi.cache[self.key] = {}
  54. def init(self):
  55. for key, value in self.cbpi.cache["actors"].iteritems():
  56. self.init_one(key)
  57. def init_one(self, id):
  58. try:
  59. actor = self.cbpi.cache["actors"][id]
  60. clazz = self.cbpi.cache[self.key].get(actor.type)["class"]
  61. cfg = actor.config.copy()
  62. cfg.update(dict(cbpi=self.cbpi, id=id))
  63. self.cbpi.cache["actors"][id].instance = clazz(**cfg)
  64. self.cbpi.emit("INIT_ACTOR", id=id)
  65. except Exception as e:
  66. print e
  67. self.cbpi._app.logger.error(e)
  68. def stop_one(self, id):
  69. self.cbpi.cache["actors"][id]["instance"].stop()
  70. self.cbpi.emit("STOP_ACTOR", id=id)
  71. def on(self, id, power=100):
  72. try:
  73. actor = self.cbpi.cache["actors"].get(int(id))
  74. actor.instance.on()
  75. actor.state = 1
  76. actor.power = power
  77. self.cbpi.ws_emit("SWITCH_ACTOR", actor)
  78. self.cbpi.emit("SWITCH_ACTOR_ON", id=id, power=power)
  79. return True
  80. except Exception as e:
  81. print e
  82. return False
  83. def off(self, id):
  84. try:
  85. actor = self.cbpi.cache["actors"].get(int(id))
  86. actor.instance.off()
  87. actor.state = 0
  88. self.cbpi.ws_emit("SWITCH_ACTOR", actor)
  89. self.cbpi.emit("SWITCH_ACTOR_OFF", id=id)
  90. return True
  91. except Exception as e:
  92. print e
  93. return False
  94. def power(self, id, power):
  95. try:
  96. actor = self.cbpi.cache["actors"].get(int(id))
  97. actor.instance.power(power)
  98. actor.power = power
  99. self.cbpi.ws_emit("SWITCH_ACTOR", actor)
  100. self.cbpi.emit("SWITCH_ACTOR_POWER_CHANGE", id=id, power=power)
  101. return True
  102. except Exception as e:
  103. print e
  104. return False
  105. def get_state(self, actor_id):
  106. print actor_id
  107. print self.cbpi
  108. class SensorCore(object):
  109. key = "sensor_types"
  110. def __init__(self, cbpi):
  111. self.cbpi = cbpi
  112. self.cbpi.cache["sensors"] = {}
  113. self.cbpi.cache["sensor_instances"] = {}
  114. self.cbpi.cache["sensor_types"] = {}
  115. def init(self):
  116. for key, value in self.cbpi.cache["sensors"].iteritems():
  117. self.init_one(key)
  118. def init_one(self, id):
  119. try:
  120. sensor = self.cbpi.cache["sensors"][id]
  121. clazz = self.cbpi.cache[self.key].get(sensor.type)["class"]
  122. cfg = sensor.config.copy()
  123. cfg.update(dict(cbpi=self.cbpi, id=id))
  124. self.cbpi.cache["sensors"][id].instance = clazz(**cfg)
  125. self.cbpi.cache["sensors"][id].instance.init()
  126. print self.cbpi.cache["sensors"][id].instance
  127. self.cbpi.emit("INIT_SENSOR", id=id)
  128. def job(obj):
  129. obj.execute()
  130. t = self.cbpi._socketio.start_background_task(target=job, obj=self.cbpi.cache["sensors"][id].instance)
  131. self.cbpi.emit("INIT_SENSOR", id=id)
  132. except Exception as e:
  133. print "ERROR"
  134. self.cbpi._app.logger.error(e)
  135. def stop_one(self, id):
  136. print "OBJ", self.cbpi.cache["sensors"][id]
  137. self.cbpi.cache["sensors"][id].instance.stop()
  138. self.cbpi.emit("STOP_SENSOR", id=id)
  139. def get_value(self, sensorid):
  140. try:
  141. return self.cbpi.cache["sensors"][sensorid].instance.value
  142. except:
  143. return None
  144. def get_state(self, actor_id):
  145. print actor_id
  146. print self.cbpi
  147. def write_log(self, id, value, prefix="sensor"):
  148. filename = "./logs/%s_%s.log" % (prefix, str(id))
  149. formatted_time = strftime("%Y-%m-%d %H:%M:%S", localtime())
  150. msg = str(formatted_time) + "," + str(value) + "\n"
  151. with open(filename, "a") as file:
  152. file.write(msg)
  153. class BrewingCore(object):
  154. def __init__(self, cbpi):
  155. self.cbpi = cbpi
  156. self.cbpi.cache["step_types"] = {}
  157. self.cbpi.cache["controller_types"] = {}
  158. def log_action(self, text):
  159. filename = "./logs/action.log"
  160. formatted_time = strftime("%Y-%m-%d %H:%M:%S", localtime())
  161. with open(filename, "a") as file:
  162. text = text.encode("utf-8")
  163. file.write("%s,%s\n" % (formatted_time, text))
  164. def get_controller(self, name):
  165. return self.cbpi.cache["controller_types"].get(name)
  166. def set_target_temp(self, id, temp):
  167. self.cbpi.cache.get("kettle")[id].target_temp = float(temp)
  168. Kettle.update(**self.cbpi.cache.get("kettle")[id].__dict__)
  169. self.cbpi.ws_emit("UPDATE_KETTLE_TARGET_TEMP", {"id": id, "target_temp": temp})
  170. self.cbpi.emit("SET_KETTLE_TARGET_TEMP", id=id, temp=temp)
  171. class FermentationCore(object):
  172. def __init__(self, cbpi):
  173. self.cbpi = cbpi
  174. self.cbpi.cache["fermenter"] = {}
  175. self.cbpi.cache["fermentation_controller_types"] = {}
  176. def get_controller(self, name):
  177. return self.cbpi.cache["fermentation_controller_types"].get(name)
  178. class CraftBeerPI(object):
  179. cache = {}
  180. eventbus = {}
  181. def __init__(self):
  182. FORMAT = '%(asctime)-15s - %(levelname)s - %(message)s'
  183. logging.basicConfig(filename='./logs/app.log', level=logging.INFO, format=FORMAT)
  184. self.cache["messages"] = []
  185. self.modules = {}
  186. self.cache["users"] = {'manuel': {'pw': 'secret'}}
  187. self.addon = Addon(self)
  188. self.actor = ActorCore(self)
  189. self.sensor = SensorCore(self)
  190. self.brewing = BrewingCore(self)
  191. self.fermentation = FermentationCore(self)
  192. self._app = Flask(__name__)
  193. self._app.secret_key = 'Cr4ftB33rP1'
  194. self._app.json_encoder = ComplexEncoder
  195. self._socketio = SocketIO(self._app, json=json, logging=False)
  196. @self._app.route('/')
  197. def index():
  198. return redirect('ui')
  199. def run(self):
  200. self.__init_db()
  201. self.loadPlugins()
  202. self.addon.init()
  203. self.sensor.init()
  204. self.actor.init()
  205. self.beep()
  206. self._socketio.run(self._app, host='0.0.0.0', port=5000)
  207. def beep(self):
  208. self.buzzer.beep()
  209. def sleep(self, seconds):
  210. self._socketio.sleep(seconds)
  211. def notify(self, headline, message, type="success", timeout=5000):
  212. msg = {"id": str(uuid.uuid1()), "type": type, "headline": headline, "message": message, "timeout": timeout}
  213. self.ws_emit("NOTIFY", msg)
  214. def ws_emit(self, key, data):
  215. self._socketio.emit(key, data, namespace='/brew')
  216. def __init_db(self, ):
  217. print "INIT DB"
  218. with self._app.app_context():
  219. db = self.get_db()
  220. try:
  221. with self._app.open_resource('../../config/schema.sql', mode='r') as f:
  222. db.cursor().executescript(f.read())
  223. db.commit()
  224. except Exception as e:
  225. print e
  226. pass
  227. def nocache(self, view):
  228. @wraps(view)
  229. def no_cache(*args, **kwargs):
  230. response = make_response(view(*args, **kwargs))
  231. response.headers['Last-Modified'] = datetime.now()
  232. response.headers[
  233. 'Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0'
  234. response.headers['Pragma'] = 'no-cache'
  235. response.headers['Expires'] = '-1'
  236. return response
  237. return update_wrapper(no_cache, view)
  238. def get_db(self):
  239. db = getattr(g, '_database', None)
  240. if db is None:
  241. def dict_factory(cursor, row):
  242. d = {}
  243. for idx, col in enumerate(cursor.description):
  244. d[col[0]] = row[idx]
  245. return d
  246. db = g._database = sqlite3.connect('craftbeerpi.db')
  247. db.row_factory = dict_factory
  248. return db
  249. def add_cache_callback(self, key, method):
  250. method.callback = True
  251. self.cache[key] = method
  252. def get_config_parameter(self, key, default):
  253. cfg = self.cache["config"].get(key)
  254. if cfg is None:
  255. return default
  256. else:
  257. return cfg.value
  258. def emit(self, key, **kwargs):
  259. print key, kwargs
  260. if self.eventbus.get(key) is not None:
  261. for value in self.eventbus[key]:
  262. if value["async"] is False:
  263. value["function"](**kwargs)
  264. else:
  265. t = self.cbpi._socketio.start_background_task(target=value["function"], **kwargs)
  266. def loadPlugins(self):
  267. for filename in os.listdir("./modules/plugins"):
  268. print filename
  269. if os.path.isdir("./modules/plugins/" + filename) is False:
  270. continue
  271. try:
  272. self.modules[filename] = import_module("modules.plugins.%s" % (filename))
  273. except Exception as e:
  274. print e
  275. self.notify("Failed to load plugin %s " % filename, str(e), type="danger", timeout=None)
  276. cbpi = CraftBeerPI()
  277. addon = cbpi.addon