From 7aa6c579e90cdd733fb4d00488c0add3cd32f9e0 Mon Sep 17 00:00:00 2001 From: wez3 Date: Sat, 21 Jan 2017 02:04:32 +0100 Subject: [PATCH] Split/cleanup the API code --- VERSION.md | 2 +- modules/api.py | 304 ++++++------------------------------------- modules/charts.py | 33 +++++ modules/domoticz.py | 41 ++++++ modules/plugins.py | 153 ++++++++++++++++++++++ modules/webconfig.py | 28 ++++ server.py | 8 +- 7 files changed, 302 insertions(+), 267 deletions(-) create mode 100644 modules/charts.py create mode 100644 modules/domoticz.py create mode 100644 modules/plugins.py create mode 100644 modules/webconfig.py diff --git a/VERSION.md b/VERSION.md index 7dea76e..6d7de6e 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.0.1 +1.0.2 diff --git a/modules/api.py b/modules/api.py index 0b01f0e..94ef4b7 100644 --- a/modules/api.py +++ b/modules/api.py @@ -3,22 +3,53 @@ from flask import request from flaskext.auth import login_required -import git -import security -import requests, json, re -import os, sys, imp, shutil +import json, os, sys +import security, charts, plugins, webconfig, domoticz apiDict = {} -indexes = {} +modules = {} def init(): global modules - modules = loadPlugins() - return 0 + modules = plugins.loadPlugins() + return def addToApi(custom, module, function): apiDict[custom] = [module, function] +@login_required() +def gateway(): + requestedUrl = request.url.split("/api") + custom = request.args.get('custom', '') + if custom == "bar_chart": + result = charts.barChart() + elif custom == "donut_chart": + result = charts.donutChart() + elif custom == "modify_config": + idx = request.args.get('idx', '') + page = request.args.get('page', '') + component = request.args.get('component', '') + description = request.args.get('description', '') + extra = request.args.get('extra', '') + webconfig.writeToConfig(idx, page, component, description, extra) + elif custom == 'indexPlugins': + result = json.dumps(plugins.indexPlugins(request.args)) + elif custom == "performUpgrade": + result = json.dumps(webconfig.performUpgrade()) + elif custom in apiDict: + module = apiDict.get(custom)[0] + function = apiDict.get(custom)[1] + call = getattr(modules[module], function) + result = call(request.args) + else: + result = domoticz.queryDomoticz(requestedUrl[1]) + try: + if not isJson(result): + result = json.dumps(result) + return security.sanitizeJSON(json.loads(result)) + except: + return "No results returned" + def setConfig(cfg, orig_cfg): global config global originalCfg @@ -26,267 +57,14 @@ def setConfig(cfg, orig_cfg): originalCfg = orig_cfg def getConfig(): - global config return config -def loadPlugins(): - plugin = {} - plugin_dir = os.getcwd() + '/plugins/' - for i in os.listdir(plugin_dir): - if not i == '__init__.py' and i.endswith('.py'): - name = i.replace('.py', '') - try: - plugin[name] = imp.load_source(name, plugin_dir + i) - plugin[name].init() - except ImportError as msg: - sys.exit("Error occured during loading imports for the plugin {}: {}".format(name, msg)) - return plugin +def getOriginalConfig(): + return originalCfg -@login_required() -def gateway(): - requestedUrl = request.url.split("/api") - custom = request.args.get('custom', '') - if custom == "bar_chart": - idxs = request.args.get('idxs', '') - idxArray = idxs.split(",") - resultArray = [] - resultDict = {} - for i in idxArray: - qResults = queryDomoticz("?type=devices&rid=" + i) - jsonObj = json.loads(qResults) - resultDict["y"] = jsonObj["result"][0]["Name"] - resultDict["a"] = jsonObj["result"][0]["Data"] - resultArray.append(resultDict.copy()) - result = json.dumps(resultArray) - elif custom == "donut_chart": - idxs = request.args.get('idxs', '') - idxArray = idxs.split(",") - resultArray = [] - resultDict = {} - for i in idxArray: - qResults = queryDomoticz("?type=devices&rid=" + i) - jsonObj = json.loads(qResults) - resultDict["label"] = jsonObj["result"][0]["Name"] - resultDict["value"] = re.findall("\d+\.?\d+", jsonObj["result"][0]["Data"]) - resultArray.append(resultDict.copy()) - result = json.dumps(resultArray) - elif custom == "modify_config": - idx = request.args.get('idx', '') - page = request.args.get('page', '') - component = request.args.get('component', '') - description = request.args.get('description', '') - extra = request.args.get('extra', '') - writeToConfig(idx, page, component, description, extra) - elif custom == 'indexPlugins': - result = json.dumps(indexPlugins(request.args)) - elif custom == "performUpgrade": - result = json.dumps(performUpgrade()) - elif custom in apiDict: - module = apiDict.get(custom)[0] - function = apiDict.get(custom)[1] - call = getattr(modules[module], function) - result = call(request.args) - else: - result = queryDomoticz(requestedUrl[1]) - try: - if not is_json(result): - result = json.dumps(result) - return security.sanitizeJSON(json.loads(result)) - except: - return "No results returned" - -def is_json(myjson): +def isJson(myjson): try: json_object = json.loads(str(myjson)) except ValueError, e: return False return True - -def queryDomoticz(url): - try: - r = requests.get('http://' + config["general_settings"]["server"]["url"] + '/json.htm' + url, - auth=(config["general_settings"]["server"].get("user"), config["general_settings"]["server"].get("password")), timeout=5.00) - except: - return {} - return r.text - -def writeToConfig(idx, page, component, description, extra): - section = dict(originalCfg[page][component]) - section[description] = idx - originalCfg[page][component] = section - originalCfg.write() - -def checkDomoticzStatus(config): - domoticzDevices = [] - domoticzScenes = [] - try: - result = json.loads(queryDomoticz("?type=devices&filter=all")) - resultScene = json.loads(queryDomoticz("?type=scenes&filter=all")) - except: - sys.exit("Domoticz is not reachable.") - for device in result["result"]: - domoticzDevices.append(device["idx"]) - if 'result' in resultScene: - for device in resultScene["result"]: - domoticzScenes.append(device["idx"]) - configuredDevicesInDomoticz(config, domoticzDevices, domoticzScenes) - -def configuredDevicesInDomoticz(config, domoticzDevices, domoticzScenes): - for k, v in config.iteritems(): - if isinstance(v, dict): - configuredDevicesInDomoticz(v, domoticzDevices, domoticzScenes) - else: - if isinstance(v, int): - if v not in domoticzDevices and v not in domoticzScenes: - sys.exit("Device and/or scene with IDX {} is not available in Domoticz".format(v)) - elif isinstance(v, list): - if (v[0].isdigit()) and (v[0] not in domoticzDevices and v[0] not in domoticzScenes): - sys.exit("Device and/or scene with IDX {} is not available in Domoticz".format(v[0])) - -def getPluginDict(): - global indexes - return indexes - -def setPluginDict(d): - global indexes - indexes = d - -def getPluginVersion(loc): - f = open(loc, 'r').read().split('\n') - v = None - for l in f: - t = l.split('=') - if t[0] == '@version': - _tmp_v = t[1].split('.') - c = 1 - _version = _tmp_v[0] + '.' - while(c < len(_tmp_v)): - _version += _tmp_v[c] - c += 1 - return float(_version) - -def getVersion(): - f = open('VERSION.md', 'r') - version = f.read().rstrip() - f.close() - return version - -def performUpgrade(): - git.cmd.Git('.').pull("https://github.com/wez3/domoboard.git") - return "Upgrade completed." - -def getCurrentBranch(): - repo = git.Repo('.') - branch = repo.active_branch - return branch.name - -def indexPlugins(params={}): - tmpFolder = 'static/tmp' - indexFolderPath = 'static/tmp/pluginsIndex/' - docsFolderPath = 'static/docs/' - installedPlugins = [] - staticFolder = ['css/', 'images/', 'fonts/', 'js/'] - indexes = getPluginDict() - pluginParts = ['templates/', 'plugins/'] - - if 'action' in params: - if not params['action'] == 'getPlugins': - try: - if not int(params['folid']) in indexes: - return "No valid plugin id specified." - except: - return "Please specify integers only." - - if 'action' in params: - if params['action'] == 'getPlugins': - folders = filter(lambda x: os.path.isdir(os.path.join(indexFolderPath, x)), - os.listdir(indexFolderPath)) - for i in folders: - i = security.sanitizePathBasename(i) - if i != '.git': - fol = {} - fol['id'] = len(indexes) - fol['folder'] = i - fol['status'] = 'install' - for filename in os.listdir('templates/'): - installedPlugins.append(filename) - - for filename in os.listdir(indexFolderPath + i + '/templates'): - if filename in installedPlugins: - installed_version = getPluginVersion(docsFolderPath + i + '_readme.md') - indexed_version = getPluginVersion(indexFolderPath + i + '/' + docsFolderPath + 'readme.md') - if indexed_version > installed_version: - fol['update'] = 'yes' - else: - fol['update'] = 'no' - fol['status'] = 'remove' - readme = open(indexFolderPath + i + '/' + docsFolderPath + 'readme.md', 'r').read().split('\n') - sumList = {} - for s in readme: - t = s.split('=') - if len(t) > 1: - fol[t[0].replace('@', '')] = t[1] - if not i in (f['folder'] for f in indexes.itervalues()): - indexes[len(indexes)] = fol - else: - for tmp in indexes.itervalues(): - if i == tmp['folder']: - for k in fol: - if k != 'id': - indexes[tmp['id']][k] = fol[k] - setPluginDict(indexes) - return indexes - elif params['action'] == 'installPlugin': - if 'folid' in params: - if indexes[int(params['folid'])]['status'] == 'install': - src_path = indexFolderPath + indexes[int(params['folid'])]['folder'] + '/' - for part in pluginParts: - for filename in os.listdir(src_path + part): - shutil.copy(src_path + part + filename, part + filename) - for f in staticFolder: - if os.path.exists(src_path + 'static/' + f): - for filename in os.listdir(src_path + '/static/' + f): - shutil.copy(src_path + 'static/' + f + filename, 'static/' + f + filename) - for filename in os.listdir(src_path + '/' + docsFolderPath): - shutil.copy(src_path + '/' + docsFolderPath + filename, docsFolderPath + indexes[int(params['folid'])]['folder'] + '_' + filename) - indexes[int(params['folid'])]['status'] = 'remove' - global modules - modules = loadPlugins() - return indexes[int(params['folid'])]['folder'] + ' installed.' - else: - return "This plugin is already installed." - elif params['action'] == 'removePlugin': - if 'folid' in params: - if indexes[int(params['folid'])]['status'] == 'remove': - src_path = indexFolderPath + indexes[int(params['folid'])]['folder'] + '/' - for part in pluginParts: - for filename in os.listdir(src_path + part): - os.remove(part + filename) - for f in staticFolder: - if os.path.exists(src_path + 'static/' + f): - for filename in os.listdir(src_path + 'static/' + f): - os.remove('static/' + f + filename) - for filename in os.listdir(src_path + '/' + docsFolderPath): - shutil.copy(src_path + '/' + docsFolderPath + filename, docsFolderPath + indexes[int(params['folid'])]['folder'] + '_' + filename) - indexes[int(params['folid'])]['status'] = 'install' - return indexes[int(params['folid'])]['folder'] + ' removed.' - else: - return "This plugin was already removed." - setPluginDict(indexes) - else: - if not os.path.exists(tmpFolder): - os.makedirs(tmpFolder) - if not os.path.exists(indexFolderPath): - os.makedirs(indexFolderPath) - - if not os.path.isfile(indexFolderPath + 'README.md'): - shutil.rmtree(indexFolderPath) - try: - git.Repo.clone_from("https://github.com/wez3/domoboard-plugins.git", indexFolderPath) - except: - print 'indexed' - else: - git.cmd.Git(indexFolderPath).pull("https://github.com/wez3/domoboard-plugins.git") - folders = filter(lambda x: os.path.isdir(os.path.join(indexFolderPath, x)), - os.listdir(indexFolderPath)) - return indexPlugins({'action': 'getPlugins'}) diff --git a/modules/charts.py b/modules/charts.py new file mode 100644 index 0000000..ade354b --- /dev/null +++ b/modules/charts.py @@ -0,0 +1,33 @@ +#!/usr/bin/python +# Provides all charts functionality + +import json, re, domoticz +from flask import request + +def barChart(): + idxs = request.args.get('idxs', '') + idxArray = idxs.split(",") + resultArray = [] + resultDict = {} + for i in idxArray: + qResults = domoticz.queryDomoticz("?type=devices&rid=" + i) + jsonObj = json.loads(qResults) + resultDict["y"] = jsonObj["result"][0]["Name"] + resultDict["a"] = jsonObj["result"][0]["Data"] + resultArray.append(resultDict.copy()) + result = json.dumps(resultArray) + return result + +def donutChart(): + idxs = request.args.get('idxs', '') + idxArray = idxs.split(",") + resultArray = [] + resultDict = {} + for i in idxArray: + qResults = domoticz.queryDomoticz("?type=devices&rid=" + i) + jsonObj = json.loads(qResults) + resultDict["label"] = jsonObj["result"][0]["Name"] + resultDict["value"] = re.findall("\d+\.?\d+", jsonObj["result"][0]["Data"]) + resultArray.append(resultDict.copy()) + result = json.dumps(resultArray) + return result diff --git a/modules/domoticz.py b/modules/domoticz.py new file mode 100644 index 0000000..1efed50 --- /dev/null +++ b/modules/domoticz.py @@ -0,0 +1,41 @@ +#!/usr/bin/python +# This file contains all Domoticz related Python functions + +import requests, json, sys +import api + +def queryDomoticz(url): + config = api.getConfig() + try: + r = requests.get('http://' + config["general_settings"]["server"]["url"] + '/json.htm' + url, + auth=(config["general_settings"]["server"].get("user"), config["general_settings"]["server"].get("password")), timeout=5.00) + except: + return {} + return r.text + +def checkDomoticzStatus(config): + domoticzDevices = [] + domoticzScenes = [] + try: + result = json.loads(queryDomoticz("?type=devices&filter=all")) + resultScene = json.loads(queryDomoticz("?type=scenes&filter=all")) + except: + sys.exit("Domoticz is not reachable.") + for device in result["result"]: + domoticzDevices.append(device["idx"]) + if 'result' in resultScene: + for device in resultScene["result"]: + domoticzScenes.append(device["idx"]) + configuredDevicesInDomoticz(config, domoticzDevices, domoticzScenes) + +def configuredDevicesInDomoticz(config, domoticzDevices, domoticzScenes): + for k, v in config.iteritems(): + if isinstance(v, dict): + configuredDevicesInDomoticz(v, domoticzDevices, domoticzScenes) + else: + if isinstance(v, int): + if v not in domoticzDevices and v not in domoticzScenes: + sys.exit("Device and/or scene with IDX {} is not available in Domoticz".format(v)) + elif isinstance(v, list): + if (v[0].isdigit()) and (v[0] not in domoticzDevices and v[0] not in domoticzScenes): + sys.exit("Device and/or scene with IDX {} is not available in Domoticz".format(v[0])) diff --git a/modules/plugins.py b/modules/plugins.py new file mode 100644 index 0000000..84f752d --- /dev/null +++ b/modules/plugins.py @@ -0,0 +1,153 @@ +#!/usr/bin/python +# This file contains the functions regarding the plugin manager. + +import git, shutil, os, imp +import security + +indexes = {} + +def loadPlugins(): + plugin = {} + plugin_dir = os.getcwd() + '/plugins/' + for i in os.listdir(plugin_dir): + if not i == '__init__.py' and i.endswith('.py'): + name = i.replace('.py', '') + try: + plugin[name] = imp.load_source(name, plugin_dir + i) + plugin[name].init() + except ImportError as msg: + sys.exit("Error occured during loading imports for the plugin {}: {}".format(name, msg)) + return plugin + +def getPluginDict(): + global indexes + return indexes + +def setPluginDict(d): + global indexes + indexes = d + +def getPluginVersion(loc): + f = open(loc, 'r').read().split('\n') + v = None + for l in f: + t = l.split('=') + if t[0] == '@version': + _tmp_v = t[1].split('.') + c = 1 + _version = _tmp_v[0] + '.' + while(c < len(_tmp_v)): + _version += _tmp_v[c] + c += 1 + return float(_version) + +def indexPlugins(params={}): + tmpFolder = 'static/tmp' + indexFolderPath = 'static/tmp/pluginsIndex/' + docsFolderPath = 'static/docs/' + installedPlugins = [] + staticFolder = ['css/', 'images/', 'fonts/', 'js/'] + indexes = getPluginDict() + pluginParts = ['templates/', 'plugins/'] + + if 'action' in params: + if not params['action'] == 'getPlugins': + try: + if not int(params['folid']) in indexes: + return "No valid plugin id specified." + except: + return "Please specify integers only." + + if 'action' in params: + if params['action'] == 'getPlugins': + folders = filter(lambda x: os.path.isdir(os.path.join(indexFolderPath, x)), + os.listdir(indexFolderPath)) + for i in folders: + i = security.sanitizePathBasename(i) + if i != '.git': + fol = {} + fol['id'] = len(indexes) + fol['folder'] = i + fol['status'] = 'install' + for filename in os.listdir('templates/'): + installedPlugins.append(filename) + + for filename in os.listdir(indexFolderPath + i + '/templates'): + if filename in installedPlugins: + installed_version = getPluginVersion(docsFolderPath + i + '_readme.md') + indexed_version = getPluginVersion(indexFolderPath + i + '/' + docsFolderPath + 'readme.md') + if indexed_version > installed_version: + fol['update'] = 'yes' + else: + fol['update'] = 'no' + fol['status'] = 'remove' + readme = open(indexFolderPath + i + '/' + docsFolderPath + 'readme.md', 'r').read().split('\n') + sumList = {} + for s in readme: + t = s.split('=') + if len(t) > 1: + fol[t[0].replace('@', '')] = t[1] + if not i in (f['folder'] for f in indexes.itervalues()): + indexes[len(indexes)] = fol + else: + for tmp in indexes.itervalues(): + if i == tmp['folder']: + for k in fol: + if k != 'id': + indexes[tmp['id']][k] = fol[k] + setPluginDict(indexes) + return indexes + elif params['action'] == 'installPlugin': + if 'folid' in params: + if indexes[int(params['folid'])]['status'] == 'install': + src_path = indexFolderPath + indexes[int(params['folid'])]['folder'] + '/' + for part in pluginParts: + for filename in os.listdir(src_path + part): + shutil.copy(src_path + part + filename, part + filename) + for f in staticFolder: + if os.path.exists(src_path + 'static/' + f): + for filename in os.listdir(src_path + '/static/' + f): + shutil.copy(src_path + 'static/' + f + filename, 'static/' + f + filename) + for filename in os.listdir(src_path + '/' + docsFolderPath): + shutil.copy(src_path + '/' + docsFolderPath + filename, docsFolderPath + indexes[int(params['folid'])]['folder'] + '_' + filename) + indexes[int(params['folid'])]['status'] = 'remove' + global modules + modules = loadPlugins() + return indexes[int(params['folid'])]['folder'] + ' installed.' + else: + return "This plugin is already installed." + elif params['action'] == 'removePlugin': + if 'folid' in params: + if indexes[int(params['folid'])]['status'] == 'remove': + src_path = indexFolderPath + indexes[int(params['folid'])]['folder'] + '/' + for part in pluginParts: + for filename in os.listdir(src_path + part): + os.remove(part + filename) + for f in staticFolder: + if os.path.exists(src_path + 'static/' + f): + for filename in os.listdir(src_path + 'static/' + f): + os.remove('static/' + f + filename) + for filename in os.listdir(src_path + '/' + docsFolderPath): + shutil.copy(src_path + '/' + docsFolderPath + filename, docsFolderPath + indexes[int(params['folid'])]['folder'] + '_' + filename) + indexes[int(params['folid'])]['status'] = 'install' + return indexes[int(params['folid'])]['folder'] + ' removed.' + else: + return "This plugin was already removed." + setPluginDict(indexes) + else: + if not os.path.exists(tmpFolder): + os.makedirs(tmpFolder) + if not os.path.exists(indexFolderPath): + os.makedirs(indexFolderPath) + + if not os.path.isfile(indexFolderPath + 'README.md'): + shutil.rmtree(indexFolderPath) + try: + git.Repo.clone_from("https://github.com/wez3/domoboard-plugins.git", indexFolderPath) + except: + print 'indexed' + else: + git.cmd.Git(indexFolderPath).pull("https://github.com/wez3/domoboard-plugins.git") + folders = filter(lambda x: os.path.isdir(os.path.join(indexFolderPath, x)), + os.listdir(indexFolderPath)) + return indexPlugins({'action': 'getPlugins'}) diff --git a/modules/webconfig.py b/modules/webconfig.py new file mode 100644 index 0000000..bed15e1 --- /dev/null +++ b/modules/webconfig.py @@ -0,0 +1,28 @@ +#!/usr/bin/python +# This file contains the functions used for web based configuration of domoboard + +import git +import api +from flask import request + +def writeToConfig(idx, page, component, description, extra): + originalCfg = api.getOriginalConfig() + section = dict(originalCfg[page][component]) + section[description] = idx + originalCfg[page][component] = section + originalCfg.write() + +def getVersion(): + f = open('VERSION.md', 'r') + version = f.read().rstrip() + f.close() + return version + +def performUpgrade(): + git.cmd.Git('.').pull("https://github.com/wez3/domoboard.git") + return "Upgrade completed." + +def getCurrentBranch(): + repo = git.Repo('.') + branch = repo.active_branch + return branch.name diff --git a/server.py b/server.py index f6d2dbb..7b54ea1 100755 --- a/server.py +++ b/server.py @@ -6,7 +6,9 @@ from collections import OrderedDict import argparse, socket, re import hashlib, configobj, json, sys, os import modules.api as api +import modules.domoticz as domoticz import modules.security as security +import modules.webconfig as webconfig app = Flask(__name__) @@ -46,8 +48,8 @@ def generatePage(): configValues = configValues, blockArray = blockArray, _csrf_token = session['_csrf_token'], - version = api.getVersion(), - branch = api.getCurrentBranch(), + version = webconfig.getVersion(), + branch = webconfig.getCurrentBranch(), debug = app.debug) else: abort(404) @@ -154,7 +156,7 @@ if __name__ == '__main__': api.setConfig(config, unsanitizedConfig) api.init() validateConfigFormat(config) - api.checkDomoticzStatus(config) + domoticz.checkDomoticzStatus(config) server_location = config["general_settings"]["server"]["url"] flask_server_location = config["general_settings"]["server"]["flask_url"] auth = Auth(app, login_url_name='login_form')