2019-10-22 23:55:00 +02:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
from flask import Flask, request, json, jsonify, abort, make_response
|
2019-11-03 01:36:23 +01:00
|
|
|
import logging
|
2019-10-22 23:55:00 +02:00
|
|
|
import argparse
|
2019-11-03 13:57:14 +01:00
|
|
|
import time
|
2019-11-05 01:18:25 +01:00
|
|
|
import dataset
|
|
|
|
import sys
|
2019-11-14 00:51:51 +01:00
|
|
|
from math import inf
|
2019-10-22 23:55:00 +02:00
|
|
|
|
2019-11-03 01:36:23 +01:00
|
|
|
CONFIG_FILE = 'config.json'
|
2019-11-05 01:18:25 +01:00
|
|
|
DB_FILE = 'db.sqlite'
|
|
|
|
LIMIT=100
|
2019-10-22 23:55:00 +02:00
|
|
|
PORT = 5000
|
2019-11-05 01:18:25 +01:00
|
|
|
# TIMEOUT_CLIENT = 10
|
2019-11-03 01:36:23 +01:00
|
|
|
core = None
|
|
|
|
|
|
|
|
# sensors
|
|
|
|
NUM_VALUES = 1000
|
2019-10-22 23:55:00 +02:00
|
|
|
|
|
|
|
def setup():
|
|
|
|
# arguments
|
|
|
|
parser = argparse.ArgumentParser(description='homecontrol')
|
2019-11-03 01:36:23 +01:00
|
|
|
parser.add_argument('-p', '--port', dest='port', type=int,
|
|
|
|
help='listening port')
|
2019-10-22 23:55:00 +02:00
|
|
|
parser.add_argument('-c', '--config', dest='config', type=str,
|
2019-11-03 01:36:23 +01:00
|
|
|
help='config file', default=CONFIG_FILE)
|
2019-11-05 01:18:25 +01:00
|
|
|
parser.add_argument('-f', '--dbfile', dest='dbfile', type=str,
|
|
|
|
help='database file', default=DB_FILE)
|
2019-10-22 23:55:00 +02:00
|
|
|
parser.add_argument('-d', '--debug', dest='debug', action='store_true',
|
2019-11-03 01:36:23 +01:00
|
|
|
help='debug mode')
|
2019-11-03 13:57:14 +01:00
|
|
|
parser.add_argument('-D', '--debugflask', dest='debugflask', action='store_true',
|
|
|
|
help='flask debug mode')
|
2019-10-22 23:55:00 +02:00
|
|
|
|
|
|
|
# parse arguments
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
# initialize config
|
|
|
|
config = {}
|
|
|
|
try:
|
2019-11-03 01:36:23 +01:00
|
|
|
config_file = open(args.config, 'r')
|
2019-10-22 23:55:00 +02:00
|
|
|
config = json.load(config_file)
|
2019-11-05 01:18:25 +01:00
|
|
|
except:
|
|
|
|
pass
|
2019-10-22 23:55:00 +02:00
|
|
|
|
|
|
|
# fill new keys with defaults
|
|
|
|
if not config.get('port'):
|
|
|
|
config['port'] = PORT
|
|
|
|
|
2019-11-05 01:18:25 +01:00
|
|
|
if not config.get('dbfile'):
|
|
|
|
config['dbfile'] = DB_FILE
|
|
|
|
|
2019-10-22 23:55:00 +02:00
|
|
|
# overwrite with arguments
|
|
|
|
if args.port:
|
|
|
|
config['port'] = args.port
|
2019-11-05 01:18:25 +01:00
|
|
|
if args.dbfile:
|
|
|
|
config['dbfile'] = args.dbfile
|
2019-10-22 23:55:00 +02:00
|
|
|
|
|
|
|
# save to file
|
|
|
|
with open(args.config, 'w') as config_file:
|
|
|
|
json.dump(config, config_file)
|
|
|
|
|
|
|
|
return args, config
|
|
|
|
|
2019-11-05 01:18:25 +01:00
|
|
|
class Logger:
|
|
|
|
def __init__(self, name, debug):
|
|
|
|
FORMAT='%(asctime)-8s %(name)12s::%(levelname)-7s %(message)s'
|
|
|
|
DATEFMT='%H:%M:%S'
|
|
|
|
logging.basicConfig(level=logging.INFO, format=FORMAT, datefmt=DATEFMT)
|
|
|
|
self.logger = logging.getLogger(name)
|
|
|
|
if debug:
|
|
|
|
self.logger.setLevel(logging.DEBUG)
|
|
|
|
|
|
|
|
def getLogger(self):
|
|
|
|
return self.logger
|
|
|
|
|
2019-11-03 01:36:23 +01:00
|
|
|
|
2019-10-22 23:55:00 +02:00
|
|
|
class Core:
|
2019-11-05 01:18:25 +01:00
|
|
|
def __init__(self, dbfile, debug):
|
|
|
|
self.logger = Logger("Core", debug).getLogger()
|
|
|
|
self.logger.info("initialization starting...")
|
|
|
|
self.actor_queue = {}
|
2019-11-14 00:51:51 +01:00
|
|
|
self.db = dataset.connect("sqlite:///%s?check_same_thread=False" % dbfile)
|
2019-11-05 01:18:25 +01:00
|
|
|
self.logger.info("initialization complete.")
|
|
|
|
|
|
|
|
|
|
|
|
def actor_add_level(self, actorId, actorType, maxLevel, level):
|
|
|
|
ret = {}
|
2019-11-14 00:51:51 +01:00
|
|
|
try:
|
|
|
|
if not self.db["actors"].find_one(actorId=actorId):
|
|
|
|
self.db['actors'].insert({"actorId": actorId, "actorType": actorType,
|
|
|
|
"maxLevel": maxLevel})
|
|
|
|
self.db['actor_levels'].insert({"actorId": actorId, "ts": time.time(), "level":
|
|
|
|
level})
|
|
|
|
except Exception as ex:
|
|
|
|
self.logger.error('Exception Type:%s, args:\n%s' % (type(ex).__name__, ex.args))
|
2019-11-05 01:18:25 +01:00
|
|
|
|
|
|
|
return ret
|
|
|
|
|
|
|
|
def actor_add_queue(self, actorId, command, data):
|
2019-11-14 00:51:51 +01:00
|
|
|
try:
|
|
|
|
if self.db["actors"].find_one(actorId=actorId):
|
|
|
|
self.actor_queue[actorId] = {"command": command, "data": data}
|
|
|
|
except Exception as ex:
|
|
|
|
self.logger.error('Exception Type:%s, args:\n%s' % (type(ex).__name__, ex.args))
|
2019-11-05 01:18:25 +01:00
|
|
|
|
|
|
|
|
|
|
|
def actor_get_actors(self):
|
|
|
|
ret = {}
|
2019-11-14 00:51:51 +01:00
|
|
|
try:
|
|
|
|
for s in self.db["actors"]:
|
|
|
|
actorId = s["actorId"]
|
|
|
|
ret[actorId] = self.actor_get_info(actorId)
|
|
|
|
except Exception as ex:
|
|
|
|
self.logger.error('Exception Type:%s, args:\n%s' % (type(ex).__name__, ex.args))
|
2019-11-05 01:18:25 +01:00
|
|
|
return ret
|
|
|
|
|
|
|
|
|
|
|
|
def actor_get_info(self, actorId):
|
|
|
|
ret = {}
|
2019-11-14 00:51:51 +01:00
|
|
|
try:
|
|
|
|
q = self.db["actors"].find_one(actorId=actorId)
|
|
|
|
ret["actorId"] = actorId
|
|
|
|
ret["maxLevel"] = "0x%x" % q["maxLevel"]
|
|
|
|
ret["actorType"] = q["actorType"]
|
|
|
|
q = self.db["actor_levels"].find_one(actorId=actorId, order_by="-ts")
|
|
|
|
ret["ts"] = q["ts"]
|
|
|
|
ret["level"] = "0x%x" % q["level"]
|
|
|
|
except Exception as ex:
|
|
|
|
self.logger.error('Exception Type:%s, args:\n%s' % (type(ex).__name__, ex.args))
|
2019-11-05 01:18:25 +01:00
|
|
|
return ret
|
|
|
|
|
|
|
|
|
|
|
|
def actor_get_levels(self, actorId, limit=None):
|
2019-11-14 00:51:51 +01:00
|
|
|
ret = {}
|
|
|
|
try:
|
|
|
|
if self.db["actors"].find_one(actorId=actorId):
|
|
|
|
ret = self.actor_get_info(actorId)
|
|
|
|
if limit:
|
|
|
|
query = self.db["actor_levels"].find(actorId=actorId, _limit=limit)
|
|
|
|
else:
|
|
|
|
query = self.db["actor_levels"].find(actorId=actorId)
|
|
|
|
levels = []
|
|
|
|
for q in query:
|
|
|
|
levels.append({"ts": q["ts"], "value": "0x%x" % q["level"]})
|
|
|
|
ret["levels"] = levels
|
|
|
|
except Exception as ex:
|
|
|
|
self.logger.error('Exception Type:%s, args:\n%s' % (type(ex).__name__, ex.args))
|
2019-11-05 01:18:25 +01:00
|
|
|
|
|
|
|
return ret
|
2019-11-03 01:36:23 +01:00
|
|
|
|
|
|
|
|
2019-11-05 01:18:25 +01:00
|
|
|
def actor_get_queue(self, actorId):
|
2019-11-03 01:36:23 +01:00
|
|
|
ret = {}
|
2019-11-14 00:51:51 +01:00
|
|
|
try:
|
|
|
|
if actorId in self.actor_queue:
|
|
|
|
ret = self.actor_queue.pop(actorId)
|
|
|
|
except Exception as ex:
|
|
|
|
self.logger.error('Exception Type:%s, args:\n%s' % (type(ex).__name__, ex.args))
|
2019-11-03 01:36:23 +01:00
|
|
|
return ret
|
|
|
|
|
2019-11-05 01:18:25 +01:00
|
|
|
def sensor_get_info(self, sensorId):
|
2019-11-03 01:36:23 +01:00
|
|
|
ret = {}
|
2019-11-14 00:51:51 +01:00
|
|
|
try:
|
|
|
|
q = self.db["sensors"].find_one(sensorId=sensorId)
|
|
|
|
ret["sensorId"] = sensorId
|
|
|
|
ret["sensorType"] = q["sensorType"]
|
|
|
|
q = self.db["sensor_values"].find_one(sensorId=sensorId, order_by="-ts")
|
|
|
|
ret["ts"] = q["ts"]
|
|
|
|
except Exception as ex:
|
|
|
|
self.logger.error('Exception Type:%s, args:\n%s' % (type(ex).__name__, ex.args))
|
2019-11-03 01:36:23 +01:00
|
|
|
return ret
|
2019-10-22 23:55:00 +02:00
|
|
|
|
|
|
|
|
2019-11-14 00:51:51 +01:00
|
|
|
def sensor_get_values(self, sensorId, min_ts, max_ts, limit=None):
|
|
|
|
ret = {}
|
|
|
|
try:
|
|
|
|
if self.db["sensors"].find_one(sensorId=sensorId):
|
|
|
|
ret = self.sensor_get_info(sensorId)
|
|
|
|
if limit:
|
|
|
|
query = self.db["sensor_values"].find(sensorId=sensorId,
|
|
|
|
ts={"between": [min_ts, max_ts]}, _limit=limit)
|
|
|
|
else:
|
|
|
|
query = self.db["sensor_values"].find(sensorId=sensorId,
|
|
|
|
ts={"between": [min_ts, max_ts]})
|
|
|
|
values = []
|
|
|
|
for q in query:
|
|
|
|
values.append({"ts": float(q["ts"]), "value": float(q["value"])})
|
|
|
|
ret["values"] = values
|
|
|
|
except Exception as ex:
|
|
|
|
self.logger.error('Exception Type:%s, args:\n%s' % (type(ex).__name__, ex.args))
|
2019-11-05 01:18:25 +01:00
|
|
|
|
|
|
|
return ret
|
2019-10-22 23:55:00 +02:00
|
|
|
|
|
|
|
|
2019-11-05 01:18:25 +01:00
|
|
|
def sensor_get_sensors(self):
|
|
|
|
ret = {}
|
2019-11-14 00:51:51 +01:00
|
|
|
try:
|
|
|
|
for s in self.db["sensors"]:
|
|
|
|
sensorId = s["sensorId"]
|
|
|
|
ret[sensorId] = self.sensor_get_info(sensorId)
|
|
|
|
except Exception as ex:
|
|
|
|
self.logger.error('Exception Type:%s, args:\n%s' % (type(ex).__name__, ex.args))
|
2019-11-05 01:18:25 +01:00
|
|
|
return ret
|
2019-10-22 23:55:00 +02:00
|
|
|
|
|
|
|
|
2019-11-05 01:18:25 +01:00
|
|
|
def sensor_add_value(self, sensorId, sensorType, value):
|
|
|
|
ret = {}
|
2019-11-14 00:51:51 +01:00
|
|
|
try:
|
|
|
|
if not self.db["sensors"].find_one(sensorId=sensorId):
|
|
|
|
self.db['sensors'].insert({"sensorId": sensorId, "sensorType": sensorType})
|
|
|
|
self.db['sensor_values'].insert({"sensorId": sensorId, "ts": time.time(), "value":
|
|
|
|
value})
|
|
|
|
except Exception as ex:
|
|
|
|
self.logger.error('Exception Type:%s, args:\n%s' % (type(ex).__name__, ex.args))
|
2019-11-05 01:18:25 +01:00
|
|
|
|
|
|
|
return ret
|
2019-11-03 01:36:23 +01:00
|
|
|
|
2019-11-05 01:18:25 +01:00
|
|
|
|
|
|
|
app = Flask(__name__)
|
2019-11-03 01:36:23 +01:00
|
|
|
|
|
|
|
|
2019-11-05 01:18:25 +01:00
|
|
|
@app.route("/actor/command", methods=['POST'])
|
|
|
|
def actor_command():
|
2019-11-03 01:36:23 +01:00
|
|
|
ret = {}
|
|
|
|
try:
|
|
|
|
content = request.json
|
2019-11-05 01:18:25 +01:00
|
|
|
actorId = content.get("id")
|
2019-11-03 13:57:14 +01:00
|
|
|
command = content.get("command")
|
|
|
|
data = content.get("data")
|
2019-11-14 00:51:51 +01:00
|
|
|
core.actor_add_queue(actorId, command, data)
|
2019-11-03 01:36:23 +01:00
|
|
|
except Exception as ex:
|
|
|
|
logger.error('Exception Type:%s, args:\n%s' % (type(ex).__name__, ex.args))
|
|
|
|
abort(400)
|
2019-11-03 13:57:14 +01:00
|
|
|
|
|
|
|
return make_response(jsonify(ret), 200)
|
|
|
|
|
|
|
|
|
2019-11-05 01:18:25 +01:00
|
|
|
@app.route("/actor/get", methods=['GET'])
|
|
|
|
def actor_get():
|
|
|
|
ret = core.actor_get_actors()
|
|
|
|
return make_response(jsonify(ret), 200)
|
|
|
|
|
|
|
|
|
|
|
|
@app.route("/actor/get_levels/<actorId>", methods=['GET'])
|
|
|
|
def actor_get_level(actorId):
|
2019-11-03 13:57:14 +01:00
|
|
|
ret = {}
|
2019-11-05 01:18:25 +01:00
|
|
|
if request.args.get("limit"):
|
|
|
|
limit=int(request.args.get("limit"))
|
2019-11-03 13:57:14 +01:00
|
|
|
else:
|
2019-11-05 01:18:25 +01:00
|
|
|
limit=None
|
|
|
|
logger.debug("actor/get_levels/%s, limit: %s" % (actorId, limit))
|
2019-11-14 00:51:51 +01:00
|
|
|
ret[actorId] = core.actor_get_levels(actorId, limit=limit)
|
2019-11-05 01:18:25 +01:00
|
|
|
return make_response(jsonify(ret), 200)
|
|
|
|
|
|
|
|
|
|
|
|
@app.route("/actor/update", methods=['POST'])
|
|
|
|
def actor_update():
|
|
|
|
ret = {}
|
|
|
|
try:
|
|
|
|
content = request.json
|
|
|
|
actorId = content.get("id")
|
|
|
|
actorType = content.get("type")
|
|
|
|
level = content.get("level")
|
|
|
|
maxLevel = content.get("maxLevel")
|
|
|
|
core.actor_add_level(actorId, actorType, maxLevel, level)
|
|
|
|
ret = core.actor_get_queue(actorId)
|
|
|
|
except Exception as ex:
|
|
|
|
logger.error('Exception Type:%s, args:\n%s' % (type(ex).__name__, ex.args))
|
|
|
|
abort(400)
|
|
|
|
|
2019-11-03 01:36:23 +01:00
|
|
|
return make_response(jsonify(ret), 200)
|
|
|
|
|
|
|
|
|
2019-11-05 01:18:25 +01:00
|
|
|
@app.route("/sensor/get", methods=['GET'])
|
|
|
|
def sensor_get_sensors():
|
|
|
|
ret = core.sensor_get_sensors()
|
2019-11-03 01:36:23 +01:00
|
|
|
return make_response(jsonify(ret), 200)
|
|
|
|
|
|
|
|
|
2019-11-05 01:18:25 +01:00
|
|
|
@app.route("/sensor/get_values/<sensorId>", methods=['GET'])
|
|
|
|
def sensor_get_values(sensorId):
|
2019-11-03 13:57:14 +01:00
|
|
|
ret = {}
|
2019-11-14 00:51:51 +01:00
|
|
|
min_ts = 0
|
|
|
|
if request.args.get("min_ts"):
|
|
|
|
min_ts=int(request.args.get("min_ts"))
|
|
|
|
max_ts = inf
|
|
|
|
limit=None
|
2019-11-05 01:18:25 +01:00
|
|
|
if request.args.get("limit"):
|
|
|
|
limit=int(request.args.get("limit"))
|
2019-11-14 00:51:51 +01:00
|
|
|
if request.args.get("max_ts"):
|
|
|
|
max_ts=int(request.args.get("max_ts"))
|
|
|
|
logger.debug("sensor/get_values/%s, [%f, %f], limit: %s" % (sensorId, min_ts, max_ts, limit))
|
|
|
|
ret[sensorId] = core.sensor_get_values(sensorId, min_ts=min_ts, max_ts=max_ts, limit=limit)
|
2019-11-03 13:57:14 +01:00
|
|
|
return make_response(jsonify(ret), 200)
|
2019-11-03 01:36:23 +01:00
|
|
|
|
|
|
|
|
2019-11-05 01:18:25 +01:00
|
|
|
@app.route("/sensor/update", methods=['POST'])
|
|
|
|
def sensor_add_value():
|
2019-11-03 01:36:23 +01:00
|
|
|
ret = {}
|
|
|
|
try:
|
|
|
|
content = request.json
|
2019-11-05 01:18:25 +01:00
|
|
|
sensorId = content.get("id")
|
|
|
|
sensorType = content.get("type")
|
2019-11-03 01:36:23 +01:00
|
|
|
value = content.get("value")
|
2019-11-05 01:18:25 +01:00
|
|
|
ret = core.sensor_add_value(sensorId, sensorType, value)
|
2019-11-03 01:36:23 +01:00
|
|
|
except Exception as ex:
|
|
|
|
logger.error('Exception Type:%s, args:\n%s' % (type(ex).__name__, ex.args))
|
|
|
|
abort(400)
|
|
|
|
return make_response(jsonify(ret), 200)
|
2019-10-22 23:55:00 +02:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
args, config = setup()
|
2019-11-05 01:18:25 +01:00
|
|
|
core = Core(config['dbfile'], args.debug)
|
|
|
|
logger = Logger("main", args.debug).getLogger()
|
2019-11-03 01:36:23 +01:00
|
|
|
|
2019-11-03 13:57:14 +01:00
|
|
|
app.run(host='0.0.0.0', port=config['port'], debug=args.debugflask)
|