diff --git a/Pipfile b/Pipfile
index fb910e4..563961f 100644
--- a/Pipfile
+++ b/Pipfile
@@ -11,6 +11,8 @@ pyyaml = "~=5.2"
atomicwrites = "~=1.3"
yarl = "~=1.3.0"
multidict = "~=4.5.2"
+aiohttp-jinja2 = "*"
+jinja2 = "*"
[dev-packages]
black = "*"
diff --git a/Pipfile.lock b/Pipfile.lock
index adf5518..0383d41 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "315b25b9f7d46a6ea88fb08170ad935b35aa2717743be4eca137e52e595066de"
+ "sha256": "740f61688ce679591a1b01f41abc1fa979f8fb1552d598e68868bc47712ba937"
},
"pipfile-spec": 6,
"requires": {},
@@ -32,6 +32,14 @@
"index": "pypi",
"version": "==3.6.2"
},
+ "aiohttp-jinja2": {
+ "hashes": [
+ "sha256:2dfe29cfd278d07cd0a851afb98471bc8ce2a830968443e40d67636f3c035d79",
+ "sha256:3b4dfe1bfd5542e254a769c18cb58d62f7f92755fec127e38d0da3436900b240"
+ ],
+ "index": "pypi",
+ "version": "==1.2.0"
+ },
"async-timeout": {
"hashes": [
"sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f",
@@ -82,6 +90,47 @@
],
"version": "==2.8"
},
+ "jinja2": {
+ "hashes": [
+ "sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f",
+ "sha256:9fe95f19286cfefaa917656583d020be14e7859c6b0252588391e47db34527de"
+ ],
+ "index": "pypi",
+ "version": "==2.10.3"
+ },
+ "markupsafe": {
+ "hashes": [
+ "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
+ "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
+ "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
+ "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
+ "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
+ "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
+ "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
+ "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
+ "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
+ "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
+ "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
+ "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
+ "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
+ "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
+ "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
+ "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
+ "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
+ "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
+ "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
+ "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
+ "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
+ "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
+ "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
+ "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
+ "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
+ "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
+ "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
+ "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"
+ ],
+ "version": "==1.1.1"
+ },
"multidict": {
"hashes": [
"sha256:024b8129695a952ebd93373e45b5d341dbb87c17ce49637b34000093f243dd4f",
@@ -636,10 +685,9 @@
},
"wcwidth": {
"hashes": [
- "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e",
- "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
+ "sha256:8fd29383f539be45b20bd4df0dc29c20ba48654a41e661925e612311e9f3c603"
],
- "version": "==0.1.7"
+ "version": "==0.1.8"
},
"wrapt": {
"hashes": [
diff --git a/bumper/__init__.py b/bumper/__init__.py
index 1ef871e..ff03f00 100644
--- a/bumper/__init__.py
+++ b/bumper/__init__.py
@@ -12,6 +12,9 @@ from logging.handlers import RotatingFileHandler
import socket
import sys
+import importlib
+import pkgutil
+from pkgutil import extend_path
def strtobool(strbool):
if str(strbool).lower() in ["true", "1", "t", "y", "on", "yes"]:
@@ -32,6 +35,8 @@ os.makedirs(data_dir, exist_ok=True) # Ensure data directory exists or create
certs_dir = os.environ.get("BUMPER_CERTS") or os.path.join(bumper_dir, "certs")
os.makedirs(certs_dir, exist_ok=True) # Ensure data directory exists or create
+
+
# Certs
ca_cert = os.environ.get("BUMPER_CA") or os.path.join(certs_dir, "ca.crt")
server_cert = os.environ.get("BUMPER_CERT") or os.path.join(certs_dir, "bumper.crt")
@@ -57,6 +62,16 @@ conf_server = None
conf_server_2 = None
xmpp_server = None
+# Plugins
+sys.path.append(os.path.join(bumper_dir, "bumper", "plugins"))
+sys.path.append(os.path.join(data_dir, "plugins"))
+
+discovered_plugins = {
+ name: importlib.import_module(name)
+ for finder, name, ispkg in pkgutil.iter_modules()
+ if name.startswith('bumper_')
+}
+
shutting_down = False
# Set format for all logs
@@ -206,10 +221,8 @@ async def start():
# Start web servers
conf_server.confserver_app()
- asyncio.create_task(conf_server.start_server())
-
- conf_server_2.confserver_app()
- asyncio.create_task(conf_server_2.start_server())
+ asyncio.create_task(conf_server.start_site(conf_server.app, address=bumper_listen, port=conf1_listen_port, usessl=True))
+ asyncio.create_task(conf_server.start_site(conf_server.app, address=bumper_listen, port=conf2_listen_port, usessl=False))
# Start maintenance
while not shutting_down:
diff --git a/bumper/confserver.py b/bumper/confserver.py
index af95f06..0e019f2 100644
--- a/bumper/confserver.py
+++ b/bumper/confserver.py
@@ -8,9 +8,12 @@ import random
import bumper
import os
from bumper.models import *
+from bumper import plugins
from datetime import datetime, timedelta
import asyncio
from aiohttp import web
+import aiohttp_jinja2
+import jinja2
import uuid
import xml.etree.ElementTree as ET
@@ -45,110 +48,85 @@ class ConfServer:
self.app = None
self.site = None
self.runner = None
+ self.runners = []
+ self.excludelogging = ["base", "remove-bot", "remove-client", "restart-service"]
def get_milli_time(self, timetoconvert):
return int(round(timetoconvert * 1000))
def confserver_app(self):
- self.app = web.Application(loop=asyncio.get_event_loop(), middlewares=[self.log_all_requests])
+ self.app = web.Application(loop=asyncio.get_event_loop(), middlewares=[
+ self.log_all_requests,
+ ])
+ aiohttp_jinja2.setup(self.app, loader=jinja2.FileSystemLoader(os.path.join(bumper.data_dir, "web","templates")))
self.app.add_routes(
[
- web.get("", self.handle_base),
- web.get("/restart_{service}", self.handle_RestartService, name='restart-service'),
- web.get(
- "/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/login",
- self.handle_login,
- ),
- web.get(
- "/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkLogin",
- self.handle_login,
- ),
- web.get( # EcoVacs Home GetUserAccountInfo
- "/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/getUserAccountInfo",
- self.handle_getUserAccountInfo,
- ),
- web.get(
- "/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/logout",
- self.handle_logout,
- ),
- web.get(
- "/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/getAuthCode",
- self.handle_getAuthCode,
- ),
- web.get( # EcoVacs Home GetAuthCode
- "/{apiversion}/{apptype}/auth/getAuthCode", self.handle_getAuthCode
- ),
- web.get(
- "/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkAgreement",
- self.handle_checkAgreement,
- ),
- web.get( # EcoVacs Home CheckAgreement
- "/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkAgreementBatch",
- self.handle_checkAgreement,
- ),
- web.get(
- "/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/common/checkVersion",
- self.handle_checkVersion,
- ),
- web.get( # EcoVacs Home CheckAPPVersion
- "/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/common/checkAPPVersion",
- self.handle_checkAPPVersion,
- ),
- web.get( # EcoVacs Home Upload Device Info
- "/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/common/uploadDeviceInfo",
- self.handle_uploadDeviceInfo,
- ),
- web.get( # EcoVacs Home GetAdByPositionType
- "/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/ad/getAdByPositionType",
- self.handle_getAdByPositionType,
- ),
- web.get( # EcoVacs Home Get Boot Screen
- "/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/ad/getBootScreen",
- self.handle_getBootScreen,
- ),
- web.get( # EcoVacs Home message hasUnreadMsg
- "/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/message/hasUnreadMsg",
- self.handle_hasUnreadMessage,
- ),
- web.post( # EcoVacs Home neng message hasUnreadMsg
- "/api/neng/message/hasUnreadMsg", self.handle_neng_hasUnreadMessage
- ),
- web.get( # EcoVacs Home message getMsgList
- "/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/message/getMsgList",
- self.handle_getMsgList,
- ),
- web.get( # EcoVacs Home common getSystemReminder
- "/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/common/getSystemReminder",
- self.handle_getSystemReminder,
- ),
- web.get( # EcoVacs Home shop getCnWapShopConfig
- "/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/shop/getCnWapShopConfig",
- self.handle_getCnWapShopConfig,
- ),
- web.get(
- "/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/campaign/homePageAlert",
- self.handle_homePageAlert,
- ),
- web.post("/api/users/user.do", self.handle_usersapi),
- web.get("/api/users/user.do", self.handle_usersapi),
- web.post("/api/appsvr/app.do", self.handle_appsvr_api), # EcoVacs Home
- web.get("/api/appsvr/app.do", self.handle_appsvr_api), # EcoVacs Home
- web.post(
- "/api/pim/product/getProductIotMap", self.handle_getProductIotMap, name='getProductIotMap'
- ),
- web.post("/api/lg/log.do", self.handle_lg_log), # EcoVacs Home
- web.post("/api/iot/devmanager.do", self.handle_devmanager_botcommand),
- web.post(
- "/api/dim/devmanager.do", self.handle_dim_devmanager
- ), # EcoVacs Home
+ web.get("", self.handle_base, name="base"),
+ web.get("/bot/remove/{did}", self.handle_RemoveBot, name='remove-bot'),
+ web.get("/client/remove/{resource}", self.handle_RemoveClient, name='remove-client'),
+ web.get("/restart_{service}", self.handle_RestartService, name='restart-service'),
web.post("/lookup.do", self.handle_lookup),
- web.get("/api/pim/file/get/{id}", self.handle_pimFile)
+
]
)
- # Direct register from app:
- # /{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/directRegister
+
+ # common api paths
+ api_v1 = {"prefix": "/v1/", "app": web.Application()} # for /v1/
+ api_v2 = {"prefix": "/v2/", "app": web.Application()} # for /v2/
+ portal_api = {"prefix": "/api/", "app": web.Application()} # for /api/
+
+ apis = {
+ "api_v1": api_v1,
+ "api_v2": api_v2,
+ "portal_api": portal_api,
+
+ }
+
+ # Load plugins
+ for plug in bumper.discovered_plugins:
+ if isinstance(bumper.discovered_plugins[plug].plugin, bumper.plugins.ConfServerApp):
+ plugin = bumper.discovered_plugins[plug].plugin
+ if plugin.plugin_type == "sub_api": # app or sub_api
+ if plugin.sub_api in apis:
+ if plugin.routes:
+ logging.debug(f"Adding confserver sub_api ({plugin.name})")
+ apis[plugin.sub_api]["app"].add_routes(plugin.routes)
+
+ elif plugin.plugin_type == "app":
+ if plugin.path_prefix and plugin.app:
+ logging.debug(f"Adding confserver plugin ({plugin.name})")
+ self.app.add_subapp(plugin.path_prefix, plugin.app)
+
+ for api in apis:
+ self.app.add_subapp(apis[api]["prefix"], apis[api]["app"])
+
+ for resource in self.app.router.resources():
+ print(resource)
+
+
+
+ async def start_site(self, app, address='localhost', port=8080, usessl=False):
+ runner = web.AppRunner(app)
+ self.runners.append(runner)
+ await runner.setup()
+ if usessl:
+ ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
+ ssl_ctx.load_cert_chain(bumper.server_cert, bumper.server_key)
+ site = web.TCPSite(
+ runner,
+ host=address,
+ port=port,
+ ssl_context=ssl_ctx,
+ )
+
+ else:
+ site = web.TCPSite(
+ runner, host=address, port=port
+ )
+
+ await site.start()
async def start_server(self):
try:
@@ -195,12 +173,12 @@ class ConfServer:
async def handle_base(self, request):
try:
- # TODO - API Options here for viewing clients, tokens, restarting the server, etc.
- # text = "Bumper!"
+
bots = bumper.db_get().table("bots").all()
clients = bumper.db_get().table("clients").all()
helperbot = bumper.mqtt_helperbot.Client.session.transitions.state
mqttserver = bumper.mqtt_server.broker
+ xmppserver = bumper.xmpp_server
mq_sessions = []
for sess in mqttserver._sessions:
tmpsess = []
@@ -224,119 +202,136 @@ class ConfServer:
]
},
],
- }
-
- return web.json_response(all)
+ "xmpp_server": xmppserver
+ }
+ resp = aiohttp_jinja2.render_template('home.jinja2', request, context=all)
+ #return web.json_response(all)
+ return resp
except Exception as e:
confserverlog.exception("{}".format(e))
@web.middleware
async def log_all_requests(self, request, handler):
-
- try:
- if request.content_length:
- if request.content_type == "application/x-www-form-urlencoded":
- postbody = await request.post()
- elif request.content_type == "application/json":
- try:
- postbody = json.loads(await request.text())
- except Exception as e:
- confserverlog.error("Request body not json: {} - {}".format(e, e.doc))
- postbody = e.doc
-
- else:
- postbody = await request.post()
- else:
- postbody = None
-
- response = await handler(request)
- if not "application/octet-stream" in response.content_type:
- logall = {
- "request": {
- "route_name": f"{request.match_info.route.name}",
- "method": f"{request.method}",
- "path": f"{request.path}",
- "query_string": f"{request.query_string}",
- "raw_path": f"{request.raw_path}",
- "raw_headers": f'{",".join(map("{}".format, request.raw_headers))}',
- "body": f"{postbody}",
- },
-
- "response": {
- "response_body": f"{json.loads(response.body)}",
- "status": f"{response.status}",
- }
- }
- else:
- logall = {
- "request": {
- "route_name": f"{request.match_info.route.name}",
- "method": f"{request.method}",
- "path": f"{request.path}",
- "query_string": f"{request.query_string}",
- "raw_path": f"{request.raw_path}",
- "raw_headers": f'{",".join(map("{}".format, request.raw_headers))}',
- "body": f"{postbody}",
- },
-
- "response": {
- "status": f"{response.status}",
- }
- }
-
- confserverlog.debug(json.dumps(logall))
+ if request._match_info.route.name not in self.excludelogging:
- return response
+ try:
+ if request.content_length:
+ if request.content_type == "application/x-www-form-urlencoded":
+ postbody = await request.post()
- except web.HTTPNotFound as notfound:
- confserverlog.debug("Request path {} not found".format(request.raw_path))
- requestlog = {
- "request": {
- "route_name": f"{request.match_info.route.name}",
- "method": f"{request.method}",
- "path": f"{request.path}",
- "query_string": f"{request.query_string}",
- "raw_path": f"{request.raw_path}",
- "raw_headers": f'{",".join(map("{}".format, request.raw_headers))}',
- "body": f"{postbody}",
- }
- }
- confserverlog.debug(json.dumps(requestlog))
- return notfound
+ elif request.content_type == "application/json":
+ try:
+ postbody = json.loads(await request.text())
+ except Exception as e:
+ confserverlog.error("Request body not json: {} - {}".format(e, e.doc))
+ postbody = e.doc
+
+ else:
+ postbody = await request.post()
+ else:
+ postbody = None
- except Exception as e:
- confserverlog.exception("{}".format(e))
- requestlog = {
- "request": {
- "route_name": f"{request.match_info.route.name}",
- "method": f"{request.method}",
- "path": f"{request.path}",
- "query_string": f"{request.query_string}",
- "raw_path": f"{request.raw_path}",
- "raw_headers": f'{",".join(map("{}".format, request.raw_headers))}',
- "body": f"{postbody}",
- }
- }
- confserverlog.debug(json.dumps(requestlog))
- return e
+ response = await handler(request)
+ if not "application/octet-stream" in response.content_type:
+ logall = {
+ "request": {
+ "route_name": f"{request.match_info.route.name}",
+ "method": f"{request.method}",
+ "path": f"{request.path}",
+ "query_string": f"{request.query_string}",
+ "raw_path": f"{request.raw_path}",
+ "raw_headers": f'{",".join(map("{}".format, request.raw_headers))}',
+ "body": f"{postbody}",
+ },
+
+ "response": {
+ "response_body": f"{json.loads(response.body)}",
+ "status": f"{response.status}",
+ }
+ }
+ else:
+ logall = {
+ "request": {
+ "route_name": f"{request.match_info.route.name}",
+ "method": f"{request.method}",
+ "path": f"{request.path}",
+ "query_string": f"{request.query_string}",
+ "raw_path": f"{request.raw_path}",
+ "raw_headers": f'{",".join(map("{}".format, request.raw_headers))}',
+ "body": f"{postbody}",
+ },
+
+ "response": {
+ "status": f"{response.status}",
+ }
+ }
+
+ confserverlog.debug(json.dumps(logall))
+
+ return response
+
+ except web.HTTPNotFound as notfound:
+ confserverlog.debug("Request path {} not found".format(request.raw_path))
+ requestlog = {
+ "request": {
+ "route_name": f"{request.match_info.route.name}",
+ "method": f"{request.method}",
+ "path": f"{request.path}",
+ "query_string": f"{request.query_string}",
+ "raw_path": f"{request.raw_path}",
+ "raw_headers": f'{",".join(map("{}".format, request.raw_headers))}',
+ "body": f"{postbody}",
+ }
+ }
+ confserverlog.debug(json.dumps(requestlog))
+ return notfound
+
+ except Exception as e:
+ confserverlog.exception("{}".format(e))
+ requestlog = {
+ "request": {
+ "route_name": f"{request.match_info.route.name}",
+ "method": f"{request.method}",
+ "path": f"{request.path}",
+ "query_string": f"{request.query_string}",
+ "raw_path": f"{request.raw_path}",
+ "raw_headers": f'{",".join(map("{}".format, request.raw_headers))}',
+ "body": f"{postbody}",
+ }
+ }
+ confserverlog.debug(json.dumps(requestlog))
+ return e
+
+ else:
+ return await handler(request)
async def restart_Helper(self):
await bumper.mqtt_helperbot.Client.disconnect()
- await bumper.mqtt_helperbot.start_helper_bot()
+ asyncio.create_task(bumper.mqtt_helperbot.start_helper_bot())
async def restart_MQTT(self):
- mqttserver = bumper.mqtt_server.broker
+
+ if not (bumper.mqtt_server.broker.transitions.state == "stopped" or bumper.mqtt_server.broker.transitions.state == "not_started"):
+ # close session writers - this was required so bots would reconnect properly after restarting
+ for sess in list(bumper.mqtt_server.broker._sessions):
+ sessobj = bumper.mqtt_server.broker._sessions[sess][1]
+ if sessobj.session.transitions.state == "connected":
+ await sessobj.writer.close()
- await bumper.mqtt_server.broker.shutdown()
- while not bumper.mqtt_server.broker.transitions.state == "stopped":
- await asyncio.sleep(0.1)
+ #await bumper.mqtt_server.broker.shutdown()
+ aloop = asyncio.get_event_loop()
+ aloop.call_later(
+ 0.1, lambda: asyncio.create_task(bumper.mqtt_server.broker.shutdown())
+ ) # In .1 seconds shutdown broker
- await bumper.mqtt_server.broker_coro()
- while not bumper.mqtt_server.broker.transitions.state == "started":
- await asyncio.sleep(0.1)
+
+ aloop = asyncio.get_event_loop()
+ aloop.call_later(
+ 1.5, lambda: asyncio.create_task(bumper.mqtt_server.broker_coro())
+ ) # In 1.5 seconds start broker
async def restart_XMPP(self):
bumper.xmpp_server.disconnect()
@@ -349,11 +344,12 @@ class ConfServer:
await self.restart_Helper()
return web.json_response({"status": "complete"})
elif service == "MQTTServer":
- await self.restart_MQTT()
+ asyncio.create_task(self.restart_MQTT())
aloop = asyncio.get_event_loop()
aloop.call_later(
- 2, lambda: asyncio.create_task(self.restart_Helper())
- ) # In 2 seconds restart Helperbot
+ 5, lambda: asyncio.create_task(self.restart_Helper())
+ ) # In 5 seconds restart Helperbot
+
return web.json_response({"status": "complete"})
elif service == "XMPPServer":
await self.restart_XMPP()
@@ -363,6 +359,33 @@ class ConfServer:
except Exception as e:
confserverlog.exception("{}".format(e))
+ pass
+
+ async def handle_RemoveBot(self, request):
+ try:
+ did = request.match_info.get("did", "")
+ bumper.bot_remove(did)
+ if bumper.bot_get(did):
+ return web.json_response({"status": "failed to remove bot"})
+ else:
+ return web.json_response({"status": "successfully removed bot"})
+
+ except Exception as e:
+ confserverlog.exception("{}".format(e))
+ pass
+
+ async def handle_RemoveClient(self, request):
+ try:
+ resource = request.match_info.get("resource", "")
+ bumper.client_remove(resource)
+ if bumper.client_get(resource):
+ return web.json_response({"status": "failed to remove client"})
+ else:
+ return web.json_response({"status": "successfully removed client"})
+
+ except Exception as e:
+ confserverlog.exception("{}".format(e))
+ pass
async def handle_login(self, request):
try:
@@ -435,655 +458,6 @@ class ConfServer:
except Exception as e:
confserverlog.exception("{}".format(e))
- def handle_getUserAccountInfo(self, request):
- try:
- user_devid = request.match_info.get("devid", "")
- countrycode = request.match_info.get("country", "us")
- apptype = request.match_info.get("apptype", "")
- user = bumper.user_by_deviceid(user_devid)
-
- if "global_" in apptype: # EcoVacs Home
- login_details = EcoVacsHome_Login()
- login_details.ucUid = "fuid_{}".format(user["userid"])
- login_details.loginName = "fusername_{}".format(user["userid"])
- login_details.mobile = None
- else:
- login_details = EcoVacs_Login()
-
- login_details.uid = "fuid_{}".format(user["userid"])
- login_details.username = "fusername_{}".format(user["userid"])
- login_details.country = countrycode
- login_details.email = "null@null.com"
-
- body = {
- "code": bumper.RETURN_API_SUCCESS,
- "data": {
- "email": login_details.email,
- "hasMobile": "N",
- "hasPassword": "Y",
- "uid": login_details.uid,
- "userName": login_details.username,
- "obfuscatedMobile": None,
- "mobile": None,
- "loginName": login_details.loginName,
- },
- "msg": "操作成功",
- "time": self.get_milli_time(datetime.utcnow().timestamp()),
- }
- return web.json_response(body)
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
- def check_token(self, apptype, countrycode, user, token):
- try:
- if bumper.check_token(user["userid"], token):
-
- if "global_" in apptype: # EcoVacs Home
- login_details = EcoVacsHome_Login()
- login_details.ucUid = "fuid_{}".format(user["userid"])
- login_details.loginName = "fusername_{}".format(user["userid"])
- login_details.mobile = None
- else:
- login_details = EcoVacs_Login()
-
- login_details.accessToken = token
- login_details.uid = "fuid_{}".format(user["userid"])
- login_details.username = "fusername_{}".format(user["userid"])
- login_details.country = countrycode
- login_details.email = "null@null.com"
-
- body = {
- "code": bumper.RETURN_API_SUCCESS,
- "data": json.loads(login_details.toJSON()),
- # {
- # "accessToken": self.generate_token(tmpuser), # Generate a token
- # "country": countrycode,
- # "email": "null@null.com",
- # "uid": "fuid_{}".format(tmpuser["userid"]),
- # "username": "fusername_{}".format(tmpuser["userid"]),
- # },
- "msg": "操作成功",
- "time": self.get_milli_time(datetime.utcnow().timestamp()),
- }
- return web.json_response(body)
-
- else:
- body = {
- "code": bumper.ERR_TOKEN_INVALID,
- "data": None,
- "msg": "当前密码错误",
- "time": self.get_milli_time(datetime.utcnow().timestamp()),
- }
- return web.json_response(body)
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
- def generate_token(self, user):
- try:
- tmpaccesstoken = uuid.uuid4().hex
- bumper.user_add_token(user["userid"], tmpaccesstoken)
- return tmpaccesstoken
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
- def generate_authcode(self, user, countrycode, token):
- try:
- tmpauthcode = "{}_{}".format(countrycode, uuid.uuid4().hex)
- bumper.user_add_authcode(user["userid"], token, tmpauthcode)
- return tmpauthcode
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
- def _auth_any(self, devid, apptype, country, request):
- try:
- user_devid = devid
- countrycode = country
- user = bumper.user_by_deviceid(user_devid)
- bots = bumper.db_get().table("bots").all()
-
- if user: # Default to user 0
- tmpuser = user
- if "global_" in apptype: # EcoVacs Home
- login_details = EcoVacsHome_Login()
- login_details.ucUid = "fuid_{}".format(tmpuser["userid"])
- login_details.loginName = "fusername_{}".format(tmpuser["userid"])
- login_details.mobile = None
- else:
- login_details = EcoVacs_Login()
-
- login_details.accessToken = self.generate_token(tmpuser)
- login_details.uid = "fuid_{}".format(tmpuser["userid"])
- login_details.username = "fusername_{}".format(tmpuser["userid"])
- login_details.country = countrycode
- login_details.email = "null@null.com"
- bumper.user_add_device(tmpuser["userid"], user_devid)
- else:
- bumper.user_add("tmpuser") # Add a new user
- tmpuser = bumper.user_get("tmpuser")
- if "global_" in apptype: # EcoVacs Home
- login_details = EcoVacsHome_Login()
- login_details.ucUid = "fuid_{}".format(tmpuser["userid"])
- login_details.loginName = "fusername_{}".format(tmpuser["userid"])
- login_details.mobile = None
- else:
- login_details = EcoVacs_Login()
-
- login_details.accessToken = self.generate_token(tmpuser)
- login_details.uid = "fuid_{}".format(tmpuser["userid"])
- login_details.username = "fusername_{}".format(tmpuser["userid"])
- login_details.country = countrycode
- login_details.email = "null@null.com"
- bumper.user_add_device(tmpuser["userid"], user_devid)
-
- for bot in bots: # Add all bots to the user
- if "did" in bot:
- bumper.user_add_bot(tmpuser["userid"], bot["did"])
- else:
- confserverlog.error("No DID for bot: {}".format(bot))
-
- if "checkLogin" in request.path: # If request was to check a token do so
- checkToken = self.check_token(
- apptype, countrycode, tmpuser, request.query["accessToken"]
- )
- isGood = json.loads(checkToken.text)
- if isGood["code"] == "0000":
- return isGood
-
- # Deactivate old tokens and authcodes
- bumper.user_revoke_expired_tokens(tmpuser["userid"])
-
- body = {
- "code": bumper.RETURN_API_SUCCESS,
- "data": json.loads(login_details.toJSON()),
- # {
- # "accessToken": self.generate_token(tmpuser), # Generate a token
- # "country": countrycode,
- # "email": "null@null.com",
- # "uid": "fuid_{}".format(tmpuser["userid"]),
- # "username": "fusername_{}".format(tmpuser["userid"]),
- # },
- "msg": "操作成功",
- "time": self.get_milli_time(datetime.utcnow().timestamp()),
- }
-
- return body
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
- async def handle_logout(self, request):
- try:
- user_devid = request.match_info.get("devid", "")
- if not user_devid == "":
- user = bumper.user_by_deviceid(user_devid)
- if user:
- if bumper.check_token(user["userid"], request.query["accessToken"]):
- # Deactivate old tokens and authcodes
- bumper.user_revoke_token(
- user["userid"], request.query["accessToken"]
- )
-
- body = {
- "code": bumper.RETURN_API_SUCCESS,
- "data": None,
- "msg": "操作成功",
- "time": self.get_milli_time(datetime.utcnow().timestamp()),
- }
-
- return web.json_response(body)
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
- async def handle_getAuthCode(self, request):
- try:
- apptype = request.match_info.get("apptype", "")
- user_devid = request.match_info.get("devid", "") # Ecovacs
- if user_devid == "":
- user_devid = request.query["deviceId"] # Ecovacs Home
-
- if not user_devid == "":
- user = bumper.user_by_deviceid(user_devid)
- token = ""
- if user:
- if "accessToken" in request.query:
- token = bumper.user_get_token(
- user["userid"], request.query["accessToken"]
- )
- if token:
- authcode = ""
- if not "authcode" in token:
- authcode = self.generate_authcode(
- user,
- request.match_info.get("country", "us"),
- request.query["accessToken"],
- )
- else:
- authcode = token["authcode"]
- if "global" in apptype:
- body = {
- "code": bumper.RETURN_API_SUCCESS,
- "data": {
- "authCode": authcode,
- "ecovacsUid": request.query["uid"],
- },
- "msg": "操作成功",
- "success": True,
- "time": self.get_milli_time(
- datetime.utcnow().timestamp()
- ),
- }
- else:
- body = {
- "code": bumper.RETURN_API_SUCCESS,
- "data": {
- "authCode": authcode,
- "ecovacsUid": request.query["uid"],
- },
- "msg": "操作成功",
- "time": self.get_milli_time(
- datetime.utcnow().timestamp()
- ),
- }
- return web.json_response(body)
-
- body = {
- "code": bumper.ERR_TOKEN_INVALID,
- "data": None,
- "msg": "当前密码错误",
- "time": self.get_milli_time(datetime.utcnow().timestamp()),
- }
-
- return web.json_response(body)
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
- async def handle_checkVersion(self, request):
- try:
- body = {
- "code": bumper.RETURN_API_SUCCESS,
- "data": {
- "c": None,
- "img": None,
- "r": 0,
- "t": None,
- "u": None,
- "ut": 0,
- "v": None,
- },
- "msg": "操作成功",
- "time": self.get_milli_time(datetime.utcnow().timestamp()),
- }
-
- return web.json_response(body)
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
- async def handle_checkAPPVersion(self, request): # EcoVacs Home
- try:
- body = {
- "code": bumper.RETURN_API_SUCCESS,
- "data": {
- "c": None,
- "downPageUrl": None,
- "img": None,
- "nextAlertTime": None,
- "r": 0,
- "t": None,
- "u": None,
- "ut": 0,
- "v": None,
- },
- "msg": "操作成功",
- "success": True,
- "time": self.get_milli_time(datetime.utcnow().timestamp()),
- }
-
- return web.json_response(body)
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
- async def handle_uploadDeviceInfo(self, request): # EcoVacs Home
- try:
- body = {
- "code": bumper.RETURN_API_SUCCESS,
- "data": None,
- "msg": "操作成功",
- "success": True,
- "time": self.get_milli_time(datetime.utcnow().timestamp()),
- }
-
- return web.json_response(body)
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
- async def handle_getAdByPositionType(self, request): # EcoVacs Home
- try:
- body = {
- "code": bumper.RETURN_API_SUCCESS,
- "data": None,
- "msg": "操作成功",
- "success": True,
- "time": self.get_milli_time(datetime.utcnow().timestamp()),
- }
-
- return web.json_response(body)
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
- async def handle_getBootScreen(self, request): # EcoVacs Home
- try:
- body = {
- "code": bumper.RETURN_API_SUCCESS,
- "data": None,
- "msg": "操作成功",
- "success": True,
- "time": self.get_milli_time(datetime.utcnow().timestamp()),
- }
-
- return web.json_response(body)
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
- async def handle_hasUnreadMessage(self, request): # EcoVacs Home
- try:
- body = {
- "code": bumper.RETURN_API_SUCCESS,
- "data": "N",
- "msg": "操作成功",
- "success": True,
- "time": self.get_milli_time(datetime.utcnow().timestamp()),
- }
-
- return web.json_response(body)
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
- async def handle_neng_hasUnreadMessage(self, request): # EcoVacs Home
- try:
- body = {"code": 0, "data": {"hasUnRead": True}}
-
- return web.json_response(body)
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
- async def handle_getMsgList(self, request): # EcoVacs Home
- try:
- body = {
- "code": bumper.RETURN_API_SUCCESS,
- "data": {"hasNextPage": 0, "items": []},
- "msg": "操作成功",
- "success": True,
- "time": self.get_milli_time(datetime.utcnow().timestamp()),
- }
-
- return web.json_response(body)
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
- async def handle_getCnWapShopConfig(self, request): # EcoVacs Home
- try:
- body = {
- "code": bumper.RETURN_API_SUCCESS,
- "data": {
- "myShopShowFlag": "N",
- "myShopUrl": "",
- "shopIndexShowFlag": "N",
- "shopIndexUrl": "",
- },
- "msg": "操作成功",
- "success": True,
- "time": self.get_milli_time(datetime.utcnow().timestamp()),
- }
-
- return web.json_response(body)
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
- async def handle_getSystemReminder(self, request): # EcoVacs Home
- try:
- body = {
- "code": bumper.RETURN_API_SUCCESS,
- "data": {
- "iosGradeTime": {"iodGradeFlag": "N"},
- "openNotification": {
- "openNotificationContent": None,
- "openNotificationFlag": "N",
- "openNotificationTitle": None,
- },
- },
- "msg": "操作成功",
- "success": True,
- "time": self.get_milli_time(datetime.utcnow().timestamp()),
- }
-
- return web.json_response(body)
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
- async def handle_checkAgreement(self, request):
- try:
- apptype = request.match_info.get("apptype", "")
- if "global_" in apptype:
- body = {
- "code": bumper.RETURN_API_SUCCESS,
- "data": [
- {
- "force": "N",
- "id": "20180804040641_7d746faf18b8cb22a50d145598fe4c90",
- "type": "USER",
- "url": "https://bumper.ecovacs.com/content/agreement?id=20180804040641_7d746faf18b8cb22a50d145598fe4c90&language=EN", # "https://gl-us-wap.ecovacs.com/content/agreement?id=20180804040641_7d746faf18b8cb22a50d145598fe4c90&language=EN
- "version": "1.01",
- },
- {
- "force": "N",
- "id": "20180804040245_4e7c56dfb7ebd3b81b1f2747d0859fac",
- "type": "PRIVACY",
- "url": "https://bumper.ecovacs.com/content/agreement?id=20180804040245_4e7c56dfb7ebd3b81b1f2747d0859fac&language=EN", # "https://gl-us-wap.ecovacs.com/content/agreement?id=20180804040245_4e7c56dfb7ebd3b81b1f2747d0859fac&language=EN"
- "version": "1.01",
- },
- ],
- "msg": "操作成功",
- "success": True,
- "time": self.get_milli_time(datetime.utcnow().timestamp()),
- }
- else:
- body = {
- "code": bumper.RETURN_API_SUCCESS,
- "data": [],
- "msg": "操作成功",
- "time": self.get_milli_time(datetime.utcnow().timestamp()),
- }
-
- return web.json_response(body)
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
- async def handle_homePageAlert(self, request):
- try:
- nextAlert = self.get_milli_time(
- (datetime.now() + timedelta(hours=12)).timestamp()
- )
-
- body = {
- "code": bumper.RETURN_API_SUCCESS,
- "data": {
- "clickSchemeUrl": None,
- "clickWebUrl": None,
- "hasCampaign": "N",
- "imageUrl": None,
- "nextAlertTime": nextAlert,
- "serverTime": self.get_milli_time(datetime.utcnow().timestamp()),
- },
- "msg": "操作成功",
- "time": self.get_milli_time(datetime.utcnow().timestamp()),
- }
-
- return web.json_response(body)
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
- async def handle_getProductIotMap(self, request):
- try:
- body = {
- "code": bumper.RETURN_API_SUCCESS,
- "data": EcoVacsHomeProducts,
- }
- return web.json_response(body)
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
- async def handle_usersapi(self, request):
- if not request.method == "GET": # Skip GET for now
- try:
-
- body = {}
- postbody = {}
- if request.content_type == "application/x-www-form-urlencoded":
- postbody = await request.post()
-
- else:
- postbody = json.loads(await request.text())
-
- todo = postbody["todo"]
- if todo == "FindBest":
- service = postbody["service"]
- if service == "EcoMsgNew":
- srvip = bumper.bumper_announce_ip
- srvport = 5223
- confserverlog.info(
- "Announcing EcoMsgNew Server to bot as: {}:{}".format(
- srvip, srvport
- )
- )
- msgserver = {"ip": srvip, "port": srvport, "result": "ok"}
- msgserver = json.dumps(msgserver)
- msgserver = msgserver.replace(
- " ", ""
- ) # bot seems to be very picky about having no spaces, only way was with text
-
- return web.json_response(text=msgserver)
-
- elif service == "EcoUpdate":
- srvip = "47.88.66.164" # EcoVacs Server
- srvport = 8005
- confserverlog.info(
- "Announcing EcoUpdate Server to bot as: {}:{}".format(
- srvip, srvport
- )
- )
- body = {"result": "ok", "ip": srvip, "port": srvport}
-
- elif todo == "loginByItToken":
- if "userId" in postbody:
- if bumper.check_authcode(postbody["userId"], postbody["token"]):
- body = {
- "resource": postbody["resource"],
- "result": "ok",
- "todo": "result",
- "token": postbody["token"],
- "userId": postbody["userId"],
- }
- else: # EcoVacs Home LoginByITToken
- loginToken = bumper.loginByItToken(postbody["token"])
- if not loginToken == {}:
- body = {
- "resource": postbody["resource"],
- "result": "ok",
- "todo": "result",
- "token": loginToken["token"],
- "userId": loginToken["userid"],
- }
- else:
- body = {"result": "fail", "todo": "result"}
-
- elif todo == "GetDeviceList":
- body = {
- "devices": bumper.db_get().table("bots").all(),
- "result": "ok",
- "todo": "result",
- }
-
- elif todo == "SetDeviceNick":
- bumper.bot_set_nick(postbody["did"], postbody["nick"])
- body = {"result": "ok", "todo": "result"}
-
- elif todo == "AddOneDevice":
- bumper.bot_set_nick(postbody["did"], postbody["nick"])
- body = {"result": "ok", "todo": "result"}
-
- elif todo == "DeleteOneDevice":
- bumper.bot_remove(postbody["did"])
- body = {"result": "ok", "todo": "result"}
-
- return web.json_response(body)
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
- # Return fail for GET
- body = {"result": "fail", "todo": "result"}
- return web.json_response(body)
-
- async def handle_appsvr_api(self, request):
- if not request.method == "GET": # Skip GET for now
- try:
-
- body = {}
- postbody = {}
- if request.content_type == "application/x-www-form-urlencoded":
- postbody = await request.post()
-
- else:
- postbody = json.loads(await request.text())
-
- todo = postbody["todo"]
-
- if todo == "GetGlobalDeviceList": # EcoVacs Home
- bots = bumper.db_get().table("bots").all()
- botlist = []
- for bot in bots:
- if bot["class"] != "":
- b = bumper.bot_toEcoVacsHome_JSON(bot)
- if (
- not b is None
- ): # Happens if the bot isn't on the EcoVacs Home list
- botlist.append(json.loads(b))
-
- body = {
- "code": 0,
- "devices": botlist,
- "ret": "ok",
- "todo": "result",
- }
-
- return web.json_response(body)
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
- # Return fail for GET
- body = {"result": "fail", "todo": "result"}
- return web.json_response(body)
-
async def handle_lookup(self, request):
try:
@@ -1131,181 +505,6 @@ class ConfServer:
except Exception as e:
confserverlog.exception("{}".format(e))
- async def handle_lg_log(self, request): # EcoVacs Home
- try:
- json_body = json.loads(await request.text())
-
- randomid = "".join(random.sample(string.ascii_letters, 6))
- did = json_body["did"]
-
- botdetails = bumper.bot_get(did)
- if botdetails:
- if not "cmdName" in json_body:
- if "td" in json_body:
- json_body["cmdName"] = json_body["td"]
-
- if not "toId" in json_body:
- json_body["toId"] = did
-
- if not "toType" in json_body:
- json_body["toType"] = botdetails["class"]
-
- if not "toRes" in json_body:
- json_body["toRes"] = botdetails["resource"]
-
- if not "payloadType" in json_body:
- json_body["payloadType"] = "x"
-
- if not "payload" in json_body:
- #json_body["payload"] = ""
- if json_body["td"] == "GetCleanLogs":
- json_body["td"] = "q"
- json_body["payload"] = ''
-
- if did != "":
- bot = bumper.bot_get(did)
- if bot["company"] == "eco-ng":
- retcmd = await bumper.mqtt_helperbot.send_command(
- json_body, randomid
- )
- body = retcmd
- confserverlog.debug("Send Bot - {}".format(json_body))
- confserverlog.debug("Bot Response - {}".format(body))
- logs = []
- logsroot = ET.fromstring(retcmd["resp"])
- if logsroot.attrib["ret"] == "ok":
- cleanlogs = logsroot.getchildren()
- for l in cleanlogs:
- cleanlog = {
- "ts": l.attrib['s'],
- "area": l.attrib['a'],
- "last": l.attrib['l'],
- "cleanType": l.attrib['t'],
- #imageUrl allows for providing images of cleanings, something to look into later
- #"imageUrl": "https://localhost:8007",
- }
- logs.append(cleanlog)
- body = {
- "ret": "ok",
- "logs": logs,
- }
-
- else:
- body = {"ret": "ok", "logs": []}
-
- confserverlog.debug("lg logs return: {}".format(json.dumps(body)))
- return web.json_response(body)
- else:
- # No response, send error back
- confserverlog.error(
- "No bots with DID: {} connected to MQTT".format(
- json_body["toId"]
- )
- )
- body = {"id": randomid, "errno": bumper.ERR_COMMON, "ret": "fail"}
- return web.json_response(body)
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
- async def handle_devmanager_botcommand(self, request):
- try:
- json_body = json.loads(await request.text())
-
- randomid = "".join(random.sample(string.ascii_letters, 6))
- did = ""
- if "toId" in json_body: # Its a command
- did = json_body["toId"]
-
- if did != "":
- bot = bumper.bot_get(did)
- if bot["company"] == "eco-ng":
- retcmd = await bumper.mqtt_helperbot.send_command(
- json_body, randomid
- )
- body = retcmd
- confserverlog.debug("Send Bot - {}".format(json_body))
- confserverlog.debug("Bot Response - {}".format(body))
- return web.json_response(body)
- else:
- # No response, send error back
- confserverlog.error(
- "No bots with DID: {} connected to MQTT".format(
- json_body["toId"]
- )
- )
- body = {
- "id": randomid,
- "errno": 500,
- "ret": "fail",
- "debug": "wait for response timed out",
- }
- return web.json_response(body)
-
- else:
- if "td" in json_body: # Seen when doing initial wifi config
- if json_body["td"] == "PollSCResult":
- body = {"ret": "ok"}
- return web.json_response(body)
-
- if json_body["td"] == "HasUnreadMsg": # EcoVacs Home
- body = {"ret": "ok", "unRead": False}
- return web.json_response(body)
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
- async def handle_dim_devmanager(self, request): # Used in EcoVacs Home App
- try:
- json_body = json.loads(await request.text())
-
- randomid = "".join(random.sample(string.ascii_letters, 6))
- did = ""
- if "toId" in json_body: # Its a command
- did = json_body["toId"]
-
- if did != "":
- bot = bumper.bot_get(did)
- if bot["company"] == "eco-ng" and bot["mqtt_connection"] == True:
- retcmd = await bumper.mqtt_helperbot.send_command(
- json_body, randomid
- )
- body = retcmd
- confserverlog.debug("Send Bot - {}".format(json_body))
- confserverlog.debug("Bot Response - {}".format(body))
- return web.json_response(body)
- else:
- # No response, send error back
- confserverlog.error(
- "No bots with DID: {} connected to MQTT".format(
- json_body["toId"]
- )
- )
- body = {"id": randomid, "errno": bumper.ERR_COMMON, "ret": "fail"}
- return web.json_response(body)
-
- else:
- if "td" in json_body: # Seen when doing initial wifi config
- if json_body["td"] == "PollSCResult":
- body = {"ret": "ok"}
- return web.json_response(body)
-
- if json_body["td"] == "HasUnreadMsg": # EcoVacs Home
- body = {"ret": "ok", "unRead": False}
- return web.json_response(body)
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
-
-
- async def handle_pimFile(self, request):
- try:
- fileID = request.match_info.get("id", "")
-
- return web.FileResponse(os.path.join(bumper.data_dir,"web","robotvac_image.jpg"))
-
- except Exception as e:
- confserverlog.exception("{}".format(e))
async def disconnect(self):
try:
@@ -1315,3 +514,356 @@ class ConfServer:
except Exception as e:
confserverlog.exception("{}".format(e))
+ class ConfServer_GeneralFunctions:
+ def __init__(self):
+ pass
+
+ def get_milli_time(self, timetoconvert):
+ return int(round(timetoconvert * 1000))
+
+ class ConfServer_AuthHandler:
+ def __init__(self):
+ self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
+ pass
+
+ def generate_token(self, user):
+ try:
+ tmpaccesstoken = uuid.uuid4().hex
+ bumper.user_add_token(user["userid"], tmpaccesstoken)
+ return tmpaccesstoken
+
+ except Exception as e:
+ confserverlog.exception("{}".format(e))
+
+ def generate_authcode(self, user, countrycode, token):
+ try:
+ tmpauthcode = "{}_{}".format(countrycode, uuid.uuid4().hex)
+ bumper.user_add_authcode(user["userid"], token, tmpauthcode)
+ return tmpauthcode
+
+ except Exception as e:
+ confserverlog.exception("{}".format(e))
+
+
+ async def login(self, request):
+ try:
+ user_devid = request.match_info.get("devid", "")
+ countrycode = request.match_info.get("country", "us")
+ apptype = request.match_info.get("apptype", "")
+ confserverlog.info(
+ "client with devid {} attempting login".format(user_devid)
+ )
+ if bumper.use_auth:
+ if (
+ not user_devid == ""
+ ): # Performing basic "auth" using devid, super insecure
+ user = bumper.user_by_deviceid(user_devid)
+ if "checkLogin" in request.path:
+ self.check_token(
+ apptype, countrycode, user, request.query["accessToken"]
+ )
+ else:
+ if "global_" in apptype: # EcoVacs Home
+ login_details = EcoVacsHome_Login()
+ login_details.ucUid = "fuid_{}".format(user["userid"])
+ login_details.loginName = "fusername_{}".format(
+ user["userid"]
+ )
+ login_details.mobile = None
+
+ else:
+ login_details = EcoVacs_Login()
+
+ # Deactivate old tokens and authcodes
+ bumper.user_revoke_expired_tokens(user["userid"])
+
+ login_details.accessToken = self.generate_token(user)
+ login_details.uid = "fuid_{}".format(user["userid"])
+ login_details.username = "fusername_{}".format(user["userid"])
+ login_details.country = countrycode
+ login_details.email = "null@null.com"
+
+ body = {
+ "code": API_ERRORS[RETURN_API_SUCCESS],
+ "data": json.loads(login_details.toJSON()),
+ # {
+ # "accessToken": self.generate_token(tmpuser), # Generate a token
+ # "country": countrycode,
+ # "email": "null@null.com",
+ # "uid": "fuid_{}".format(tmpuser["userid"]),
+ # "username": "fusername_{}".format(tmpuser["userid"]),
+ # },
+ "msg": "操作成功",
+ "time": self.get_milli_time(datetime.utcnow().timestamp()),
+ }
+
+ return web.json_response(body)
+
+ body = {
+ "code": bumper.ERR_USER_NOT_ACTIVATED,
+ "data": None,
+ "msg": "当前密码错误",
+ "time": self.get_milli_time(datetime.utcnow().timestamp()),
+ }
+
+ return web.json_response(body)
+
+ else:
+ return web.json_response(
+ self._auth_any(user_devid, apptype, countrycode, request)
+ )
+
+ except Exception as e:
+ confserverlog.exception("{}".format(e))
+
+
+ async def get_AuthCode(self, request):
+ try:
+ apptype = request.match_info.get("apptype", "")
+ user_devid = request.match_info.get("devid", "") # Ecovacs
+ if user_devid == "":
+ user_devid = request.query["deviceId"] # Ecovacs Home
+
+ if not user_devid == "":
+ user = bumper.user_by_deviceid(user_devid)
+ token = ""
+ if user:
+ if "accessToken" in request.query:
+ token = bumper.user_get_token(
+ user["userid"], request.query["accessToken"]
+ )
+ if token:
+ authcode = ""
+ if not "authcode" in token:
+ authcode = self.generate_authcode(
+ user,
+ request.match_info.get("country", "us"),
+ request.query["accessToken"],
+ )
+ else:
+ authcode = token["authcode"]
+ if "global" in apptype:
+ body = {
+ "code": bumper.RETURN_API_SUCCESS,
+ "data": {
+ "authCode": authcode,
+ "ecovacsUid": request.query["uid"],
+ },
+ "msg": "操作成功",
+ "success": True,
+ "time": self.get_milli_time(
+ datetime.utcnow().timestamp()
+ ),
+ }
+ else:
+ body = {
+ "code": bumper.RETURN_API_SUCCESS,
+ "data": {
+ "authCode": authcode,
+ "ecovacsUid": request.query["uid"],
+ },
+ "msg": "操作成功",
+ "time": self.get_milli_time(
+ datetime.utcnow().timestamp()
+ ),
+ }
+ return web.json_response(body)
+
+ body = {
+ "code": bumper.ERR_TOKEN_INVALID,
+ "data": None,
+ "msg": "当前密码错误",
+ "time": self.get_milli_time(datetime.utcnow().timestamp()),
+ }
+
+ return web.json_response(body)
+
+ except Exception as e:
+ confserverlog.exception("{}".format(e))
+
+ def check_token(self, apptype, countrycode, user, token):
+ try:
+ if bumper.check_token(user["userid"], token):
+
+ if "global_" in apptype: # EcoVacs Home
+ login_details = EcoVacsHome_Login()
+ login_details.ucUid = "fuid_{}".format(user["userid"])
+ login_details.loginName = "fusername_{}".format(user["userid"])
+ login_details.mobile = None
+ else:
+ login_details = EcoVacs_Login()
+
+ login_details.accessToken = token
+ login_details.uid = "fuid_{}".format(user["userid"])
+ login_details.username = "fusername_{}".format(user["userid"])
+ login_details.country = countrycode
+ login_details.email = "null@null.com"
+
+ body = {
+ "code": bumper.RETURN_API_SUCCESS,
+ "data": json.loads(login_details.toJSON()),
+ # {
+ # "accessToken": self.generate_token(tmpuser), # Generate a token
+ # "country": countrycode,
+ # "email": "null@null.com",
+ # "uid": "fuid_{}".format(tmpuser["userid"]),
+ # "username": "fusername_{}".format(tmpuser["userid"]),
+ # },
+ "msg": "操作成功",
+ "time": self.get_milli_time(datetime.utcnow().timestamp()),
+ }
+ return web.json_response(body)
+
+ else:
+ body = {
+ "code": bumper.ERR_TOKEN_INVALID,
+ "data": None,
+ "msg": "当前密码错误",
+ "time": self.get_milli_time(datetime.utcnow().timestamp()),
+ }
+ return web.json_response(body)
+
+ except Exception as e:
+ confserverlog.exception("{}".format(e))
+
+ def _auth_any(self, devid, apptype, country, request):
+ try:
+ user_devid = devid
+ countrycode = country
+ user = bumper.user_by_deviceid(user_devid)
+ bots = bumper.db_get().table("bots").all()
+
+ if user: # Default to user 0
+ tmpuser = user
+ if "global_" in apptype: # EcoVacs Home
+ login_details = EcoVacsHome_Login()
+ login_details.ucUid = "fuid_{}".format(tmpuser["userid"])
+ login_details.loginName = "fusername_{}".format(tmpuser["userid"])
+ login_details.mobile = None
+ else:
+ login_details = EcoVacs_Login()
+
+ login_details.accessToken = self.generate_token(tmpuser)
+ login_details.uid = "fuid_{}".format(tmpuser["userid"])
+ login_details.username = "fusername_{}".format(tmpuser["userid"])
+ login_details.country = countrycode
+ login_details.email = "null@null.com"
+ bumper.user_add_device(tmpuser["userid"], user_devid)
+ else:
+ bumper.user_add("tmpuser") # Add a new user
+ tmpuser = bumper.user_get("tmpuser")
+ if "global_" in apptype: # EcoVacs Home
+ login_details = EcoVacsHome_Login()
+ login_details.ucUid = "fuid_{}".format(tmpuser["userid"])
+ login_details.loginName = "fusername_{}".format(tmpuser["userid"])
+ login_details.mobile = None
+ else:
+ login_details = EcoVacs_Login()
+
+ login_details.accessToken = self.generate_token(tmpuser)
+ login_details.uid = "fuid_{}".format(tmpuser["userid"])
+ login_details.username = "fusername_{}".format(tmpuser["userid"])
+ login_details.country = countrycode
+ login_details.email = "null@null.com"
+ bumper.user_add_device(tmpuser["userid"], user_devid)
+
+ for bot in bots: # Add all bots to the user
+ if "did" in bot:
+ bumper.user_add_bot(tmpuser["userid"], bot["did"])
+ else:
+ confserverlog.error("No DID for bot: {}".format(bot))
+
+ if "checkLogin" in request.path: # If request was to check a token do so
+ checkToken = self.check_token(
+ apptype, countrycode, tmpuser, request.query["accessToken"]
+ )
+ isGood = json.loads(checkToken.text)
+ if isGood["code"] == "0000":
+ return isGood
+
+ # Deactivate old tokens and authcodes
+ bumper.user_revoke_expired_tokens(tmpuser["userid"])
+
+ body = {
+ "code": bumper.RETURN_API_SUCCESS,
+ "data": json.loads(login_details.toJSON()),
+ # {
+ # "accessToken": self.generate_token(tmpuser), # Generate a token
+ # "country": countrycode,
+ # "email": "null@null.com",
+ # "uid": "fuid_{}".format(tmpuser["userid"]),
+ # "username": "fusername_{}".format(tmpuser["userid"]),
+ # },
+ "msg": "操作成功",
+ "time": self.get_milli_time(datetime.utcnow().timestamp()),
+ }
+
+ return body
+
+ except Exception as e:
+ confserverlog.exception("{}".format(e))
+
+
+ def getUserAccountInfo(self, request):
+ try:
+ user_devid = request.match_info.get("devid", "")
+ countrycode = request.match_info.get("country", "us")
+ apptype = request.match_info.get("apptype", "")
+ user = bumper.user_by_deviceid(user_devid)
+
+ if "global_" in apptype: # EcoVacs Home
+ login_details = EcoVacsHome_Login()
+ login_details.ucUid = "fuid_{}".format(user["userid"])
+ login_details.loginName = "fusername_{}".format(user["userid"])
+ login_details.mobile = None
+ else:
+ login_details = EcoVacs_Login()
+
+ login_details.uid = "fuid_{}".format(user["userid"])
+ login_details.username = "fusername_{}".format(user["userid"])
+ login_details.country = countrycode
+ login_details.email = "null@null.com"
+
+ body = {
+ "code": bumper.RETURN_API_SUCCESS,
+ "data": {
+ "email": login_details.email,
+ "hasMobile": "N",
+ "hasPassword": "Y",
+ "uid": login_details.uid,
+ "userName": login_details.username,
+ "obfuscatedMobile": None,
+ "mobile": None,
+ "loginName": login_details.loginName,
+ },
+ "msg": "操作成功",
+ "time": self.get_milli_time(datetime.utcnow().timestamp()),
+ }
+ return web.json_response(body)
+
+ except Exception as e:
+ confserverlog.exception("{}".format(e))
+
+ async def logout(self, request):
+ try:
+ user_devid = request.match_info.get("devid", "")
+ if not user_devid == "":
+ user = bumper.user_by_deviceid(user_devid)
+ if user:
+ if bumper.check_token(user["userid"], request.query["accessToken"]):
+ # Deactivate old tokens and authcodes
+ bumper.user_revoke_token(
+ user["userid"], request.query["accessToken"]
+ )
+
+ body = {
+ "code": bumper.RETURN_API_SUCCESS,
+ "data": None,
+ "msg": "操作成功",
+ "time": self.get_milli_time(datetime.utcnow().timestamp()),
+ }
+
+ return web.json_response(body)
+
+ except Exception as e:
+ confserverlog.exception("{}".format(e))
\ No newline at end of file
diff --git a/bumper/db.py b/bumper/db.py
index 52f6ba1..71c158a 100644
--- a/bumper/db.py
+++ b/bumper/db.py
@@ -286,7 +286,8 @@ def bot_add(sn, did, devclass, resource, company):
def bot_remove(did):
bots = db_get().table("bots")
bot = bot_get(did)
- bots.remove(doc_ids=[bot.doc_id])
+ if bot:
+ bots.remove(doc_ids=[bot.doc_id])
def bot_get(did):
@@ -344,6 +345,11 @@ def client_add(userid, realm, resource):
bumperlog.info("Adding new client with resource {}".format(newclient.resource))
client_full_upsert(newclient.asdict())
+def client_remove(resource):
+ clients = db_get().table("clients")
+ client = client_get(resource)
+ if client:
+ clients.remove(doc_ids=[client.doc_id])
def client_get(resource):
clients = db_get().table("clients")
diff --git a/bumper/mqttserver.py b/bumper/mqttserver.py
index f28a3f5..f53d1f8 100644
--- a/bumper/mqttserver.py
+++ b/bumper/mqttserver.py
@@ -27,7 +27,7 @@ class MQTTHelperBot:
def __init__(self, address):
self.address = address
- self.client_id = "helper1@bumper/helper1"
+ self.client_id = "helperbot@bumper/helperbot"
self.command_responses = []
async def start_helper_bot(self):
@@ -35,7 +35,7 @@ class MQTTHelperBot:
try:
if self.Client is None:
self.Client = MQTTClient(
- client_id=self.client_id, config={"check_hostname": False}
+ client_id=self.client_id, config={"check_hostname": False, "reconnect_retries": 20}
)
await self.Client.connect(
@@ -44,89 +44,26 @@ class MQTTHelperBot:
)
await self.Client.subscribe(
[
- ("iot/p2p/+/+/+/+/helper1/bumper/helper1/+/+/+", QOS_0),
+ ("iot/p2p/+/+/+/+/helperbot/bumper/helperbot/+/+/+", QOS_0),
("iot/p2p/+", QOS_0),
("iot/atr/+", QOS_0),
]
)
- asyncio.create_task(self.get_msg())
+# except ConnectionRefusedError as e:
+# helperbotlog.Error(e)
+# pass
- except ConnectionRefusedError as e:
- helperbotlog.Error(e)
- pass
+# except asyncio.CancelledError as e:
+# pass
- except asyncio.CancelledError as e:
- pass
-
- except hbmqtt.client.ConnectException as e:
- helperbotlog.Error(e)
- pass
+# except hbmqtt.client.ConnectException as e:
+# helperbotlog.Error(e)
+# pass
except Exception as e:
helperbotlog.exception("{}".format(e))
- async def get_msg(self):
- while True:
- message = await self.Client.deliver_message()
-
- if str(message.topic).split("/")[6] == "helper1":
- # Response to command
- helperbotlog.debug(
- "Received Response - Topic: {} - Message: {}".format(
- message.topic, str(message.data.decode("utf-8"))
- )
- )
- self.command_responses.append(
- {
- "time": time.time(),
- "topic": message.topic,
- "payload": str(message.data.decode("utf-8")),
- }
- )
- elif str(message.topic).split("/")[3] == "helper1":
- # Helperbot sending command
- helperbotlog.debug(
- "Send Command - Topic: {} - Message: {}".format(
- message.topic, str(message.data.decode("utf-8"))
- )
- )
- elif str(message.topic).split("/")[1] == "atr":
- # Broadcast message received on atr
- if str(message.topic).split("/")[2] == "errors":
- boterrorlog.error(
- "Received Error - Topic: {} - Message: {}".format(
- message.topic, str(message.data.decode("utf-8"))
- )
- )
- else:
- helperbotlog.debug(
- "Received Broadcast - Topic: {} - Message: {}".format(
- message.topic, str(message.data.decode("utf-8"))
- )
- )
-
- else:
- helperbotlog.debug(
- "Received Message - Topic: {} - Message: {}".format(
- message.topic, str(message.data.decode("utf-8"))
- )
- )
-
- # Cleanup "expired messages" > 60 seconds from time
- for msg in self.command_responses:
- expire_time = (
- datetime.fromtimestamp(msg["time"])
- + timedelta(seconds=self.expire_msg_seconds)
- ).timestamp()
- if time.time() > expire_time:
- helperbotlog.debug(
- "Pruning Message Due To Expiration - Message Topic: {}".format(
- msg["topic"]
- )
- )
- self.command_responses.remove(msg)
-
async def wait_for_resp(self, requestid):
try:
@@ -139,8 +76,7 @@ class MQTTHelperBot:
if len(self.command_responses) > 0:
for msg in self.command_responses:
topic = str(msg["topic"]).split("/")
- if topic[6] == "helper1" and topic[10] == requestid:
- # helperbotlog.debug('VacBot MQTT Response: Topic: %s Payload: %s' % (msg['topic'], msg['payload']))
+ if topic[6] == "helperbot" and topic[10] == requestid:
if topic[11] == "j":
resppayload = json.loads(msg["payload"])
else:
@@ -175,7 +111,7 @@ class MQTTHelperBot:
async def send_command(self, cmdjson, requestid):
if not self.Client._handler.writer is None:
try:
- ttopic = "iot/p2p/{}/helper1/bumper/helper1/{}/{}/{}/q/{}/{}".format(
+ ttopic = "iot/p2p/{}/helperbot/bumper/helperbot/{}/{}/{}/q/{}/{}".format(
cmdjson["cmdName"],
cmdjson["toId"],
cmdjson["toType"],
@@ -207,32 +143,46 @@ class MQTTHelperBot:
class MQTTServer:
- default_config = {}
+ default_config = None
broker = None
async def broker_coro(self):
mqttserverlog.info(
"Starting MQTT Server at {}:{}".format(self.address[0], self.address[1])
- )
- self.broker = hbmqtt.broker.Broker(config=self.default_config)
+ )
try:
await self.broker.start()
except hbmqtt.broker.BrokerException as e:
mqttserverlog.exception(e)
- asyncio.create_task(bumper.shutdown())
+ #asyncio.create_task(bumper.shutdown())
pass
except Exception as e:
mqttserverlog.exception("{}".format(e))
- asyncio.create_task(bumper.shutdown())
+ #asyncio.create_task(bumper.shutdown())
+ pass
- def __init__(self, address):
+ def __init__(self, address, **kwargs):
try:
self.address = address
+ # Default config opts
+ passwd_file = os.path.join(
+ os.path.join(bumper.data_dir, "passwd")
+ ) # For file auth, set user:hash in passwd file see (https://hbmqtt.readthedocs.io/en/latest/references/hbmqtt.html#configuration-example)
+
+ allow_anon = False
+
+ for key, value in kwargs.items():
+ if key == "password_file":
+ passwd_file = kwargs["password_file"]
+
+ elif key == "allow_anonymous":
+ allow_anon = kwargs["allow_anonymous"] # Set to True to allow anonymous authentication
+
# The below adds a plugin to the hbmqtt.broker.plugins without having to futz with setup.py
distribution = pkg_resources.Distribution("hbmqtt.broker.plugins")
bumper_plugin = pkg_resources.EntryPoint.parse(
@@ -252,17 +202,17 @@ class MQTTServer:
"keyfile": bumper.server_key,
},
},
- "sys_interval": 10,
+ "sys_interval": 0,
"auth": {
- "allow-anonymous": False, # Set to True to allow anonymous authentication
- "password-file": os.path.join(
- os.path.join(bumper.data_dir, "passwd")
- ), # For file auth, set user:hash in passwd file see (https://hbmqtt.readthedocs.io/en/latest/references/hbmqtt.html#configuration-example)
+ "allow-anonymous": allow_anon,
+ "password-file": passwd_file,
"plugins": ["bumper"], # Bumper plugin provides auth and handling of bots/clients connecting
},
"topic-check": {"enabled": False},
}
+ self.broker = hbmqtt.broker.Broker(config=self.default_config)
+
except Exception as e:
mqttserverlog.exception("{}".format(e))
@@ -284,13 +234,6 @@ class BumperMQTTServer_Plugin:
async def authenticate(self, *args, **kwargs):
authenticated = False
- if not self.auth_config:
- # auth config section not found
- self.context.logger.warning(
- "'auth' section not found in context configuration"
- )
- return False
-
try:
session = kwargs.get("session", None)
@@ -320,7 +263,7 @@ class BumperMQTTServer_Plugin:
realm = tmpclientdetail[0]
resource = tmpclientdetail[1]
- if userid == "helper1":
+ if userid == "helperbot":
mqttserverlog.info(f"Bumper Authentication Success - Helperbot: {client_id}")
authenticated = True
else:
@@ -383,8 +326,6 @@ class BumperMQTTServer_Plugin:
self.context.logger.debug(f"{(len(self._users))} user(s) read from file {password_file}")
except FileNotFoundError:
self.context.logger.warning(f"Password file {password_file} not found")
- else:
- self.context.logger.debug("Configuration parameter 'password_file' not found")
async def on_broker_client_connected(self, client_id):
@@ -401,6 +342,69 @@ class BumperMQTTServer_Plugin:
bumper.client_set_mqtt(client["resource"], True)
return
+ async def on_broker_message_received(self, client_id, message):
+ self.handle_helperbot_msg(client_id, message)
+
+ def handle_helperbot_msg(self, client_id, message):
+
+ if str(message.topic).split("/")[6] == "helperbot":
+ # Response to command
+ helperbotlog.debug(
+ "Received Response - Topic: {} - Message: {}".format(
+ message.topic, str(message.data.decode("utf-8"))
+ )
+ )
+ bumper.mqtt_helperbot.command_responses.append(
+ {
+ "time": time.time(),
+ "topic": message.topic,
+ "payload": str(message.data.decode("utf-8")),
+ }
+ )
+ elif str(message.topic).split("/")[3] == "helperbot":
+ # Helperbot sending command
+ helperbotlog.debug(
+ "Send Command - Topic: {} - Message: {}".format(
+ message.topic, str(message.data.decode("utf-8"))
+ )
+ )
+ elif str(message.topic).split("/")[1] == "atr":
+ # Broadcast message received on atr
+ if str(message.topic).split("/")[2] == "errors":
+ boterrorlog.error(
+ "Received Error - Topic: {} - Message: {}".format(
+ message.topic, str(message.data.decode("utf-8"))
+ )
+ )
+ else:
+ helperbotlog.debug(
+ "Received Broadcast - Topic: {} - Message: {}".format(
+ message.topic, str(message.data.decode("utf-8"))
+ )
+ )
+
+ else:
+ helperbotlog.debug(
+ "Received Message - Topic: {} - Message: {}".format(
+ message.topic, str(message.data.decode("utf-8"))
+ )
+ )
+
+ # Cleanup "expired messages" > 60 seconds from time
+ for msg in bumper.mqtt_helperbot.command_responses:
+ expire_time = (
+ datetime.fromtimestamp(msg["time"])
+ + timedelta(seconds=bumper.mqtt_helperbot.expire_msg_seconds)
+ ).timestamp()
+ if time.time() > expire_time:
+ helperbotlog.debug(
+ "Pruning Message Due To Expiration - Message Topic: {}".format(
+ msg["topic"]
+ )
+ )
+ bumper.mqtt_helperbot.command_responses.remove(msg)
+
+
async def on_broker_client_disconnected(self, client_id):
didsplit = str(client_id).split("@")
diff --git a/bumper/plugins.py b/bumper/plugins.py
new file mode 100644
index 0000000..d1cbc9b
--- /dev/null
+++ b/bumper/plugins.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python3
+import asyncio
+from aiohttp import web
+
+class ConfServerApp():
+ name = None
+ plugin_type = None
+ path_prefix = None
+ app = None
+ sub_api = None
+ routes = None
+
+
diff --git a/bumper/plugins/bumper_confserver_portal_appsvr.py b/bumper/plugins/bumper_confserver_portal_appsvr.py
new file mode 100644
index 0000000..523ec71
--- /dev/null
+++ b/bumper/plugins/bumper_confserver_portal_appsvr.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python3
+import asyncio
+from aiohttp import web
+from bumper import plugins
+import logging
+import bumper
+from bumper.models import *
+from bumper import plugins
+from datetime import datetime, timedelta
+
+
+class portal_api_appsvr(plugins.ConfServerApp):
+
+ def __init__(self):
+ self.name = "portal_api_appsvr"
+ self.plugin_type = "sub_api"
+ self.sub_api = "portal_api"
+
+ self.routes = [
+
+ web.route("*", "/appsvr/app.do", self.handle_appsvr_api, name="portal_api_appsvr_app"),
+
+ ]
+
+ self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
+
+ async def handle_appsvr_api(self, request):
+ if not request.method == "GET": # Skip GET for now
+ try:
+
+ body = {}
+ postbody = {}
+ if request.content_type == "application/x-www-form-urlencoded":
+ postbody = await request.post()
+
+ else:
+ postbody = json.loads(await request.text())
+
+ todo = postbody["todo"]
+
+ if todo == "GetGlobalDeviceList": # EcoVacs Home
+ bots = bumper.db_get().table("bots").all()
+ botlist = []
+ for bot in bots:
+ if bot["class"] != "":
+ b = bumper.bot_toEcoVacsHome_JSON(bot)
+ if (
+ not b is None
+ ): # Happens if the bot isn't on the EcoVacs Home list
+ botlist.append(json.loads(b))
+
+ body = {
+ "code": 0,
+ "devices": botlist,
+ "ret": "ok",
+ "todo": "result",
+ }
+
+ return web.json_response(body)
+
+ except Exception as e:
+ logging.exception("{}".format(e))
+
+ # Return fail for GET
+ body = {"result": "fail", "todo": "result"}
+ return web.json_response(body)
+
+plugin = portal_api_appsvr()
+
diff --git a/bumper/plugins/bumper_confserver_portal_dim.py b/bumper/plugins/bumper_confserver_portal_dim.py
new file mode 100644
index 0000000..78e4583
--- /dev/null
+++ b/bumper/plugins/bumper_confserver_portal_dim.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+import asyncio
+from aiohttp import web
+from bumper import plugins
+import logging
+import bumper
+from bumper.models import *
+from bumper import plugins
+from datetime import datetime, timedelta
+import string
+import random
+
+
+class portal_api_dim(plugins.ConfServerApp):
+
+ def __init__(self):
+ self.name = "portal_api_dimr"
+ self.plugin_type = "sub_api"
+ self.sub_api = "portal_api"
+
+ self.routes = [
+
+ web.route("*", "/dim/devmanager.do", self.handle_dim_devmanager, name="portal_api_dim_devmanager"),
+
+ ]
+
+ self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
+
+ async def handle_dim_devmanager(self, request): # Used in EcoVacs Home App
+ try:
+ json_body = json.loads(await request.text())
+
+ randomid = "".join(random.sample(string.ascii_letters, 6))
+ did = ""
+ if "toId" in json_body: # Its a command
+ did = json_body["toId"]
+
+ if did != "":
+ bot = bumper.bot_get(did)
+ if bot["company"] == "eco-ng" and bot["mqtt_connection"] == True:
+ retcmd = await bumper.mqtt_helperbot.send_command(
+ json_body, randomid
+ )
+ body = retcmd
+ logging.debug("Send Bot - {}".format(json_body))
+ logging.debug("Bot Response - {}".format(body))
+ return web.json_response(body)
+ else:
+ # No response, send error back
+ logging.error(
+ "No bots with DID: {} connected to MQTT".format(
+ json_body["toId"]
+ )
+ )
+ body = {"id": randomid, "errno": bumper.ERR_COMMON, "ret": "fail"}
+ return web.json_response(body)
+
+ else:
+ if "td" in json_body: # Seen when doing initial wifi config
+ if json_body["td"] == "PollSCResult":
+ body = {"ret": "ok"}
+ return web.json_response(body)
+
+ if json_body["td"] == "HasUnreadMsg": # EcoVacs Home
+ body = {"ret": "ok", "unRead": False}
+ return web.json_response(body)
+
+ except Exception as e:
+ logging.exception("{}".format(e))
+
+plugin = portal_api_dim()
+
diff --git a/bumper/plugins/bumper_confserver_portal_iot.py b/bumper/plugins/bumper_confserver_portal_iot.py
new file mode 100644
index 0000000..6023db6
--- /dev/null
+++ b/bumper/plugins/bumper_confserver_portal_iot.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python3
+import asyncio
+from aiohttp import web
+from bumper import plugins
+import logging
+import bumper
+from bumper.models import *
+from bumper import plugins
+from datetime import datetime, timedelta
+import string
+import random
+
+class portal_api_iot(plugins.ConfServerApp):
+
+ def __init__(self):
+ self.name = "portal_api_iot"
+ self.plugin_type = "sub_api"
+ self.sub_api = "portal_api"
+
+ self.routes = [
+
+ web.route("*", "/iot/devmanager.do", self.handle_devmanager_botcommand, name="portal_api_iot_devmanager"),
+
+ ]
+
+ self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
+
+ async def handle_devmanager_botcommand(self, request):
+ try:
+ json_body = json.loads(await request.text())
+
+ randomid = "".join(random.sample(string.ascii_letters, 6))
+ did = ""
+ if "toId" in json_body: # Its a command
+ did = json_body["toId"]
+
+ if did != "":
+ bot = bumper.bot_get(did)
+ if bot["company"] == "eco-ng":
+ retcmd = await bumper.mqtt_helperbot.send_command(
+ json_body, randomid
+ )
+ body = retcmd
+ logging.debug("Send Bot - {}".format(json_body))
+ logging.debug("Bot Response - {}".format(body))
+ return web.json_response(body)
+ else:
+ # No response, send error back
+ logging.error(
+ "No bots with DID: {} connected to MQTT".format(
+ json_body["toId"]
+ )
+ )
+ body = {
+ "id": randomid,
+ "errno": 500,
+ "ret": "fail",
+ "debug": "wait for response timed out",
+ }
+ return web.json_response(body)
+
+ else:
+ if "td" in json_body: # Seen when doing initial wifi config
+ if json_body["td"] == "PollSCResult":
+ body = {"ret": "ok"}
+ return web.json_response(body)
+
+ if json_body["td"] == "HasUnreadMsg": # EcoVacs Home
+ body = {"ret": "ok", "unRead": False}
+ return web.json_response(body)
+
+ except Exception as e:
+ logging.exception("{}".format(e))
+
+plugin = portal_api_iot()
+
diff --git a/bumper/plugins/bumper_confserver_portal_lg.py b/bumper/plugins/bumper_confserver_portal_lg.py
new file mode 100644
index 0000000..c43d59b
--- /dev/null
+++ b/bumper/plugins/bumper_confserver_portal_lg.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python3
+import asyncio
+from aiohttp import web
+from bumper import plugins
+import logging
+import bumper
+from bumper.models import *
+from bumper import plugins
+from datetime import datetime, timedelta
+import os
+import string
+import random
+import xml.etree.ElementTree as ET
+
+class portal_api_lg(plugins.ConfServerApp):
+
+ def __init__(self):
+ self.name = "portal_api_lg"
+ self.plugin_type = "sub_api"
+ self.sub_api = "portal_api"
+
+ self.routes = [
+
+ web.route("*", "/lg/log.do", self.handle_lg_log, name="portal_api_lg_log"),
+
+ ]
+
+ self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
+
+ async def handle_lg_log(self, request): # EcoVacs Home
+ try:
+ json_body = json.loads(await request.text())
+
+ randomid = "".join(random.sample(string.ascii_letters, 6))
+ did = json_body["did"]
+
+ botdetails = bumper.bot_get(did)
+ if botdetails:
+ if not "cmdName" in json_body:
+ if "td" in json_body:
+ json_body["cmdName"] = json_body["td"]
+
+ if not "toId" in json_body:
+ json_body["toId"] = did
+
+ if not "toType" in json_body:
+ json_body["toType"] = botdetails["class"]
+
+ if not "toRes" in json_body:
+ json_body["toRes"] = botdetails["resource"]
+
+ if not "payloadType" in json_body:
+ json_body["payloadType"] = "x"
+
+ if not "payload" in json_body:
+ #json_body["payload"] = ""
+ if json_body["td"] == "GetCleanLogs":
+ json_body["td"] = "q"
+ json_body["payload"] = ''
+
+ if did != "":
+ bot = bumper.bot_get(did)
+ if bot["company"] == "eco-ng":
+ retcmd = await bumper.mqtt_helperbot.send_command(
+ json_body, randomid
+ )
+ body = retcmd
+ logging.debug("Send Bot - {}".format(json_body))
+ logging.debug("Bot Response - {}".format(body))
+ logs = []
+ logsroot = ET.fromstring(retcmd["resp"])
+ if logsroot.attrib["ret"] == "ok":
+ cleanlogs = logsroot.getchildren()
+ for l in cleanlogs:
+ cleanlog = {
+ "ts": l.attrib['s'],
+ "area": l.attrib['a'],
+ "last": l.attrib['l'],
+ "cleanType": l.attrib['t'],
+ #imageUrl allows for providing images of cleanings, something to look into later
+ #"imageUrl": "https://localhost:8007",
+ }
+ logs.append(cleanlog)
+ body = {
+ "ret": "ok",
+ "logs": logs,
+ }
+
+ else:
+ body = {"ret": "ok", "logs": []}
+
+ logging.debug("lg logs return: {}".format(json.dumps(body)))
+ return web.json_response(body)
+ else:
+ # No response, send error back
+ logging.error(
+ "No bots with DID: {} connected to MQTT".format(
+ json_body["toId"]
+ )
+ )
+ body = {"id": randomid, "errno": bumper.ERR_COMMON, "ret": "fail"}
+ return web.json_response(body)
+
+ except Exception as e:
+ logging.exception("{}".format(e))
+
+plugin = portal_api_lg()
+
diff --git a/bumper/plugins/bumper_confserver_portal_neng.py b/bumper/plugins/bumper_confserver_portal_neng.py
new file mode 100644
index 0000000..944c3aa
--- /dev/null
+++ b/bumper/plugins/bumper_confserver_portal_neng.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python3
+import asyncio
+from aiohttp import web
+from bumper import plugins
+import logging
+import bumper
+from bumper.models import *
+from bumper import plugins
+from datetime import datetime, timedelta
+
+
+class portal_api_neng(plugins.ConfServerApp):
+
+ def __init__(self):
+ self.name = "portal_api_neng"
+ self.plugin_type = "sub_api"
+ self.sub_api = "portal_api"
+
+ self.routes = [
+
+ web.route("*", "/neng/message/hasUnreadMsg", self.handle_neng_hasUnreadMessage, name="portal_api_neng_hasUnreadMessage"),
+
+ ]
+
+ self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
+
+ async def handle_neng_hasUnreadMessage(self, request): # EcoVacs Home
+ try:
+ body = {"code": 0, "data": {"hasUnRead": True}}
+
+ return web.json_response(body)
+
+ except Exception as e:
+ logging.exception("{}".format(e))
+
+plugin = portal_api_neng()
+
diff --git a/bumper/plugins/bumper_confserver_portal_pim.py b/bumper/plugins/bumper_confserver_portal_pim.py
new file mode 100644
index 0000000..2473823
--- /dev/null
+++ b/bumper/plugins/bumper_confserver_portal_pim.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python3
+import asyncio
+from aiohttp import web
+from bumper import plugins
+import logging
+import bumper
+from bumper.models import *
+from bumper import plugins
+from datetime import datetime, timedelta
+import os
+
+class portal_api_pim(plugins.ConfServerApp):
+
+ def __init__(self):
+ self.name = "portal_api_pim"
+ self.plugin_type = "sub_api"
+ self.sub_api = "portal_api"
+
+ self.routes = [
+
+ web.route("*", "/pim/product/getProductIotMap", self.handle_getProductIotMap, name="portal_api_pim_getProductIotMap"),
+ web.route("*", "/pim/file/get/{id}", self.handle_pimFile, name="portal_api_pim_file"),
+
+ ]
+
+ self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
+
+ async def handle_getProductIotMap(self, request):
+ try:
+ body = {
+ "code": bumper.RETURN_API_SUCCESS,
+ "data": EcoVacsHomeProducts,
+ }
+ return web.json_response(body)
+
+ except Exception as e:
+ logging.exception("{}".format(e))
+
+ async def handle_pimFile(self, request):
+ try:
+ fileID = request.match_info.get("id", "")
+
+ return web.FileResponse(os.path.join(bumper.data_dir,"web","robotvac_image.jpg"))
+
+ except Exception as e:
+ logging.exception("{}".format(e))
+
+plugin = portal_api_pim()
+
diff --git a/bumper/plugins/bumper_confserver_portal_users.py b/bumper/plugins/bumper_confserver_portal_users.py
new file mode 100644
index 0000000..fd4d5f8
--- /dev/null
+++ b/bumper/plugins/bumper_confserver_portal_users.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python3
+import asyncio
+from aiohttp import web
+from bumper import plugins
+import logging
+import bumper
+from bumper.models import *
+from bumper import plugins
+from datetime import datetime, timedelta
+
+
+class portal_api_users(plugins.ConfServerApp):
+
+ def __init__(self):
+ self.name = "portal_api_users"
+ self.plugin_type = "sub_api"
+ self.sub_api = "portal_api"
+
+ self.routes = [
+
+ web.route("*", "/users/user.do", self.handle_usersapi, name="portal_api_users_user"),
+
+ ]
+
+ self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
+
+ async def handle_usersapi(self, request):
+ if not request.method == "GET": # Skip GET for now
+ try:
+
+ body = {}
+ postbody = {}
+ if request.content_type == "application/x-www-form-urlencoded":
+ postbody = await request.post()
+
+ else:
+ postbody = json.loads(await request.text())
+
+ todo = postbody["todo"]
+ if todo == "FindBest":
+ service = postbody["service"]
+ if service == "EcoMsgNew":
+ srvip = bumper.bumper_announce_ip
+ srvport = 5223
+ logging.info(
+ "Announcing EcoMsgNew Server to bot as: {}:{}".format(
+ srvip, srvport
+ )
+ )
+ msgserver = {"ip": srvip, "port": srvport, "result": "ok"}
+ msgserver = json.dumps(msgserver)
+ msgserver = msgserver.replace(
+ " ", ""
+ ) # bot seems to be very picky about having no spaces, only way was with text
+
+ return web.json_response(text=msgserver)
+
+ elif service == "EcoUpdate":
+ srvip = "47.88.66.164" # EcoVacs Server
+ srvport = 8005
+ logging.info(
+ "Announcing EcoUpdate Server to bot as: {}:{}".format(
+ srvip, srvport
+ )
+ )
+ body = {"result": "ok", "ip": srvip, "port": srvport}
+
+ elif todo == "loginByItToken":
+ if "userId" in postbody:
+ if bumper.check_authcode(postbody["userId"], postbody["token"]):
+ body = {
+ "resource": postbody["resource"],
+ "result": "ok",
+ "todo": "result",
+ "token": postbody["token"],
+ "userId": postbody["userId"],
+ }
+ else: # EcoVacs Home LoginByITToken
+ loginToken = bumper.loginByItToken(postbody["token"])
+ if not loginToken == {}:
+ body = {
+ "resource": postbody["resource"],
+ "result": "ok",
+ "todo": "result",
+ "token": loginToken["token"],
+ "userId": loginToken["userid"],
+ }
+ else:
+ body = {"result": "fail", "todo": "result"}
+
+ elif todo == "GetDeviceList":
+ body = {
+ "devices": bumper.db_get().table("bots").all(),
+ "result": "ok",
+ "todo": "result",
+ }
+
+ elif todo == "SetDeviceNick":
+ bumper.bot_set_nick(postbody["did"], postbody["nick"])
+ body = {"result": "ok", "todo": "result"}
+
+ elif todo == "AddOneDevice":
+ bumper.bot_set_nick(postbody["did"], postbody["nick"])
+ body = {"result": "ok", "todo": "result"}
+
+ elif todo == "DeleteOneDevice":
+ bumper.bot_remove(postbody["did"])
+ body = {"result": "ok", "todo": "result"}
+
+ return web.json_response(body)
+
+ except Exception as e:
+ logging.exception("{}".format(e))
+
+ # Return fail for GET
+ body = {"result": "fail", "todo": "result"}
+ return web.json_response(body)
+
+plugin = portal_api_users()
+
diff --git a/bumper/plugins/bumper_confserver_v1_global_auth.py b/bumper/plugins/bumper_confserver_v1_global_auth.py
new file mode 100644
index 0000000..bbceb70
--- /dev/null
+++ b/bumper/plugins/bumper_confserver_v1_global_auth.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python3
+import asyncio
+from aiohttp import web
+from bumper import plugins
+import logging
+import bumper
+from bumper.models import *
+from bumper import plugins
+from datetime import datetime, timedelta
+
+
+class v1_global_auth(plugins.ConfServerApp):
+
+ def __init__(self):
+ self.name = "v1_global_auth"
+ self.plugin_type = "sub_api"
+ self.sub_api = "api_v1"
+
+ authhandler = bumper.ConfServer.ConfServer_AuthHandler()
+ self.routes = [
+ web.route("*", "/global/auth/getAuthCode", authhandler.get_AuthCode, name="v1_global_auth_getAuthCode"),
+ ]
+
+ self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
+
+
+plugin = v1_global_auth()
+
diff --git a/bumper/plugins/bumper_confserver_v1_private_ad.py b/bumper/plugins/bumper_confserver_v1_private_ad.py
new file mode 100644
index 0000000..2e7df5b
--- /dev/null
+++ b/bumper/plugins/bumper_confserver_v1_private_ad.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+import asyncio
+from aiohttp import web
+from bumper import plugins
+import logging
+import bumper
+from bumper.models import *
+from bumper import plugins
+from datetime import datetime, timedelta
+
+
+class v1_private_ad(plugins.ConfServerApp):
+
+ def __init__(self):
+ self.name = "v1_private_ad"
+ self.plugin_type = "sub_api"
+ self.sub_api = "api_v1"
+
+ self.routes = [
+ web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/ad/getAdByPositionType", self.handle_getAdByPositionType, name="v1_ad_getAdByPositionType"),
+ web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/ad/getBootScreen", self.handle_getBootScreen, name="v1_ad_getBootScreen"),
+
+ ]
+
+ self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
+
+ async def handle_getAdByPositionType(self, request): # EcoVacs Home
+ try:
+ body = {
+ "code": bumper.RETURN_API_SUCCESS,
+ "data": None,
+ "msg": "操作成功",
+ "success": True,
+ "time": self.get_milli_time(datetime.utcnow().timestamp()),
+ }
+
+ return web.json_response(body)
+
+ except Exception as e:
+ logging.exception("{}".format(e))
+
+ async def handle_getBootScreen(self, request): # EcoVacs Home
+ try:
+ body = {
+ "code": bumper.RETURN_API_SUCCESS,
+ "data": None,
+ "msg": "操作成功",
+ "success": True,
+ "time": self.get_milli_time(datetime.utcnow().timestamp()),
+ }
+
+ return web.json_response(body)
+
+ except Exception as e:
+ logging.exception("{}".format(e))
+
+plugin = v1_private_ad()
+
diff --git a/bumper/plugins/bumper_confserver_v1_private_campaign.py b/bumper/plugins/bumper_confserver_v1_private_campaign.py
new file mode 100644
index 0000000..468ca8c
--- /dev/null
+++ b/bumper/plugins/bumper_confserver_v1_private_campaign.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python3
+import asyncio
+from aiohttp import web
+from bumper import plugins
+import logging
+import bumper
+from bumper.models import *
+from bumper import plugins
+from datetime import datetime, timedelta
+
+
+class v1_private_campaign(plugins.ConfServerApp):
+
+ def __init__(self):
+ self.name = "v1_private_campaign"
+ self.plugin_type = "sub_api"
+ self.sub_api = "api_v1"
+
+ self.routes = [
+
+ web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/campaign/homePageAlert", self.handle_homePageAlert, name="v1_campaign_homePageAlert"),
+
+ ]
+
+ self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
+
+ async def handle_homePageAlert(self, request):
+ try:
+ nextAlert = self.get_milli_time(
+ (datetime.now() + timedelta(hours=12)).timestamp()
+ )
+
+ body = {
+ "code": bumper.RETURN_API_SUCCESS,
+ "data": {
+ "clickSchemeUrl": None,
+ "clickWebUrl": None,
+ "hasCampaign": "N",
+ "imageUrl": None,
+ "nextAlertTime": nextAlert,
+ "serverTime": self.get_milli_time(datetime.utcnow().timestamp()),
+ },
+ "msg": "操作成功",
+ "time": self.get_milli_time(datetime.utcnow().timestamp()),
+ }
+
+ return web.json_response(body)
+
+ except Exception as e:
+ logging.exception("{}".format(e))
+
+
+plugin = v1_private_campaign()
+
diff --git a/bumper/plugins/bumper_confserver_v1_private_common.py b/bumper/plugins/bumper_confserver_v1_private_common.py
new file mode 100644
index 0000000..71a0034
--- /dev/null
+++ b/bumper/plugins/bumper_confserver_v1_private_common.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python3
+import asyncio
+from aiohttp import web
+from bumper import plugins
+import logging
+import bumper
+from bumper.models import *
+from bumper import plugins
+from datetime import datetime, timedelta
+
+
+class v1_private_common(plugins.ConfServerApp):
+
+ def __init__(self):
+ self.name = "v1_private_common"
+ self.plugin_type = "sub_api"
+ self.sub_api = "api_v1"
+
+ self.routes = [
+ web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/common/checkAPPVersion", self.handle_checkAPPVersion, name="v1_common_checkAppVersion"),
+ web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/common/checkVersion", self.handle_checkVersion, name="v1_common_checkVersion"),
+ web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/common/uploadDeviceInfo", self.handle_uploadDeviceInfo, name="v1_common_uploadDeviceInfo"),
+ web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/common/getSystemReminder", self.handle_getSystemReminder, name="v1_common_getSystemReminder"),
+
+ ]
+
+ self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
+
+ async def handle_checkVersion(self, request):
+ try:
+ body = {
+ "code": bumper.RETURN_API_SUCCESS,
+ "data": {
+ "c": None,
+ "img": None,
+ "r": 0,
+ "t": None,
+ "u": None,
+ "ut": 0,
+ "v": None,
+ },
+ "msg": "操作成功",
+ "time": self.get_milli_time(datetime.utcnow().timestamp()),
+ }
+
+ return web.json_response(body)
+
+ except Exception as e:
+ logging.exception("{}".format(e))
+
+ async def handle_checkAPPVersion(self, request): # EcoVacs Home
+ try:
+ body = {
+ "code": bumper.RETURN_API_SUCCESS,
+ "data": {
+ "c": None,
+ "downPageUrl": None,
+ "img": None,
+ "nextAlertTime": None,
+ "r": 0,
+ "t": None,
+ "u": None,
+ "ut": 0,
+ "v": None,
+ },
+ "msg": "操作成功",
+ "success": True,
+ "time": self.get_milli_time(datetime.utcnow().timestamp()),
+ }
+
+ return web.json_response(body)
+
+ except Exception as e:
+ logging.exception("{}".format(e))
+
+ async def handle_uploadDeviceInfo(self, request): # EcoVacs Home
+ try:
+ body = {
+ "code": bumper.RETURN_API_SUCCESS,
+ "data": None,
+ "msg": "操作成功",
+ "success": True,
+ "time": self.get_milli_time(datetime.utcnow().timestamp()),
+ }
+
+ return web.json_response(body)
+
+ except Exception as e:
+ logging.exception("{}".format(e))
+
+ async def handle_getSystemReminder(self, request): # EcoVacs Home
+ try:
+ body = {
+ "code": bumper.RETURN_API_SUCCESS,
+ "data": {
+ "iosGradeTime": {"iodGradeFlag": "N"},
+ "openNotification": {
+ "openNotificationContent": None,
+ "openNotificationFlag": "N",
+ "openNotificationTitle": None,
+ },
+ },
+ "msg": "操作成功",
+ "success": True,
+ "time": self.get_milli_time(datetime.utcnow().timestamp()),
+ }
+
+ return web.json_response(body)
+
+ except Exception as e:
+ logging.exception("{}".format(e))
+
+
+plugin = v1_private_common()
+
diff --git a/bumper/plugins/bumper_confserver_v1_private_message.py b/bumper/plugins/bumper_confserver_v1_private_message.py
new file mode 100644
index 0000000..7e1d6d1
--- /dev/null
+++ b/bumper/plugins/bumper_confserver_v1_private_message.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+import asyncio
+from aiohttp import web
+from bumper import plugins
+import logging
+import bumper
+from bumper.models import *
+from bumper import plugins
+from datetime import datetime, timedelta
+
+
+class v1_private_message(plugins.ConfServerApp):
+
+ def __init__(self):
+ self.name = "v1_private_message"
+ self.plugin_type = "sub_api"
+ self.sub_api = "api_v1"
+
+ self.routes = [
+
+ web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/message/hasUnreadMsg", self.handle_hasUnreadMessage, name="v1_message_hasUnreadMsg"),
+ web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/message/getMsgList", self.handle_getMsgList, name="v1_message_getMsgList"),
+
+ ]
+
+ self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
+
+ async def handle_hasUnreadMessage(self, request): # EcoVacs Home
+ try:
+ body = {
+ "code": bumper.RETURN_API_SUCCESS,
+ "data": "N",
+ "msg": "操作成功",
+ "success": True,
+ "time": self.get_milli_time(datetime.utcnow().timestamp()),
+ }
+
+ return web.json_response(body)
+
+ except Exception as e:
+ logging.exception("{}".format(e))
+
+ async def handle_getMsgList(self, request): # EcoVacs Home
+ try:
+ body = {
+ "code": bumper.RETURN_API_SUCCESS,
+ "data": {"hasNextPage": 0, "items": []},
+ "msg": "操作成功",
+ "success": True,
+ "time": self.get_milli_time(datetime.utcnow().timestamp()),
+ }
+
+ return web.json_response(body)
+
+ except Exception as e:
+ logging.exception("{}".format(e))
+
+plugin = v1_private_message()
+
diff --git a/bumper/plugins/bumper_confserver_v1_private_shop.py b/bumper/plugins/bumper_confserver_v1_private_shop.py
new file mode 100644
index 0000000..343f13a
--- /dev/null
+++ b/bumper/plugins/bumper_confserver_v1_private_shop.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python3
+import asyncio
+from aiohttp import web
+from bumper import plugins
+import logging
+import bumper
+from bumper.models import *
+from bumper import plugins
+from datetime import datetime, timedelta
+
+
+class v1_private_shop(plugins.ConfServerApp):
+
+ def __init__(self):
+ self.name = "v1_private_shop"
+ self.plugin_type = "sub_api"
+ self.sub_api = "api_v1"
+
+ self.routes = [
+
+ web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/shop/getCnWapShopConfig", self.handle_getCnWapShopConfig, name="v1_shop_getCnWapShopConfig"),
+
+ ]
+
+ self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
+
+ async def handle_getCnWapShopConfig(self, request): # EcoVacs Home
+ try:
+ body = {
+ "code": bumper.RETURN_API_SUCCESS,
+ "data": {
+ "myShopShowFlag": "N",
+ "myShopUrl": "",
+ "shopIndexShowFlag": "N",
+ "shopIndexUrl": "",
+ },
+ "msg": "操作成功",
+ "success": True,
+ "time": self.get_milli_time(datetime.utcnow().timestamp()),
+ }
+
+ return web.json_response(body)
+
+ except Exception as e:
+ logging.exception("{}".format(e))
+
+plugin = v1_private_shop()
+
diff --git a/bumper/plugins/bumper_confserver_v1_private_user.py b/bumper/plugins/bumper_confserver_v1_private_user.py
new file mode 100644
index 0000000..397fcd1
--- /dev/null
+++ b/bumper/plugins/bumper_confserver_v1_private_user.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python3
+import asyncio
+from aiohttp import web
+from bumper import plugins
+import logging
+import bumper
+from bumper.models import *
+from bumper import plugins
+from datetime import datetime, timedelta
+
+
+class v1_private_user(plugins.ConfServerApp):
+
+ def __init__(self):
+
+ self.name = "v1_private_user"
+ self.plugin_type = "sub_api"
+ self.sub_api = "api_v1"
+
+ authhandler = bumper.ConfServer.ConfServer_AuthHandler()
+ self.routes = [
+ web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/login", authhandler.login, name="v1_user_login"),
+ web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkLogin", authhandler.login, name="v1_user_checkLogin"),
+ web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/getAuthCode", authhandler.get_AuthCode, name="v1_user_getAuthCode"),
+ web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/logout", authhandler.logout, name="v1_user_logout"),
+ web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkAgreement", self.handle_checkAgreement,name="v1_user_checkAgreement"),
+ web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkAgreementBatch", self.handle_checkAgreement,name="v1_user_checkAgreementBatch"),
+ web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/getUserAccountInfo", authhandler.getUserAccountInfo,name="v1_user_getUserAccountInfo"),
+ # Direct register from app:
+ # /{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/directRegister
+
+ ]
+
+ self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
+
+ async def handle_checkAgreement(self, request):
+ try:
+ apptype = request.match_info.get("apptype", "")
+ if "global_" in apptype:
+ body = {
+ "code": bumper.RETURN_API_SUCCESS,
+ "data": [
+ {
+ "force": "N",
+ "id": "20180804040641_7d746faf18b8cb22a50d145598fe4c90",
+ "type": "USER",
+ "url": "https://bumper.ecovacs.com/content/agreement?id=20180804040641_7d746faf18b8cb22a50d145598fe4c90&language=EN", # "https://gl-us-wap.ecovacs.com/content/agreement?id=20180804040641_7d746faf18b8cb22a50d145598fe4c90&language=EN
+ "version": "1.01",
+ },
+ {
+ "force": "N",
+ "id": "20180804040245_4e7c56dfb7ebd3b81b1f2747d0859fac",
+ "type": "PRIVACY",
+ "url": "https://bumper.ecovacs.com/content/agreement?id=20180804040245_4e7c56dfb7ebd3b81b1f2747d0859fac&language=EN", # "https://gl-us-wap.ecovacs.com/content/agreement?id=20180804040245_4e7c56dfb7ebd3b81b1f2747d0859fac&language=EN"
+ "version": "1.01",
+ },
+ ],
+ "msg": "操作成功",
+ "success": True,
+ "time": self.get_milli_time(datetime.utcnow().timestamp()),
+ }
+ else:
+ body = {
+ "code": bumper.RETURN_API_SUCCESS,
+ "data": [],
+ "msg": "操作成功",
+ "time": self.get_milli_time(datetime.utcnow().timestamp()),
+ }
+
+ return web.json_response(body)
+
+ except Exception as e:
+ logging.exception("{}".format(e))
+
+plugin = v1_private_user()
+
diff --git a/bumper/plugins/bumper_confserver_v2_private_user.py b/bumper/plugins/bumper_confserver_v2_private_user.py
new file mode 100644
index 0000000..5a3c7a3
--- /dev/null
+++ b/bumper/plugins/bumper_confserver_v2_private_user.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python3
+import asyncio
+from aiohttp import web
+from bumper import plugins
+import logging
+import bumper
+from bumper.models import *
+from bumper import plugins
+from datetime import datetime, timedelta
+
+
+class v2_private_user(plugins.ConfServerApp):
+
+ def __init__(self):
+
+ self.name = "v2_private_user"
+ self.plugin_type = "sub_api"
+ self.sub_api = "api_v2"
+
+ authhandler = bumper.ConfServer.ConfServer_AuthHandler()
+ self.routes = [
+ web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkLogin", authhandler.login, name="v2_user_checkLogin"),
+ ]
+
+ self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
+
+
+plugin = v2_private_user()
+
diff --git a/data/web/templates/home.jinja2 b/data/web/templates/home.jinja2
new file mode 100644
index 0000000..5e74303
--- /dev/null
+++ b/data/web/templates/home.jinja2
@@ -0,0 +1,196 @@
+
+
+
+
+ Bumper
+
+
+
+
+
+
+
+
Bumper
+
+
+
+
+
+
+
+
+
+ Status: {% if mqtt_server[0].state == "started" %}
{{ mqtt_server[0].state }} {% else %}
{{ mqtt_server[0].state }} {% endif %}
+ Sessions: {{ mqtt_server[1].sessions[0].count }}
+
+
+
+ | username | clientid | state |
+
+ {% for sess in mqtt_server[1].sessions[1].clients %}
+
+ | {{ sess[0].username }} |
+ {{ sess[0].client_id }} |
+ {{ sess[0].state }} |
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+ Status: {% if xmpp_server.server._serving == True %}
running {% else %}
not running {% endif %}
+ Clients: {{ xmpp_server.clients | length }}
+
+
+
+ | uid | jid | state |
+
+ {% for client in xmpp_server.clients %}
+
+ | {{ client.uid }} |
+ {{ client.bumper_jid }} |
+ {% if client.state == 4 %} connected {% else %} not connected {% endif %} |
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+ Status: {% if helperbot[0].state == "connected" %} {{ helperbot[0].state }} {% else %} {{ helperbot[0].state }} {% endif %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | SN | Nickname | Class | DID | Resource | Company | MQTT Connected | XMPP Connected | Action |
+
+ {% for bot in bots %}
+
+ | {{ bot.name }} |
+ {{ bot.nick }} |
+ {{ bot.class }} |
+ {{ bot.did }} |
+ {{ bot.resource }} |
+ {{ bot.company}} |
+
+ {{ bot.mqtt_connection }} |
+ {{ bot.xmpp_connection }} |
+ |
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+ | User ID | Realm | Resource | MQTT Connected | XMPP Connected | Action |
+
+ {% for client in clients %}
+
+ | {{ client.userid }} |
+ {{ client.realm }} |
+ {{ client.resource }} |
+ {{ client.mqtt_connection }} |
+ {{ client.xmpp_connection }} |
+ |
+
+ {% endfor %}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 29019d1..46c5ac6 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,5 @@
aiohttp==3.6.2
+aiohttp-jinja2==1.2.0
async-timeout==3.0.1
atomicwrites==1.3.0
attrs==19.3.0
@@ -6,9 +7,11 @@ chardet==3.0.4
docopt==0.6.2
hbmqtt==0.9.5
idna==2.8
+Jinja2==2.10.3
+MarkupSafe==1.1.1
multidict==4.5.2
passlib==1.7.2
-PyYAML==5.2
+PyYAML==5.3b1
six==1.13.0
tinydb==3.15.2
transitions==0.7.1
diff --git a/tests/test_confserver.py b/tests/test_confserver.py
index 6e35542..2149673 100644
--- a/tests/test_confserver.py
+++ b/tests/test_confserver.py
@@ -9,6 +9,9 @@ import pytest_aiohttp
import pytest_asyncio
import datetime, time
from aiohttp import web
+import logging
+from testfixtures import LogCapture
+from unittest.mock import MagicMock
def create_confserver():
@@ -37,6 +40,31 @@ async def test_confserver_ssl():
conf_server.confserver_app()
asyncio.create_task(conf_server.start_server())
+async def test_confserver_exceptions():
+ with LogCapture() as l:
+
+ conf_server = bumper.ConfServer(("127.0.0.1", 8007), usessl=True)
+ conf_server.confserver_app()
+ conf_server.site = web.TCPSite
+
+ #bind permission
+ conf_server.site.start = mock.Mock(side_effect=OSError(1, "error while attempting to bind on address ('127.0.0.1', 8007): permission denied"))
+ await conf_server.start_server()
+
+ #asyncio Cancel
+ conf_server.site = web.TCPSite
+ conf_server.site.start = mock.Mock(side_effect=asyncio.CancelledError)
+ await conf_server.start_server()
+
+ #general exception
+ conf_server.site = web.TCPSite
+ conf_server.site.start = mock.Mock(side_effect=Exception(1, "general"))
+ await conf_server.start_server()
+
+ l.check_present(
+ ("confserver", "ERROR", "error while attempting to bind on address ('127.0.0.1', 8007): permission denied")
+ )
+
async def test_confserver_no_ssl():
conf_server = bumper.ConfServer(("127.0.0.1", 111111), usessl=False)
@@ -62,9 +90,15 @@ async def test_base(aiohttp_client):
# Start MQTT
mqtt_address = ("127.0.0.1", 8883)
- mqtt_server = bumper.MQTTServer(mqtt_address)
+ mqtt_server = bumper.MQTTServer(mqtt_address, password_file="tests/passwd")
bumper.mqtt_server = mqtt_server
await mqtt_server.broker_coro()
+
+ # Start XMPP
+ xmpp_address = ("127.0.0.1", 5223)
+ xmpp_server = bumper.XMPPServer(xmpp_address)
+ bumper.xmpp_server = xmpp_server
+ await xmpp_server.start_async_server()
# Start Helperbot
mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address)
@@ -79,13 +113,16 @@ async def test_base(aiohttp_client):
await mqtt_server.broker.shutdown()
+ bumper.xmpp_server.disconnect()
+
+
async def test_restartService(aiohttp_client):
remove_existing_db()
bumper.db = "tests/tmp.db" # Set db location for testing
# Start MQTT
mqtt_address = ("127.0.0.1", 8883)
- mqtt_server = bumper.MQTTServer(mqtt_address)
+ mqtt_server = bumper.MQTTServer(mqtt_address, password_file="tests/passwd")
bumper.mqtt_server = mqtt_server
await mqtt_server.broker_coro()
@@ -112,10 +149,20 @@ async def test_restartService(aiohttp_client):
assert resp.status == 200
mqtt_helperbot.Client.disconnect()
- await mqtt_server.broker.shutdown()
+ await mqtt_server.broker.shutdown()
xmpp_server.disconnect()
+async def test_RemoveBot(aiohttp_client):
+ client = await aiohttp_client(create_app)
+ resp = await client.get("/bot/remove/test_did")
+ assert resp.status == 200
+
+async def test_RemoveClient(aiohttp_client):
+ client = await aiohttp_client(create_app)
+ resp = await client.get("/client/remove/test_resource")
+ assert resp.status == 200
+
async def test_login(aiohttp_client):
remove_existing_db()
diff --git a/tests/test_db.py b/tests/test_db.py
index 52c49a4..a23b368 100644
--- a/tests/test_db.py
+++ b/tests/test_db.py
@@ -141,5 +141,8 @@ def test_client_db():
bumper.client_get("resource_123")["xmpp_connection"] == False
) # Test that xmpp was set False for client
assert (
- len(bumper.get_disconnected_xmpp_clients()) == 1
+ len(bumper.get_disconnected_xmpp_clients()) > 0
) # Test len of connected xmpp clients is 1
+
+ bumper.client_remove("resource_123")
+ assert bumper.client_get("resource_123") == None
diff --git a/tests/test_mqttserver.py b/tests/test_mqttserver.py
index 6cb9730..70205bc 100644
--- a/tests/test_mqttserver.py
+++ b/tests/test_mqttserver.py
@@ -15,7 +15,7 @@ import time
async def test_helperbot_message():
mqtt_address = ("127.0.0.1", 8883)
- mqtt_server = bumper.MQTTServer(mqtt_address)
+ mqtt_server = bumper.MQTTServer(mqtt_address, password_file="tests/passwd")
await mqtt_server.broker_coro()
with LogCapture() as l:
@@ -31,10 +31,9 @@ async def test_helperbot_message():
await mqtt_helperbot.Client.publish(
msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0
)
- try:
- await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1)
- except asyncio.TimeoutError:
- pass
+
+ await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1)
+
l.check_present(
(
"helperbot",
@@ -53,20 +52,19 @@ async def test_helperbot_message():
) # Check helperbot is connected
msg_payload = "{}"
msg_topic_name = (
- "iot/p2p/GetWKVer/helper1/bumper/helper1/bot_serial/ls1ok3/wC3g/q/iCmuqp/j"
+ "iot/p2p/GetWKVer/helperbot/bumper/helperbot/bot_serial/ls1ok3/wC3g/q/iCmuqp/j"
)
await mqtt_helperbot.Client.publish(
msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0
)
- try:
- await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1)
- except asyncio.TimeoutError:
- pass
+
+ await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1)
+
l.check_present(
(
"helperbot",
"DEBUG",
- "Send Command - Topic: iot/p2p/GetWKVer/helper1/bumper/helper1/bot_serial/ls1ok3/wC3g/q/iCmuqp/j - Message: {}",
+ "Send Command - Topic: iot/p2p/GetWKVer/helperbot/bumper/helperbot/bot_serial/ls1ok3/wC3g/q/iCmuqp/j - Message: {}",
)
) # Check send command message was logged
l.clear()
@@ -80,20 +78,19 @@ async def test_helperbot_message():
) # Check helperbot is connected
msg_payload = '{"ret":"ok","ver":"0.13.5"}'
msg_topic_name = (
- "iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/helper1/bumper/helper1/p/iCmuqp/j"
+ "iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/helperbot/bumper/helperbot/p/iCmuqp/j"
)
await mqtt_helperbot.Client.publish(
msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0
)
- try:
- await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1)
- except asyncio.TimeoutError:
- pass
+
+ await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1)
+
l.check_present(
(
"helperbot",
"DEBUG",
- 'Received Response - Topic: iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/helper1/bumper/helper1/p/iCmuqp/j - Message: {"ret":"ok","ver":"0.13.5"}',
+ 'Received Response - Topic: iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/helperbot/bumper/helperbot/p/iCmuqp/j - Message: {"ret":"ok","ver":"0.13.5"}',
)
) # Check received response message was logged
l.clear()
@@ -107,21 +104,20 @@ async def test_helperbot_message():
) # Check helperbot is connected
msg_payload = "test"
msg_topic_name = (
- "iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/TESTBAD/bumper/helper1/p/iCmuqp/j"
+ "iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/TESTBAD/bumper/helperbot/p/iCmuqp/j"
)
await mqtt_helperbot.Client.publish(
msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0
)
- try:
- await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1)
- except asyncio.TimeoutError:
- pass
+
+ await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1)
+
l.check_present(
(
"helperbot",
"DEBUG",
- "Received Message - Topic: iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/TESTBAD/bumper/helper1/p/iCmuqp/j - Message: test",
+ "Received Message - Topic: iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/TESTBAD/bumper/helperbot/p/iCmuqp/j - Message: test",
)
) # Check received message was logged
l.clear()
@@ -138,10 +134,8 @@ async def test_helperbot_message():
await mqtt_helperbot.Client.publish(
msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0
)
- try:
- await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1)
- except asyncio.TimeoutError:
- pass
+
+ await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1)
l.check_present(
(
@@ -158,20 +152,21 @@ async def test_helperbot_message():
async def test_helperbot_expire_message():
mqtt_address = ("127.0.0.1", 8883)
- mqtt_server = bumper.MQTTServer(mqtt_address)
+ mqtt_server = bumper.MQTTServer(mqtt_address, password_file="tests/passwd")
await mqtt_server.broker_coro()
with LogCapture("helperbot") as l:
# Test broadcast message
mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address)
+ bumper.mqtt_helperbot = mqtt_helperbot
await mqtt_helperbot.start_helper_bot()
assert (
mqtt_helperbot.Client._connected_state._value == True
) # Check helperbot is connected
expire_msg_payload = '{"ret":"ok","ver":"0.13.5"}'
- expire_msg_topic_name = "iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/helper1/bumper/helper1/p/testgood/j"
+ expire_msg_topic_name = "iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/helperbot/bumper/helperbot/p/testgood/j"
currenttime = time.time()
mqtt_helperbot.command_responses.append(
{
@@ -187,7 +182,7 @@ async def test_helperbot_expire_message():
"payload": expire_msg_payload,
} in mqtt_helperbot.command_responses # check message is in command_responses
- await asyncio.sleep(0.2)
+ await asyncio.sleep(0.1)
mqtt_helperbot.expire_msg_seconds = (
0.1
) # Set expire message seconds to 0.1 so we don't wait 10 seconds
@@ -197,10 +192,9 @@ async def test_helperbot_expire_message():
msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0
) # Send another message to force get_msg
- try:
- await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1)
- except asyncio.TimeoutError:
- pass
+
+ await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1)
+
assert {
"time": currenttime,
@@ -220,14 +214,16 @@ async def test_helperbot_expire_message():
mqtt_helperbot.Client.disconnect()
await mqtt_server.broker.shutdown()
+
async def test_helperbot_sendcommand():
mqtt_address = ("127.0.0.1", 8883)
- mqtt_server = bumper.MQTTServer(mqtt_address)
+ mqtt_server = bumper.MQTTServer(mqtt_address, password_file="tests/passwd")
await mqtt_server.broker_coro()
mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address)
+ bumper.mqtt_helperbot = mqtt_helperbot
await mqtt_helperbot.start_helper_bot()
assert (
mqtt_helperbot.Client._connected_state._value == True
@@ -267,7 +263,7 @@ async def test_helperbot_sendcommand():
# Send response beforehand
msg_payload = '{"ret":"ok","ver":"0.13.5"}'
msg_topic_name = (
- "iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/helper1/bumper/helper1/p/testgood/j"
+ "iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/helperbot/bumper/helperbot/p/testgood/j"
)
await mqtt_helperbot.Client.publish(
msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0
@@ -306,7 +302,7 @@ async def test_helperbot_sendcommand():
# Send response beforehand
msg_payload = ""
msg_topic_name = (
- "iot/p2p/GetLifeSpan/bot_serial/ls1ok3/wC3g/helper1/bumper/helper1/p/testx/q"
+ "iot/p2p/GetLifeSpan/bot_serial/ls1ok3/wC3g/helperbot/bumper/helperbot/p/testx/q"
)
await mqtt_helperbot.Client.publish(
msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0
@@ -351,7 +347,7 @@ async def test_helperbot_sendcommand():
msg_payload = '{"body":{"code":0,"data":{"area":0,"cid":"111","start":"1569378657","time":6,"type":"auto"},"msg":"ok"},"header":{"fwVer":"1.6.4","hwVer":"0.1.1","pri":1,"ts":"1569380074036","tzm":480,"ver":"0.0.1"}}'
msg_topic_name = (
- "iot/p2p/getStats/bot_serial/ls1ok3/wC3g/helper1/bumper/helper1/p/testj/j"
+ "iot/p2p/getStats/bot_serial/ls1ok3/wC3g/helperbot/bumper/helperbot/p/testj/j"
)
await mqtt_helperbot.Client.publish(
msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0
@@ -368,6 +364,7 @@ async def test_helperbot_sendcommand():
mqtt_helperbot.Client.disconnect()
await mqtt_server.broker.shutdown()
+
async def test_mqttserver():
@@ -378,26 +375,7 @@ async def test_mqttserver():
mqtt_address = ("127.0.0.1", 8883)
- mqtt_server = bumper.MQTTServer(mqtt_address)
-
- mqtt_server.default_config = {
- "listeners": {
- "default": {"type": "tcp"},
- "tls1": {
- "bind": "{}:{}".format(mqtt_address[0], mqtt_address[1]),
- "ssl": "on",
- "certfile": bumper.server_cert,
- "keyfile": bumper.server_key,
- },
- },
- "sys_interval": 10,
- "auth": {
- "allow-anonymous": True, # Set to True to allow anonymous authentication
- "password-file": "tests/passwd", # For file auth, set user:hash in passwd file see (https://hbmqtt.readthedocs.io/en/latest/references/hbmqtt.html#configuration-example)
- "plugins": ["bumper"], # Bumper plugin provides auth and handling of bots/clients connecting
- },
- "topic-check": {"enabled": False},
- }
+ mqtt_server = bumper.MQTTServer(mqtt_address, password_file="tests/passwd", allow_anonymous=True)
await mqtt_server.broker_coro()
@@ -463,147 +441,41 @@ async def test_mqttserver():
) # Check client is disconnected
# bad password
- try:
+ with LogCapture() as l:
+
await test_client.Client.connect(
f"mqtts://test-client:notvalid!@{test_client.address[0]}:{test_client.address[1]}/",
cafile=bumper.ca_cert, cleansession=True
)
- assert (
- test_client.Client._connected_state._value == False
- ) # Check client is connected
-
- except Exception as ae:
- pass
-
- # no username in file
- try:
+ l.check_present(
+ ("mqttserver", "INFO", "File Authentication Failed - Username: test-client - ClientID: test-file-auth"),
+ order_matters=False
+ )
+ # no username in file
await test_client.Client.connect(
f"mqtts://test-client-noexist:notvalid!@{test_client.address[0]}:{test_client.address[1]}/",
cafile=bumper.ca_cert, cleansession=True
)
- assert (
- test_client.Client._connected_state._value == False
- ) # Check client is connected
- except Exception as ae:
- pass
-
-
-
- await asyncio.sleep(0.1)
-
- await mqtt_server.broker.shutdown()
- await asyncio.sleep(0.1)
-
-async def test_passwordfile_badhash_mqttserver():
- mqtt_address = ("127.0.0.1", 8883)
-
- mqtt_server = bumper.MQTTServer(mqtt_address)
-
- mqtt_server.default_config = {
- "listeners": {
- "default": {"type": "tcp"},
- "tls1": {
- "bind": "{}:{}".format(mqtt_address[0], mqtt_address[1]),
- "ssl": "on",
- "certfile": bumper.server_cert,
- "keyfile": bumper.server_key,
- },
- },
- "sys_interval": 10,
- "auth": {
- "allow-anonymous": True, # Set to True to allow anonymous authentication
- "password-file": "tests/passwd_bad", # For file auth, set user:hash in passwd file see (https://hbmqtt.readthedocs.io/en/latest/references/hbmqtt.html#configuration-example)
- "plugins": ["bumper"], # Bumper plugin provides auth and handling of bots/clients connecting
- },
- "topic-check": {"enabled": False},
- }
-
- await mqtt_server.broker_coro()
- await asyncio.sleep(0.1)
-
- # bad password
- try:
- test_client = bumper.MQTTHelperBot(mqtt_address)
-
- await test_client.Client.connect(
- f"mqtts://test-client:notvalid!@{test_client.address[0]}:{test_client.address[1]}/",
- cafile=bumper.ca_cert, cleansession=True
+ l.check_present(
+ ("mqttserver", "INFO", 'File Authentication Failed - No Entry for Username: test-client-noexist - ClientID: test-file-auth'),
+ order_matters=False
)
-
- assert (
- test_client.Client._connected_state._value == False
- ) # Check client is connected
-
-
- except Exception as ae:
- pass
await mqtt_server.broker.shutdown()
- await asyncio.sleep(0.1)
+
async def test_nofileauth_mqttserver():
- try:
+ with LogCapture() as l:
mqtt_address = ("127.0.0.1", 8883)
-
- mqtt_server = bumper.MQTTServer(mqtt_address)
-
- mqtt_server.default_config = {
- "listeners": {
- "default": {"type": "tcp"},
- "tls1": {
- "bind": "{}:{}".format(mqtt_address[0], mqtt_address[1]),
- "ssl": "on",
- "certfile": bumper.server_cert,
- "keyfile": bumper.server_key,
- },
- },
- "sys_interval": 10,
- "auth": {
- "allow-anonymous": True, # Set to True to allow anonymous authentication
- "password-file": "tests/passwd-notfound", # For file auth, set user:hash in passwd file see (https://hbmqtt.readthedocs.io/en/latest/references/hbmqtt.html#configuration-example)
- "plugins": ["bumper"], # Bumper plugin provides auth and handling of bots/clients connecting
- },
- "topic-check": {"enabled": False},
- }
-
+ mqtt_server = bumper.MQTTServer(mqtt_address, password_file="tests/passwd-notfound")
await mqtt_server.broker_coro()
+ await mqtt_server.broker.shutdown()
- except:
- pass
-
-
-async def test_passwordfile_opt_missing_mqttserver():
- try:
-
- mqtt_address = ("127.0.0.1", 8883)
-
- mqtt_server = bumper.MQTTServer(mqtt_address)
-
- mqtt_server.default_config = {
- "listeners": {
- "default": {"type": "tcp"},
- "tls1": {
- "bind": "{}:{}".format(mqtt_address[0], mqtt_address[1]),
- "ssl": "on",
- "certfile": bumper.server_cert,
- "keyfile": bumper.server_key,
- },
- },
- "sys_interval": 10,
- "auth": {
- "allow-anonymous": True, # Set to True to allow anonymous authentication
-
- "plugins": ["bumper"], # Bumper plugin provides auth and handling of bots/clients connecting
- },
- "topic-check": {"enabled": False},
- }
-
- await mqtt_server.broker_coro()
- await mqtt_server.broker.shutdown()
-
- except:
- pass
+ l.check_present(
+ ("hbmqtt.broker.plugins.bumper", "WARNING", 'Password file tests/passwd-notfound not found'),
+ order_matters=False
+ )