diff --git a/.dockerignore b/.dockerignore index a6766a5..f4a3930 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,20 @@ +# Logs +logs + +# Tests +tests + +# Examples +examples + +# Docs +docs + +# Certs +certs + +.coverage + # Markdown *.md diff --git a/Dockerfile b/Dockerfile index 912db45..dde7ee9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,15 +22,11 @@ RUN apk add build-base FROM base -COPY requirements.txt /requirements.txt +COPY . /bumper + +WORKDIR /bumper # install required python packages RUN pip3 install -r requirements.txt -WORKDIR /bumper - -# Copy only required folders instead of all -COPY create_certs/ create_certs/ -COPY bumper/ bumper/ - ENTRYPOINT ["python3", "-m", "bumper"] diff --git a/bumper/__init__.py b/bumper/__init__.py index dc90528..3b15523 100644 --- a/bumper/__init__.py +++ b/bumper/__init__.py @@ -26,13 +26,10 @@ def strtobool(strbool): # os.environ['PYTHONASYNCIODEBUG'] = '1' # Uncomment to enable ASYNCIODEBUG bumper_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) -log_to_stdout = os.environ.get("LOG_TO_STDOUT") - # Set defaults from environment variables first # Folders -if not log_to_stdout: - logs_dir = os.environ.get("BUMPER_LOGS") or os.path.join(bumper_dir, "logs") - os.makedirs(logs_dir, exist_ok=True) # Ensure logs directory exists or create +logs_dir = os.environ.get("BUMPER_LOGS") or os.path.join(bumper_dir, "logs") +os.makedirs(logs_dir, exist_ok=True) # Ensure logs directory exists or create data_dir = os.environ.get("BUMPER_DATA") or os.path.join(bumper_dir, "data") 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") @@ -57,8 +54,8 @@ bumper_announce_ip = os.environ.get("BUMPER_ANNOUNCE_IP") or bumper_listen bumper_debug = strtobool(os.environ.get("BUMPER_DEBUG")) or False use_auth = False token_validity_seconds = 3600 # 1 hour -oauth_validity_days = 15 db = None +bumper_proxy_mode = strtobool(os.environ.get("BUMPER_PROXY_MODE")) or False mqtt_server = None mqtt_helperbot = None @@ -84,102 +81,83 @@ logformat = logging.Formatter( ) bumperlog = logging.getLogger("bumper") -if not log_to_stdout: - bumper_rotate = RotatingFileHandler("logs/bumper.log", maxBytes=5000000, backupCount=5) - bumper_rotate.setFormatter(logformat) - bumperlog.addHandler(bumper_rotate) -else: - bumperlog.addHandler(logging.StreamHandler(sys.stdout)) +bumper_rotate = RotatingFileHandler("logs/bumper.log", maxBytes=5000000, backupCount=5) +bumper_rotate.setFormatter(logformat) +bumperlog.addHandler(bumper_rotate) # Override the logging level # bumperlog.setLevel(logging.INFO) confserverlog = logging.getLogger("confserver") -if not log_to_stdout: - conf_rotate = RotatingFileHandler( - "logs/confserver.log", maxBytes=5000000, backupCount=5 - ) - conf_rotate.setFormatter(logformat) - confserverlog.addHandler(conf_rotate) -else: - confserverlog.addHandler(logging.StreamHandler(sys.stdout)) +conf_rotate = RotatingFileHandler( + "logs/confserver.log", maxBytes=5000000, backupCount=5 +) +conf_rotate.setFormatter(logformat) +confserverlog.addHandler(conf_rotate) # Override the logging level # confserverlog.setLevel(logging.INFO) mqttserverlog = logging.getLogger("mqttserver") -if not log_to_stdout: - mqtt_rotate = RotatingFileHandler( - "logs/mqttserver.log", maxBytes=5000000, backupCount=5 - ) - mqtt_rotate.setFormatter(logformat) - mqttserverlog.addHandler(mqtt_rotate) -else: - mqttserverlog.addHandler(logging.StreamHandler(sys.stdout)) +mqtt_rotate = RotatingFileHandler( + "logs/mqttserver.log", maxBytes=5000000, backupCount=5 +) +mqtt_rotate.setFormatter(logformat) +mqttserverlog.addHandler(mqtt_rotate) +# Override the logging level +# mqttserverlog.setLevel(logging.INFO) + +proxymodelog = logging.getLogger("proxymode") +proxymode_rotate = RotatingFileHandler( + "logs/proxymode.log", maxBytes=5000000, backupCount=5 +) +proxymode_rotate.setFormatter(logformat) +proxymodelog.addHandler(proxymode_rotate) + # Override the logging level # mqttserverlog.setLevel(logging.INFO) ### Additional MQTT Logs translog = logging.getLogger("transitions") -if not log_to_stdout: - translog.addHandler(mqtt_rotate) -else: - translog.addHandler(logging.StreamHandler(sys.stdout)) +translog.addHandler(mqtt_rotate) translog.setLevel(logging.CRITICAL + 1) # Ignore this logger logging.getLogger("passlib").setLevel(logging.CRITICAL + 1) # Ignore this logger brokerlog = logging.getLogger("hbmqtt.broker") #brokerlog.setLevel( # logging.CRITICAL + 1 #) # Ignore this logger #There are some sublogs that could be set if needed (.plugins) -if not log_to_stdout: - brokerlog.addHandler(mqtt_rotate) -else: - brokerlog.addHandler(logging.StreamHandler(sys.stdout)) +brokerlog.addHandler(mqtt_rotate) protolog = logging.getLogger("hbmqtt.mqtt.protocol") #protolog.setLevel( # logging.CRITICAL + 1 #) # Ignore this logger -if not log_to_stdout: - protolog.addHandler(mqtt_rotate) -else: - protolog.addHandler(logging.StreamHandler(sys.stdout)) +protolog.addHandler(mqtt_rotate) clientlog = logging.getLogger("hbmqtt.client") #clientlog.setLevel(logging.CRITICAL + 1) # Ignore this logger -if not log_to_stdout: - clientlog.addHandler(mqtt_rotate) -else: - clientlog.addHandler(logging.StreamHandler(sys.stdout)) +clientlog.addHandler(mqtt_rotate) + helperbotlog = logging.getLogger("helperbot") -if not log_to_stdout: - helperbot_rotate = RotatingFileHandler( - "logs/helperbot.log", maxBytes=5000000, backupCount=5 - ) - helperbot_rotate.setFormatter(logformat) - helperbotlog.addHandler(helperbot_rotate) -else: - helperbotlog.addHandler(logging.StreamHandler(sys.stdout)) +helperbot_rotate = RotatingFileHandler( + "logs/helperbot.log", maxBytes=5000000, backupCount=5 +) +helperbot_rotate.setFormatter(logformat) +helperbotlog.addHandler(helperbot_rotate) # Override the logging level # helperbotlog.setLevel(logging.INFO) boterrorlog = logging.getLogger("boterror") -if not log_to_stdout: - boterrorlog_rotate = RotatingFileHandler( - "logs/boterror.log", maxBytes=5000000, backupCount=5 - ) - boterrorlog_rotate.setFormatter(logformat) - boterrorlog.addHandler(boterrorlog_rotate) -else: - boterrorlog.addHandler(logging.StreamHandler(sys.stdout)) +boterrorlog_rotate = RotatingFileHandler( + "logs/boterror.log", maxBytes=5000000, backupCount=5 +) +boterrorlog_rotate.setFormatter(logformat) +boterrorlog.addHandler(boterrorlog_rotate) # Override the logging level # boterrorlog.setLevel(logging.INFO) xmppserverlog = logging.getLogger("xmppserver") -if not log_to_stdout: - xmpp_rotate = RotatingFileHandler( - "logs/xmppserver.log", maxBytes=5000000, backupCount=5 - ) - xmpp_rotate.setFormatter(logformat) - xmppserverlog.addHandler(xmpp_rotate) -else: - xmppserverlog.addHandler(logging.StreamHandler(sys.stdout)) +xmpp_rotate = RotatingFileHandler( + "logs/xmppserver.log", maxBytes=5000000, backupCount=5 +) +xmpp_rotate.setFormatter(logformat) +xmppserverlog.addHandler(xmpp_rotate) # Override the logging level # xmppserverlog.setLevel(logging.INFO) @@ -193,6 +171,11 @@ xmpp_listen_port = 5223 async def start(): + #config_proxyMode_deleteTable() #delete existing proxymode table + + #Reset xmpp/mqtt to false in database for bots and clients + bot_reset_connectionStatus() + client_reset_connectionStatus() try: loop = asyncio.get_event_loop() @@ -237,8 +220,9 @@ async def start(): xmpp_server = XMPPServer((bumper_listen, xmpp_listen_port)) # Start MQTT Server - # await start otherwise we get an error connecting the helper bot - await asyncio.create_task(mqtt_server.broker_coro()) + asyncio.create_task(mqtt_server.broker_coro()) + + await asyncio.sleep(0.5) #Wait half a sec for broker to start # Start MQTT Helperbot asyncio.create_task(mqtt_helperbot.start_helper_bot()) @@ -254,7 +238,22 @@ async def start(): await asyncio.sleep(0.1) # Start web servers - conf_server.confserver_app() + if bumper_proxy_mode: + bumperlog.info("Proxy Mode Enabled") + if config_proxyMode_countEntries() == 0: # check if proxymode servers are entered + bumperlog.info("Proxy Mode - No Servers, Loading Defaults (US)") + config_proxyMode_defaults() # set defaults if 0 + + configproxy = config_proxyMode_getall() + cntentries = len(configproxy) + proxymodelog.info(f"Loaded {cntentries} entries from proxyconfig") + for entry in configproxy: + proxymodelog.info(f"Config Entry {entry}") + + conf_server.confserver_proxy_app() + else: + conf_server.confserver_app() + 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)) @@ -266,7 +265,6 @@ async def start(): async def maintenance(): revoke_expired_tokens() - revoke_expired_oauths() async def shutdown(): @@ -385,12 +383,17 @@ def main(argv=None): help="announce address to bots on checkin", ) parser.add_argument("--debug", action="store_true", help="enable debug logs") + parser.add_argument("--proxy-mode", action="store_true", help="enable proxy mode") args = parser.parse_args(args=argv) if args.debug: bumper_debug = True + if args.proxy_mode: + global bumper_proxy_mode + bumper_proxy_mode = True + if args.listen: bumper_listen = args.listen diff --git a/bumper/confserver.py b/bumper/confserver.py index 65e0143..dfe348b 100644 --- a/bumper/confserver.py +++ b/bumper/confserver.py @@ -1,16 +1,23 @@ #!/usr/bin/env python3 -import asyncio +import json import logging -import os import ssl - +import string +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 import aiohttp_jinja2 import jinja2 -from aiohttp import web +import uuid +import xml.etree.ElementTree as ET -from bumper import plugins -from bumper.models import * class aiohttp_filter(logging.Filter): @@ -35,6 +42,7 @@ logging.getLogger("aiohttp.access").addFilter( aiohttp_filter() ) # Add logging filter above to aiohttp.access +proxymodelog = logging.getLogger("proxymode") class ConfServer: def __init__(self, address, usessl=False): @@ -49,6 +57,115 @@ class ConfServer: def get_milli_time(self, timetoconvert): return int(round(timetoconvert * 1000)) + def confserver_proxy_app(self): + + self.app = web.Application(middlewares=[ + self.log_all_requests, + ]) + aiohttp_jinja2.setup(self.app, loader=jinja2.FileSystemLoader(os.path.join(bumper.bumper_dir,"bumper","web","templates"))) + + + self.app.add_routes( + [ + + 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.route("*", "/{path:.*}", self.handle_proxy, name="confserver_proxy"), + ] + ) + + return self.app + + async def handle_proxy(self, request): + + try: + ecoresp = "" + + server_port = 443 #default to 443 + if "_SSLProtocolTransport" != type(request.transport).__name__ and "_SelectorSocketTransport" != type(request.transport).__name__: #check not ssl transport class + if "_extra" in request.transport: + if "sockname" in request.transport._extra: + server_port = request.transport._extra["sockname"][1] + + if request.raw_path == "/": + return await self.handle_base(request) + if request.raw_path == "/lookup.do": + return await self.handle_lookup(request) #use bumper to handle lookup so bot gets Bumper IP and not Ecovacs + + matchproxy = bumper.config_proxyMode_getServerIP("app", request.host) + + if matchproxy: + proxymodelog.info(f"Matched {request.host} to entry in proxyconfig!") + ecorequest = f"{request.scheme}://{matchproxy}" + else: + proxymodelog.info(f"No match for {request.host} in proxyconfig!") + if "ecovacs.com" in request.host: + proxymodelog.info(f"ecovacs.com in {request.host} defaulting to ecovacs.com IP!") + matchproxy = bumper.config_proxyMode_getServerIP("app", "ecovacs.com") + ecorequest = f"{request.scheme}://{matchproxy}" + elif "ecouser.net" in request.host: + proxymodelog.info(f"ecouser.net in {request.host} defaulting to ecouser.net IP!") + matchproxy = bumper.config_proxyMode_getServerIP("app", "ecouser.net") + ecorequest = f"{request.scheme}://{matchproxy}" + else: + proxymodelog.info(f"No matches for {request.host} defaulting to ecovacs.com IP!") + matchproxy = bumper.config_proxyMode_getServerIP("app", "ecovacs.com") + ecorequest = f"{request.scheme}://{matchproxy}" + + if server_port != 443: + ecorequest = f"{ecorequest}:{server_port}" + + proxymodelog.info(f"{request.host} - {ecorequest}") + ecorequest = f"{ecorequest}{request.path_qs}" + requestheaders = {'host': request.host} + async with aiohttp.ClientSession(headers=requestheaders, connector=aiohttp.TCPConnector(verify_ssl=False)) as session: + if request.content.total_bytes > 0: + proxymodelog.info(f"HTTP Proxy Request to EcoVacs (body=true) (host:{request.host}) - {ecorequest} - {request._read_bytes}") + if request.content_type == "application/x-www-form-urlencoded": # android apps use form + fdata = await request.post() + async with session.request(request.method, ecorequest, data=fdata) as resp: + ecoresp = await resp.text() + proxymodelog.info(f"HTTP Proxy Response from EcoVacs (URL: {ecorequest}) - (Status: {resp.status}) - {ecoresp}") + else: # handle json + jdata = request._read_bytes.decode('utf8') + jdata = json.loads(jdata) + async with session.request(request.method, ecorequest, json=jdata) as resp: + ecoresp = await resp.text() + proxymodelog.info(f"HTTP Proxy Response from EcoVacs (URL: {ecorequest}) - (Status: {resp.status}) - {ecoresp}") + + else: + proxymodelog.info(f"HTTP Proxy Request to EcoVacs (body=false) (host:{request.host}) - {ecorequest}") + async with session.request(request.method, ecorequest) as resp: + if resp.content_type == "application/octet-stream": + ecoresp = await resp.read() + proxymodelog.info(f"HTTP Proxy Response from EcoVacs (URL: {ecorequest}) - (Status: {resp.status}) - ") + else: + ecoresp = await resp.text() + proxymodelog.info(f"HTTP Proxy Response from EcoVacs (URL: {ecorequest}) - (Status: {resp.status}) - {ecoresp}") + + if resp.status == 200: + if resp.content_type == "application/json": + ecoresp = json.loads(ecoresp) + return web.json_response(ecoresp) + elif resp.content_type == "application/octet-stream": + return web.Response(body=ecoresp) + else: + return web.Response(text=ecoresp) + + else: + return web.Response(text=ecoresp) + + except asyncio.CancelledError as e: + proxymodelog.error(f"Request cancelled or timeout - {ecorequest} - {jdata}") + return web.Response(text="") + pass + + except Exception as e: + proxymodelog.exception("{}".format(e)) + return web.Response(text="") + + def confserver_app(self): self.app = web.Application(loop=asyncio.get_event_loop(), middlewares=[ self.log_all_requests, @@ -57,12 +174,13 @@ class ConfServer: self.app.add_routes( [ - web.get("", self.handle_base, name="base"), + + 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.post("/newauth.do", self.handle_newauth), + ] ) @@ -104,9 +222,11 @@ class ConfServer: 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) @@ -123,6 +243,36 @@ class ConfServer: ) await site.start() + + def start_site_thread(self, app, address='localhost', port=8080, usessl=False): + #test for new thread and loop + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + runner = web.AppRunner(app) + self.runners.append(runner) + #await runner.setup() + loop.run_until_complete(runner.setup()) #for thread test + + 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() + #for thread test + loop.run_until_complete(site.start()) + loop.run_forever() async def start_server(self): try: @@ -211,18 +361,8 @@ class ConfServer: async def log_all_requests(self, request, handler): if request._match_info.route.name not in self.excludelogging: - to_log = { - "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))}', - } - } + try: - postbody = None if request.content_length: if request.content_type == "application/x-www-form-urlencoded": postbody = await request.post() @@ -236,33 +376,100 @@ class ConfServer: else: postbody = await request.post() - - to_log["request"]["body"] = f"{postbody}" + else: + postbody = None response = await handler(request) - if response is None: - confserverlog.warning("Response was null!") - confserverlog.warning(json.dumps(to_log)) - return response + if response: + if not "application/octet-stream" in response.content_type: + try: + 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}", + }, - to_log["response"] = { - "status": f"{response.status}", - } - if not "application/octet-stream" in response.content_type: - to_log["response"]["body"] = f"{json.loads(response.body)}" + "response": { + #"response_body": f"{json.loads(response.body)}", + "response_body": f"{json.loads(response.text)}", + "status": f"{response.status}", + } + } + except Exception as e: + 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}", + }, - confserverlog.debug(json.dumps(to_log)) + "response": { + #"response_body": f"{json.loads(response.body)}", + "response_body": f"{(response.text)}", + "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)) - confserverlog.debug(json.dumps(to_log)) + 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)) - confserverlog.error(json.dumps(to_log)) + 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: @@ -466,26 +673,6 @@ class ConfServer: except Exception as e: confserverlog.exception("{}".format(e)) - async def handle_newauth(self, request): - # Bumper is only returning the submitted token. No reason yet to create another new token - try: - if request.content_type == "application/x-www-form-urlencoded": - postbody = await request.post() - else: - postbody = json.loads(await request.text()) - - confserverlog.debug(postbody) - - body = { - "authCode": postbody["itToken"], - "result": "ok", - "todo": "result" - } - - return web.json_response(body) - - except Exception as e: - confserverlog.exception("{}".format(e)) async def disconnect(self): try: diff --git a/bumper/db.py b/bumper/db.py index 304add3..dfa001e 100644 --- a/bumper/db.py +++ b/bumper/db.py @@ -1,13 +1,13 @@ #!/usr/bin/env python3 +import bumper +from bumper.models import VacBotClient, VacBotDevice, BumperUser, EcoVacsHomeProducts +from tinydb import TinyDB, Query +from tinydb.storages import MemoryStorage +from datetime import datetime, timedelta +import os import json import logging -import os -from datetime import datetime, timedelta -from tinydb import TinyDB, Query - -import bumper -from bumper.models import VacBotClient, VacBotDevice, BumperUser, EcoVacsHomeProducts, OAuth bumperlog = logging.getLogger("bumper") @@ -32,10 +32,65 @@ def db_get(): db.table("clients", cache_size=0) db.table("bots", cache_size=0) db.table("tokens", cache_size=0) - db.table("oauth", cache_size=0) + db.table("config_proxymode", cache_size=0) return db +def config_proxyMode_deleteTable(): + opendb = db_get() + opendb.purge_table("config_proxymode") + +def config_proxyMode_defaults(): + defaults = [ + {"type":"app","host":"gl-us-api.ecovacs.com","ip":"47.252.51.29","match":"gl-"}, + {"type":"app","host":"gl-us-openapi.ecovacs.com","ip":"47.252.51.29"}, + {"type":"app","host":"portal-ww.ecouser.net","ip":"47.88.66.164","match":"portal-"}, + {"type":"app","host":"bigdata-northamerica.ecovacs.com","ip":"47.88.66.111"}, + {"type":"app","host":"bigdata-international.ecovacs.com","ip":"47.88.132.151","match":"bigdata-"}, + {"type":"app","host":"eco-us-api.ecovacs.com","ip":"47.89.135.130","match":"eco-"}, + {"type":"app","host":"ecovacs.com","ip":"47.90.210.46"}, + {"type":"app","host":"ecouser.net","ip":"116.62.93.217"}, + {"type":"mqtt_server","host":"mq-ww.ecouser.net","ip":"47.254.52.46"}, + ] + opendb = db_get() + with opendb: + config = opendb.table("config_proxymode") + config.insert_multiple(defaults) + +def config_proxyMode_getServerIP(type, host): + opendb = db_get() + with opendb: + proxyconfig = opendb.table("config_proxymode") + proxy = Query() + if type == "mqtt_server": + entry = proxyconfig.get((proxy.type == type)) + + else: + entry = proxyconfig.get((proxy.type == type) & (proxy.host == host)) + + if entry: + return entry["ip"] + + else: + proxylist = proxyconfig.search(Query()) + for proxy in proxylist: # check for sub matches + if "match" in proxy: + if proxy["match"] in host: + return proxy["ip"] + return None + +def config_proxyMode_countEntries(): + opendb = db_get() + with opendb: + config = opendb.table("config_proxymode") + return len(config) + +def config_proxyMode_getall(): + opendb = db_get() + with opendb: + config = opendb.table("config_proxymode") + return config.search(Query()) + def user_add(userid): newuser = BumperUser() @@ -203,55 +258,6 @@ def user_revoke_authcode(userid, token, authcode): ) -def revoke_expired_oauths(): - opendb = db_get() - with opendb: - table = opendb.table("oauth") - entries = table.all() - - for i in entries: - oauth = OAuth(**i) - if datetime.now() >= datetime.fromisoformat(oauth.expire_at): - bumperlog.debug( - "Removing oauth {} due to expiration".format(oauth.access_token) - ) - table.remove(doc_ids=[i.doc_id]) - - -def user_revoke_expired_oauths(userid): - opendb = db_get() - with opendb: - table = opendb.table("oauth") - search = table.search(Query().userid == userid) - for i in search: - oauth = OAuth(**i) - if datetime.now() >= datetime.fromisoformat(oauth.expire_at): - bumperlog.debug( - "Removing oauth {} due to expiration".format(oauth.access_token) - ) - table.remove(doc_ids=[i.doc_id]) - - -def user_add_oauth(userid) -> OAuth: - user_revoke_expired_oauths(userid) - opendb = db_get() - with opendb: - table = opendb.table("oauth") - entry = table.get(Query().userid == userid) - if entry: - return OAuth(**entry) - else: - oauth = OAuth.create_new(userid) - bumperlog.debug("Adding oauth {} for userid {}".format(oauth.access_token, userid)) - table.insert(oauth.toDB()) - return oauth - - -def token_by_authcode(authcode): - tokens = db_get().table("tokens") - return tokens.get(Query().authcode == authcode) - - def get_disconnected_xmpp_clients(): clients = db_get().table("clients") Client = Query() @@ -264,8 +270,8 @@ def check_authcode(uid, authcode): tmpauth = tokens.get( (Query().authcode == authcode) & ( # Match authcode - (Query().userid == uid.replace("fuid_", "")) - | (Query().userid == "fuid_{}".format(uid)) + (Query().userid == uid.replace("fuid_", "")) + | (Query().userid == "fuid_{}".format(uid)) ) # Userid with or without fuid_ ) if tmpauth: @@ -296,8 +302,8 @@ def check_token(uid, token): tmpauth = tokens.get( (Query().token == token) & ( # Match token - (Query().userid == uid.replace("fuid_", "")) - | (Query().userid == "fuid_{}".format(uid)) + (Query().userid == uid.replace("fuid_", "")) + | (Query().userid == "fuid_{}".format(uid)) ) # Userid with or without fuid_ ) if tmpauth: @@ -325,7 +331,7 @@ def bot_add(sn, did, devclass, resource, company): bot = bot_get(did) if not bot: # Not existing bot in database if ( - not devclass == "" or "@" not in sn or "tmp" not in sn + not devclass == "" or "@" not in sn or "tmp" not in sn ): # try to prevent bad additions to the bot list bumperlog.info( "Adding new bot with SN: {} DID: {}".format(newbot.name, newbot.did) @@ -339,6 +345,11 @@ def bot_remove(did): if bot: bots.remove(doc_ids=[bot.doc_id]) +def bot_reset_connectionStatus(): + bots = db_get().table("bots") + for bot in bots: + bot_set_mqtt(bot["did"], False) + bot_set_xmpp(bot["did"], False) def bot_get(did): bots = db_get().table("bots") @@ -352,21 +363,6 @@ def bot_toEcoVacsHome_JSON(bot): # EcoVacs Home bot["UILogicId"] = botprod["product"]["UILogicId"] bot["ota"] = botprod["product"]["ota"] bot["icon"] = botprod["product"]["iconUrl"] - bot["model"] = botprod["product"]["model"] - bot["pip"] = botprod["product"]["_id"] - bot["deviceName"] = botprod["product"]["name"] - bot["materialNo"] = botprod["product"]["materialNo"] - bot["product_category"] = "DEEBOT" if botprod["product"]["name"].startswith("DEEBOT") else "UNKNOWN" - # bot["updateInfo"] = { - # "changeLog": "", - # "needUpdate": False - # } - # bot["service"] = { - # "jmq": "jmq-ngiot-eu.dc.ww.ecouser.net", - # "mqs": "api-ngiot.dc-as.ww.ecouser.net" - # } - bot["status"] = 1 if bot["mqtt_connection"] or bot["xmpp_connection"] else 0 - return json.dumps( bot, default=lambda o: o.__dict__, sort_keys=False ) # , indent=4) @@ -410,6 +406,11 @@ def client_add(userid, realm, resource): bumperlog.info("Adding new client with resource {}".format(newclient.resource)) client_full_upsert(newclient.asdict()) +def client_reset_connectionStatus(): + clients = db_get().table("clients") + for client in clients: + client_set_mqtt(client["resource"], False) + client_set_xmpp(client["resource"], False) def client_remove(resource): clients = db_get().table("clients") @@ -417,7 +418,6 @@ def client_remove(resource): if client: clients.remove(doc_ids=[client.doc_id]) - def client_get(resource): clients = db_get().table("clients") Client = Query() diff --git a/bumper/models.py b/bumper/models.py index e547926..7177596 100644 --- a/bumper/models.py +++ b/bumper/models.py @@ -1,14 +1,10 @@ #!/usr/bin/env python3 import json -import uuid -from datetime import datetime, timedelta - -import bumper class VacBotDevice(object): def __init__( - self, did="", vac_bot_device_class="", resource="", name="", nick="", company="" + self, did="", vac_bot_device_class="", resource="", name="", nick="", company="" ): self.vac_bot_device_class = vac_bot_device_class self.company = company @@ -87,1654 +83,714 @@ class EcoVacsHome_Login(EcoVacs_Login): ucUid = "" -class OAuth: - access_token = "" - expire_at = "" - refresh_token = "" - userId = "" - - def __init__(self, **entries): - self.__dict__.update(entries) - - @classmethod - def create_new(cls, userId: str): - oauth = OAuth() - oauth.userId = userId - oauth.access_token = uuid.uuid4().hex - oauth.expire_at = "{}".format(datetime.utcnow() + timedelta(days=bumper.oauth_validity_days)) - oauth.refresh_token = uuid.uuid4().hex - return oauth - - def toDB(self): - return self.__dict__ - - def toResponse(self): - data = self.__dict__ - data["expire_at"] = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time( - datetime.fromisoformat(self.expire_at).timestamp()) - return data - - -# EcoVacs Home Product IOT Map - 2021-04-15 +# EcoVacs Home Product IOT Map - 2020-01-05 # https://portal-ww.ecouser.net/api/pim/product/getProductIotMap EcoVacsHomeProducts = [ - { - "classid": "4f0c4e", - "product": { - "_id": "5d2c5fcd4d60de0001eaf2a5", - "materialNo": "70200000227", - "name": "AT01", - "icon": "5d2d996c4d60de0001eaf2b5", - "model": "AT01", - "UILogicId": "AT_01G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": False, - "alexa": False - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d2d996c4d60de0001eaf2b5" - } - }, - { - "classid": "vsc5ia", - "product": { - "_id": "5c763eba280fda0001770b81", - "materialNo": "702-0000-0163", - "name": "DEEBOT 500", - "icon": "5c874326280fda0001770d2a", - "model": "D500", - "UILogicId": "D_500", - "ota": False, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c874326280fda0001770d2a" - } - }, - { - "classid": "zi1uwd", - "product": { - "_id": "5d78f4e878d8b60001e23edc", - "materialNo": "3", - "name": "DEEBOT U3 LINE FRIENDS", - "icon": "5da834a8d66cd10001f58265", - "model": "DK4G.11", - "UILogicId": "DK_4GLINE", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5da834a8d66cd10001f58265" - } - }, - { - "classid": "12baap", - "product": { - "_id": "5ea8d1fe73193e3bef67c551", - "materialNo": "110-1919-1801", - "name": "DEEBOT U2 PRO", - "icon": "606425981269020008a9627b", - "model": "U2_APAC2", - "UILogicId": "U2_HIGH_G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606425981269020008a9627b" - } - }, - { - "classid": "02uwxm", - "product": { - "_id": "5ae1481e7ccd1a0001e1f69e", - "materialNo": "110-1715-0201", - "name": "DEEBOT OZMO Slim10 Series", - "icon": "5b1dddc48bc45700014035a1", - "model": "SLIM10", - "UILogicId": "D_OZMO_SLIM10", - "ota": False, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b1dddc48bc45700014035a1" - } - }, - { - "classid": "eyi9jv", - "product": { - "_id": "5b7b65f364e1680001a08b54", - "materialNo": "702-0000-0203", - "name": "DEEBOT 715", - "icon": "5b7b65f176f7f10001e9a0c2", - "model": "DV3G", - "UILogicId": "D_700", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b7b65f176f7f10001e9a0c2" - } - }, - { - "classid": "9rft3c", - "product": { - "_id": "5e14196a6e71b80001b60fda", - "materialNo": "191165", - "name": "DEEBOT OZMO T5", - "icon": "6062795ad18cbd0008e2fce8", - "model": "DX9G_T5", - "UILogicId": "DX_9G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": False, - "alexa": False - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/6062795ad18cbd0008e2fce8" - } - }, - { - "classid": "141", - "product": { - "_id": "5cae97c9128519000168596f", - "materialNo": "110-1638-0101", - "name": "DEEBOT M81 Pro", - "icon": "5d2c2aa64d60de0001eaf1f6", - "model": "M81", - "UILogicId": "ECO_INTL_141", - "ota": False, - "supportType": { - "share": False, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c2aa64d60de0001eaf1f6" - } - }, - { - "classid": "fqxoiu", - "product": { - "_id": "5e8e8d8a032edd8457c66bfb", - "materialNo": "110-1921-1100", - "name": "DEEBOT OZMO T8+", - "icon": "605b059217c95b0008ff20d4", - "model": "OT8+", - "UILogicId": "DT_8G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/605b059217c95b0008ff20d4" - } - }, - { - "classid": "u6eqoa", - "product": { - "_id": "5e993f2f6a299d0bd506d665", - "materialNo": "110-1919-1401", - "name": "DEEBOT U2 PRO", - "icon": "606425a11269020008a9627d", - "model": "U2_APAC", - "UILogicId": "U2_HIGH_G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606425a11269020008a9627d" - } - }, - { - "classid": "dl8fht", - "product": { - "_id": "5acb0fa87c295c0001876ecf", - "materialNo": "702-0000-0170", - "name": "DEEBOT 600 Series", - "icon": "5acc32067c295c0001876eea", - "model": "D600", - "UILogicId": "D_600", - "ota": False, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5acc32067c295c0001876eea" - } - }, - { - "classid": "yna5xi", - "product": { - "_id": "5c19a91ca1e6ee000178224a", - "materialNo": "110-1820-0101", - "name": "DEEBOT OZMO 950 Series", - "icon": "606278df4a84d700082b39f1", - "model": "DX9G", - "UILogicId": "DX_9G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606278df4a84d700082b39f1" - } - }, - { - "classid": "123", - "product": { - "_id": "5cae9b201285190001685977", - "materialNo": "110-1639-0102", - "name": "DEEBOT Slim2 Series", - "icon": "5d2c150dba13eb00013feaae", - "model": "Slim2", - "UILogicId": "ECO_INTL_123", - "ota": False, - "supportType": { - "share": False, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c150dba13eb00013feaae" - } - }, - { - "classid": "140", - "product": { - "_id": "5cd43b4cf542e00001dc2dec", - "materialNo": "110-1639-0011", - "name": "DEEBOT Slim Neo", - "icon": "5d2c152f4d60de0001eaf1f4", - "model": "SlimNeo", - "UILogicId": "ECO_INTL_140", - "ota": False, - "supportType": { - "share": False, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c152f4d60de0001eaf1f4" - } - }, - { - "classid": "q1v5dn", - "product": { - "_id": "5d312ae18d8d430001817002", - "materialNo": "70200000228", - "name": "AT01", - "icon": "5d83375f6b6a570001569e26", - "model": "AT01", - "UILogicId": "AT_01G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": False, - "alexa": False - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d83375f6b6a570001569e26" - } - }, - { - "classid": "16wdph", - "product": { - "_id": "5d280ce344af3600013839ab", - "materialNo": "702-0000-0171", - "name": "DEEBOT 661", - "icon": "5d280ce3350e7a0001e84c95", - "model": "D661", - "UILogicId": "D_661", - "ota": False, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d280ce3350e7a0001e84c95" - } - }, - { - "classid": "rvo6ev", - "product": { - "_id": "5e859780648255c8bf530e14", - "materialNo": "110-1919-1001", - "name": "DEEBOT U2", - "icon": "606426feb0a931000860fad5", - "model": "U2_AL", - "UILogicId": "U2_HIGH_MODE_G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606426feb0a931000860fad5" - } - }, - { - "classid": "09m4bu", - "product": { - "_id": "5e9d340b8c92c777ab83557f", - "materialNo": "130-6225-0605", - "name": "K650", - "icon": "5ef31b8cee3c1200075b6f67", - "model": "K651G", - "UILogicId": "K650_G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ef31b8cee3c1200075b6f67" - } - }, - { - "classid": "y2qy3m", - "product": { - "_id": "5ea8d28922838d15795ed88d", - "materialNo": "110-1919-1701", - "name": "DEEBOT U2 PRO", - "icon": "606425784a84d700082b39f6", - "model": "U2_APAC3", - "UILogicId": "U2_HIGH_G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606425784a84d700082b39f6" - } - }, - { - "classid": "0xyhhr", - "product": { - "_id": "5ca4716312851900016858cd", - "materialNo": "110-1825-0201", - "name": "DEEBOT OZMO 700", - "icon": "5d117d4f0ac6ad00012b792d", - "model": "DV5G", - "UILogicId": "DV_5G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d117d4f0ac6ad00012b792d" - } - }, - { - "classid": "ipzjy0", - "product": { - "_id": "5e9923878c92c7676b835555", - "materialNo": "110-1919-0702", - "name": "DEEBOT U2", - "icon": "606426c64a84d700082b39fa", - "model": "U2_EL1", - "UILogicId": "U2_HIGH_G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606426c64a84d700082b39fa" - } - }, - { - "classid": "hsgwhi", - "product": { - "_id": "5de9b6fb787cdf0001ef98ac", - "materialNo": "113-1931-0001", - "name": "ANDY", - "icon": "5e731a4a06f6de700464c69d", - "model": "ANDY", - "UILogicId": "ATMOBOT_ANDY", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": False, - "alexa": False - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5e731a4a06f6de700464c69d" - } - }, - { - "classid": "h18jkh", - "product": { - "_id": "5e8e8d2a032edd3c03c66bf7", - "materialNo": "110-1921-0400", - "name": "DEEBOT OZMO T8", - "icon": "5e8e8d146482551d72530e47", - "model": "OT8G", - "UILogicId": "DT_8G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5e8e8d146482551d72530e47" - } - }, - { - "classid": "126", - "product": { - "_id": "5ca32ab212851900016858c7", - "materialNo": "702-0000-0136", - "name": "DEEBOT N79", - "icon": "5ca32ab2e9e9270001354b3d", - "model": "N79", - "UILogicId": "ECO_INTL_126", - "ota": False, - "supportType": { - "share": False, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ca32ab2e9e9270001354b3d" - } - }, - { - "classid": "x5d34r", - "product": { - "_id": "5de0d86ed88546000195239a", - "materialNo": "110-1913-0101", - "name": "DEEBOT OZMO T8 AIVI", - "icon": "605053e7fc527c00087fda1e", - "model": "DXAI_INTL", - "UILogicId": "DX_AIG", - "ota": True, - "supportType": { - "share": False, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/605053e7fc527c00087fda1e" - } - }, - { - "classid": "ls1ok3", - "product": { - "_id": "5b6561060506b100015c8868", - "materialNo": "110-1711-0201", - "name": "DEEBOT 900 Series", - "icon": "5ba4a2cb6c2f120001c32839", - "model": "DN5G", - "UILogicId": "D_900", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ba4a2cb6c2f120001c32839" - } - }, - { - "classid": "y79a7u", - "product": { - "_id": "5b04c0227ccd1a0001e1f6a8", - "materialNo": "110-1810-0101", - "name": "DEEBOT OZMO 900 Series", - "icon": "5b04c0217ccd1a0001e1f6a7", - "model": "DN5G", - "UILogicId": "D_OZMO_900", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b04c0217ccd1a0001e1f6a7" - } - }, - { - "classid": "vi829v", - "product": { - "_id": "5c19a8f3a1e6ee0001782247", - "materialNo": "110-1819-0101", - "name": "DEEBOT OZMO 920 Series", - "icon": "606278d3fc527c00087fdb08", - "model": "DX5G", - "UILogicId": "DX_5G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606278d3fc527c00087fdb08" - } - }, - { - "classid": "55aiho", - "product": { - "_id": "5e698a6306f6de52c264c61b", - "materialNo": "110-1921-1101", - "name": "DEEBOT OZMO T8+", - "icon": "605be27250928b0007c13264", - "model": "T8+", - "UILogicId": "DT_8G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/605be27250928b0007c13264" - } - }, - { - "classid": "gd4uut", - "product": { - "_id": "5bc8189d68142800016a6937", - "materialNo": "110-1803-0101", - "name": "DEEBOT OZMO 960", - "icon": "5e8da019032edd9008c66bf0", - "model": "DG7G", - "UILogicId": "DR_935G", - "ota": True, - "supportType": { - "share": False, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5e8da019032edd9008c66bf0" - } - }, - { - "classid": "130", - "product": { - "_id": "5cae98d01285190001685974", - "materialNo": "110-1629-0201", - "name": "DEEBOT OZMO 610 Series", - "icon": "5d4b7640de51dd0001fee131", - "model": "OZMO600", - "UILogicId": "ECO_INTL_130", - "ota": False, - "supportType": { - "share": False, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d4b7640de51dd0001fee131" - } - }, - { - "classid": "2pv572", - "product": { - "_id": "5d1474630ac6ad00012b7940", - "materialNo": "110-1810-0107", - "name": "DEEBOT OZMO 905", - "icon": "5d1474632a6bd50001b5b6f3", - "model": "OZMO905", - "UILogicId": "D_OZMO_900", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d1474632a6bd50001b5b6f3" - } - }, - { - "classid": "xb83mv", - "product": { - "_id": "5d246180350e7a0001e84bea", - "materialNo": "88393939393", - "name": "DEEBOT U3", - "icon": "5d3fe649de51dd0001fee0de", - "model": "SLIM4_INTL", - "UILogicId": "DK_4G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d3fe649de51dd0001fee0de" - } - }, - { - "classid": "ar5bjb", - "product": { - "_id": "5e58a73d36e8f3cab08f031f", - "materialNo": "130-6211-0610", - "name": "DEEBOT 665", - "icon": "5e58a2df36e8f39e318f031d", - "model": "D665", - "UILogicId": "D_661", - "ota": False, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5e58a2df36e8f39e318f031d" - } - }, - { - "classid": "7j1tu6", - "product": { - "_id": "5e993e566a299d449a06d65a", - "materialNo": "110-1919-0801", - "name": "DEEBOT U2 PRO", - "icon": "6064260545505e0008e5cb49", - "model": "U2_EH2", - "UILogicId": "U2_HIGH_G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/6064260545505e0008e5cb49" - } - }, - { - "classid": "ts2ofl", - "product": { - "_id": "5e993e8b8c92c753dd83555f", - "materialNo": "110-1919-0901", - "name": "DEEBOT U2", - "icon": "606425dfd18cbd0008e2fcf3", - "model": "U2_AUL", - "UILogicId": "U2_HIGH_G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606425dfd18cbd0008e2fcf3" - } - }, - { - "classid": "125", - "product": { - "_id": "5cae9703128519000168596a", - "materialNo": "110-1638-0102", - "name": "DEEBOT M80 Pro", - "icon": "5d2c14414d60de0001eaf1f2", - "model": "M80", - "UILogicId": "ECO_INTL_125", - "ota": False, - "supportType": { - "share": False, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c14414d60de0001eaf1f2" - } - }, - { - "classid": "jh3ry2", - "product": { - "_id": "5de9b77f0136c00001cb1f8e", - "materialNo": "113-1931-0003", - "name": "AVA", - "icon": "6049b34d1269020008a95aef", - "model": "AVA", - "UILogicId": "ATMOBOT_AVA", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": False, - "alexa": False - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/6049b34d1269020008a95aef" - } - }, - { - "classid": "wlqdkp", - "product": { - "_id": "5e9924018c92c7c480835559", - "materialNo": "110-1919-0701", - "name": "DEEBOT U2", - "icon": "6064263ad18cbd0008e2fcf4", - "model": "U2_EL2", - "UILogicId": "U2_HIGH_G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/6064263ad18cbd0008e2fcf4" - } - }, - { - "classid": "c0lwyn", - "product": { - "_id": "5e993eef6a299d7e4a06d660", - "materialNo": "110-1919-1301", - "name": "DEEBOT U2 PRO", - "icon": "606425ab4a84d700082b39f7", - "model": "U2_AUH", - "UILogicId": "U2_HIGH_G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606425ab4a84d700082b39f7" - } - }, - { - "classid": "emzppx", - "product": { - "_id": "5c763f35280fda0001770b84", - "materialNo": "702-0000-0169", - "name": "DEEBOT 501", - "icon": "5c931fef280fda0001770d7e", - "model": "D501", - "UILogicId": "D_500", - "ota": False, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c931fef280fda0001770d7e" - } - }, - { - "classid": "115", - "product": { - "_id": "5bbedd2822d57f00018c13b7", - "materialNo": "110-1602-0101", - "name": "DEEBOT OZMO/PRO 930 Series", - "icon": "5cf711aeb0acfc000179ff8a", - "model": "DR930", - "UILogicId": "DR_930G", - "ota": True, - "supportType": { - "share": False, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5cf711aeb0acfc000179ff8a" - } - }, - { - "classid": "jr3pqa", - "product": { - "_id": "5b43077b8bc457000140363e", - "materialNo": "702-0000-0202", - "name": "DEEBOT 711", - "icon": "5b5ac4cc8d5a56000111e769", - "model": "D711", - "UILogicId": "D_700", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b5ac4cc8d5a56000111e769" - } - }, - { - "classid": "142", - "product": { - "_id": "5ca1ca7a12851900016858bd", - "materialNo": "110-1640-0101", - "name": "DEEBOT Mini2", - "icon": "5ca1ca79e9e9270001354b2d", - "model": "Mini2", - "UILogicId": "ECO_INTL_142", - "ota": False, - "supportType": { - "share": False, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ca1ca79e9e9270001354b2d" - } - }, - { - "classid": "aqdd5p", - "product": { - "_id": "5cb7cfba179839000114d762", - "materialNo": "110-1711-0001", - "name": "DEEBOT DE55", - "icon": "5cb7cfbab72c4d00010e5fc7", - "model": "DE5G", - "UILogicId": "D_900", - "ota": True, - "supportType": { - "share": False, - "tmjl": False, - "assistant": False, - "alexa": False - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5cb7cfbab72c4d00010e5fc7" - } - }, - { - "classid": "152", - "product": { - "_id": "5cbd97b961526a00019799bd", - "materialNo": "110-1628-0302", - "name": "DEEBOT", - "icon": "5d4b7628de51dd0001fee12f", - "model": "D600", - "UILogicId": "ECO_INTL_152", - "ota": False, - "supportType": { - "share": False, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d4b7628de51dd0001fee12f" - } - }, - { - "classid": "d0cnel", - "product": { - "_id": "5ceba1c6d85b4d0001776986", - "materialNo": "702-0000-0204", - "name": "DEEBOT 711s", - "icon": "5d157f9f77a3a60001051f69", - "model": "D711S", - "UILogicId": "D_700", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d157f9f77a3a60001051f69" - } - }, - { - "classid": "u4h1uk", - "product": { - "_id": "5e993eba8c92c71489835564", - "materialNo": "110-1919-1101", - "name": "DEEBOT U2 PRO", - "icon": "606425bed18cbd0008e2fcf2", - "model": "U2_JP", - "UILogicId": "U2_HIGH_G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606425bed18cbd0008e2fcf2" - } - }, - { - "classid": "3ab24g", - "product": { - "_id": "5e9d34418c92c717e9835583", - "materialNo": "130-6225-0302", - "name": "K650", - "icon": "5ef31b80f5dcdf000767cf4d", - "model": "K652G", - "UILogicId": "K650_RANDOM_G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ef31b80f5dcdf000767cf4d" - } - }, - { - "classid": "9akc61", - "product": { - "_id": "5c763f8263023c0001e7f855", - "materialNo": "20200115005", - "name": "DEEBOT 505", - "icon": "5c932067280fda0001770d7f", - "model": "D505", - "UILogicId": "D_500", - "ota": False, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c932067280fda0001770d7f" - } - }, - { - "classid": "159", - "product": { - "_id": "5ca32bc2e9e9270001354b41", - "materialNo": "110-1629-0203", - "name": "DEEBOT OZMO 601", - "icon": "5d4b7606de51dd0001fee12d", - "model": "159", - "UILogicId": "ECO_INTL_159", - "ota": False, - "supportType": { - "share": False, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d4b7606de51dd0001fee12d" - } - }, - { - "classid": "b742vd", - "product": { - "_id": "5e699a4106f6de83ea64c620", - "materialNo": "110-1921-0301", - "name": "DEEBOT OZMO T8", - "icon": "5e8e93a7032edd3f5ec66d4a", - "model": "T8G", - "UILogicId": "DT_8G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5e8e93a7032edd3f5ec66d4a" - } - }, - { - "classid": "uv242z", - "product": { - "_id": "5b5149b4ac0b87000148c128", - "materialNo": "702-0000-0205", - "name": "DEEBOT 710", - "icon": "5b5ac4e45f21100001882bb9", - "model": "D700", - "UILogicId": "D_700", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b5ac4e45f21100001882bb9" - } - }, - { - "classid": "155", - "product": { - "_id": "5cce893813afb7000195d6af", - "materialNo": "702-0000-0164", - "name": "DEEBOT N79S/SE", - "icon": "5cd4ca505b032200015a455d", - "model": "DN622", - "UILogicId": "ECO_INTL_155", - "ota": False, - "supportType": { - "share": False, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5cd4ca505b032200015a455d" - } - }, - { - "classid": "1qdu4z", - "product": { - "_id": "5de9b7f50136c00001cb1f96", - "materialNo": "117-1923-0101", - "name": "Aaron", - "icon": "5e71c7df298f0d9cabfef86f", - "model": "AT80K", - "UILogicId": "AT80", - "ota": False, - "supportType": { - "share": True, - "tmjl": False, - "assistant": False, - "alexa": False - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5e71c7df298f0d9cabfef86f" - } - }, - { - "classid": "4zfacv", - "product": { - "_id": "5bf2596f23244a00013f2f13", - "materialNo": "910", - "name": "DEEBOT 910", - "icon": "5c778731280fda0001770ba0", - "model": "DN2_INTL", - "UILogicId": "DN_2G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c778731280fda0001770ba0" - } - }, - { - "classid": "nq9yhl", - "product": { - "_id": "5e8597b0032edd333ac66bbf", - "materialNo": "110-1919-0601", - "name": "DEEBOT U2 PRO", - "icon": "606426ebb0a931000860fad4", - "model": "U2_AH", - "UILogicId": "U2_HIGH_MODE_G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606426ebb0a931000860fad4" - } - }, - { - "classid": "m7lqzi", - "product": { - "_id": "5c653edf7b93c700013f12cc", - "materialNo": "113-1708-0001", - "name": "ATMOBOT Pro", - "icon": "5d2c63a5ba13eb00013feab7", - "model": "AA30G", - "UILogicId": "AA_30G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": False, - "alexa": False - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c63a5ba13eb00013feab7" - } - }, - { - "classid": "r8ead0", - "product": { - "_id": "5c763f63280fda0001770b88", - "materialNo": "702-0000-0161", - "name": "DEEBOT 502", - "icon": "5c93204b63023c0001e7faa7", - "model": "D502", - "UILogicId": "D_500", - "ota": False, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c93204b63023c0001e7faa7" - } - }, - { - "classid": "jjccwk", - "product": { - "_id": "5ce7870cd85b4d0001775db9", - "materialNo": "20200115004", - "name": "DEEBOT OZMO 750", - "icon": "5d3aa309ba13eb00013feb69", - "model": "DV6G", - "UILogicId": "DV_6G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d3aa309ba13eb00013feb69" - } - }, - { - "classid": "129", - "product": { - "_id": "5ca31df1e9e9270001354b35", - "materialNo": "110-1628-0101", - "name": "DEEBOT M86", - "icon": "5ca31df112851900016858c0", - "model": "M86", - "UILogicId": "ECO_INTL_129", - "ota": False, - "supportType": { - "share": False, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ca31df112851900016858c0" - } - }, - { - "classid": "165", - "product": { - "_id": "5ca32a11e9e9270001354b39", - "materialNo": "702-0000-0189", - "name": "DEEBOT N79T/W", - "icon": "5ca32a1012851900016858c6", - "model": "N79T", - "UILogicId": "ECO_INTL_165", - "ota": False, - "supportType": { - "share": False, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ca32a1012851900016858c6" - } - }, - { - "classid": "jffnlf", - "product": { - "_id": "5e53208426be716edf4b55cf", - "materialNo": "130-6311-1702", - "name": "DEEBOT N3 MAX", - "icon": "5e53207a26be71596c4b55cd", - "model": "DU3G", - "UILogicId": "DU_6G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": False, - "alexa": False - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5e53207a26be71596c4b55cd" - } - }, - { - "classid": "d4v1pm", - "product": { - "_id": "5e9924416a299dddac06d656", - "materialNo": "110-1919-0802", - "name": "DEEBOT U2 PRO", - "icon": "606426254a84d700082b39f9", - "model": "U2_EH1", - "UILogicId": "U2_HIGH_G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606426254a84d700082b39f9" - } - }, - { - "classid": "34vhpm", - "product": { - "_id": "5edd998afdd6a30008da039b", - "materialNo": "110-1913-0501", - "name": "DEEBOT T8 AIVI +", - "icon": "605050031269020008a95af9", - "model": "DXAIS_TW", - "UILogicId": "DX_AIG", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/605050031269020008a95af9" - } - }, - { - "classid": "tpnwyu", - "product": { - "_id": "5edd9a4075f2fc000636086c", - "materialNo": "110-1913-0401", - "name": "DEEBOT T8 AIVI +", - "icon": "6050503e1269020008a95afa", - "model": "DXAIS_HK", - "UILogicId": "DX_AIG", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/6050503e1269020008a95afa" - } - }, - { - "classid": "wgxm70", - "product": { - "_id": "5ed5e4d3a719ea460ec3216c", - "materialNo": "110-1921-0012", - "name": "DEEBOT T8", - "icon": "5edf2bbedb28cc00062f8bd7", - "model": "T8GC", - "UILogicId": "DT_8G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5edf2bbedb28cc00062f8bd7" - } - }, - { - "classid": "1zqysa", - "product": { - "_id": "5ee85c64fdd6a30008da0af4", - "materialNo": "110-1919-2201", - "name": "DEEBOT U2 POWER", - "icon": "6064258d1269020008a9627a", - "model": "U2_HK", - "UILogicId": "U2_HIGH_NOMAG_G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/6064258d1269020008a9627a" - } - }, - { - "classid": "chmi0g", - "product": { - "_id": "5ee85cabfdd6a30008da0af8", - "materialNo": "110-1919-2301", - "name": "DEEBOT U2 POWER", - "icon": "606425821269020008a96279", - "model": "U2_TW", - "UILogicId": "U2_HIGH_NOMAG_G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606425821269020008a96279" - } - }, - { - "classid": "p5nx9u", - "product": { - "_id": "5f0d45404a3cbe00073d17db", - "materialNo": "110-2022-0001", - "name": "yeedi 2 hybrid", - "icon": "5f59e774c0f03a0008ee72e0", - "model": "K750", - "UILogicId": "DK_750", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5f59e774c0f03a0008ee72e0" - } - }, - { - "classid": "n6cwdb", - "product": { - "_id": "5f729454a541bd000881cc7b", - "materialNo": "110-2009-0900", - "name": "DEEBOT N8", - "icon": "60627a92fc527c00087fdb0a", - "model": "N8_LDS_WHITE_N", - "UILogicId": "T5_SE_G_DTOF", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/60627a92fc527c00087fdb0a" - } - }, - { - "classid": "lhbd50", - "product": { - "_id": "5f88195e6cf8de0008ed7c11", - "materialNo": "110-2010-1001", - "name": "DEEBOT T9+", - "icon": "603f51243b03f50007b6c2ca", - "model": "DEEBOT_OZMO_T9PLUS", - "UILogicId": "T9_PRO_G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/603f51243b03f50007b6c2ca" - } - }, - { - "classid": "ucn2xe", - "product": { - "_id": "5f8819156cf8de0008ed7c0d", - "materialNo": "110-2010-0301", - "name": "DEEBOT T9", - "icon": "603f510e3b03f50007b6c2c9", - "model": "DEEBOT_OZMO_T9", - "UILogicId": "T9_PRO_G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/603f510e3b03f50007b6c2c9" - } - }, - { - "classid": "0bdtzz", - "product": { - "_id": "5fa105c6d16a99000667eb54", - "materialNo": "110-1921-0404", - "name": "DEEBOT OZMO T8 PURE", - "icon": "5fa105bbd16a99000667eb52", - "model": "OT8_PURE", - "UILogicId": "DT_8G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5fa105bbd16a99000667eb52" - } - }, - { - "classid": "r5zxjr", - "product": { - "_id": "5fa2441169320300086ff812", - "materialNo": "110-2009-0101", - "name": "DEEBOT N7", - "icon": "60627c09b0a931000860facd", - "model": "N7_LDS", - "UILogicId": "T5_SE_G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/60627c09b0a931000860facd" - } - }, - { - "classid": "r5y7re", - "product": { - "_id": "5fa4b1f8c7260e000858584b", - "materialNo": "110-2009-0902", - "name": "DEEBOT N8", - "icon": "60627a9cb0a931000860fac7", - "model": "N8_DTOF_TW", - "UILogicId": "T5_SE_G_DTOF", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/60627a9cb0a931000860fac7" - } - }, - { - "classid": "snxbvc", - "product": { - "_id": "5fa4e31dd16a99000667eb94", - "materialNo": "110-2008-0102", - "name": "DEEBOT N8 PRO", - "icon": "60627bbfb0a931000860fac9", - "model": "N8_PRO_WHITE", - "UILogicId": "DT_8SE_G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/60627bbfb0a931000860fac9" - } - }, - { - "classid": "7bryc5", - "product": { - "_id": "5fa4e248c7260e0008585850", - "materialNo": "110-2029-0001", - "name": "DEEBOT N8+", - "icon": "5fb474d4d16a99000667edd9", - "model": "N8_PLUS_WHITE", - "UILogicId": "N8_PLUS", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5fb474d4d16a99000667edd9" - } - }, - { - "classid": "b2jqs4", - "product": { - "_id": "5fbc7cc69601440008b24469", - "materialNo": "110-2029-0002", - "name": "DEEBOT N8+", - "icon": "5feaeb27d4cb3a0006679047", - "model": "N8_PLUS_BLACK", - "UILogicId": "N8_PLUS", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5feaeb27d4cb3a0006679047" - } - }, - { - "classid": "yu362x", - "product": { - "_id": "5fbc7da39601440008b2446d", - "materialNo": "110-2008-0103", - "name": "DEEBOT N8 PRO", - "icon": "60627bde50928b0007c13273", - "model": "N8_PRO_BLACK", - "UILogicId": "DT_8SE_G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/60627bde50928b0007c13273" - } - }, - { - "classid": "ifbw08", - "product": { - "_id": "5fbc7e18c7260e0008585c8f", - "materialNo": "110-2008-0901", - "name": "DEEBOT N8 PRO+", - "icon": "5feaeb47d4cb3a0006679048", - "model": "N8_PRO_PLUS_WHITE", - "UILogicId": "DT_8SE_G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5feaeb47d4cb3a0006679048" - } - }, - { - "classid": "85as7h", - "product": { - "_id": "5fbc7f5069320300086ffa5e", - "materialNo": "110-2008-0902", - "name": "DEEBOT N8 PRO+", - "icon": "5feaeb585f437d0008e0e00c", - "model": "N8_PRO_PLUS_BLACK", - "UILogicId": "DT_8SE_G", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5feaeb585f437d0008e0e00c" - } - }, - { - "classid": "ty84oi", - "product": { - "_id": "5fbcac79c7260e0008585c94", - "materialNo": "110-2029-1201", - "name": "DEEBOT N8", - "icon": "60627bcafc527c00087fdb0c", - "model": "N8_WHITE", - "UILogicId": "N8_PLUS", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/60627bcafc527c00087fdb0c" - } - }, - { - "classid": "36xnxf", - "product": { - "_id": "5fbcacf369320300086ffa63", - "materialNo": "110-2029-0701", - "name": "DEEBOT N8", - "icon": "60627bd517c95b0008ff20ec", - "model": "N8_BLACK\t", - "UILogicId": "N8_PLUS", - "ota": True, - "supportType": { - "share": True, - "tmjl": False, - "assistant": True, - "alexa": True - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/60627bd517c95b0008ff20ec" - } - }, - { - "classid": "2pj946", - "product": { - "_id": "5fd1e362bbf4b30006cd9255", - "materialNo": "111-2005-0301", - "name": "WINBOT 920", - "icon": "6049b3631269020008a95af0", - "model": "W920_INT", - "UILogicId": "winbot_g", - "ota": True, - "supportType": { - "share": False, - "tmjl": False, - "assistant": False, - "alexa": False - }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/6049b3631269020008a95af0" - } + { + "classid": "dl8fht", + "product": { + "_id": "5acb0fa87c295c0001876ecf", + "materialNo": "702-0000-0170", + "name": "DEEBOT 600 Series", + "icon": "5acc32067c295c0001876eea", + "UILogicId": "D_600", + "ota": False, + "supportType": { + "share": True, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5acc32067c295c0001876eea" } + }, + { + "classid": "02uwxm", + "product": { + "_id": "5ae1481e7ccd1a0001e1f69e", + "materialNo": "110-1715-0201", + "name": "DEEBOT OZMO Slim10 Series", + "icon": "5b1dddc48bc45700014035a1", + "UILogicId": "D_OZMO_SLIM10", + "ota": False, + "supportType": { + "share": True, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b1dddc48bc45700014035a1" + } + }, + { + "classid": "y79a7u", + "product": { + "_id": "5b04c0227ccd1a0001e1f6a8", + "materialNo": "110-1810-0101", + "name": "DEEBOT OZMO 900 Series", + "icon": "5b04c0217ccd1a0001e1f6a7", + "UILogicId": "D_OZMO_900", + "ota": True, + "supportType": { + "share": True, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b04c0217ccd1a0001e1f6a7" + } + }, + { + "classid": "jr3pqa", + "product": { + "_id": "5b43077b8bc457000140363e", + "materialNo": "702-0000-0202", + "name": "DEEBOT 711", + "icon": "5b5ac4cc8d5a56000111e769", + "UILogicId": "D_700", + "ota": True, + "supportType": { + "share": True, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b5ac4cc8d5a56000111e769" + } + }, + { + "classid": "uv242z", + "product": { + "_id": "5b5149b4ac0b87000148c128", + "materialNo": "702-0000-0205", + "name": "DEEBOT 710", + "icon": "5b5ac4e45f21100001882bb9", + "UILogicId": "D_700", + "ota": True, + "supportType": { + "share": True, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b5ac4e45f21100001882bb9" + } + }, + { + "classid": "ls1ok3", + "product": { + "_id": "5b6561060506b100015c8868", + "materialNo": "110-1711-0201", + "name": "DEEBOT 900 Series", + "icon": "5ba4a2cb6c2f120001c32839", + "UILogicId": "D_900", + "ota": True, + "supportType": { + "share": True, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ba4a2cb6c2f120001c32839" + } + }, + { + "classid": "eyi9jv", + "product": { + "_id": "5b7b65f364e1680001a08b54", + "materialNo": "702-0000-0202", + "name": "DEEBOT 715", + "icon": "5b7b65f176f7f10001e9a0c2", + "UILogicId": "D_700", + "ota": True, + "supportType": { + "share": True, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b7b65f176f7f10001e9a0c2" + } + }, + { + "classid": "4zfacv", + "product": { + "_id": "5bf2596f23244a00013f2f13", + "materialNo": "910", + "name": "DEEBOT 910", + "icon": "5c778731280fda0001770ba0", + "UILogicId": "DN_2G", + "ota": True, + "supportType": { + "share": True, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c778731280fda0001770ba0" + } + }, + { + "classid": "115", + "product": { + "_id": "5bbedd2822d57f00018c13b7", + "materialNo": "110-1602-0101", + "name": "DEEBOT OZMO/PRO 930 Series", + "icon": "5cf711aeb0acfc000179ff8a", + "UILogicId": "DR_930G", + "ota": True, + "supportType": { + "share": False, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5cf711aeb0acfc000179ff8a" + } + }, + { + "classid": "vi829v", + "product": { + "_id": "5c19a8f3a1e6ee0001782247", + "materialNo": "110-1819-0101", + "name": "DEEBOT OZMO 920 Series", + "icon": "5c9c7995e9e9270001354ab4", + "UILogicId": "DX_5G", + "ota": True, + "supportType": { + "share": True, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c9c7995e9e9270001354ab4" + } + }, + { + "classid": "yna5xi", + "product": { + "_id": "5c19a91ca1e6ee000178224a", + "materialNo": "110-1820-0101", + "name": "DEEBOT OZMO 950 Series", + "icon": "5caafd7e1285190001685965", + "UILogicId": "DX_9G", + "ota": True, + "supportType": { + "share": True, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5caafd7e1285190001685965" + } + }, + { + "classid": "gd4uut", + "product": { + "_id": "5bc8189d68142800016a6937", + "materialNo": "110-1803-0101", + "name": "DEEBOT OZMO 960", + "icon": "5c7384767b93c700013f12e7", + "UILogicId": "DR_935G", + "ota": True, + "supportType": { + "share": False, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c7384767b93c700013f12e7" + } + }, + { + "classid": "m7lqzi", + "product": { + "_id": "5c653edf7b93c700013f12cc", + "materialNo": "113-1708-0001", + "name": "ATMOBOT Pro", + "icon": "5d2c63a5ba13eb00013feab7", + "UILogicId": "AA_30G", + "ota": True, + "supportType": { + "share": True, + "tmjl": False, + "assistant": False, + "alexa": False + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c63a5ba13eb00013feab7" + } + }, + { + "classid": "9akc61", + "product": { + "_id": "5c763f8263023c0001e7f855", + "materialNo": "702-0000-0163", + "name": "DEEBOT 505", + "icon": "5c932067280fda0001770d7f", + "UILogicId": "D_500", + "ota": False, + "supportType": { + "share": True, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c932067280fda0001770d7f" + } + }, + { + "classid": "r8ead0", + "product": { + "_id": "5c763f63280fda0001770b88", + "materialNo": "702-0000-0163", + "name": "DEEBOT 502", + "icon": "5c93204b63023c0001e7faa7", + "UILogicId": "D_500", + "ota": False, + "supportType": { + "share": True, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c93204b63023c0001e7faa7" + } + }, + { + "classid": "emzppx", + "product": { + "_id": "5c763f35280fda0001770b84", + "materialNo": "702-0000-0163", + "name": "DEEBOT 501", + "icon": "5c931fef280fda0001770d7e", + "UILogicId": "D_500", + "ota": False, + "supportType": { + "share": True, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c931fef280fda0001770d7e" + } + }, + { + "classid": "vsc5ia", + "product": { + "_id": "5c763eba280fda0001770b81", + "materialNo": "702-0000-0163", + "name": "DEEBOT 500", + "icon": "5c874326280fda0001770d2a", + "UILogicId": "D_500", + "ota": False, + "supportType": { + "share": True, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c874326280fda0001770d2a" + } + }, + { + "classid": "142", + "product": { + "_id": "5ca1ca7a12851900016858bd", + "materialNo": "110-1640-0101", + "name": "DEEBOT Mini2", + "icon": "5ca1ca79e9e9270001354b2d", + "UILogicId": "ECO_INTL_142", + "ota": False, + "supportType": { + "share": False, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ca1ca79e9e9270001354b2d" + } + }, + { + "classid": "129", + "product": { + "_id": "5ca31df1e9e9270001354b35", + "materialNo": "110-1628-0101", + "name": "DEEBOT M86", + "icon": "5ca31df112851900016858c0", + "UILogicId": "ECO_INTL_129", + "ota": False, + "supportType": { + "share": False, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ca31df112851900016858c0" + } + }, + { + "classid": "165", + "product": { + "_id": "5ca32a11e9e9270001354b39", + "materialNo": "702-0000-0189", + "name": "DEEBOT N79T/W", + "icon": "5ca32a1012851900016858c6", + "UILogicId": "ECO_INTL_165", + "ota": False, + "supportType": { + "share": False, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ca32a1012851900016858c6" + } + }, + { + "classid": "126", + "product": { + "_id": "5ca32ab212851900016858c7", + "materialNo": "702-0000-0136", + "name": "DEEBOT N79", + "icon": "5ca32ab2e9e9270001354b3d", + "UILogicId": "ECO_INTL_126", + "ota": False, + "supportType": { + "share": False, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ca32ab2e9e9270001354b3d" + } + }, + { + "classid": "159", + "product": { + "_id": "5ca32bc2e9e9270001354b41", + "materialNo": "110-1629-0203", + "name": "DEEBOT OZMO 601", + "icon": "5d4b7606de51dd0001fee12d", + "UILogicId": "ECO_INTL_159", + "ota": False, + "supportType": { + "share": False, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d4b7606de51dd0001fee12d" + } + }, + { + "classid": "0xyhhr", + "product": { + "_id": "5ca4716312851900016858cd", + "materialNo": "110-1825-0201", + "name": "DEEBOT OZMO 700", + "icon": "5d117d4f0ac6ad00012b792d", + "UILogicId": "DV_5G", + "ota": True, + "supportType": { + "share": True, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d117d4f0ac6ad00012b792d" + } + }, + { + "classid": "125", + "product": { + "_id": "5cae9703128519000168596a", + "materialNo": "110-1638-0102", + "name": "DEEBOT M80 Pro", + "icon": "5d2c14414d60de0001eaf1f2", + "UILogicId": "ECO_INTL_125", + "ota": False, + "supportType": { + "share": False, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c14414d60de0001eaf1f2" + } + }, + { + "classid": "141", + "product": { + "_id": "5cae97c9128519000168596f", + "materialNo": "110-1638-0101", + "name": "DEEBOT M81 Pro", + "icon": "5d2c2aa64d60de0001eaf1f6", + "UILogicId": "ECO_INTL_141", + "ota": False, + "supportType": { + "share": False, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c2aa64d60de0001eaf1f6" + } + }, + { + "classid": "130", + "product": { + "_id": "5cae98d01285190001685974", + "materialNo": "110-1629-0201", + "name": "DEEBOT OZMO 610 Series", + "icon": "5d4b7640de51dd0001fee131", + "UILogicId": "ECO_INTL_130", + "ota": False, + "supportType": { + "share": False, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d4b7640de51dd0001fee131" + } + }, + { + "classid": "123", + "product": { + "_id": "5cae9b201285190001685977", + "materialNo": "110-1639-0102", + "name": "DEEBOT Slim2 Series", + "icon": "5d2c150dba13eb00013feaae", + "UILogicId": "ECO_INTL_123", + "ota": False, + "supportType": { + "share": False, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c150dba13eb00013feaae" + } + }, + { + "classid": "aqdd5p", + "product": { + "_id": "5cb7cfba179839000114d762", + "materialNo": "110-1711-0001", + "name": "DEEBOT DE55", + "icon": "5cb7cfbab72c4d00010e5fc7", + "UILogicId": "D_900", + "ota": True, + "supportType": { + "share": False, + "tmjl": False, + "assistant": False, + "alexa": False + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5cb7cfbab72c4d00010e5fc7" + } + }, + { + "classid": "152", + "product": { + "_id": "5cbd97b961526a00019799bd", + "materialNo": "110-1628-0302", + "name": "DEEBOT", + "icon": "5d4b7628de51dd0001fee12f", + "UILogicId": "ECO_INTL_152", + "ota": False, + "supportType": { + "share": False, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d4b7628de51dd0001fee12f" + } + }, + { + "classid": "155", + "product": { + "_id": "5cce893813afb7000195d6af", + "materialNo": "702-0000-0163", + "name": "DEEBOT N79S/SE", + "icon": "5cd4ca505b032200015a455d", + "UILogicId": "ECO_INTL_155", + "ota": False, + "supportType": { + "share": False, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5cd4ca505b032200015a455d" + } + }, + { + "classid": "jjccwk", + "product": { + "_id": "5ce7870cd85b4d0001775db9", + "materialNo": "110-1825-0201", + "name": "DEEBOT OZMO 750", + "icon": "5d3aa309ba13eb00013feb69", + "UILogicId": "DV_6G", + "ota": True, + "supportType": { + "share": True, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d3aa309ba13eb00013feb69" + } + }, + { + "classid": "d0cnel", + "product": { + "_id": "5ceba1c6d85b4d0001776986", + "materialNo": "702-0000-0202", + "name": "DEEBOT 711s", + "icon": "5d157f9f77a3a60001051f69", + "UILogicId": "D_700", + "ota": True, + "supportType": { + "share": True, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d157f9f77a3a60001051f69" + } + }, + { + "classid": "140", + "product": { + "_id": "5cd43b4cf542e00001dc2dec", + "materialNo": "110-1639-0011", + "name": "DEEBOT Slim Neo", + "icon": "5d2c152f4d60de0001eaf1f4", + "UILogicId": "ECO_INTL_140", + "ota": False, + "supportType": { + "share": False, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c152f4d60de0001eaf1f4" + } + }, + { + "classid": "2pv572", + "product": { + "_id": "5d1474630ac6ad00012b7940", + "materialNo": "110-1810-0107", + "name": "DEEBOT OZMO 905", + "icon": "5d1474632a6bd50001b5b6f3", + "UILogicId": "D_OZMO_900", + "ota": True, + "supportType": { + "share": True, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d1474632a6bd50001b5b6f3" + } + }, + { + "classid": "xb83mv", + "product": { + "_id": "5d246180350e7a0001e84bea", + "materialNo": "88393939393", + "name": "DEEBOT U3", + "icon": "5d3fe649de51dd0001fee0de", + "UILogicId": "DK_4G", + "ota": True, + "supportType": { + "share": True, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d3fe649de51dd0001fee0de" + } + }, + { + "classid": "4f0c4e", + "product": { + "_id": "5d2c5fcd4d60de0001eaf2a5", + "materialNo": "70200000227", + "name": "AT01", + "icon": "5d2d996c4d60de0001eaf2b5", + "UILogicId": "AT_01G", + "ota": True, + "supportType": { + "share": True, + "tmjl": False, + "assistant": False, + "alexa": False + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d2d996c4d60de0001eaf2b5" + } + }, + { + "classid": "q1v5dn", + "product": { + "_id": "5d312ae18d8d430001817002", + "materialNo": "70200000228", + "name": "AT01", + "icon": "5d83375f6b6a570001569e26", + "UILogicId": "AT_01G", + "ota": True, + "supportType": { + "share": True, + "tmjl": False, + "assistant": False, + "alexa": False + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d83375f6b6a570001569e26" + } + }, + { + "classid": "16wdph", + "product": { + "_id": "5d280ce344af3600013839ab", + "materialNo": "702-0000-0170", + "name": "DEEBOT 661", + "icon": "5d280ce3350e7a0001e84c95", + "UILogicId": "D_661", + "ota": False, + "supportType": { + "share": True, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d280ce3350e7a0001e84c95" + } + }, + { + "classid": "zi1uwd", + "product": { + "_id": "5d78f4e878d8b60001e23edc", + "materialNo": "3", + "name": "DEEBOT U3 LINE FRIENDS", + "icon": "5da834a8d66cd10001f58265", + "UILogicId": "DK_4GLINE", + "ota": True, + "supportType": { + "share": True, + "tmjl": False, + "assistant": True, + "alexa": True + }, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5da834a8d66cd10001f58265" + } + } ] + RETURN_API_SUCCESS = "0000" ERR_ACTIVATE_TOKEN_TIMEOUT = "1006" ERR_COMMON = "0001" @@ -1774,3 +830,4 @@ API_ERRORS = { ERR_WRONG_EMAIL_ADDRESS: "1008", ERR_WRONG_PWD_FROMATE: "1009", } + diff --git a/bumper/mqttserver.py b/bumper/mqttserver.py index f53d1f8..f6f61ed 100644 --- a/bumper/mqttserver.py +++ b/bumper/mqttserver.py @@ -14,10 +14,21 @@ import json from datetime import datetime, timedelta import bumper from passlib.apps import custom_app_context as pwd_context +import ssl +import tempfile + +from urllib.parse import urlparse, urlunparse +from hbmqtt.mqtt.protocol.client_handler import ClientProtocolHandler +from hbmqtt.adapters import StreamReaderAdapter, StreamWriterAdapter, WebSocketsReader, WebSocketsWriter +from websockets.uri import InvalidURI +from websockets.exceptions import InvalidHandshake +from hbmqtt.mqtt.protocol.handler import ProtocolHandlerException +from hbmqtt.mqtt.connack import CONNECTION_ACCEPTED helperbotlog = logging.getLogger("helperbot") boterrorlog = logging.getLogger("boterror") mqttserverlog = logging.getLogger("mqttserver") +proxymodelog = logging.getLogger("proxymode") class MQTTHelperBot: @@ -44,9 +55,7 @@ class MQTTHelperBot: ) await self.Client.subscribe( [ - ("iot/p2p/+/+/+/+/helperbot/bumper/helperbot/+/+/+", QOS_0), - ("iot/p2p/+", QOS_0), - ("iot/atr/+", QOS_0), + ("iot/#", QOS_0), ] ) @@ -216,10 +225,120 @@ class MQTTServer: except Exception as e: mqttserverlog.exception("{}".format(e)) +class BumperProxyModeMQTTClient(MQTTClient): + ecohelpername = "" + async def _connect_coro(self): #Override default to ignore ssl verification + kwargs = dict() + + # Decode URI attributes + uri_attributes = urlparse(self.session.broker_uri) + scheme = uri_attributes.scheme + secure = True if scheme in ('mqtts', 'wss') else False + self.session.username = self.session.username if self.session.username else uri_attributes.username + self.session.password = self.session.password if self.session.password else uri_attributes.password + self.session.remote_address = uri_attributes.hostname + self.session.remote_port = uri_attributes.port + if scheme in ('mqtt', 'mqtts') and not self.session.remote_port: + self.session.remote_port = 8883 if scheme == 'mqtts' else 1883 + if scheme in ('ws', 'wss') and not self.session.remote_port: + self.session.remote_port = 443 if scheme == 'wss' else 80 + if scheme in ('ws', 'wss'): + # Rewrite URI to conform to https://tools.ietf.org/html/rfc6455#section-3 + uri = (scheme, self.session.remote_address + ":" + str(self.session.remote_port), uri_attributes[2], + uri_attributes[3], uri_attributes[4], uri_attributes[5]) + self.session.broker_uri = urlunparse(uri) + # Init protocol handler + #if not self._handler: + self._handler = ClientProtocolHandler(self.plugins_manager, loop=self._loop) + + if secure: + sc = ssl.create_default_context( + ssl.Purpose.SERVER_AUTH, + cafile=self.session.cafile, + capath=self.session.capath, + cadata=self.session.cadata) + if 'certfile' in self.config and 'keyfile' in self.config: + sc.load_cert_chain(self.config['certfile'], self.config['keyfile']) + if 'check_hostname' in self.config and isinstance(self.config['check_hostname'], bool): + sc.check_hostname = self.config['check_hostname'] + + sc.verify_mode = ssl.CERT_NONE #Ignore verify of cert + kwargs['ssl'] = sc + + try: + reader = None + writer = None + self._connected_state.clear() + # Open connection + if scheme in ('mqtt', 'mqtts'): + conn_reader, conn_writer = \ + await asyncio.open_connection( + self.session.remote_address, + self.session.remote_port, loop=self._loop, **kwargs) + reader = StreamReaderAdapter(conn_reader) + writer = StreamWriterAdapter(conn_writer) + elif scheme in ('ws', 'wss'): + websocket = await websockets.connect( + self.session.broker_uri, + subprotocols=['mqtt'], + loop=self._loop, + extra_headers=self.extra_headers, + **kwargs) + reader = WebSocketsReader(websocket) + writer = WebSocketsWriter(websocket) + # Start MQTT protocol + self._handler.attach(self.session, reader, writer) + return_code = await self._handler.mqtt_connect() + if return_code is not CONNECTION_ACCEPTED: + self.session.transitions.disconnect() + self.logger.warning("Connection rejected with code '%s'" % return_code) + exc = ConnectException("Connection rejected by broker") + exc.return_code = return_code + raise exc + else: + # Handle MQTT protocol + await self._handler.start() + self.session.transitions.connect() + self._connected_state.set() + self.logger.debug("connected to %s:%s" % (self.session.remote_address, self.session.remote_port)) + return return_code + except InvalidURI as iuri: + self.logger.warning("connection failed: invalid URI '%s'" % self.session.broker_uri) + self.session.transitions.disconnect() + raise ConnectException("connection failed: invalid URI '%s'" % self.session.broker_uri, iuri) + except InvalidHandshake as ihs: + self.logger.warning("connection failed: invalid websocket handshake") + self.session.transitions.disconnect() + raise ConnectException("connection failed: invalid websocket handshake", ihs) + except (ProtocolHandlerException, ConnectionError, OSError) as e: + self.logger.warning("MQTT connection failed: %r" % e) + self.session.transitions.disconnect() + raise ConnectException(e) + + async def get_msg(self): + try: + while self._connected_state._value: + message = await self.deliver_message() + msgdata = str(message.data.decode("utf-8")) + + proxymodelog.info(f"MQTT Proxy Client - Message Received From Ecovacs - Topic: {message.topic} - Message: {msgdata}") + ttopic = message.topic.split("/") + self.ecohelpername = ttopic[3] + ttopic[3] = "proxyhelper" + ttopic_comb = "/".join(ttopic) + proxymodelog.info(f"MQTT Proxy Client - Converted Topic From {message.topic} TO {ttopic_comb}") + proxymodelog.info(f"MQTT Proxy Client - Proxy Forward Message to Helperbot - Topic: {ttopic_comb} - Message: {msgdata.encode()}") + await bumper.mqtt_helperbot.Client.publish( + ttopic_comb, msgdata.encode(), QOS_0 + ) + + except Exception as e: + proxymodelog.error(f"MQTT Proxy Client - get_msg Exception - {e}") class BumperMQTTServer_Plugin: + proxyclients = {} def __init__(self, context): - self.context = context + self.context = context try: self.auth_config = self.context.config["auth"] self._users = dict() @@ -232,6 +351,8 @@ class BumperMQTTServer_Plugin: except Exception as e: mqttserverlog.exception("{}".format(e)) + + async def authenticate(self, *args, **kwargs): authenticated = False @@ -257,6 +378,32 @@ class BumperMQTTServer_Plugin: mqttserverlog.info(f"Bumper Authentication Success - Bot - SN: {username} - DID: {didsplit[0]} - Class: {tmpbotdetail[0]}") authenticated = True + if authenticated and bumper.bumper_proxy_mode: + mqtt_server = bumper.config_proxyMode_getServerIP("mqtt_server","") + if mqtt_server: + proxymodelog.info(f"MQTT Proxy Mode - Using server {mqtt_server}") + else: + proxymodelog.error(f"MQTT Proxy Mode - No server found! Load defaults or set mqtt_server in config_proxymode table!") + proxymodelog.exception(f"MQTT Proxy Mode - Exiting due to no MQTT Server configured!") + exit(1) + + proxymodelog.info(f"MQTT Proxy Mode - Proxy Bot to MQTT - Client_id: {client_id} - Username: {username}") + + self.proxyclients[client_id] = BumperProxyModeMQTTClient( + client_id=client_id, config={"check_hostname": False} + ) + + try: + await self.proxyclients[client_id].connect( + f"mqtts://{username}:{password}@{mqtt_server}:8883", + ) + except Exception as e: + mqttserverlog.error(f"MQTT Proxy Mode - Exception connecting with proxy to ecovacs - {e}") + pass + proxymodelog.info(f"MQTT Proxy Mode - Proxy Bot Connected - Client_id: {client_id}") + asyncio.create_task(self.proxyclients[client_id].get_msg()) + + else: tmpclientdetail = str(didsplit[1]).split("/") userid = didsplit[0] @@ -327,6 +474,20 @@ class BumperMQTTServer_Plugin: except FileNotFoundError: self.context.logger.warning(f"Password file {password_file} not found") + async def on_broker_client_subscribed(self, client_id, topic, qos): + if bumper.bumper_proxy_mode: #if proxy mode, also subscribe on ecovacs server + if client_id in self.proxyclients: + await self.proxyclients[client_id].subscribe( + [ + (topic, qos) + ] + ) + else: + proxymodelog.info(f"MQTT Proxy Mode - New MQTT Topic Subscription - Client: {client_id} - Topic: {topic}") + + #return + #pass + async def on_broker_client_connected(self, client_id): didsplit = str(client_id).split("@") @@ -336,16 +497,39 @@ class BumperMQTTServer_Plugin: bumper.bot_set_mqtt(bot["did"], True) return - clientresource = didsplit[1].split("/")[1] - client = bumper.client_get(clientresource) - if client: - bumper.client_set_mqtt(client["resource"], True) - return + if len(didsplit) > 1: + clientresource = didsplit[1].split("/")[1] + client = bumper.client_get(clientresource) + if client: + 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) + await self.handle_helperbot_msg(client_id, message) - def handle_helperbot_msg(self, client_id, message): + async def handle_helperbot_msg(self, client_id, message): + if bumper.bumper_proxy_mode: + if client_id in self.proxyclients: + msgdata = str(message.data.decode("utf-8")) + if not str(message.topic).split("/")[3] == "proxyhelper": # if from proxyhelper, don't send back to ecovacs...yet + if str(message.topic).split("/")[6] == "proxyhelper": + ttopic = message.topic.split("/") + ttopic[6] = self.proxyclients[client_id].ecohelpername + ttopic_join = "/".join(ttopic) + proxymodelog.info(f"MQTT Proxy Client - Bot Message Converted Topic From {message.topic} TO {ttopic_join} with message: {msgdata}") + else: + ttopic_join = message.topic + proxymodelog.info(f"MQTT Proxy Client - Bot Message From {ttopic_join} with message: {msgdata}") + + try: + # Send back to ecovacs + proxymodelog.info(f"MQTT Proxy Client - Proxy Forward Message to Ecovacs - Topic: {ttopic_join} - Message: {msgdata.encode()}") + await self.proxyclients[client_id].publish( + ttopic_join, msgdata.encode(), message.qos + ) + except Exception as e: + proxymodelog.error(f"MQTT Proxy Client - Forwarding to Ecovacs Exception - {e}") + if str(message.topic).split("/")[6] == "helperbot": # Response to command @@ -407,6 +591,10 @@ class BumperMQTTServer_Plugin: async def on_broker_client_disconnected(self, client_id): + if bumper.bumper_proxy_mode: + if client_id in self.proxyclients: + await self.proxyclients[client_id].disconnect() + didsplit = str(client_id).split("@") bot = bumper.bot_get(didsplit[0]) @@ -414,8 +602,9 @@ class BumperMQTTServer_Plugin: bumper.bot_set_mqtt(bot["did"], False) return - clientresource = didsplit[1].split("/")[1] - client = bumper.client_get(clientresource) - if client: - bumper.client_set_mqtt(client["resource"], False) - return + if len(didsplit) > 1: + clientresource = didsplit[1].split("/")[1] + client = bumper.client_get(clientresource) + if client: + bumper.client_set_mqtt(client["resource"], False) + return diff --git a/bumper/plugins/bumper_confserver_portal_appsvr.py b/bumper/plugins/bumper_confserver_portal_appsvr.py index a92ca66..2083135 100644 --- a/bumper/plugins/bumper_confserver_portal_appsvr.py +++ b/bumper/plugins/bumper_confserver_portal_appsvr.py @@ -1,28 +1,30 @@ #!/usr/bin/env python3 -import logging - +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.plugin_type = "sub_api" self.sub_api = "portal_api" - + self.routes = [ - web.route("*", "/appsvr/app.do", self.handle_appsvr_app, name="portal_api_appsvr_app"), - web.route("*", "/appsvr/service/list", self.handle_appsvr_service_list, name="portal_api_appsvr_service_list"), - web.route("*", "/appsvr/oauth_callback", self.handle_appsvr_oauth_callback, name="portal_api_appsvr_oauth_callback"), + + 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_app(self, request): + async def handle_appsvr_api(self, request): if not request.method == "GET": # Skip GET for now try: @@ -135,7 +137,7 @@ class portal_api_appsvr(plugins.ConfServerApp): # "did": "did", # "lang": "EN", # "mid": "ls1ok3" - # } + # } # example response # { # "todo": "result", @@ -145,7 +147,7 @@ class portal_api_appsvr(plugins.ConfServerApp): # "mailTitle": "I'm sharing my DEEBOT and you're invited!" # }, # "ret": "ok" - # } + # } except Exception as e: @@ -153,59 +155,7 @@ class portal_api_appsvr(plugins.ConfServerApp): # Return fail for GET body = {"result": "fail", "todo": "result"} - return web.json_response(body) - - async def handle_appsvr_service_list(self, request): - try: - # original urls comment out as they are sub sub domain, which the current certificate is not valid - # using url, where the certs is valid - # data = { - # "account": "users-base.dc-eu.ww.ecouser.net", - # "jmq": "jmq-ngiot-eu.dc.ww.ecouser.net", - # "lb": "lbo.ecouser.net", - # "magw": "api-app.dc-eu.ww.ecouser.net", - # "msgcloud": "msg-eu.ecouser.net:5223", - # "ngiotLb": "jmq-ngiot-eu.area.ww.ecouser.net", - # "rop": "api-rop.dc-eu.ww.ecouser.net" - # } - - data = { - "account": "users-base.ecouser.net", - "jmq": "jmq-ngiot-eu.ecouser.net", - "lb": "lbo.ecouser.net", - "magw": "api-app.ecouser.net", - "msgcloud": "msg-eu.ecouser.net:5223", - "ngiotLb": "jmq-ngiot-eu.ecouser.net", - "rop": "api-rop.ecouser.net" - } - - body = { - "code": 0, - "data": data, - "ret": "ok", - "todo": "result" - } - - return web.json_response(body) - - except Exception as e: - logging.exception("{}".format(e)) - - async def handle_appsvr_oauth_callback(self, request): - try: - token = bumper.token_by_authcode(request.query["code"]) - oauth = bumper.user_add_oauth(token["userid"]) - body = { - "code": 0, - "data": oauth.toResponse(), - "ret": "ok", - "todo": "result" - } - - return web.json_response(body) - - except Exception as e: - logging.exception("{}".format(e)) - - + return web.json_response(body) + plugin = portal_api_appsvr() + diff --git a/bumper/plugins/bumper_confserver_portal_ecms.py b/bumper/plugins/bumper_confserver_portal_ecms.py deleted file mode 100644 index e408d18..0000000 --- a/bumper/plugins/bumper_confserver_portal_ecms.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python3 -import logging - -from aiohttp import web - -from bumper import plugins -from bumper.models import * - - -class portal_api_ecms(plugins.ConfServerApp): - - def __init__(self): - self.name = "portal_api_ecms" - self.plugin_type = "sub_api" - self.sub_api = "portal_api" - - self.routes = [ - web.route("*", "/ecms/app/ad/res", self.handle_ad_res, name="portal_api_ecms_ad_res"), - ] - - self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time - - async def handle_ad_res(self, request): - try: - body = { - "code": 0, - "data": [], - "message": "success", - "success": True - } - - return web.json_response(body) - - except Exception as e: - logging.exception("{}".format(e)) - - -plugin = portal_api_ecms() diff --git a/bumper/plugins/bumper_confserver_portal_iot.py b/bumper/plugins/bumper_confserver_portal_iot.py index 486fe37..6455ffd 100644 --- a/bumper/plugins/bumper_confserver_portal_iot.py +++ b/bumper/plugins/bumper_confserver_portal_iot.py @@ -29,7 +29,7 @@ class portal_api_iot(plugins.ConfServerApp): try: json_body = json.loads(await request.text()) - randomid = "".join(random.sample(string.ascii_letters, 4)) + randomid = "".join(random.sample(string.ascii_letters, 6)) did = "" if "toId" in json_body: # Its a command did = json_body["toId"] diff --git a/bumper/plugins/bumper_confserver_portal_lg.py b/bumper/plugins/bumper_confserver_portal_lg.py index 77b9709..c43d59b 100644 --- a/bumper/plugins/bumper_confserver_portal_lg.py +++ b/bumper/plugins/bumper_confserver_portal_lg.py @@ -1,22 +1,24 @@ #!/usr/bin/env python3 -import logging -import random -import string -import xml.etree.ElementTree as ET - +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.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"), @@ -26,11 +28,10 @@ class portal_api_lg(plugins.ConfServerApp): self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time async def handle_lg_log(self, request): # EcoVacs Home - randomid = "".join(random.sample(string.ascii_letters, 6)) - 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) @@ -52,14 +53,14 @@ class portal_api_lg(plugins.ConfServerApp): json_body["payloadType"] = "x" if not "payload" in json_body: - # json_body["payload"] = "" + #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": + if bot["company"] == "eco-ng": retcmd = await bumper.mqtt_helperbot.send_command( json_body, randomid ) @@ -73,11 +74,11 @@ class portal_api_lg(plugins.ConfServerApp): for l in cleanlogs: cleanlog = { "ts": l.attrib['s'], - "area": l.attrib['a'], + "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", + #imageUrl allows for providing images of cleanings, something to look into later + #"imageUrl": "https://localhost:8007", } logs.append(cleanlog) body = { @@ -97,12 +98,11 @@ class portal_api_lg(plugins.ConfServerApp): 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)) - body = {"id": randomid, "errno": bumper.ERR_COMMON, "ret": "fail"} - return web.json_response(body) - - plugin = portal_api_lg() + diff --git a/bumper/plugins/bumper_confserver_portal_pim.py b/bumper/plugins/bumper_confserver_portal_pim.py index 2c7bee1..286165f 100644 --- a/bumper/plugins/bumper_confserver_portal_pim.py +++ b/bumper/plugins/bumper_confserver_portal_pim.py @@ -1,27 +1,29 @@ #!/usr/bin/env python3 -import logging -import os - +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.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"), web.route("*", "/pim/product/getConfignetAll", self.handle_getConfignetAll, name="portal_api_pim_getConfignetAll"), web.route("*", "/pim/product/getConfigGroups", self.handle_getConfigGroups, name="portal_api_pim_getConfigGroups"), web.route("*", "/pim/dictionary/getErrDetail", self.handle_getErrDetail, name="portal_api_pim_getErrDetail"), - web.route("*", "/pim/product/software/config/batch", self.handle_product_config_batch, name="portal_api_pim_product_config_batch"), + ] self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time @@ -41,10 +43,10 @@ class portal_api_pim(plugins.ConfServerApp): try: fileID = request.match_info.get("id", "") - return web.FileResponse(os.path.join(bumper.bumper_dir, "bumper", "web", "images", "robotvac_image.jpg")) - + return web.FileResponse(os.path.join(bumper.bumper_dir,"bumper","web","images","robotvac_image.jpg")) + except Exception as e: - logging.exception("{}".format(e)) + logging.exception("{}".format(e)) async def handle_getConfignetAll(self, request): try: @@ -52,7 +54,7 @@ class portal_api_pim(plugins.ConfServerApp): return web.json_response(body) except Exception as e: - logging.exception("{}".format(e)) + logging.exception("{}".format(e)) async def handle_getConfigGroups(self, request): try: @@ -60,48 +62,20 @@ class portal_api_pim(plugins.ConfServerApp): return web.json_response(body) except Exception as e: - logging.exception("{}".format(e)) + logging.exception("{}".format(e)) async def handle_getErrDetail(self, request): try: body = { - "code": -1, - "data": [], - "msg": "This errcode's detail is not exists" - } + "code": -1, + "data": [], + "msg": "This errcode's detail is not exists" + } return web.json_response(body) except Exception as e: - logging.exception("{}".format(e)) - - async def handle_product_config_batch(self, request): - try: - json_body = json.loads(await request.text()) - data = [] - for pid in json_body["pids"]: - for productConfig in productConfigBatch: - if pid == productConfig["pid"]: - data.append(productConfig) - continue - - # not found in productConfigBatch - # some devices don't have any product configuration - data.append({ - "cfg": {}, - "pid": pid - }) - - body = { - "code": 200, - "data": data, - "message": "success" - } - return web.json_response(body) - - except Exception as e: - logging.exception("{}".format(e)) - - + logging.exception("{}".format(e)) + plugin = portal_api_pim() confignetAllResponse = { @@ -2706,175 +2680,4 @@ configGroupsResponse = { "configFailedUrl": "https://portal-ww.ecouser.net/api/pim/configfail.html?lang=en&defaultLang=en", "contactUS": "helper" } -} - -productConfigBatch = [ - { - "pid": "5e14196a6e71b80001b60fda", - "cfg": { - "supported": { - "tmallstand": False, - "video": False, - "battery": True, - "clean": True, - "charge": True - } - } - }, - { - "pid": "5e8e8d8a032edd8457c66bfb", - "cfg": { - "supported": { - "tmallstand": False, - "video": False, - "battery": True, - "clean": True, - "charge": True - } - } - }, - { - "pid": "5c19a91ca1e6ee000178224a", - "cfg": { - "supported": { - "tmallstand": False, - "video": False, - "battery": True, - "clean": True, - "charge": True - } - } - }, - { - "pid": "5e8e8d2a032edd3c03c66bf7", - "cfg": { - "supported": { - "tmallstand": False, - "video": False, - "battery": True, - "clean": True, - "charge": True - } - } - }, - { - "pid": "5de0d86ed88546000195239a", - "cfg": { - "supported": { - "tmallstand": False, - "video": True, - "battery": True, - "clean": True, - "charge": True - } - } - }, - { - "pid": "5c19a8f3a1e6ee0001782247", - "cfg": { - "supported": { - "tmallstand": False, - "video": False, - "battery": True, - "clean": True, - "charge": True - } - } - }, - { - "pid": "5e698a6306f6de52c264c61b", - "cfg": { - "supported": { - "tmallstand": False, - "video": False, - "battery": True, - "clean": True, - "charge": True - } - } - }, - { - "pid": "5e699a4106f6de83ea64c620", - "cfg": { - "supported": { - "tmallstand": False, - "video": False, - "battery": True, - "clean": True, - "charge": True - } - } - }, - { - "pid": "5edd998afdd6a30008da039b", - "cfg": { - "supported": { - "tmallstand": False, - "video": True, - "battery": True, - "clean": True, - "charge": True - } - } - }, - { - "pid": "5edd9a4075f2fc000636086c", - "cfg": { - "supported": { - "tmallstand": False, - "video": True, - "battery": True, - "clean": True, - "charge": True - } - } - }, - { - "pid": "5ed5e4d3a719ea460ec3216c", - "cfg": { - "supported": { - "tmallstand": False, - "video": False, - "battery": True, - "clean": True, - "charge": True - } - } - }, - { - "pid": "5f88195e6cf8de0008ed7c11", - "cfg": { - "supported": { - "tmallstand": False, - "video": False, - "battery": True, - "clean": True, - "charge": True - } - } - }, - { - "pid": "5f8819156cf8de0008ed7c0d", - "cfg": { - "supported": { - "tmallstand": False, - "video": False, - "battery": True, - "clean": True, - "charge": True - } - } - }, - { - "pid": "5fa105c6d16a99000667eb54", - "cfg": { - "supported": { - "tmallstand": False, - "video": False, - "battery": True, - "clean": True, - "charge": True - } - } - } -] +} \ No newline at end of file diff --git a/bumper/plugins/bumper_confserver_portal_rapp.py b/bumper/plugins/bumper_confserver_portal_rapp.py deleted file mode 100644 index ca5ef99..0000000 --- a/bumper/plugins/bumper_confserver_portal_rapp.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python3 -from aiohttp import web -import logging -from bumper.models import * -from bumper import plugins - - -class api_rapp(plugins.ConfServerApp): - - def __init__(self): - self.name = "api_rapp" - self.plugin_type = "sub_api" - self.sub_api = "portal_api" - - self.routes = [ - web.route("*", "/rapp/sds/user/data/map/get", self.handle_map_get, name="api_rapp"), - ] - - self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time - - async def handle_map_get(self, request): - try: - body = { - "code": 0, - "data": { - "data": { - "name": "My Home" - }, - "tag": None - }, - "message": "success" - } - - return web.json_response(body) - - except Exception as e: - logging.exception("{}".format(e)) - -plugin = api_rapp() diff --git a/bumper/plugins/bumper_confserver_v1_private_common.py b/bumper/plugins/bumper_confserver_v1_private_common.py index f84d944..71a0034 100644 --- a/bumper/plugins/bumper_confserver_v1_private_common.py +++ b/bumper/plugins/bumper_confserver_v1_private_common.py @@ -1,28 +1,26 @@ #!/usr/bin/env python3 -import logging - +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.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"), - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/common/getConfig",self.handle_getConfig, name="v1_common_getConfig"), - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/common/getAreas",self.handle_getAreas, name="v1_common_getAreas"), - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/common/getAgreementURLBatch", self.handle_getAgreementURLBatch, name="v1_common_getAgreementURLBatch"), - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/common/getTimestamp", self.handle_getTimestamp, name="v1_common_getTimestamp"), + 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"), ] @@ -73,7 +71,7 @@ class v1_private_common(plugins.ConfServerApp): return web.json_response(body) except Exception as e: - logging.exception("{}".format(e)) + logging.exception("{}".format(e)) async def handle_uploadDeviceInfo(self, request): # EcoVacs Home try: @@ -88,7 +86,7 @@ class v1_private_common(plugins.ConfServerApp): return web.json_response(body) except Exception as e: - logging.exception("{}".format(e)) + logging.exception("{}".format(e)) async def handle_getSystemReminder(self, request): # EcoVacs Home try: @@ -110,353 +108,8 @@ class v1_private_common(plugins.ConfServerApp): return web.json_response(body) except Exception as e: - logging.exception("{}".format(e)) - - async def handle_getConfig(self, request): - try: - data = [] - for key in request.query["keys"].split(','): - data.append({ - "key": key, - "value": "Y" - }) - - body = { - "code": bumper.RETURN_API_SUCCESS, - "data": data, - "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_getAreas(self, request): - try: - body = { - "code": bumper.RETURN_API_SUCCESS, - "data": AREA_LIST, - "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_getAgreementURLBatch(self, request): # EcoVacs Home - try: - body = { - "code": bumper.RETURN_API_SUCCESS, - "data": [ - { - "acceptTime": None, - "force": None, - "id": "20180804040641_7d746faf18b8cb22a50d145598fe4c90", - "type": "USER", - "url": "https://gl-eu-wap.ecovacs.com/content/agreement?id=20180804040641_7d746faf18b8cb22a50d145598fe4c90&language=EN", - "version": "1.03" - }, - { - "acceptTime": None, - "force": None, - "id": "20180804040245_4e7c56dfb7ebd3b81b1f2747d0859fac", - "type": "PRIVACY", - "url": "https://gl-eu-wap.ecovacs.com/content/agreement?id=20180804040245_4e7c56dfb7ebd3b81b1f2747d0859fac&language=EN", - "version": "1.03" - } - ], - "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_getTimestamp(self, request): # EcoVacs Home - try: - time = self.get_milli_time(datetime.utcnow().timestamp()) - body = { - "code": bumper.RETURN_API_SUCCESS, - "data": { - "timestamp": time - }, - "msg": "操作成功", - "success": True, - "time": time, - } - - return web.json_response(body) - - except Exception as e: - logging.exception("{}".format(e)) + logging.exception("{}".format(e)) plugin = v1_private_common() -AREA_LIST = {"currentVersion": 231, - "areaList": [{"areaKey": "JP", "chsName": "日本", "enName": "Japan", "pyFirst": "R"}, - {"areaKey": "MY", "chsName": "马来西亚", "enName": "Malaysia", "pyFirst": "M"}, - {"areaKey": "DE", "chsName": "德国", "enName": "Germany", "pyFirst": "D"}, - {"areaKey": "LI", "chsName": "列支敦斯登", "enName": "Liechtenstein", "pyFirst": "L"}, - {"areaKey": "AT", "chsName": "奥地利", "enName": "Austria", "pyFirst": "A"}, - {"areaKey": "TW", "chsName": "台湾", "enName": "Taiwan", "pyFirst": "T"}, - {"areaKey": "FR", "chsName": "法国", "enName": "France", "pyFirst": "F"}, - {"areaKey": "CN", "chsName": "中国大陆", "enName": "China Mainland", "pyFirst": "Z"}, - {"areaKey": "SG", "chsName": "新加坡", "enName": "Singapore", "pyFirst": "X"}, - {"areaKey": "RE", "chsName": "留尼汪岛", "enName": "Reunion Island", "pyFirst": "L"}, - {"areaKey": "EH", "chsName": "西撒哈拉", "enName": "Western Sahara", "pyFirst": "X"}, - {"areaKey": "WF", "chsName": "瓦利斯群岛和富图纳群岛", "enName": "Wallis and Futuna Islands", - "pyFirst": "W"}, - {"areaKey": "KP", "chsName": "朝鲜", "enName": "North Korea", "pyFirst": "C"}, - {"areaKey": "ZW", "chsName": "津巴布韦", "enName": "Zimbabwe", "pyFirst": "J"}, - {"areaKey": "VI", "chsName": "美属维尔京群岛", "enName": "United States Virgin Islands", - "pyFirst": "M"}, - {"areaKey": "PF", "chsName": "法属玻里尼西亚", "enName": "French Polynesia", - "pyFirst": "F"}, - {"areaKey": "DJ", "chsName": "吉布提", "enName": "Djibouti", "pyFirst": "J"}, - {"areaKey": "KZ", "chsName": "哈萨克斯坦", "enName": "Kazakhstan", "pyFirst": "H"}, - {"areaKey": "TV", "chsName": "图瓦卢", "enName": "Tuvalu", "pyFirst": "T"}, - {"areaKey": "VU", "chsName": "瓦努阿图", "enName": "Vanuatu", "pyFirst": "W"}, - {"areaKey": "IN", "chsName": "印度", "enName": "India", "pyFirst": "Y"}, - {"areaKey": "CM", "chsName": "喀麦隆", "enName": "Cameroon", "pyFirst": "K"}, - {"areaKey": "LK", "chsName": "斯里兰卡", "enName": "Sri Lanka", "pyFirst": "S"}, - {"areaKey": "CC", "chsName": "科科斯群岛", "enName": "Cocos Islands", "pyFirst": "K"}, - {"areaKey": "KY", "chsName": "开曼群岛", "enName": "Cayman Islands", "pyFirst": "K"}, - {"areaKey": "QA", "chsName": "卡塔尔", "enName": "Qatar", "pyFirst": "K"}, - {"areaKey": "AZ", "chsName": "阿塞拜疆", "enName": "Azerbaijan", "pyFirst": "A"}, - {"areaKey": "HN", "chsName": "洪都拉斯", "enName": "Honduras", "pyFirst": "H"}, - {"areaKey": "AW", "chsName": "阿鲁巴岛", "enName": "Aruba", "pyFirst": "A"}, - {"areaKey": "KH", "chsName": "柬埔寨", "enName": "Cambodia", "pyFirst": "J"}, - {"areaKey": "CO", "chsName": "哥伦比亚", "enName": "Colombia", "pyFirst": "G"}, - {"areaKey": "IR", "chsName": "伊朗", "enName": "Iran", "pyFirst": "Y"}, - {"areaKey": "ZA", "chsName": "南非", "enName": "South Africa", "pyFirst": "N"}, - {"areaKey": "UY", "chsName": "乌拉圭", "enName": "Uruguay", "pyFirst": "W"}, - {"areaKey": "GU", "chsName": "关岛", "enName": "Guam", "pyFirst": "G"}, - {"areaKey": "GH", "chsName": "加纳", "enName": "Ghana", "pyFirst": "J"}, - {"areaKey": "GN", "chsName": "几内亚", "enName": "Guynea", "pyFirst": "J"}, - {"areaKey": "MH", "chsName": "马绍尔群岛", "enName": "Marshall Islands", - "pyFirst": "M"}, - {"areaKey": "SE", "chsName": "瑞典", "enName": "Sweden", "pyFirst": "R"}, - {"areaKey": "SB", "chsName": "所罗门群岛", "enName": "Solomon Islands", - "pyFirst": "S"}, - {"areaKey": "NE", "chsName": "尼日尔", "enName": "Niger", "pyFirst": "N"}, - {"areaKey": "HT", "chsName": "海地", "enName": "Haiti", "pyFirst": "H"}, - {"areaKey": "PL", "chsName": "波兰", "enName": "Poland", "pyFirst": "B"}, - {"areaKey": "DO", "chsName": "多米尼加共和国", "enName": "Dominican Republic", - "pyFirst": "D"}, - {"areaKey": "PS", "chsName": "巴勒斯坦", "enName": "Palestine", "pyFirst": "B"}, - {"areaKey": "KW", "chsName": "科威特", "enName": "Kuwait", "pyFirst": "K"}, - {"areaKey": "UZ", "chsName": "乌兹别克斯坦", "enName": "Republic of Uzbekistan", - "pyFirst": "W"}, - {"areaKey": "GD", "chsName": "格林纳达", "enName": "Grenada", "pyFirst": "G"}, - {"areaKey": "KG", "chsName": "吉尔吉斯斯坦", "enName": "Kyrgyzstan", "pyFirst": "J"}, - {"areaKey": "JO", "chsName": "约旦", "enName": "Jordan", "pyFirst": "Y"}, - {"areaKey": "IL", "chsName": "以色列", "enName": "Israel", "pyFirst": "Y"}, - {"areaKey": "UK", "chsName": "英国", "enName": "United Kingdom", "pyFirst": "Y"}, - {"areaKey": "MW", "chsName": "马拉维", "enName": "Malawi", "pyFirst": "M"}, - {"areaKey": "MC", "chsName": "摩纳哥", "enName": "Monaco", "pyFirst": "M"}, - {"areaKey": "IC", "chsName": "加那利群岛", "enName": "Canary Islands", "pyFirst": "J"}, - {"areaKey": "JM", "chsName": "牙买加", "enName": "Jamaica", "pyFirst": "Y"}, - {"areaKey": "MP", "chsName": "北马里亚纳群岛", "enName": "The Northern Mariana Islands", - "pyFirst": "B"}, - {"areaKey": "BH", "chsName": "巴林岛", "enName": "Bahrain", "pyFirst": "B"}, - {"areaKey": "MK", "chsName": "马其顿", "enName": "Macedonia", "pyFirst": "M"}, - {"areaKey": "ET", "chsName": "埃塞俄比亚", "enName": "Ethiopia", "pyFirst": "A"}, - {"areaKey": "CL", "chsName": "智利", "enName": "Chile", "pyFirst": "Z"}, - {"areaKey": "GP", "chsName": "瓜德罗普岛", "enName": "Guadeloupe", "pyFirst": "G"}, - {"areaKey": "FK", "chsName": "福克兰群岛", "enName": "Falkland Islands", - "pyFirst": "F"}, - {"areaKey": "GL", "chsName": "格陵兰", "enName": "Greenland", "pyFirst": "G"}, - {"areaKey": "BF", "chsName": "布基纳法索", "enName": "Burkina Faso", "pyFirst": "B"}, - {"areaKey": "GI", "chsName": "直布罗陀", "enName": "Gibraltar", "pyFirst": "Z"}, - {"areaKey": "MV", "chsName": "马尔代夫", "enName": "Maldives", "pyFirst": "M"}, - {"areaKey": "CU", "chsName": "古巴", "enName": "Cuba", "pyFirst": "G"}, - {"areaKey": "LS", "chsName": "莱索托", "enName": "Lesotho", "pyFirst": "L"}, - {"areaKey": "MA", "chsName": "摩洛哥", "enName": "Morocco", "pyFirst": "M"}, - {"areaKey": "AL", "chsName": "阿尔巴尼亚", "enName": "Albania", "pyFirst": "A"}, - {"areaKey": "AF", "chsName": "阿富汗", "enName": "Afghanistan", "pyFirst": "A"}, - {"areaKey": "CA", "chsName": "加拿大", "enName": "Canada", "pyFirst": "J"}, - {"areaKey": "BB", "chsName": "巴巴多斯", "enName": "Barbados", "pyFirst": "B"}, - {"areaKey": "LC", "chsName": "圣卢西亚岛", "enName": "Saint Lucia", "pyFirst": "S"}, - {"areaKey": "PN", "chsName": "皮特克恩岛", "enName": "Pitcairn Island", - "pyFirst": "P"}, - {"areaKey": "LV", "chsName": "拉脱维亚", "enName": "Latvia", "pyFirst": "L"}, - {"areaKey": "NO", "chsName": "挪威", "enName": "Norway", "pyFirst": "N"}, - {"areaKey": "BE", "chsName": "比利时", "enName": "Belgium", "pyFirst": "B"}, - {"areaKey": "VE", "chsName": "委内瑞拉", "enName": "Venezuela", "pyFirst": "W"}, - {"areaKey": "MQ", "chsName": "马提尼克", "enName": "Martinique", "pyFirst": "M"}, - {"areaKey": "GY", "chsName": "圭亚那", "enName": "Guyana", "pyFirst": "G"}, - {"areaKey": "AM", "chsName": "亚美尼亚", "enName": "Armenia", "pyFirst": "Y"}, - {"areaKey": "EC", "chsName": "厄瓜多尔", "enName": "Ecuador", "pyFirst": "E"}, - {"areaKey": "CV", "chsName": "佛得角", "enName": "Cape Verde", "pyFirst": "F"}, - {"areaKey": "NZ", "chsName": "新西兰", "enName": "New Zealand", "pyFirst": "X"}, - {"areaKey": "RO", "chsName": "罗马尼亚", "enName": "Romania", "pyFirst": "L"}, - {"areaKey": "DM", "chsName": "多米尼加", "enName": "Dominica", "pyFirst": "D"}, - {"areaKey": "TZ", "chsName": "坦桑尼亚", "enName": "Tanzania", "pyFirst": "T"}, - {"areaKey": "BD", "chsName": "孟加拉国", "enName": "Bangladesh", "pyFirst": "M"}, - {"areaKey": "TD", "chsName": "乍得", "enName": "Chad", "pyFirst": "Z"}, - {"areaKey": "LT", "chsName": "立陶宛", "enName": "Lithuania", "pyFirst": "L"}, - {"areaKey": "TJ", "chsName": "塔吉克斯坦", "enName": "Tajikistan", "pyFirst": "T"}, - {"areaKey": "TK", "chsName": "托克劳", "enName": "Tokelau", "pyFirst": "T"}, - {"areaKey": "BS", "chsName": "巴哈马群岛", "enName": "Bahamas", "pyFirst": "B"}, - {"areaKey": "MM", "chsName": "缅甸", "enName": "Myanmar", "pyFirst": "M"}, - {"areaKey": "BI", "chsName": "布隆迪", "enName": "Burundi", "pyFirst": "B"}, - {"areaKey": "PY", "chsName": "巴拉圭", "enName": "Paraguay", "pyFirst": "B"}, - {"areaKey": "SK", "chsName": "斯洛伐克", "enName": "Slovakia", "pyFirst": "S"}, - {"areaKey": "FI", "chsName": "芬兰", "enName": "Finland", "pyFirst": "F"}, - {"areaKey": "GA", "chsName": "加蓬", "enName": "Gabon", "pyFirst": "J"}, - {"areaKey": "DZ", "chsName": "阿尔及利亚", "enName": "Algeria", "pyFirst": "A"}, - {"areaKey": "FO", "chsName": "法罗群岛", "enName": "Faroe Islands", "pyFirst": "F"}, - {"areaKey": "ZM", "chsName": "赞比亚", "enName": "Zambia", "pyFirst": "Z"}, - {"areaKey": "NU", "chsName": "纽埃", "enName": "Niue", "pyFirst": "N"}, - {"areaKey": "ER", "chsName": "厄立特里亚国", "enName": "Eritrea", "pyFirst": "E"}, - {"areaKey": "HK", "chsName": "香港", "enName": "Hong Kong", "pyFirst": "X"}, - {"areaKey": "IT", "chsName": "意大利", "enName": "Italy", "pyFirst": "Y"}, - {"areaKey": "MS", "chsName": "蒙特色拉特岛", "enName": "Montserrat", "pyFirst": "M"}, - {"areaKey": "EE", "chsName": "爱沙尼亚", "enName": "Estonia", "pyFirst": "A"}, - {"areaKey": "WS", "chsName": "萨摩亚", "enName": "Samoa", "pyFirst": "S"}, - {"areaKey": "TG", "chsName": "多哥", "enName": "Togo", "pyFirst": "D"}, - {"areaKey": "ML", "chsName": "马里", "enName": "Mali", "pyFirst": "M"}, - {"areaKey": "GF", "chsName": "法属圭亚那", "enName": "French Guyana", "pyFirst": "F"}, - {"areaKey": "KM", "chsName": "科摩罗", "enName": "Comoros", "pyFirst": "K"}, - {"areaKey": "ID", "chsName": "印度尼西亚", "enName": "Indonesia", "pyFirst": "Y"}, - {"areaKey": "KE", "chsName": "肯尼亚", "enName": "Kenya", "pyFirst": "K"}, - {"areaKey": "EG", "chsName": "埃及", "enName": "Egypt", "pyFirst": "A"}, - {"areaKey": "NF", "chsName": "诺福克岛", "enName": "Norfolk Island", "pyFirst": "N"}, - {"areaKey": "RS", "chsName": "塞尔维亚", "enName": "Serbia", "pyFirst": "S"}, - {"areaKey": "TR", "chsName": "土耳其", "enName": "Turkey", "pyFirst": "T"}, - {"areaKey": "DK", "chsName": "丹麦", "enName": "Denmark", "pyFirst": "D"}, - {"areaKey": "AD", "chsName": "安道尔", "enName": "Andorra", "pyFirst": "A"}, - {"areaKey": "LR", "chsName": "利比里亚", "enName": "Liberia", "pyFirst": "L"}, - {"areaKey": "AE", "chsName": "阿拉伯联合酋长国", "enName": "United Arab Emirates", - "pyFirst": "A"}, - {"areaKey": "CH", "chsName": "瑞士", "enName": "Switzerland", "pyFirst": "R"}, - {"areaKey": "AU", "chsName": "澳大利亚", "enName": "Australia", "pyFirst": "A"}, - {"areaKey": "TP", "chsName": "东帝汶", "enName": "East Timor", "pyFirst": "D"}, - {"areaKey": "LY", "chsName": "利比亚", "enName": "Libya", "pyFirst": "L"}, - {"areaKey": "RW", "chsName": "卢旺达", "enName": "Rwanda", "pyFirst": "L"}, - {"areaKey": "SA", "chsName": "沙特阿拉伯", "enName": "Saudi Arabia", "pyFirst": "S"}, - {"areaKey": "AR", "chsName": "阿根廷", "enName": "Argentina", "pyFirst": "A"}, - {"areaKey": "GM", "chsName": "冈比亚", "enName": "Gambia", "pyFirst": "G"}, - {"areaKey": "BY", "chsName": "白俄罗斯", "enName": "Belarus", "pyFirst": "B"}, - {"areaKey": "SL", "chsName": "塞拉利昂", "enName": "Sierra Leone", "pyFirst": "S"}, - {"areaKey": "TM", "chsName": "土库曼斯坦", "enName": "Turkmenistan", "pyFirst": "T"}, - {"areaKey": "AG", "chsName": "安提瓜和巴布达", "enName": "Antigua and Barbuda", - "pyFirst": "A"}, - {"areaKey": "MR", "chsName": "毛里塔尼亚", "enName": "Mauritania", "pyFirst": "M"}, - {"areaKey": "PT", "chsName": "葡萄牙", "enName": "Portugal", "pyFirst": "P"}, - {"areaKey": "BW", "chsName": "博茨瓦纳", "enName": "Botswana", "pyFirst": "B"}, - {"areaKey": "GT", "chsName": "危地马拉", "enName": "Guatemala", "pyFirst": "W"}, - {"areaKey": "BT", "chsName": "不丹", "enName": "Bhutan", "pyFirst": "B"}, - {"areaKey": "AI", "chsName": "安圭拉岛", "enName": "Anguilla", "pyFirst": "A"}, - {"areaKey": "OM", "chsName": "阿曼", "enName": "Oman", "pyFirst": "A"}, - {"areaKey": "KI", "chsName": "基里巴斯", "enName": "Kiribati", "pyFirst": "J"}, - {"areaKey": "UA", "chsName": "乌克兰", "enName": "Ukraine", "pyFirst": "W"}, - {"areaKey": "YE", "chsName": "也门", "enName": "Yemen", "pyFirst": "Y"}, - {"areaKey": "DR", "chsName": "刚果民主共和国", - "enName": "Democratic Republic of the Congo", "pyFirst": "G"}, - {"areaKey": "MD", "chsName": "摩尔多瓦", "enName": "Moldova", "pyFirst": "M"}, - {"areaKey": "GW", "chsName": "几内亚比绍", "enName": "Guinea-Bissau", "pyFirst": "J"}, - {"areaKey": "CG", "chsName": "刚果布共和国", "enName": "Congo Brazzaville", - "pyFirst": "G"}, - {"areaKey": "SN", "chsName": "塞内加尔", "enName": "Senegal", "pyFirst": "S"}, - {"areaKey": "BA", "chsName": "波黑", "enName": "Bosnia Hercegovina", - "pyFirst": "B"}, - {"areaKey": "MO", "chsName": "澳门", "enName": "Macao", "pyFirst": "A"}, - {"areaKey": "KN", "chsName": "圣基茨和尼维斯", "enName": "Saint Kitts and Nevis", - "pyFirst": "S"}, - {"areaKey": "TO", "chsName": "汤加", "enName": "Tonga", "pyFirst": "T"}, - {"areaKey": "NG", "chsName": "尼日利亚", "enName": "Nigeria", "pyFirst": "N"}, - {"areaKey": "TT", "chsName": "特立尼达和多巴哥", "enName": "Trinidad and Tobago", - "pyFirst": "T"}, - {"areaKey": "CF", "chsName": "中非共和国", "enName": "Central African Republic", - "pyFirst": "Z"}, - {"areaKey": "PE", "chsName": "秘鲁", "enName": "Peru", "pyFirst": "M"}, - {"areaKey": "PG", "chsName": "巴布亚新几内亚", "enName": "Papua New Guinea", - "pyFirst": "B"}, - {"areaKey": "CX", "chsName": "圣延岛", "enName": "Christmas Island", "pyFirst": "S"}, - {"areaKey": "AN", "chsName": "安的列斯", "enName": "Netherlands Antilles", - "pyFirst": "A"}, - {"areaKey": "BO", "chsName": "玻利维亚", "enName": "Bolivia", "pyFirst": "B"}, - {"areaKey": "IQ", "chsName": "伊拉克", "enName": "Iraq", "pyFirst": "Y"}, - {"areaKey": "NP", "chsName": "尼泊尔", "enName": "Nepal", "pyFirst": "N"}, - {"areaKey": "BJ", "chsName": "贝宁", "enName": "Benin", "pyFirst": "B"}, - {"areaKey": "VN", "chsName": "越南", "enName": "Vietnam", "pyFirst": "Y"}, - {"areaKey": "NI", "chsName": "尼加拉瓜", "enName": "Nicaragua", "pyFirst": "N"}, - {"areaKey": "PW", "chsName": "帕劳群岛", "enName": "Palau", "pyFirst": "P"}, - {"areaKey": "SO", "chsName": "索马里", "enName": "Somalia", "pyFirst": "S"}, - {"areaKey": "SM", "chsName": "圣马力诺", "enName": "San Marino", "pyFirst": "S"}, - {"areaKey": "NR", "chsName": "瑙鲁", "enName": "Nauru", "pyFirst": "N"}, - {"areaKey": "BN", "chsName": "文莱", "enName": "Brunei Darussalam", "pyFirst": "W"}, - {"areaKey": "MZ", "chsName": "莫桑比克", "enName": "Mozambique", "pyFirst": "M"}, - {"areaKey": "GR", "chsName": "希腊", "enName": "Greece", "pyFirst": "X"}, - {"areaKey": "TN", "chsName": "突尼斯", "enName": "Tunisia", "pyFirst": "T"}, - {"areaKey": "RU", "chsName": "俄罗斯", "enName": "Russian Federation", - "pyFirst": "E"}, - {"areaKey": "MG", "chsName": "马达加斯加岛", "enName": "Madagascar", "pyFirst": "M"}, - {"areaKey": "NA", "chsName": "纳米比亚", "enName": "Namibia", "pyFirst": "N"}, - {"areaKey": "CQ", "chsName": "赤道几内亚", "enName": "Equatorial Guinea", - "pyFirst": "C"}, - {"areaKey": "SR", "chsName": "苏里南", "enName": "Suriname", "pyFirst": "S"}, - {"areaKey": "MU", "chsName": "毛里求斯", "enName": "Mauritius", "pyFirst": "M"}, - {"areaKey": "LA", "chsName": "老挝", "enName": "Laos", "pyFirst": "L"}, - {"areaKey": "US", "chsName": "美国", "enName": "United States", "pyFirst": "M"}, - {"areaKey": "ST", "chsName": "圣多美与普林希比共和国", "enName": "Sao Tome and Principe", - "pyFirst": "S"}, - {"areaKey": "BM", "chsName": "百慕大群岛", "enName": "Bermuda", "pyFirst": "B"}, - {"areaKey": "LU", "chsName": "卢森堡", "enName": "Luxembourg", "pyFirst": "L"}, - {"areaKey": "CR", "chsName": "哥斯达黎加", "enName": "Costa Rica", "pyFirst": "G"}, - {"areaKey": "KR", "chsName": "韩国", "enName": "South Korea", "pyFirst": "H"}, - {"areaKey": "CZ", "chsName": "捷克", "enName": "Czech Republic", "pyFirst": "J"}, - {"areaKey": "MX", "chsName": "墨西哥", "enName": "Mexico", "pyFirst": "M"}, - {"areaKey": "SH", "chsName": "圣赫勒拿岛", "enName": "St Helena", "pyFirst": "S"}, - {"areaKey": "AO", "chsName": "安哥拉", "enName": "Angola", "pyFirst": "A"}, - {"areaKey": "MN", "chsName": "蒙古", "enName": "Mongolia", "pyFirst": "M"}, - {"areaKey": "VC", "chsName": "圣文森特和格林纳丁斯", - "enName": "Saint Vincent and the Grenadines", "pyFirst": "S"}, - {"areaKey": "PH", "chsName": "菲律宾", "enName": "Philippines", "pyFirst": "F"}, - {"areaKey": "SC", "chsName": "塞舌尔", "enName": "Seychelles", "pyFirst": "S"}, - {"areaKey": "CK", "chsName": "库克群岛", "enName": "Cook Islands", "pyFirst": "K"}, - {"areaKey": "PK", "chsName": "巴基斯坦", "enName": "Pakistan", "pyFirst": "B"}, - {"areaKey": "HR", "chsName": "克罗地亚", "enName": "Croatia", "pyFirst": "K"}, - {"areaKey": "TH", "chsName": "泰国", "enName": "Thailand", "pyFirst": "T"}, - {"areaKey": "SI", "chsName": "斯洛文尼亚", "enName": "Slovenia", "pyFirst": "S"}, - {"areaKey": "VG", "chsName": "英属维尔京群岛", "enName": "British Virgin Islands", - "pyFirst": "Y"}, - {"areaKey": "SY", "chsName": "阿拉伯叙利亚共和国", "enName": "Syrian Arab Republic", - "pyFirst": "A"}, - {"areaKey": "CY", "chsName": "塞浦路斯", "enName": "Cyprus", "pyFirst": "S"}, - {"areaKey": "BR", "chsName": "巴西", "enName": "Brazil", "pyFirst": "B"}, - {"areaKey": "LB", "chsName": "黎巴嫩", "enName": "Lebanon", "pyFirst": "L"}, - {"areaKey": "IS", "chsName": "冰岛", "enName": "Iceland", "pyFirst": "B"}, - {"areaKey": "PA", "chsName": "巴拿马", "enName": "Panama", "pyFirst": "B"}, - {"areaKey": "FM", "chsName": "密克罗尼西亚", "enName": "Micronesia", "pyFirst": "M"}, - {"areaKey": "VA", "chsName": "梵蒂冈", "enName": "Vatican City State", - "pyFirst": "F"}, - {"areaKey": "NC", "chsName": "新喀里多尼亚", "enName": "New Caledonia", "pyFirst": "X"}, - {"areaKey": "MT", "chsName": "马尔他", "enName": "Malta", "pyFirst": "M"}, - {"areaKey": "BG", "chsName": "保加利亚", "enName": "Bulgaria", "pyFirst": "B"}, - {"areaKey": "ES", "chsName": "西班牙", "enName": "Spain", "pyFirst": "X"}, - {"areaKey": "CI", "chsName": "象牙海岸", "enName": "Ivory Coast", "pyFirst": "X"}, - {"areaKey": "IE", "chsName": "爱尔兰", "enName": "Ireland", "pyFirst": "A"}, - {"areaKey": "BZ", "chsName": "伯利兹城", "enName": "Belize", "pyFirst": "B"}, - {"areaKey": "SZ", "chsName": "斯威士兰", "enName": "Swaziland", "pyFirst": "S"}, - {"areaKey": "SV", "chsName": "萨尔瓦多", "enName": "EI Salvador", "pyFirst": "S"}, - {"areaKey": "GE", "chsName": "格鲁吉亚", "enName": "Georgia", "pyFirst": "G"}, - {"areaKey": "SD", "chsName": "苏丹", "enName": "Sudan", "pyFirst": "S"}, - {"areaKey": "PR", "chsName": "波多黎各", "enName": "Puerto Rico", "pyFirst": "B"}, - {"areaKey": "FJ", "chsName": "斐济", "enName": "Fiji", "pyFirst": "F"}, - {"areaKey": "NL", "chsName": "荷兰", "enName": "Netherlands", "pyFirst": "H"}, - {"areaKey": "UG", "chsName": "乌干达", "enName": "Uganda", "pyFirst": "W"}, - {"areaKey": "HU", "chsName": "匈牙利", "enName": "Hungary", "pyFirst": "X"}, - {"areaKey": "TC", "chsName": "特克斯和凯科斯群岛", "enName": "Turks and Caicos Islands", - "pyFirst": "T"}]} diff --git a/bumper/plugins/bumper_confserver_v1_private_user.py b/bumper/plugins/bumper_confserver_v1_private_user.py index ab04b54..68beb33 100644 --- a/bumper/plugins/bumper_confserver_v1_private_user.py +++ b/bumper/plugins/bumper_confserver_v1_private_user.py @@ -27,9 +27,6 @@ class v1_private_user(plugins.ConfServerApp): 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"), web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/getUserMenuInfo", self.handle_getUserMenuInfo,name="v1_user_getUserMenuInfo"), - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/changeArea", self.handle_changeArea, name="v1_user_changeArea"), - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/queryChangeArea", self.handle_changeArea, name="v1_user_queryChangeArea"), - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/acceptAgreementBatch", self.handle_acceptAgreementBatch, name="v1_user_acceptAgreementBatch"), # Direct register from app: # /{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/directRegister #Register by email @@ -147,37 +144,5 @@ class v1_private_user(plugins.ConfServerApp): except Exception as e: logging.exception("{}".format(e)) - async def handle_changeArea(self, request): - try: - body = { - "code": bumper.RETURN_API_SUCCESS, - "data": { - "isNeedReLogin": "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_acceptAgreementBatch(self, request): - 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_user() diff --git a/bumper/plugins/bumper_confserver_v1_private_userSetting.py b/bumper/plugins/bumper_confserver_v1_private_userSetting.py deleted file mode 100644 index 3a7720f..0000000 --- a/bumper/plugins/bumper_confserver_v1_private_userSetting.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python3 -from aiohttp import web -import logging -import bumper -from bumper import plugins -from datetime import datetime - - -class v1_private_userSetting(plugins.ConfServerApp): - - def __init__(self): - - self.name = "v1_private_userSetting" - self.plugin_type = "sub_api" - self.sub_api = "api_v1" - - self.routes = [ - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/userSetting/getSuggestionSetting", self.handle_getSuggestionSetting,name="v1_userSetting_getSuggestionSetting"), - ] - - self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time - - async def handle_getSuggestionSetting(self, request): - try: - - body = { - "code": bumper.RETURN_API_SUCCESS, - "data": { - "acceptSuggestion": "Y", - "itemList": [ - { - "name": "Aktionen/Angebote/Ereignisse", - "settingKey": "MARKETING", - "val": "Y" - }, - { - "name": "Benutzerbefragung", - "settingKey": "QUESTIONNAIRE", - "val": "Y" - }, - { - "name": "Produkt-Upgrade/Hilfe für Benutzer", - "settingKey": "INTRODUCTION", - "val": "Y" - } - ] - }, - "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_userSetting() diff --git a/create_certs/Bumper_SAN.txt b/create_certs/Bumper_SAN.txt index 2ffe727..9994fc3 100644 --- a/create_certs/Bumper_SAN.txt +++ b/create_certs/Bumper_SAN.txt @@ -3,8 +3,4 @@ ecovacs.com ecouser.net *.ecouser.net ecovacs.net -*.ecovacs.net -*.ww.ecouser.net -*.dc-eu.ww.ecouser.net -*.dc.ww.ecouser.net -*.area.ww.ecouser.net \ No newline at end of file +*.ecovacs.net \ No newline at end of file diff --git a/docs/Create_Certs.md b/docs/Create_Certs.md index 626c994..daef11c 100644 --- a/docs/Create_Certs.md +++ b/docs/Create_Certs.md @@ -53,9 +53,9 @@ I get it, you don't trust create_certs and want to do it manually. The easiest ### Create a Root CA -1. Create csrconfig_ca.txt for use in later commands +1. Create csrconfig.txt for use in later commands -***csrconfig_ca.txt*** +***csrconfig.txt*** ```` [ req ] default_md = sha256 @@ -70,9 +70,9 @@ keyUsage=critical,keyCertSign,cRLSign basicConstraints=critical,CA:true,pathlen:1 ```` -1. Create certconfig_ca.txt for use in later commands +1. Create certconfig.txt for use in later commands -***certconfig_ca.txt*** +***certconfig.txt*** ```` [ req ] default_md = sha256 @@ -91,21 +91,21 @@ basicConstraints=critical,CA:true,pathlen:1 1. Generate the RSA private key - `openssl genrsa -out ca.key 4096` + `openssl genpkey -outform PEM -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out priv.key` 1. Create the CSR - `openssl req -new -nodes -key ca.key -config csrconfig_ca.txt -out ca.csr` + `openssl req -new -nodes -key priv.key -config csrconfig.txt -out cert.csr` 1. Self-sign your CSR - `openssl req -x509 -nodes -in ca.csr -days 1095 -key ca.key -config certconfig_ca.txt -extensions req_ext -out ca.crt` + `openssl req -x509 -nodes -in cert.csr -days 3650 -key priv.key -config certconfig.txt -extensions req_ext -out cert.crt` ### Create the Server Certificate -1. Create csrconfig_bumper.txt for use in later commands +1. Create csrconfig.txt for use in later commands -***csrconfig_bumper.txt*** +***csrconfig.txt*** ```` [ req ] default_md = sha256 @@ -127,15 +127,11 @@ DNS.2 = ecouser.net DNS.3 = *.ecouser.net DNS.4 = ecovacs.net DNS.5 = *.ecovacs.net -DNS.6 = *.ww.ecouser.net -DNS.7 = *.dc-eu.ww.ecouser.net -DNS.8 = *.dc.ww.ecouser.net -DNS.9 = *.area.ww.ecouser.net ```` -1. Create certconfig_bumper.txt for use in later commands +1. Create certconfig.txt for use in later commands -***certconfig_bumper.txt*** +***certconfig.txt*** ```` [ req ] default_md = sha256 @@ -159,23 +155,19 @@ DNS.2 = ecouser.net DNS.3 = *.ecouser.net DNS.4 = ecovacs.net DNS.5 = *.ecovacs.net -DNS.6 = *.ww.ecouser.net -DNS.7 = *.dc-eu.ww.ecouser.net -DNS.8 = *.dc.ww.ecouser.net -DNS.9 = *.area.ww.ecouser.net ```` 1. Generate the RSA private key - `openssl genrsa -out bumper.key 4096` + `openssl genpkey -outform PEM -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out priv.key` 1. Create the CSR - `openssl req -new -nodes -key bumper.key -config csrconfig_bumper.txt -out bumper.csr` + `openssl req -new -nodes -key priv.key -config csrconfig.txt -out cert.csr` 1. Sign your CSR with a root CA cert - `openssl x509 -req -in bumper.csr -days 365 -CA ca.crt -CAkey ca.key -extfile certconfig_bumper.txt -extensions req_ext -CAcreateserial -out bumper.crt` + `openssl x509 -req -in cert.csr -days 3650 -CA ca.crt -CAkey priv.key -extfile certconfig.txt -extensions req_ext -CAserial /tmp/tmp-10593TSH1OlVSxC7C -CAcreateserial -out cert.crt` ## Using a Custom CA/Self diff --git a/docs/DNS_Setup.md b/docs/DNS_Setup.md index e433b8d..a519be8 100644 --- a/docs/DNS_Setup.md +++ b/docs/DNS_Setup.md @@ -26,7 +26,6 @@ If overriding DNS for the top-level domains isn't an option, you'll need to conf **Note:** Depending on country, your phone/robot may be using a different domain. Most of these domains contain country-specific placeholders. Not all domains have been documented at this point, and this list will be updated as more are identified/seen. The preferred way to ensure Bumper works is to override the full domains as above. -**Note:** The app dynamically gets the required domains from the endpoint `api/appsvr/service/list` and therefore ecovacs can use different domains for different models. Replacement Examples: @@ -47,7 +46,6 @@ Replacement Examples: | `eco-{countrycode}-api.ecovacs.com` | Used for Login | | `gl-{countrycode}-api.ecovacs.com` | Used by EcoVacs Home app | | `gl-{countrycode}-openapi.ecovacs.com` | Used by EcoVacs Home app | -| `portal.ecouser.net` | Used for Login and Rest API | | `portal-{countrycode}.ecouser.net` | Used for Login and Rest API | | `portal-{region}.ecouser.net` | Used for Login and Rest API | | `portal-ww.ecouser.net` | Used for various Rest APIs | @@ -61,11 +59,4 @@ Replacement Examples: | `recommender.ecovacs.com` | Used by Ecovacs Home app | | `bigdata-international.ecovacs.com` | Telemetry/tracking | | `bigdata-northamerica.ecovacs.com` | Telemetry/tracking | -| `bigdata-europe.ecovacs.com` | Telemetry/tracking | | `bigdata-{unknown regions}.ecovacs.com` | Telemetry/tracking | -| `api-app.ww.ecouser.net` | Api for App (v2+) | -| `api-app.dc-{region}.ww.ecouser.net` | Api for App (v2+) | -| `users-base.dc-{region}.ww.ecouser.net` | Accounts for App (v2+) | -| `jmq-ngiot-{region}.dc.ww.ecouser.net` | MQTT for App (v2+) | -| `api-rop.dc-{region}.ww.ecouser.net` | App (v2+) | -| `jmq-ngiot-{region}.area.ww.ecouser.net`| App (v2+) | \ No newline at end of file diff --git a/docs/Docker.md b/docs/Docker.md index 95d8f49..d6cc839 100644 --- a/docs/Docker.md +++ b/docs/Docker.md @@ -41,14 +41,4 @@ Optionally you can map existing directories for logs, data, and certs. ```` docker run -it -e "BUMPER_ANNOUNCE_IP=X.X.X.X" -p 443:443 -p 8007:8007 -p 8883:8883 -p 5223:5223 -v /home/user/bumper/data:/bumper/data --name bumper bmartin5692/bumper -```` - -# Docker-compose - -A docker-compose example can be found in the ["example" folder](https://github.com/bmartin5692/bumper/tree/master/example/docker-compose). - -The docker-compose starts two services: -- bumper itself -- nginx proxy, which redirects MQTT traffic on port `443` to port `8883` - -The redirection is required as the app v2+ and robots with a newer firmware are connecting to the mqtt server on port 433. +```` \ No newline at end of file diff --git a/docs/Env_Var.md b/docs/Env_Var.md index 4d3378b..f5b0697 100644 --- a/docs/Env_Var.md +++ b/docs/Env_Var.md @@ -11,5 +11,4 @@ Bumper has a number of environment variables to help with custom deployments and | BUMPER_KEY | {full path to bumper.key location} | The private server key (bumper.key) to be used by the Bumper server | | BUMPER_LOGS | {full path to logs directory} | The directory where logs should be stored | | BUMPER_DATA | {full path to data directory} | The directory where persistent data should be stored (bumper.db) | -| BUMPER_DEBUG | true | Run Bumper with debug mode/logging | -| LOG_TO_STDOUT | true | Instead of logging to logs/, logs to to STDOUT | \ No newline at end of file +| BUMPER_DEBUG | true | Run Bumper with debug mode/logging | \ No newline at end of file diff --git a/example/docker-compose/docker-compose.yaml b/example/docker-compose/docker-compose.yaml deleted file mode 100644 index 70c47d5..0000000 --- a/example/docker-compose/docker-compose.yaml +++ /dev/null @@ -1,45 +0,0 @@ ---- -version: "3.6" - -networks: - bumper: - internal: true - -services: - nginx: - depends_on: - - bumper - image: nginx:alpine - networks: - default: - bumper: - ports: - - 443:443 - - 5223:5223 - - 8007:8007 - - 8883:8883 - restart: unless-stopped - volumes: - - /etc/timezone:/etc/timezone:ro - - /etc/localtime:/etc/localtime:ro - - ./nginx/:/etc/nginx:ro # See config file below - - bumper: - image: bmartin5692/bumper - restart: unless-stopped - networks: - bumper: - - environment: - PUID: 1000 - PGID: 1000 - TZ: Europe/Rome - BUMPER_ANNOUNCE_IP: XXX # Insert your IP - BUMPER_LISTEN: 0.0.0.0 - # BUMPER_DEBUG: "true" - LOG_TO_STDOUT: "true" - volumes: - - /etc/timezone:/etc/timezone:ro - - /etc/localtime:/etc/localtime:ro - - ./config:/bumper/data - - ./certs:/bumper/certs \ No newline at end of file diff --git a/example/docker-compose/nginx/nginx.conf b/example/docker-compose/nginx/nginx.conf deleted file mode 100644 index 8bd0da8..0000000 --- a/example/docker-compose/nginx/nginx.conf +++ /dev/null @@ -1,41 +0,0 @@ -error_log stderr; -pid /var/run/nginx.pid; - -events { } - -stream { - resolver 127.0.0.11 ipv6=off; #docker dns server - map_hash_bucket_size 64; - - map $ssl_preread_server_name $internalport { - # redirect all requests, which contain "mq" in the SNI -> MQTT - ~^.*(mq).*\.eco(vacs|user)\.(net|com)$ 8883; - - # the rest of eco(user|vacs) requests - ~^.*eco(vacs|user)\.(net|com)$ 443; - - # mapping default to MQTT as the bots are connecting directly to the ip without SNI - default 8883; - } - - server { - listen 443; - ssl_preread on; - proxy_pass bumper:$internalport; - } - - server { - listen 5223; - proxy_pass bumper:5223; - } - - server { - listen 8007; - proxy_pass bumper:8007; - } - - server { - listen 8883; - proxy_pass bumper:8883; - } -} \ No newline at end of file diff --git a/tests/test_models.py b/tests/test_models.py deleted file mode 100644 index cd84a47..0000000 --- a/tests/test_models.py +++ /dev/null @@ -1,14 +0,0 @@ -from bumper import OAuth - - -def test_oauth(): - userId = "test" - o_auth = OAuth.create_new(userId) - assert o_auth is not None - assert o_auth.userId == userId - assert o_auth.access_token is not None - assert o_auth.expire_at is not None - assert o_auth.refresh_token is not None - - data = o_auth.toResponse() - assert data is not None