From 80f778c031de65e937bee72a84fc81aa7563cb45 Mon Sep 17 00:00:00 2001 From: Robert Resch Date: Sun, 30 Jan 2022 11:53:54 +0100 Subject: [PATCH] run black --- bumper/__init__.py | 31 +- bumper/__main__.py | 2 +- bumper/confserver.py | 300 +- bumper/db.py | 31 +- bumper/models.py | 525 +- bumper/mqttserver.py | 130 +- bumper/plugins.py | 5 +- .../bumper_confserver_portal_appsvr.py | 270 +- .../plugins/bumper_confserver_portal_dim.py | 26 +- .../plugins/bumper_confserver_portal_ecms.py | 19 +- .../plugins/bumper_confserver_portal_iot.py | 26 +- bumper/plugins/bumper_confserver_portal_lg.py | 15 +- .../plugins/bumper_confserver_portal_neng.py | 76 +- .../plugins/bumper_confserver_portal_pim.py | 4973 ++++++++--------- .../plugins/bumper_confserver_portal_rapp.py | 19 +- .../plugins/bumper_confserver_portal_users.py | 22 +- .../bumper_confserver_upload_global.py | 36 +- .../bumper_confserver_v1_global_auth.py | 15 +- .../bumper_confserver_v1_private_ad.py | 28 +- .../bumper_confserver_v1_private_campaign.py | 25 +- .../bumper_confserver_v1_private_common.py | 776 ++- .../bumper_confserver_v1_private_message.py | 31 +- .../bumper_confserver_v1_private_shop.py | 24 +- .../bumper_confserver_v1_private_user.py | 189 +- ...umper_confserver_v1_private_userSetting.py | 26 +- .../bumper_confserver_v2_private_user.py | 17 +- bumper/util.py | 12 +- bumper/xmppserver.py | 42 +- tests/const.py | 2 +- tests/test_confserver.py | 84 +- tests/test_init.py | 1 - tests/test_mqttserver.py | 142 +- tests/test_xmppserver.py | 5 +- 33 files changed, 4040 insertions(+), 3885 deletions(-) diff --git a/bumper/__init__.py b/bumper/__init__.py index 04080fa..fae5aad 100644 --- a/bumper/__init__.py +++ b/bumper/__init__.py @@ -66,7 +66,7 @@ sys.path.append(os.path.join(data_dir, "plugins")) discovered_plugins = { name: importlib.import_module(name) for finder, name, ispkg in pkgutil.iter_modules() - if name.startswith('bumper_') + if name.startswith("bumper_") } shutting_down = False @@ -104,9 +104,9 @@ async def start(): return if not ( - os.path.exists(ca_cert) - and os.path.exists(server_cert) - and os.path.exists(server_key) + os.path.exists(ca_cert) + and os.path.exists(server_cert) + and os.path.exists(server_key) ): bumperlog.fatal("Certificate(s) don't exist at paths specified") return @@ -143,9 +143,15 @@ async def start(): # Start web servers conf_server.confserver_app() asyncio.create_task( - conf_server.start_site(conf_server.app, address=bumper_listen, port=conf1_listen_port, usessl=True)) + 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)) + conf_server.start_site( + conf_server.app, address=bumper_listen, port=conf2_listen_port, usessl=False + ) + ) # Start maintenance while not shutting_down: @@ -200,18 +206,17 @@ def main(argv=None): try: if not ( - os.path.exists(ca_cert) - and os.path.exists(server_cert) - and os.path.exists(server_key) + os.path.exists(ca_cert) + and os.path.exists(server_cert) + and os.path.exists(server_key) ): msg = "No certs found! Please generate them (More infos in the docs)" bumperlog.fatal(msg) sys.exit(msg) - if not ( - os.path.exists(os.path.join(data_dir, "passwd")) - ): - with open(os.path.join(data_dir, "passwd"), 'w'): pass + if not (os.path.exists(os.path.join(data_dir, "passwd"))): + with open(os.path.join(data_dir, "passwd"), "w"): + pass parser = argparse.ArgumentParser() parser.add_argument( diff --git a/bumper/__main__.py b/bumper/__main__.py index 7434132..d2c45b2 100644 --- a/bumper/__main__.py +++ b/bumper/__main__.py @@ -1,4 +1,4 @@ import bumper if __name__ == "__main__": - bumper.main() \ No newline at end of file + bumper.main() diff --git a/bumper/confserver.py b/bumper/confserver.py index 8dd4918..e8fb066 100644 --- a/bumper/confserver.py +++ b/bumper/confserver.py @@ -13,6 +13,7 @@ from bumper import plugins from bumper.models import * from .util import get_logger + class aiohttp_filter(logging.Filter): def filter(self, record): if ( @@ -21,10 +22,7 @@ class aiohttp_filter(logging.Filter): record.levelno = 10 record.levelname = "DEBUG" - if ( - record.levelno == 10 - and get_logger("confserver").getEffectiveLevel() == 10 - ): + if record.levelno == 10 and get_logger("confserver").getEffectiveLevel() == 10: return True else: return False @@ -50,60 +48,75 @@ class ConfServer: return int(round(timetoconvert * 1000)) def confserver_app(self): - self.app = web.Application(loop=asyncio.get_event_loop(), middlewares=[ - self.log_all_requests, - ]) - aiohttp_jinja2.setup(self.app, loader=jinja2.FileSystemLoader(os.path.join(bumper.bumper_dir,"bumper","web","templates"))) + self.app = web.Application( + loop=asyncio.get_event_loop(), + middlewares=[ + self.log_all_requests, + ], + ) + aiohttp_jinja2.setup( + self.app, + loader=jinja2.FileSystemLoader( + os.path.join(bumper.bumper_dir, "bumper", "web", "templates") + ), + ) self.app.add_routes( [ 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.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), ] ) # common api paths - api_v1 = {"prefix": "/v1/", "app": web.Application()} # for /v1/ - api_v2 = {"prefix": "/v2/", "app": web.Application()} # for /v2/ - portal_api = {"prefix": "/api/", "app": web.Application()} # for /api/ - upload_api = {"prefix": "/upload/", "app": web.Application()} # for /upload/ - + api_v1 = {"prefix": "/v1/", "app": web.Application()} # for /v1/ + api_v2 = {"prefix": "/v2/", "app": web.Application()} # for /v2/ + portal_api = {"prefix": "/api/", "app": web.Application()} # for /api/ + upload_api = {"prefix": "/upload/", "app": web.Application()} # for /upload/ + apis = { "api_v1": api_v1, "api_v2": api_v2, "portal_api": portal_api, "upload_api": upload_api, - } - + # Load plugins for plug in bumper.discovered_plugins: - if isinstance(bumper.discovered_plugins[plug].plugin, bumper.plugins.ConfServerApp): - plugin = bumper.discovered_plugins[plug].plugin - if plugin.plugin_type == "sub_api": # app or sub_api + if isinstance( + bumper.discovered_plugins[plug].plugin, bumper.plugins.ConfServerApp + ): + plugin = bumper.discovered_plugins[plug].plugin + if plugin.plugin_type == "sub_api": # app or sub_api if plugin.sub_api in apis: if plugin.routes: logging.debug(f"Adding confserver sub_api ({plugin.name})") apis[plugin.sub_api]["app"].add_routes(plugin.routes) - + elif plugin.plugin_type == "app": if plugin.path_prefix and plugin.app: logging.debug(f"Adding confserver plugin ({plugin.name})") - self.app.add_subapp(plugin.path_prefix, plugin.app) - - for api in apis: + self.app.add_subapp(plugin.path_prefix, plugin.app) + + for api in apis: self.app.add_subapp(apis[api]["prefix"], apis[api]["app"]) - #for resource in self.app.router.resources(): + # for resource in self.app.router.resources(): # print(resource) - - - async def start_site(self, app, address='localhost', port=8080, usessl=False): + async def start_site(self, app, address="localhost", port=8080, usessl=False): runner = web.AppRunner(app) self.runners.append(runner) await runner.setup() @@ -118,9 +131,7 @@ class ConfServer: ) else: - site = web.TCPSite( - runner, host=address, port=port - ) + site = web.TCPSite(runner, host=address, port=port) await site.start() @@ -178,12 +189,14 @@ class ConfServer: mq_sessions = [] for sess in mqttserver._sessions: tmpsess = [] - tmpsess.append({ - "username": mqttserver._sessions[sess][0].username, - "client_id": mqttserver._sessions[sess][0].client_id, - "state": mqttserver._sessions[sess][0].transitions.state, - }) - + tmpsess.append( + { + "username": mqttserver._sessions[sess][0].username, + "client_id": mqttserver._sessions[sess][0].client_id, + "state": mqttserver._sessions[sess][0].transitions.state, + } + ) + mq_sessions.append(tmpsess) all = { "bots": bots, @@ -198,10 +211,10 @@ class ConfServer: ] }, ], - "xmpp_server": xmppserver - } - resp = aiohttp_jinja2.render_template('home.jinja2', request, context=all) - #return web.json_response(all) + "xmpp_server": xmppserver, + } + resp = aiohttp_jinja2.render_template("home.jinja2", request, context=all) + # return web.json_response(all) return resp except Exception as e: @@ -233,7 +246,7 @@ class ConfServer: except Exception as e: confserverlog.error(f"Request body not json: {e} - {e.doc}") postbody = e.doc - + else: postbody = await request.post() @@ -252,7 +265,7 @@ class ConfServer: to_log["response"]["body"] = f"{json.loads(response.body)}" confserverlog.debug(json.dumps(to_log)) - + return response except web.HTTPNotFound as notfound: @@ -263,7 +276,7 @@ class ConfServer: except Exception as e: confserverlog.exception(f"{e}") confserverlog.error(json.dumps(to_log)) - return e + return e else: return await handler(request) @@ -274,24 +287,26 @@ class ConfServer: asyncio.create_task(bumper.mqtt_helperbot.start_helper_bot()) async def restart_MQTT(self): - - if not (bumper.mqtt_server.broker.transitions.state == "stopped" or bumper.mqtt_server.broker.transitions.state == "not_started"): + + if not ( + bumper.mqtt_server.broker.transitions.state == "stopped" + or bumper.mqtt_server.broker.transitions.state == "not_started" + ): # close session writers - this was required so bots would reconnect properly after restarting - for sess in list(bumper.mqtt_server.broker._sessions): + for sess in list(bumper.mqtt_server.broker._sessions): sessobj = bumper.mqtt_server.broker._sessions[sess][1] if sessobj.session.transitions.state == "connected": await sessobj.writer.close() - #await bumper.mqtt_server.broker.shutdown() + # await bumper.mqtt_server.broker.shutdown() aloop = asyncio.get_event_loop() aloop.call_later( - 0.1, lambda: asyncio.create_task(bumper.mqtt_server.broker.shutdown()) + 0.1, lambda: asyncio.create_task(bumper.mqtt_server.broker.shutdown()) ) # In .1 seconds shutdown broker - aloop = asyncio.get_event_loop() aloop.call_later( - 1.5, lambda: asyncio.create_task(bumper.mqtt_server.broker_coro()) + 1.5, lambda: asyncio.create_task(bumper.mqtt_server.broker_coro()) ) # In 1.5 seconds start broker async def restart_XMPP(self): @@ -310,7 +325,7 @@ class ConfServer: aloop.call_later( 5, lambda: asyncio.create_task(self.restart_Helper()) ) # In 5 seconds restart Helperbot - + return web.json_response({"status": "complete"}) elif service == "XMPPServer": await self.restart_XMPP() @@ -333,29 +348,27 @@ class ConfServer: except Exception as e: confserverlog.exception(f"{e}") - pass + pass async def handle_RemoveClient(self, request): - try: + try: resource = request.match_info.get("resource", "") bumper.client_remove(resource) if bumper.client_get(resource): - return web.json_response({"status": "failed to remove client"}) + return web.json_response({"status": "failed to remove client"}) else: - return web.json_response({"status": "successfully removed client"}) + return web.json_response({"status": "successfully removed client"}) except Exception as e: confserverlog.exception(f"{e}") - pass + pass async def handle_login(self, request): try: user_devid = request.match_info.get("devid", "") countrycode = request.match_info.get("country", "us") apptype = request.match_info.get("apptype", "") - confserverlog.info( - f"client with devid {user_devid} attempting login" - ) + confserverlog.info(f"client with devid {user_devid} attempting login") if bumper.use_auth: if ( not user_devid == "" @@ -476,11 +489,7 @@ class ConfServer: confserverlog.debug(postbody) - body = { - "authCode": postbody["itToken"], - "result": "ok", - "todo": "result" - } + body = {"authCode": postbody["itToken"], "result": "ok", "todo": "result"} return web.json_response(body) @@ -500,13 +509,15 @@ class ConfServer: pass def get_milli_time(self, timetoconvert): - return int(round(timetoconvert * 1000)) + return int(round(timetoconvert * 1000)) class ConfServer_AuthHandler: def __init__(self): - self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + self.get_milli_time = ( + bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + ) pass - + def generate_token(self, user): try: tmpaccesstoken = uuid.uuid4().hex @@ -523,80 +534,80 @@ class ConfServer: return tmpauthcode except Exception as e: - confserverlog.exception(f"{e}") - + confserverlog.exception(f"{e}") async def login(self, request): - try: - user_devid = request.match_info.get("devid", "") - countrycode = request.match_info.get("country", "us") - apptype = request.match_info.get("apptype", "") - confserverlog.info( - f"client with devid {user_devid} attempting login" - ) - if bumper.use_auth: - if ( - not user_devid == "" - ): # Performing basic "auth" using devid, super insecure - user = bumper.user_by_deviceid(user_devid) - if "checkLogin" in request.path: - self.check_token( - apptype, countrycode, user, request.query["accessToken"] + try: + user_devid = request.match_info.get("devid", "") + countrycode = request.match_info.get("country", "us") + apptype = request.match_info.get("apptype", "") + confserverlog.info(f"client with devid {user_devid} attempting login") + if bumper.use_auth: + if ( + not user_devid == "" + ): # Performing basic "auth" using devid, super insecure + user = bumper.user_by_deviceid(user_devid) + if "checkLogin" in request.path: + self.check_token( + apptype, countrycode, user, request.query["accessToken"] + ) + else: + if "global_" in apptype: # EcoVacs Home + login_details = EcoVacsHome_Login() + login_details.ucUid = "fuid_{}".format(user["userid"]) + login_details.loginName = "fusername_{}".format( + user["userid"] ) + login_details.mobile = None + else: - if "global_" in apptype: # EcoVacs Home - login_details = EcoVacsHome_Login() - login_details.ucUid = "fuid_{}".format(user["userid"]) - login_details.loginName = "fusername_{}".format( - user["userid"] - ) - login_details.mobile = None + login_details = EcoVacs_Login() - else: - login_details = EcoVacs_Login() + # Deactivate old tokens and authcodes + bumper.user_revoke_expired_tokens(user["userid"]) - # Deactivate old tokens and authcodes - bumper.user_revoke_expired_tokens(user["userid"]) + login_details.accessToken = self.generate_token(user) + login_details.uid = "fuid_{}".format(user["userid"]) + login_details.username = "fusername_{}".format( + user["userid"] + ) + login_details.country = countrycode + login_details.email = "null@null.com" - login_details.accessToken = self.generate_token(user) - login_details.uid = "fuid_{}".format(user["userid"]) - login_details.username = "fusername_{}".format(user["userid"]) - login_details.country = countrycode - login_details.email = "null@null.com" + body = { + "code": API_ERRORS[RETURN_API_SUCCESS], + "data": json.loads(login_details.toJSON()), + # { + # "accessToken": self.generate_token(tmpuser), # Generate a token + # "country": countrycode, + # "email": "null@null.com", + # "uid": "fuid_{}".format(tmpuser["userid"]), + # "username": "fusername_{}".format(tmpuser["userid"]), + # }, + "msg": "操作成功", + "time": self.get_milli_time( + datetime.utcnow().timestamp() + ), + } - body = { - "code": API_ERRORS[RETURN_API_SUCCESS], - "data": json.loads(login_details.toJSON()), - # { - # "accessToken": self.generate_token(tmpuser), # Generate a token - # "country": countrycode, - # "email": "null@null.com", - # "uid": "fuid_{}".format(tmpuser["userid"]), - # "username": "fusername_{}".format(tmpuser["userid"]), - # }, - "msg": "操作成功", - "time": self.get_milli_time(datetime.utcnow().timestamp()), - } + return web.json_response(body) - return web.json_response(body) + body = { + "code": bumper.ERR_USER_NOT_ACTIVATED, + "data": None, + "msg": "当前密码错误", + "time": self.get_milli_time(datetime.utcnow().timestamp()), + } - body = { - "code": bumper.ERR_USER_NOT_ACTIVATED, - "data": None, - "msg": "当前密码错误", - "time": self.get_milli_time(datetime.utcnow().timestamp()), - } + return web.json_response(body) - return web.json_response(body) - - else: - return web.json_response( - self._auth_any(user_devid, apptype, countrycode, request) - ) - - except Exception as e: - confserverlog.exception(f"{e}") + else: + return web.json_response( + self._auth_any(user_devid, apptype, countrycode, request) + ) + except Exception as e: + confserverlog.exception(f"{e}") async def get_AuthCode(self, request): try: @@ -660,7 +671,7 @@ class ConfServer: return web.json_response(body) except Exception as e: - confserverlog.exception(f"{e}") + confserverlog.exception(f"{e}") def check_token(self, apptype, countrycode, user, token): try: @@ -705,7 +716,7 @@ class ConfServer: return web.json_response(body) except Exception as e: - confserverlog.exception(f"{e}") + confserverlog.exception(f"{e}") def _auth_any(self, devid, apptype, country, request): try: @@ -719,7 +730,9 @@ class ConfServer: if "global_" in apptype: # EcoVacs Home login_details = EcoVacsHome_Login() login_details.ucUid = "fuid_{}".format(tmpuser["userid"]) - login_details.loginName = "fusername_{}".format(tmpuser["userid"]) + login_details.loginName = "fusername_{}".format( + tmpuser["userid"] + ) login_details.mobile = None else: login_details = EcoVacs_Login() @@ -736,7 +749,9 @@ class ConfServer: if "global_" in apptype: # EcoVacs Home login_details = EcoVacsHome_Login() login_details.ucUid = "fuid_{}".format(tmpuser["userid"]) - login_details.loginName = "fusername_{}".format(tmpuser["userid"]) + login_details.loginName = "fusername_{}".format( + tmpuser["userid"] + ) login_details.mobile = None else: login_details = EcoVacs_Login() @@ -754,7 +769,9 @@ class ConfServer: else: confserverlog.error(f"No DID for bot: {bot}") - if "checkLogin" in request.path: # If request was to check a token do so + if ( + "checkLogin" in request.path + ): # If request was to check a token do so checkToken = self.check_token( apptype, countrycode, tmpuser, request.query["accessToken"] ) @@ -782,8 +799,7 @@ class ConfServer: return body except Exception as e: - confserverlog.exception(f"{e}") - + confserverlog.exception(f"{e}") def getUserAccountInfo(self, request): try: @@ -851,7 +867,7 @@ class ConfServer: return web.json_response(body) except Exception as e: - confserverlog.exception(f"{e}") + confserverlog.exception(f"{e}") async def logout(self, request): try: @@ -859,7 +875,9 @@ class ConfServer: if not user_devid == "": user = bumper.user_by_deviceid(user_devid) if user: - if bumper.check_token(user["userid"], request.query["accessToken"]): + if bumper.check_token( + user["userid"], request.query["accessToken"] + ): # Deactivate old tokens and authcodes bumper.user_revoke_token( user["userid"], request.query["accessToken"] @@ -875,4 +893,4 @@ class ConfServer: return web.json_response(body) except Exception as e: - confserverlog.exception(f"{e}") \ No newline at end of file + confserverlog.exception(f"{e}") diff --git a/bumper/db.py b/bumper/db.py index 075ada1..09e99ef 100644 --- a/bumper/db.py +++ b/bumper/db.py @@ -8,7 +8,13 @@ from tinydb import TinyDB, Query import bumper from .util import get_logger -from bumper.models import VacBotClient, VacBotDevice, BumperUser, EcoVacsHomeProducts, OAuth +from bumper.models import ( + VacBotClient, + VacBotDevice, + BumperUser, + EcoVacsHomeProducts, + OAuth, +) bumperlog = get_logger("bumper") @@ -265,8 +271,8 @@ def check_authcode(uid, authcode): tmpauth = tokens.get( (Query().authcode == authcode) & ( # Match authcode - (Query().userid == uid.replace("fuid_", "")) - | (Query().userid == f"fuid_{uid}") + (Query().userid == uid.replace("fuid_", "")) + | (Query().userid == f"fuid_{uid}") ) # Userid with or without fuid_ ) if tmpauth: @@ -279,7 +285,8 @@ def loginByItToken(authcode): bumperlog.debug(f"Checking for authcode: {authcode}") tokens = db_get().table("tokens") tmpauth = tokens.get( - Query().authcode == authcode + Query().authcode + == authcode # & ( # Match authcode # (Query().userid == uid.replace("fuid_", "")) # | (Query().userid == "fuid_{}".format(uid)) @@ -297,8 +304,8 @@ def check_token(uid, token): tmpauth = tokens.get( (Query().token == token) & ( # Match token - (Query().userid == uid.replace("fuid_", "")) - | (Query().userid == f"fuid_{uid}") + (Query().userid == uid.replace("fuid_", "")) + | (Query().userid == f"fuid_{uid}") ) # Userid with or without fuid_ ) if tmpauth: @@ -326,11 +333,9 @@ 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( - f"Adding new bot with SN: {newbot.name} DID: {newbot.did}" - ) + bumperlog.info(f"Adding new bot with SN: {newbot.name} DID: {newbot.did}") bot_full_upsert(newbot.asdict()) @@ -357,7 +362,11 @@ def bot_toEcoVacsHome_JSON(bot): # EcoVacs Home 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["product_category"] = ( + "DEEBOT" + if botprod["product"]["name"].startswith("DEEBOT") + else "UNKNOWN" + ) # bot["updateInfo"] = { # "changeLog": "", # "needUpdate": False diff --git a/bumper/models.py b/bumper/models.py index 0a3c533..2a79321 100644 --- a/bumper/models.py +++ b/bumper/models.py @@ -8,7 +8,7 @@ import bumper class VacBotDevice: 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 @@ -101,7 +101,9 @@ class OAuth: oauth = OAuth() oauth.userId = userId oauth.access_token = uuid.uuid4().hex - oauth.expire_at = f"{datetime.utcnow() + timedelta(days=bumper.oauth_validity_days)}" + oauth.expire_at = ( + f"{datetime.utcnow() + timedelta(days=bumper.oauth_validity_days)}" + ) oauth.refresh_token = uuid.uuid4().hex return oauth @@ -110,8 +112,11 @@ class OAuth: def toResponse(self): data = self.__dict__ - data["expire_at"] = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time( - datetime.fromisoformat(self.expire_at).timestamp()) + data[ + "expire_at" + ] = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time( + datetime.fromisoformat(self.expire_at).timestamp() + ) return data @@ -132,10 +137,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": False, - "alexa": False + "alexa": False, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d2d996c4d60de0001eaf2b5" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d2d996c4d60de0001eaf2b5", + }, }, { "classid": "vsc5ia", @@ -151,10 +156,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c874326280fda0001770d2a" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c874326280fda0001770d2a", + }, }, { "classid": "zi1uwd", @@ -170,10 +175,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5da834a8d66cd10001f58265" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5da834a8d66cd10001f58265", + }, }, { "classid": "12baap", @@ -189,10 +194,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606425981269020008a9627b" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606425981269020008a9627b", + }, }, { "classid": "02uwxm", @@ -208,10 +213,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b1dddc48bc45700014035a1" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b1dddc48bc45700014035a1", + }, }, { "classid": "eyi9jv", @@ -227,10 +232,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b7b65f176f7f10001e9a0c2" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b7b65f176f7f10001e9a0c2", + }, }, { "classid": "9rft3c", @@ -246,10 +251,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": False, - "alexa": False + "alexa": False, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/6062795ad18cbd0008e2fce8" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/6062795ad18cbd0008e2fce8", + }, }, { "classid": "141", @@ -265,10 +270,10 @@ EcoVacsHomeProducts = [ "share": False, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c2aa64d60de0001eaf1f6" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c2aa64d60de0001eaf1f6", + }, }, { "classid": "fqxoiu", @@ -284,10 +289,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/605b059217c95b0008ff20d4" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/605b059217c95b0008ff20d4", + }, }, { "classid": "u6eqoa", @@ -303,10 +308,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606425a11269020008a9627d" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606425a11269020008a9627d", + }, }, { "classid": "dl8fht", @@ -322,10 +327,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5acc32067c295c0001876eea" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5acc32067c295c0001876eea", + }, }, { "classid": "yna5xi", @@ -341,10 +346,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606278df4a84d700082b39f1" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606278df4a84d700082b39f1", + }, }, { "classid": "123", @@ -360,10 +365,10 @@ EcoVacsHomeProducts = [ "share": False, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c150dba13eb00013feaae" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c150dba13eb00013feaae", + }, }, { "classid": "140", @@ -379,10 +384,10 @@ EcoVacsHomeProducts = [ "share": False, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c152f4d60de0001eaf1f4" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c152f4d60de0001eaf1f4", + }, }, { "classid": "q1v5dn", @@ -398,10 +403,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": False, - "alexa": False + "alexa": False, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d83375f6b6a570001569e26" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d83375f6b6a570001569e26", + }, }, { "classid": "16wdph", @@ -417,10 +422,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d280ce3350e7a0001e84c95" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d280ce3350e7a0001e84c95", + }, }, { "classid": "rvo6ev", @@ -436,10 +441,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606426feb0a931000860fad5" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606426feb0a931000860fad5", + }, }, { "classid": "09m4bu", @@ -455,10 +460,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ef31b8cee3c1200075b6f67" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ef31b8cee3c1200075b6f67", + }, }, { "classid": "y2qy3m", @@ -474,10 +479,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606425784a84d700082b39f6" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606425784a84d700082b39f6", + }, }, { "classid": "0xyhhr", @@ -493,10 +498,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d117d4f0ac6ad00012b792d" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d117d4f0ac6ad00012b792d", + }, }, { "classid": "ipzjy0", @@ -512,10 +517,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606426c64a84d700082b39fa" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606426c64a84d700082b39fa", + }, }, { "classid": "hsgwhi", @@ -531,10 +536,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": False, - "alexa": False + "alexa": False, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5e731a4a06f6de700464c69d" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5e731a4a06f6de700464c69d", + }, }, { "classid": "h18jkh", @@ -550,10 +555,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5e8e8d146482551d72530e47" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5e8e8d146482551d72530e47", + }, }, { "classid": "126", @@ -569,10 +574,10 @@ EcoVacsHomeProducts = [ "share": False, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ca32ab2e9e9270001354b3d" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ca32ab2e9e9270001354b3d", + }, }, { "classid": "x5d34r", @@ -588,10 +593,10 @@ EcoVacsHomeProducts = [ "share": False, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/605053e7fc527c00087fda1e" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/605053e7fc527c00087fda1e", + }, }, { "classid": "ls1ok3", @@ -607,10 +612,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ba4a2cb6c2f120001c32839" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ba4a2cb6c2f120001c32839", + }, }, { "classid": "y79a7u", @@ -626,10 +631,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b04c0217ccd1a0001e1f6a7" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b04c0217ccd1a0001e1f6a7", + }, }, { "classid": "vi829v", @@ -645,10 +650,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606278d3fc527c00087fdb08" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606278d3fc527c00087fdb08", + }, }, { "classid": "55aiho", @@ -664,10 +669,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/605be27250928b0007c13264" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/605be27250928b0007c13264", + }, }, { "classid": "gd4uut", @@ -683,10 +688,10 @@ EcoVacsHomeProducts = [ "share": False, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5e8da019032edd9008c66bf0" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5e8da019032edd9008c66bf0", + }, }, { "classid": "130", @@ -702,10 +707,10 @@ EcoVacsHomeProducts = [ "share": False, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d4b7640de51dd0001fee131" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d4b7640de51dd0001fee131", + }, }, { "classid": "2pv572", @@ -721,10 +726,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d1474632a6bd50001b5b6f3" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d1474632a6bd50001b5b6f3", + }, }, { "classid": "xb83mv", @@ -740,10 +745,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d3fe649de51dd0001fee0de" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d3fe649de51dd0001fee0de", + }, }, { "classid": "ar5bjb", @@ -759,10 +764,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5e58a2df36e8f39e318f031d" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5e58a2df36e8f39e318f031d", + }, }, { "classid": "7j1tu6", @@ -778,10 +783,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/6064260545505e0008e5cb49" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/6064260545505e0008e5cb49", + }, }, { "classid": "ts2ofl", @@ -797,10 +802,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606425dfd18cbd0008e2fcf3" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606425dfd18cbd0008e2fcf3", + }, }, { "classid": "125", @@ -816,10 +821,10 @@ EcoVacsHomeProducts = [ "share": False, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c14414d60de0001eaf1f2" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c14414d60de0001eaf1f2", + }, }, { "classid": "jh3ry2", @@ -835,10 +840,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": False, - "alexa": False + "alexa": False, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/6049b34d1269020008a95aef" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/6049b34d1269020008a95aef", + }, }, { "classid": "wlqdkp", @@ -854,10 +859,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/6064263ad18cbd0008e2fcf4" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/6064263ad18cbd0008e2fcf4", + }, }, { "classid": "c0lwyn", @@ -873,10 +878,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606425ab4a84d700082b39f7" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606425ab4a84d700082b39f7", + }, }, { "classid": "emzppx", @@ -892,10 +897,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c931fef280fda0001770d7e" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c931fef280fda0001770d7e", + }, }, { "classid": "115", @@ -911,10 +916,10 @@ EcoVacsHomeProducts = [ "share": False, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5cf711aeb0acfc000179ff8a" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5cf711aeb0acfc000179ff8a", + }, }, { "classid": "jr3pqa", @@ -930,10 +935,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b5ac4cc8d5a56000111e769" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b5ac4cc8d5a56000111e769", + }, }, { "classid": "142", @@ -949,10 +954,10 @@ EcoVacsHomeProducts = [ "share": False, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ca1ca79e9e9270001354b2d" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ca1ca79e9e9270001354b2d", + }, }, { "classid": "aqdd5p", @@ -968,10 +973,10 @@ EcoVacsHomeProducts = [ "share": False, "tmjl": False, "assistant": False, - "alexa": False + "alexa": False, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5cb7cfbab72c4d00010e5fc7" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5cb7cfbab72c4d00010e5fc7", + }, }, { "classid": "152", @@ -987,10 +992,10 @@ EcoVacsHomeProducts = [ "share": False, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d4b7628de51dd0001fee12f" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d4b7628de51dd0001fee12f", + }, }, { "classid": "d0cnel", @@ -1006,10 +1011,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d157f9f77a3a60001051f69" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d157f9f77a3a60001051f69", + }, }, { "classid": "u4h1uk", @@ -1025,10 +1030,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606425bed18cbd0008e2fcf2" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606425bed18cbd0008e2fcf2", + }, }, { "classid": "3ab24g", @@ -1044,10 +1049,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ef31b80f5dcdf000767cf4d" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ef31b80f5dcdf000767cf4d", + }, }, { "classid": "9akc61", @@ -1063,10 +1068,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c932067280fda0001770d7f" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c932067280fda0001770d7f", + }, }, { "classid": "159", @@ -1082,10 +1087,10 @@ EcoVacsHomeProducts = [ "share": False, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d4b7606de51dd0001fee12d" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d4b7606de51dd0001fee12d", + }, }, { "classid": "b742vd", @@ -1101,10 +1106,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5e8e93a7032edd3f5ec66d4a" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5e8e93a7032edd3f5ec66d4a", + }, }, { "classid": "uv242z", @@ -1120,10 +1125,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b5ac4e45f21100001882bb9" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b5ac4e45f21100001882bb9", + }, }, { "classid": "155", @@ -1139,10 +1144,10 @@ EcoVacsHomeProducts = [ "share": False, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5cd4ca505b032200015a455d" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5cd4ca505b032200015a455d", + }, }, { "classid": "1qdu4z", @@ -1158,10 +1163,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": False, - "alexa": False + "alexa": False, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5e71c7df298f0d9cabfef86f" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5e71c7df298f0d9cabfef86f", + }, }, { "classid": "4zfacv", @@ -1177,10 +1182,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c778731280fda0001770ba0" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c778731280fda0001770ba0", + }, }, { "classid": "nq9yhl", @@ -1196,10 +1201,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606426ebb0a931000860fad4" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606426ebb0a931000860fad4", + }, }, { "classid": "m7lqzi", @@ -1215,10 +1220,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": False, - "alexa": False + "alexa": False, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c63a5ba13eb00013feab7" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c63a5ba13eb00013feab7", + }, }, { "classid": "r8ead0", @@ -1234,10 +1239,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c93204b63023c0001e7faa7" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c93204b63023c0001e7faa7", + }, }, { "classid": "jjccwk", @@ -1253,10 +1258,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d3aa309ba13eb00013feb69" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5d3aa309ba13eb00013feb69", + }, }, { "classid": "129", @@ -1272,10 +1277,10 @@ EcoVacsHomeProducts = [ "share": False, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ca31df112851900016858c0" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ca31df112851900016858c0", + }, }, { "classid": "165", @@ -1291,10 +1296,10 @@ EcoVacsHomeProducts = [ "share": False, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ca32a1012851900016858c6" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ca32a1012851900016858c6", + }, }, { "classid": "jffnlf", @@ -1310,10 +1315,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": False, - "alexa": False + "alexa": False, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5e53207a26be71596c4b55cd" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5e53207a26be71596c4b55cd", + }, }, { "classid": "d4v1pm", @@ -1329,10 +1334,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606426254a84d700082b39f9" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606426254a84d700082b39f9", + }, }, { "classid": "34vhpm", @@ -1348,10 +1353,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/605050031269020008a95af9" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/605050031269020008a95af9", + }, }, { "classid": "tpnwyu", @@ -1367,10 +1372,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/6050503e1269020008a95afa" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/6050503e1269020008a95afa", + }, }, { "classid": "wgxm70", @@ -1386,10 +1391,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5edf2bbedb28cc00062f8bd7" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5edf2bbedb28cc00062f8bd7", + }, }, { "classid": "1zqysa", @@ -1405,10 +1410,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/6064258d1269020008a9627a" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/6064258d1269020008a9627a", + }, }, { "classid": "chmi0g", @@ -1424,10 +1429,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606425821269020008a96279" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/606425821269020008a96279", + }, }, { "classid": "p5nx9u", @@ -1443,10 +1448,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5f59e774c0f03a0008ee72e0" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5f59e774c0f03a0008ee72e0", + }, }, { "classid": "n6cwdb", @@ -1462,10 +1467,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/60627a92fc527c00087fdb0a" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/60627a92fc527c00087fdb0a", + }, }, { "classid": "lhbd50", @@ -1481,10 +1486,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/603f51243b03f50007b6c2ca" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/603f51243b03f50007b6c2ca", + }, }, { "classid": "ucn2xe", @@ -1500,10 +1505,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/603f510e3b03f50007b6c2c9" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/603f510e3b03f50007b6c2c9", + }, }, { "classid": "0bdtzz", @@ -1519,10 +1524,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5fa105bbd16a99000667eb52" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5fa105bbd16a99000667eb52", + }, }, { "classid": "r5zxjr", @@ -1538,10 +1543,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/60627c09b0a931000860facd" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/60627c09b0a931000860facd", + }, }, { "classid": "r5y7re", @@ -1557,10 +1562,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/60627a9cb0a931000860fac7" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/60627a9cb0a931000860fac7", + }, }, { "classid": "snxbvc", @@ -1576,10 +1581,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/60627bbfb0a931000860fac9" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/60627bbfb0a931000860fac9", + }, }, { "classid": "7bryc5", @@ -1595,10 +1600,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5fb474d4d16a99000667edd9" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5fb474d4d16a99000667edd9", + }, }, { "classid": "b2jqs4", @@ -1614,10 +1619,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5feaeb27d4cb3a0006679047" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5feaeb27d4cb3a0006679047", + }, }, { "classid": "yu362x", @@ -1633,10 +1638,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/60627bde50928b0007c13273" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/60627bde50928b0007c13273", + }, }, { "classid": "ifbw08", @@ -1652,10 +1657,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5feaeb47d4cb3a0006679048" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5feaeb47d4cb3a0006679048", + }, }, { "classid": "85as7h", @@ -1671,10 +1676,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5feaeb585f437d0008e0e00c" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5feaeb585f437d0008e0e00c", + }, }, { "classid": "ty84oi", @@ -1690,10 +1695,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/60627bcafc527c00087fdb0c" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/60627bcafc527c00087fdb0c", + }, }, { "classid": "36xnxf", @@ -1709,10 +1714,10 @@ EcoVacsHomeProducts = [ "share": True, "tmjl": False, "assistant": True, - "alexa": True + "alexa": True, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/60627bd517c95b0008ff20ec" - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/60627bd517c95b0008ff20ec", + }, }, { "classid": "2pj946", @@ -1728,11 +1733,11 @@ EcoVacsHomeProducts = [ "share": False, "tmjl": False, "assistant": False, - "alexa": False + "alexa": False, }, - "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/6049b3631269020008a95af0" - } - } + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/6049b3631269020008a95af0", + }, + }, ] RETURN_API_SUCCESS = "0000" diff --git a/bumper/mqttserver.py b/bumper/mqttserver.py index f850ffc..8b9a90f 100644 --- a/bumper/mqttserver.py +++ b/bumper/mqttserver.py @@ -22,7 +22,6 @@ boterrorlog = get_logger("boterror") class CommandDto: - def __init__(self, payload_type: str) -> None: self._payload_type = payload_type self._event = asyncio.Event() @@ -44,8 +43,9 @@ class MQTTHelperBot: Client = None def __init__(self, host: str, port: int, timeout: float = 60): - self._commands: MutableMapping[str, CommandDto] = TTLCache(maxsize=timeout * 60, - ttl=timeout*1.1) + self._commands: MutableMapping[str, CommandDto] = TTLCache( + maxsize=timeout * 60, ttl=timeout * 1.1 + ) self._host = host self._port = port self.client_id = "helperbot@bumper/helperbot" @@ -56,16 +56,20 @@ class MQTTHelperBot: return self._commands @property - def timeout(self)->float: + def timeout(self) -> float: return self._timeout async def start_helper_bot(self): try: if self.Client is None: - self.Client = MQTTClient(client_id=self.client_id, - config={"check_hostname": False, "reconnect_retries": 20}) + self.Client = MQTTClient( + client_id=self.client_id, + config={"check_hostname": False, "reconnect_retries": 20}, + ) - await self.Client.connect(f"mqtts://{self._host}:{self._port}/", cafile=bumper.ca_cert) + await self.Client.connect( + f"mqtts://{self._host}:{self._port}/", cafile=bumper.ca_cert + ) await self.Client.subscribe( [ ("iot/p2p/+/+/+/+/helperbot/bumper/helperbot/+/+/+", QOS_0), @@ -78,12 +82,10 @@ class MQTTHelperBot: async def _wait_for_resp(self, command_dto: CommandDto, request_id: str): try: - payload = await asyncio.wait_for(command_dto.wait_for_response(), timeout=self.timeout) - return { - "id": request_id, - "ret": "ok", - "resp": payload - } + payload = await asyncio.wait_for( + command_dto.wait_for_response(), timeout=self.timeout + ) + return {"id": request_id, "ret": "ok", "resp": payload} except asyncio.TimeoutError: helperbotlog.debug("wait_for_resp timeout reached") except asyncio.CancelledError as e: @@ -143,9 +145,7 @@ class MQTTServer: self._port = port # Default config opts - passwd_file = os.path.join( - os.path.join(bumper.data_dir, "passwd") - ) + passwd_file = os.path.join(os.path.join(bumper.data_dir, "passwd")) # For file auth, set user:hash in passwd file see # (https://hbmqtt.readthedocs.io/en/latest/references/hbmqtt.html#configuration-example) @@ -156,7 +156,9 @@ class MQTTServer: passwd_file = kwargs["password_file"] elif key == "allow_anonymous": - allow_anon = kwargs["allow_anonymous"] # Set to True to allow anonymous authentication + allow_anon = kwargs[ + "allow_anonymous" + ] # Set to True to allow anonymous authentication # The below adds a plugin to the hbmqtt.broker.plugins without having to futz with setup.py distribution = pkg_resources.Distribution("hbmqtt.broker.plugins") @@ -181,7 +183,9 @@ class MQTTServer: "auth": { "allow-anonymous": allow_anon, "password-file": passwd_file, - "plugins": ["bumper"], # Bumper plugin provides auth and handling of bots/clients connecting + "plugins": [ + "bumper" + ], # Bumper plugin provides auth and handling of bots/clients connecting }, "topic-check": {"enabled": False}, } @@ -235,7 +239,7 @@ class BumperMQTTServer_Plugin: if "@" in client_id: didsplit = str(client_id).split("@") if not ( # if ecouser or bumper aren't in details it is a bot - "ecouser" in didsplit[1] or "bumper" in didsplit[1] + "ecouser" in didsplit[1] or "bumper" in didsplit[1] ): tmpbotdetail = str(didsplit[1]).split("/") bumper.bot_add( @@ -245,8 +249,10 @@ class BumperMQTTServer_Plugin: tmpbotdetail[1], "eco-ng", ) - mqttserverlog.info(f"Bumper Authentication Success - Bot - SN: {username} - DID: {didsplit[0]}" - f" - Class: {tmpbotdetail[0]}") + mqttserverlog.info( + f"Bumper Authentication Success - Bot - SN: {username} - DID: {didsplit[0]}" + f" - Class: {tmpbotdetail[0]}" + ) authenticated = True else: tmpclientdetail = str(didsplit[1]).split("/") @@ -255,27 +261,40 @@ class BumperMQTTServer_Plugin: resource = tmpclientdetail[1] if userid == "helperbot": - mqttserverlog.info(f"Bumper Authentication Success - Helperbot: {client_id}") + mqttserverlog.info( + f"Bumper Authentication Success - Helperbot: {client_id}" + ) authenticated = True - elif bumper.check_authcode(didsplit[0], password) or not bumper.use_auth: + elif ( + bumper.check_authcode(didsplit[0], password) + or not bumper.use_auth + ): bumper.client_add(userid, realm, resource) - mqttserverlog.info(f"Bumper Authentication Success - Client - Username: {username} - " - f"ClientID: {client_id}") + mqttserverlog.info( + f"Bumper Authentication Success - Client - Username: {username} - " + f"ClientID: {client_id}" + ) authenticated = True - # Check for File Auth - if username and not authenticated: # If there is a username and it isn't already authenticated + # Check for File Auth + if ( + username and not authenticated + ): # If there is a username and it isn't already authenticated hash = self._users.get(username, None) if hash: # If there is a matching entry in passwd, check hash authenticated = pwd_context.verify(password, hash) if authenticated: mqttserverlog.info( - f"File Authentication Success - Username: {username} - ClientID: {client_id}") + f"File Authentication Success - Username: {username} - ClientID: {client_id}" + ) else: - mqttserverlog.info(f"File Authentication Failed - Username: {username} - ClientID: {client_id}") + mqttserverlog.info( + f"File Authentication Failed - Username: {username} - ClientID: {client_id}" + ) else: mqttserverlog.info( - f"File Authentication Failed - No Entry for Username: {username} - ClientID: {client_id}") + f"File Authentication Failed - No Entry for Username: {username} - ClientID: {client_id}" + ) except Exception as e: mqttserverlog.exception( @@ -285,28 +304,39 @@ class BumperMQTTServer_Plugin: # Check for allow anonymous allow_anonymous = self.auth_config.get("allow-anonymous", True) - if allow_anonymous and not authenticated: # If anonymous auth is allowed and it isn't already authenticated + if ( + allow_anonymous and not authenticated + ): # If anonymous auth is allowed and it isn't already authenticated authenticated = True self.context.logger.debug( - f"Anonymous Authentication Success: config allows anonymous - Username: {username}") - mqttserverlog.info(f"Anonymous Authentication Success: config allows anonymous - Username: {username}") + f"Anonymous Authentication Success: config allows anonymous - Username: {username}" + ) + mqttserverlog.info( + f"Anonymous Authentication Success: config allows anonymous - Username: {username}" + ) return authenticated def _read_password_file(self): - password_file = self.auth_config.get('password-file', None) + password_file = self.auth_config.get("password-file", None) if password_file: try: with open(password_file) as f: - self.context.logger.debug(f"Reading user database from {password_file}") + self.context.logger.debug( + f"Reading user database from {password_file}" + ) for l in f: line = l.strip() - if not line.startswith('#'): # Allow comments in files + if not line.startswith("#"): # Allow comments in files (username, pwd_hash) = line.split(sep=":", maxsplit=3) if username: self._users[username] = pwd_hash - self.context.logger.debug(f"user: {username} - hash: {pwd_hash}") - self.context.logger.debug(f"{(len(self._users))} user(s) read from file {password_file}") + self.context.logger.debug( + f"user: {username} - hash: {pwd_hash}" + ) + self.context.logger.debug( + f"{(len(self._users))} user(s) read from file {password_file}" + ) except FileNotFoundError: self.context.logger.warning(f"Password file {password_file} not found") @@ -332,20 +362,32 @@ class BumperMQTTServer_Plugin: data_decoded = str(message.data.decode("utf-8")) if topic_split[6] == "helperbot": # Response to command - helperbotlog.debug(f"Received Response - Topic: {topic} - Message: {data_decoded}") + helperbotlog.debug( + f"Received Response - Topic: {topic} - Message: {data_decoded}" + ) if topic_split[10] in bumper.mqtt_helperbot.commands: - bumper.mqtt_helperbot.commands[topic_split[10]].add_response(data_decoded) + bumper.mqtt_helperbot.commands[topic_split[10]].add_response( + data_decoded + ) elif topic_split[3] == "helperbot": # Helperbot sending command - helperbotlog.debug(f"Send Command - Topic: {topic} - Message: {data_decoded}") + helperbotlog.debug( + f"Send Command - Topic: {topic} - Message: {data_decoded}" + ) elif topic_split[1] == "atr": # Broadcast message received on atr if topic_split[2] == "errors": - boterrorlog.error(f"Received Error - Topic: {topic} - Message: {data_decoded}") + boterrorlog.error( + f"Received Error - Topic: {topic} - Message: {data_decoded}" + ) else: - helperbotlog.debug(f"Received Broadcast - Topic: {topic} - Message: {data_decoded}") + helperbotlog.debug( + f"Received Broadcast - Topic: {topic} - Message: {data_decoded}" + ) else: - helperbotlog.debug(f"Received Message - Topic: {topic} - Message: {data_decoded}") + helperbotlog.debug( + f"Received Message - Topic: {topic} - Message: {data_decoded}" + ) async def on_broker_client_disconnected(self, client_id): self._set_client_connected(client_id, False) diff --git a/bumper/plugins.py b/bumper/plugins.py index d1cbc9b..e3934f3 100644 --- a/bumper/plugins.py +++ b/bumper/plugins.py @@ -2,12 +2,11 @@ import asyncio from aiohttp import web -class ConfServerApp(): + +class ConfServerApp: name = None plugin_type = None path_prefix = None app = None sub_api = None routes = None - - diff --git a/bumper/plugins/bumper_confserver_portal_appsvr.py b/bumper/plugins/bumper_confserver_portal_appsvr.py index b2ee984..60dbb85 100644 --- a/bumper/plugins/bumper_confserver_portal_appsvr.py +++ b/bumper/plugins/bumper_confserver_portal_appsvr.py @@ -8,152 +8,167 @@ from bumper.models import * class portal_api_appsvr(plugins.ConfServerApp): - def __init__(self): self.name = "portal_api_appsvr" self.plugin_type = "sub_api" self.sub_api = "portal_api" self.routes = [ - web.route("*", "/appsvr/app.do", self.handle_appsvr_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_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", + ), ] - self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + self.get_milli_time = ( + bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + ) async def handle_appsvr_app(self, request): - if not request.method == "GET": # Skip GET for now - try: + if not request.method == "GET": # Skip GET for now + try: - body = {} - postbody = {} - if request.content_type == "application/x-www-form-urlencoded": - postbody = await request.post() + body = {} + postbody = {} + if request.content_type == "application/x-www-form-urlencoded": + postbody = await request.post() - else: - postbody = json.loads(await request.text()) + else: + postbody = json.loads(await request.text()) - todo = postbody["todo"] + todo = postbody["todo"] - if todo == "GetGlobalDeviceList": # EcoVacs Home - bots = bumper.db_get().table("bots").all() - botlist = [] - for bot in bots: - if bot["class"] != "": - b = bumper.bot_toEcoVacsHome_JSON(bot) - if ( - not b is None - ): # Happens if the bot isn't on the EcoVacs Home list - botlist.append(json.loads(b)) + if todo == "GetGlobalDeviceList": # EcoVacs Home + bots = bumper.db_get().table("bots").all() + botlist = [] + for bot in bots: + if bot["class"] != "": + b = bumper.bot_toEcoVacsHome_JSON(bot) + if ( + not b is None + ): # Happens if the bot isn't on the EcoVacs Home list + botlist.append(json.loads(b)) - body = { - "code": 0, - "devices": botlist, - "ret": "ok", - "todo": "result", - } + body = { + "code": 0, + "devices": botlist, + "ret": "ok", + "todo": "result", + } - return web.json_response(body) + return web.json_response(body) - #elif todo == "GetShareDeviceList": - # example response - # { - # "code": 0, - # "devices": [ - # { - # "deviceName": "DEEBOT 900 Series (Cleaner Cleaner)", - # "did": "did", - # "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ba4a2cb6c2f120001c32839", - # "mid": "ls1ok3", - # "ownUsers": { - # "isMe": true, - # "nickname": "user@gmail.com", - # "user": "cg****" - # }, - # "resource": "wC3g", - # "share": true, - # "shareUsers": [] - # } - # ], - # "ret": "ok", - # "todo": "result" - # } + # elif todo == "GetShareDeviceList": + # example response + # { + # "code": 0, + # "devices": [ + # { + # "deviceName": "DEEBOT 900 Series (Cleaner Cleaner)", + # "did": "did", + # "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ba4a2cb6c2f120001c32839", + # "mid": "ls1ok3", + # "ownUsers": { + # "isMe": true, + # "nickname": "user@gmail.com", + # "user": "cg****" + # }, + # "resource": "wC3g", + # "share": true, + # "shareUsers": [] + # } + # ], + # "ret": "ok", + # "todo": "result" + # } - # if shared shareUsers - # "shareUsers": [ - # { - # "isMe": false, - # "nickname": "user@gmail.com", - # "status": "sharing", - # "user": "eafg****" - # } - # ] + # if shared shareUsers + # "shareUsers": [ + # { + # "isMe": false, + # "nickname": "user@gmail.com", + # "status": "sharing", + # "user": "eafg****" + # } + # ] - #elif todo == "ShareDevice": - # example post - # { - # "todo": "ShareDevice", - # "accountType": "EMAIL", - # "auth": { - # "realm": "ecouser.net", - # "resource": "res", - # "token": "token***", - # "userid": "cg***", - # "with": "users" - # }, - # "country": "US", - # "did": "did", - # "resource": "wC3g", - # "username": "email to share to" - # } + # elif todo == "ShareDevice": + # example post + # { + # "todo": "ShareDevice", + # "accountType": "EMAIL", + # "auth": { + # "realm": "ecouser.net", + # "resource": "res", + # "token": "token***", + # "userid": "cg***", + # "with": "users" + # }, + # "country": "US", + # "did": "did", + # "resource": "wC3g", + # "username": "email to share to" + # } - #fail response (no user) - # { - # "todo": "result", - # "code": -3, - # "errno": -3, - # "ret": "fail" - # } + # fail response (no user) + # { + # "todo": "result", + # "code": -3, + # "errno": -3, + # "ret": "fail" + # } - #success response - #{"ret":"ok","code":0,"todo":"result"} + # success response + # {"ret":"ok","code":0,"todo":"result"} - #elif todo == "ShareUnRegisterDevice": - # example post - # { - # "todo": "ShareUnRegisterDevice", - # "account": "email to share to", - # "auth": { - # "realm": "ecouser.net", - # "resource": "res", - # "token": "toke", - # "userid": "userid", - # "with": "users" - # }, - # "country": "US", - # "did": "did", - # "lang": "EN", - # "mid": "ls1ok3" - # } - # example response - # { - # "todo": "result", - # "code": 0, - # "data": { - # "mailContent": "

Hey there,

\n\t\t\tCheck out my new awesome robot vacuum: DEEBOT 900 Series!
\n\t\t\tDownload the ECOVACS HOME App and sign up with your email: brian@bmartin.net, so you and I can control this kick-ass robot together.

\n\t\t\tIOS: https://itunes.apple.com/us/app/ecovacs-home/id1329458504?l=zh&ls=1&mt=8
\n\t\t\tAndroid: https://play.google.com/store/apps/details?id=com.eco.global.app

\n\t\t\tThis invitation is valid within 7 days.
\n\t\t\tIf I'm sending to the wrong person, please ignore this email.

Thank you.

", - # "mailTitle": "I'm sharing my DEEBOT and you're invited!" - # }, - # "ret": "ok" - # } + # elif todo == "ShareUnRegisterDevice": + # example post + # { + # "todo": "ShareUnRegisterDevice", + # "account": "email to share to", + # "auth": { + # "realm": "ecouser.net", + # "resource": "res", + # "token": "toke", + # "userid": "userid", + # "with": "users" + # }, + # "country": "US", + # "did": "did", + # "lang": "EN", + # "mid": "ls1ok3" + # } + # example response + # { + # "todo": "result", + # "code": 0, + # "data": { + # "mailContent": "

Hey there,

\n\t\t\tCheck out my new awesome robot vacuum: DEEBOT 900 Series!
\n\t\t\tDownload the ECOVACS HOME App and sign up with your email: brian@bmartin.net, so you and I can control this kick-ass robot together.

\n\t\t\tIOS: https://itunes.apple.com/us/app/ecovacs-home/id1329458504?l=zh&ls=1&mt=8
\n\t\t\tAndroid: https://play.google.com/store/apps/details?id=com.eco.global.app

\n\t\t\tThis invitation is valid within 7 days.
\n\t\t\tIf I'm sending to the wrong person, please ignore this email.

Thank you.

", + # "mailTitle": "I'm sharing my DEEBOT and you're invited!" + # }, + # "ret": "ok" + # } + except Exception as e: + logging.exception(f"{e}") - except Exception as e: - logging.exception(f"{e}") - - # Return fail for GET - body = {"result": "fail", "todo": "result"} - return web.json_response(body) + # Return fail for GET + body = {"result": "fail", "todo": "result"} + return web.json_response(body) async def handle_appsvr_service_list(self, request): try: @@ -176,15 +191,10 @@ class portal_api_appsvr(plugins.ConfServerApp): "magw": "api-app.ecouser.net", "msgcloud": "msg-eu.ecouser.net:5223", "ngiotLb": "jmq-ngiot-eu.ecouser.net", - "rop": "api-rop.ecouser.net" + "rop": "api-rop.ecouser.net", } - body = { - "code": 0, - "data": data, - "ret": "ok", - "todo": "result" - } + body = {"code": 0, "data": data, "ret": "ok", "todo": "result"} return web.json_response(body) @@ -199,7 +209,7 @@ class portal_api_appsvr(plugins.ConfServerApp): "code": 0, "data": oauth.toResponse(), "ret": "ok", - "todo": "result" + "todo": "result", } return web.json_response(body) diff --git a/bumper/plugins/bumper_confserver_portal_dim.py b/bumper/plugins/bumper_confserver_portal_dim.py index fd71d74..9f4aa29 100644 --- a/bumper/plugins/bumper_confserver_portal_dim.py +++ b/bumper/plugins/bumper_confserver_portal_dim.py @@ -12,19 +12,23 @@ import random class portal_api_dim(plugins.ConfServerApp): - def __init__(self): self.name = "portal_api_dimr" - self.plugin_type = "sub_api" + self.plugin_type = "sub_api" self.sub_api = "portal_api" - + self.routes = [ - - web.route("*", "/dim/devmanager.do", self.handle_dim_devmanager, name="portal_api_dim_devmanager"), - + web.route( + "*", + "/dim/devmanager.do", + self.handle_dim_devmanager, + name="portal_api_dim_devmanager", + ), ] - self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + self.get_milli_time = ( + bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + ) async def handle_dim_devmanager(self, request): # Used in EcoVacs Home App try: @@ -66,11 +70,11 @@ class portal_api_dim(plugins.ConfServerApp): return web.json_response(body) if json_body["td"] == "ReceiveShareDevice": # EcoVacs Home - body = {"ret":"ok"} - return web.json_response(body) + body = {"ret": "ok"} + return web.json_response(body) except Exception as e: - logging.exception(f"{e}") + logging.exception(f"{e}") + plugin = portal_api_dim() - diff --git a/bumper/plugins/bumper_confserver_portal_ecms.py b/bumper/plugins/bumper_confserver_portal_ecms.py index 152456d..ead6976 100644 --- a/bumper/plugins/bumper_confserver_portal_ecms.py +++ b/bumper/plugins/bumper_confserver_portal_ecms.py @@ -8,26 +8,27 @@ 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"), + 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 + 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 - } + body = {"code": 0, "data": [], "message": "success", "success": True} return web.json_response(body) diff --git a/bumper/plugins/bumper_confserver_portal_iot.py b/bumper/plugins/bumper_confserver_portal_iot.py index f9721df..85c4e61 100644 --- a/bumper/plugins/bumper_confserver_portal_iot.py +++ b/bumper/plugins/bumper_confserver_portal_iot.py @@ -10,21 +10,26 @@ from datetime import datetime, timedelta import string import random -class portal_api_iot(plugins.ConfServerApp): +class portal_api_iot(plugins.ConfServerApp): def __init__(self): self.name = "portal_api_iot" - self.plugin_type = "sub_api" + self.plugin_type = "sub_api" self.sub_api = "portal_api" - - self.routes = [ - - web.route("*", "/iot/devmanager.do", self.handle_devmanager_botcommand, name="portal_api_iot_devmanager"), + self.routes = [ + web.route( + "*", + "/iot/devmanager.do", + self.handle_devmanager_botcommand, + name="portal_api_iot_devmanager", + ), ] - self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time - + self.get_milli_time = ( + bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + ) + async def handle_devmanager_botcommand(self, request): try: json_body = json.loads(await request.text()) @@ -70,12 +75,11 @@ class portal_api_iot(plugins.ConfServerApp): return web.json_response(body) if json_body["td"] == "PreWifiConfig": # EcoVacs Home - body = {"ret":"ok"} + body = {"ret": "ok"} return web.json_response(body) - except Exception as e: logging.exception(f"{e}") -plugin = portal_api_iot() +plugin = portal_api_iot() diff --git a/bumper/plugins/bumper_confserver_portal_lg.py b/bumper/plugins/bumper_confserver_portal_lg.py index 015e7a0..85afac5 100644 --- a/bumper/plugins/bumper_confserver_portal_lg.py +++ b/bumper/plugins/bumper_confserver_portal_lg.py @@ -11,19 +11,18 @@ from bumper.models import * class portal_api_lg(plugins.ConfServerApp): - def __init__(self): self.name = "portal_api_lg" self.plugin_type = "sub_api" self.sub_api = "portal_api" self.routes = [ - web.route("*", "/lg/log.do", self.handle_lg_log, name="portal_api_lg_log"), - ] - self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + 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)) @@ -72,10 +71,10 @@ class portal_api_lg(plugins.ConfServerApp): cleanlogs = logsroot.getchildren() for l in cleanlogs: cleanlog = { - "ts": l.attrib['s'], - "area": l.attrib['a'], - "last": l.attrib['l'], - "cleanType": l.attrib['t'], + "ts": l.attrib["s"], + "area": l.attrib["a"], + "last": l.attrib["l"], + "cleanType": l.attrib["t"], # imageUrl allows for providing images of cleanings, something to look into later # "imageUrl": "https://localhost:8007", } diff --git a/bumper/plugins/bumper_confserver_portal_neng.py b/bumper/plugins/bumper_confserver_portal_neng.py index d4938de..a591006 100644 --- a/bumper/plugins/bumper_confserver_portal_neng.py +++ b/bumper/plugins/bumper_confserver_portal_neng.py @@ -10,21 +10,35 @@ from datetime import datetime, timedelta class portal_api_neng(plugins.ConfServerApp): - def __init__(self): self.name = "portal_api_neng" - self.plugin_type = "sub_api" + self.plugin_type = "sub_api" self.sub_api = "portal_api" - - self.routes = [ - - web.route("*", "/neng/message/hasUnreadMsg", self.handle_neng_hasUnreadMessage, name="portal_api_neng_hasUnreadMessage"), - web.route("*", "/neng/message/getShareMsgs", self.handle_neng_getShareMsgs, name="portal_api_neng_getShareMsgs"), - web.route("*", "/neng/message/getlist", self.handle_neng_getlist, name="portal_api_neng_getlist"), + self.routes = [ + web.route( + "*", + "/neng/message/hasUnreadMsg", + self.handle_neng_hasUnreadMessage, + name="portal_api_neng_hasUnreadMessage", + ), + web.route( + "*", + "/neng/message/getShareMsgs", + self.handle_neng_getShareMsgs, + name="portal_api_neng_getShareMsgs", + ), + web.route( + "*", + "/neng/message/getlist", + self.handle_neng_getlist, + name="portal_api_neng_getlist", + ), ] - self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + self.get_milli_time = ( + bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + ) async def handle_neng_hasUnreadMessage(self, request): # EcoVacs Home try: @@ -37,7 +51,7 @@ class portal_api_neng(plugins.ConfServerApp): async def handle_neng_getShareMsgs(self, request): # EcoVacs Home try: - body = {"code":0,"data":{"hasNext":False,"msgs":[]}} + body = {"code": 0, "data": {"hasNext": False, "msgs": []}} # share msg response # { @@ -65,33 +79,33 @@ class portal_api_neng(plugins.ConfServerApp): return web.json_response(body) except Exception as e: - logging.exception(f"{e}") + logging.exception(f"{e}") async def handle_neng_getlist(self, request): # EcoVacs Home try: - body = {"code":0,"data":{"hasNext":False,"msgs":[]}} + body = {"code": 0, "data": {"hasNext": False, "msgs": []}} - #Sample Message - # { - # "id": "5da0ac9d636aec5107627ac4", - # "ts": 1570811036877, - # "did": "bot did", - # "cid": "ls1ok3", - # "name": "DEEBOT 900 Series", - # "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ba4a2cb6c2f120001c32839", - # "eventTypeId": "5aab824bb62ce30001f9a702", - # "title": "DEEBOT is off the floor.", - # "body": "DEEBOT is off the floor. Please put it back.", - # "read": false, - # "UILogicId": "D_900", - # "type": "web", - # "url": "https://portal-ww.ecouser.net/api/pim/eventdetail.html?id=5ba21e44aed83800015b9ca8" # Off the floor instructions - # } + # Sample Message + # { + # "id": "5da0ac9d636aec5107627ac4", + # "ts": 1570811036877, + # "did": "bot did", + # "cid": "ls1ok3", + # "name": "DEEBOT 900 Series", + # "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ba4a2cb6c2f120001c32839", + # "eventTypeId": "5aab824bb62ce30001f9a702", + # "title": "DEEBOT is off the floor.", + # "body": "DEEBOT is off the floor. Please put it back.", + # "read": false, + # "UILogicId": "D_900", + # "type": "web", + # "url": "https://portal-ww.ecouser.net/api/pim/eventdetail.html?id=5ba21e44aed83800015b9ca8" # Off the floor instructions + # } return web.json_response(body) except Exception as e: - logging.exception(f"{e}") - -plugin = portal_api_neng() + logging.exception(f"{e}") + +plugin = portal_api_neng() diff --git a/bumper/plugins/bumper_confserver_portal_pim.py b/bumper/plugins/bumper_confserver_portal_pim.py index 701a3d3..287d727 100644 --- a/bumper/plugins/bumper_confserver_portal_pim.py +++ b/bumper/plugins/bumper_confserver_portal_pim.py @@ -9,22 +9,53 @@ from bumper.models import * class portal_api_pim(plugins.ConfServerApp): - def __init__(self): self.name = "portal_api_pim" self.plugin_type = "sub_api" self.sub_api = "portal_api" self.routes = [ - web.route("*", "/pim/product/getProductIotMap", self.handle_getProductIotMap, name="portal_api_pim_getProductIotMap"), - web.route("*", "/pim/file/get/{id}", self.handle_pimFile, name="portal_api_pim_file"), - 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"), + 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 + self.get_milli_time = ( + bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + ) async def handle_getProductIotMap(self, request): try: @@ -41,7 +72,11 @@ 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(f"{e}") @@ -67,7 +102,7 @@ class portal_api_pim(plugins.ConfServerApp): body = { "code": -1, "data": [], - "msg": "This errcode's detail is not exists" + "msg": "This errcode's detail is not exists", } return web.json_response(body) @@ -86,16 +121,9 @@ class portal_api_pim(plugins.ConfServerApp): # not found in productConfigBatch # some devices don't have any product configuration - data.append({ - "cfg": {}, - "pid": pid - }) + data.append({"cfg": {}, "pid": pid}) - body = { - "code": 200, - "data": data, - "message": "success" - } + body = {"code": 200, "data": data, "message": "success"} return web.json_response(body) except Exception as e: @@ -105,2607 +133,2278 @@ class portal_api_pim(plugins.ConfServerApp): plugin = portal_api_pim() confignetAllResponse = { - "code": 0, - "data": [ - { - "groupId": "5ae147f27ccd1a0001e1f69c", - "sort": 60, - "groupName": "DEEBOT OZMO Slim10 Series", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1715-0201", - "mid": "02uwxm", - "ota": False, - "supportVer": { - "Android": "1.0.0", - "IOS": "1.0.0" - }, - "smartTypes": [], - "steps": [ + "code": 0, + "data": [ { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" + "groupId": "5ae147f27ccd1a0001e1f69c", + "sort": 60, + "groupName": "DEEBOT OZMO Slim10 Series", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1715-0201", + "mid": "02uwxm", + "ota": False, + "supportVer": {"Android": "1.0.0", "IOS": "1.0.0"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5b1dddc48bc45700014035a1", }, { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5b1dddc48bc45700014035a1" + "groupId": "5ca32b5de9e9270001354b3f", + "sort": 80, + "groupName": "DEEBOT OZMO 601", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "SPA", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1629-0203", + "mid": "159", + "ota": False, + "supportVer": {"Android": "1.1.7", "IOS": "1.1.7"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d4b7606de51dd0001fee12d", + }, + { + "groupId": "5ca32b5de9e9270001354b3f", + "sort": 80, + "groupName": "DEEBOT", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "SPA", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1629-0203", + "mid": "159", + "ota": False, + "supportVer": {"Android": "1.1.7", "IOS": "1.1.7"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d4b7628de51dd0001fee12f", + }, + { + "groupId": "5ca32b5de9e9270001354b3f", + "sort": 80, + "groupName": "DEEBOT OZMO 610 Series", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "SPA", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1629-0203", + "mid": "159", + "ota": False, + "supportVer": {"Android": "1.1.7", "IOS": "1.1.7"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d4b7640de51dd0001fee131", + }, + { + "groupId": "5cae9662e9e9270001354b55", + "sort": 150, + "groupName": "DEEBOT M80 Pro", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "SPA", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1638-0102", + "mid": "125", + "ota": False, + "supportVer": {"Android": "1.1.7", "IOS": "1.1.7"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c14414d60de0001eaf1f2", + }, + { + "groupId": "5bbedcd922d57f00018c13b6", + "sort": 30, + "groupName": "DEEBOT OZMO/PRO 930 Series", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "HK_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1803-0101", + "mid": "115", + "ota": False, + "supportVer": {"Android": "1.1.8", "IOS": "1.1.8"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5cf711aeb0acfc000179ff8a", + }, + { + "groupId": "5b6560760506b100015c8867", + "sort": 130, + "groupName": "DEEBOT 900 Series", + "isPopular": False, + "belongApp": ["ecoglobal", "ecodeebot"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1711-0201", + "mid": "ls1ok3", + "ota": False, + "supportVer": {"Android": "1.0.7", "IOS": "1.0.7"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ba4a2cb6c2f120001c32839", + }, + { + "groupId": "5b6560760506b100015c8867", + "sort": 130, + "groupName": "DEEBOT 910", + "isPopular": False, + "belongApp": ["ecoglobal", "ecodeebot"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1711-0201", + "mid": "ls1ok3", + "ota": False, + "supportVer": {"Android": "1.0.7", "IOS": "1.0.7"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5c778731280fda0001770ba0", + }, + { + "groupId": "5cae9793e9e9270001354b57", + "sort": 160, + "groupName": "DEEBOT M81 Pro", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "SPA", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1638-0101", + "mid": "141", + "ota": False, + "supportVer": {"Android": "1.1.7", "IOS": "1.1.7"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c2aa64d60de0001eaf1f6", + }, + { + "groupId": "5c19a835a1e6ee0001782245", + "sort": 20, + "groupName": "DEEBOT OZMO 920 Series", + "isPopular": True, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 1, + "materialNo": "110-1819-0101", + "mid": "vi829v", + "ota": False, + "supportVer": {"Android": "1.1.5", "IOS": "1.1.5"}, + "smartTypes": ["MQ_AP"], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "Briefly press the RESET button for 1 second and then release. You will hear that DEEBOT is ready for network setup.", + "retryText": "", + "confirmText": "I've heard the sound", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5c9c7995e9e9270001354ab4", + }, + { + "groupId": "5bc8187422d57f00018c13ba", + "sort": 50, + "groupName": "DEEBOT OZMO 960", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1803-0101", + "mid": "gd4uut", + "ota": False, + "supportVer": {"Android": "1.1.5", "IOS": "1.1.5"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5c7384767b93c700013f12e7", + }, + { + "groupId": "5b04bf1d7ccd1a0001e1f6a6", + "sort": 10, + "groupName": "DEEBOT OZMO 900 Series", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1810-0101", + "mid": "y79a7u", + "ota": False, + "supportVer": {"Android": "1.0.0", "IOS": "1.0.0"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5b04c0217ccd1a0001e1f6a7", + }, + { + "groupId": "5b04bf1d7ccd1a0001e1f6a6", + "sort": 10, + "groupName": "DEEBOT OZMO 905", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1810-0101", + "mid": "y79a7u", + "ota": False, + "supportVer": {"Android": "1.0.0", "IOS": "1.0.0"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d1474632a6bd50001b5b6f3", + }, + { + "groupId": "5c763de8280fda0001770b7f", + "sort": 100, + "groupName": "DEEBOT 500", + "isPopular": True, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "702-0000-0163", + "mid": "vsc5ia", + "ota": False, + "supportVer": {"Android": "1.1.5", "IOS": "1.1.5"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5df9d6b7c783810001d3d06b", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5dfc34d15d21490001700e14", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5c874326280fda0001770d2a", + }, + { + "groupId": "5c763de8280fda0001770b7f", + "sort": 100, + "groupName": "DEEBOT 501", + "isPopular": True, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "702-0000-0163", + "mid": "vsc5ia", + "ota": False, + "supportVer": {"Android": "1.1.5", "IOS": "1.1.5"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5df9d6b7c783810001d3d06b", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5dfc34d15d21490001700e14", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5c931fef280fda0001770d7e", + }, + { + "groupId": "5c763de8280fda0001770b7f", + "sort": 100, + "groupName": "DEEBOT 502", + "isPopular": True, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "702-0000-0163", + "mid": "vsc5ia", + "ota": False, + "supportVer": {"Android": "1.1.5", "IOS": "1.1.5"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5df9d6b7c783810001d3d06b", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5dfc34d15d21490001700e14", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5c93204b63023c0001e7faa7", + }, + { + "groupId": "5c763de8280fda0001770b7f", + "sort": 100, + "groupName": "DEEBOT 505", + "isPopular": True, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "702-0000-0163", + "mid": "vsc5ia", + "ota": False, + "supportVer": {"Android": "1.1.5", "IOS": "1.1.5"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5df9d6b7c783810001d3d06b", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5dfc34d15d21490001700e14", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5c932067280fda0001770d7f", + }, + { + "groupId": "5cae9aa5e9e9270001354b5d", + "sort": 230, + "groupName": "DEEBOT Slim2 Series", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "SPA", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1639-0102", + "mid": "123", + "ota": False, + "supportVer": {"Android": "1.1.7", "IOS": "1.1.7"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c150dba13eb00013feaae", + }, + { + "groupId": "5cae9aa5e9e9270001354b5d", + "sort": 230, + "groupName": "DEEBOT Slim Neo", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "SPA", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1639-0102", + "mid": "123", + "ota": False, + "supportVer": {"Android": "1.1.7", "IOS": "1.1.7"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c152f4d60de0001eaf1f4", + }, + { + "groupId": "5c19a8a0ddfc1f0001ede8e0", + "sort": 40, + "groupName": "DEEBOT OZMO 950 Series", + "isPopular": True, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 1, + "materialNo": "110-1820-0101", + "mid": "yna5xi", + "ota": False, + "supportVer": {"Android": "1.1.5", "IOS": "1.1.5"}, + "smartTypes": ["MQ_AP"], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "Briefly press the RESET button for 1 second and then release. You will hear that DEEBOT is ready for network setup.", + "retryText": "", + "confirmText": "I've heard the sound", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5caafd7e1285190001685965", + }, + { + "groupId": "5ca4711412851900016858cb", + "sort": 70, + "groupName": "DEEBOT OZMO 700", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 1, + "materialNo": "110-1825-0201", + "mid": "0xyhhr", + "ota": False, + "supportVer": {"Android": "1.1.5", "IOS": "1.1.5"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "Briefly press the RESET button for 1 second and then release. You will hear that DEEBOT is ready for network setup.", + "retryText": "", + "confirmText": "I've heard the sound", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d117d4f0ac6ad00012b792d", + }, + { + "groupId": "5ca4711412851900016858cb", + "sort": 70, + "groupName": "DEEBOT OZMO 750", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 1, + "materialNo": "110-1825-0201", + "mid": "0xyhhr", + "ota": False, + "supportVer": {"Android": "1.1.5", "IOS": "1.1.5"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "Briefly press the RESET button for 1 second and then release. You will hear that DEEBOT is ready for network setup.", + "retryText": "", + "confirmText": "I've heard the sound", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d3aa309ba13eb00013feb69", + }, + { + "groupId": "5acb0f2e7c295c0001876eb4", + "sort": 110, + "groupName": "DEEBOT 600 Series", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "702-0000-0170", + "mid": "dl8fht", + "ota": False, + "supportVer": {"Android": "1.0.0", "IOS": "1.0.0"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8e36591fd5d0001892541", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b8cd75f76f7f10001e9a0de", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5acc32067c295c0001876eea", + }, + { + "groupId": "5acb0f2e7c295c0001876eb4", + "sort": 110, + "groupName": "DEEBOT 661", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "702-0000-0170", + "mid": "dl8fht", + "ota": False, + "supportVer": {"Android": "1.0.0", "IOS": "1.0.0"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8e36591fd5d0001892541", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b8cd75f76f7f10001e9a0de", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d280ce3350e7a0001e84c95", + }, + { + "groupId": "5ca31ce8e9e9270001354b33", + "sort": 170, + "groupName": "DEEBOT M86", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "SPA", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1628-0101", + "mid": "129", + "ota": False, + "supportVer": {"Android": "1.1.7", "IOS": "1.1.7"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ca31df112851900016858c0", + }, + { + "groupId": "5d2460f244af360001383992", + "sort": 9, + "groupName": "DEEBOT U3", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "939393939393", + "mid": "xb83mv", + "ota": False, + "supportVer": {"Android": "1.1.8", "IOS": "1.1.8"}, + "smartTypes": ["MQ_AP"], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "Briefly press the RESET button for 1 second and then release. You will hear that DEEBOT is ready for network setup.", + "retryText": "", + "confirmText": "I've heard the sound", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d3fe649de51dd0001fee0de", + }, + { + "groupId": "5d2460f244af360001383992", + "sort": 9, + "groupName": "DEEBOT U3 LINE FRIENDS", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "939393939393", + "mid": "xb83mv", + "ota": False, + "supportVer": {"Android": "1.1.8", "IOS": "1.1.8"}, + "smartTypes": ["MQ_AP"], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "Briefly press the RESET button for 1 second and then release. You will hear that DEEBOT is ready for network setup.", + "retryText": "", + "confirmText": "I've heard the sound", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5da834a8d66cd10001f58265", + }, + { + "groupId": "5ca1c9a3e9e9270001354b2b", + "sort": 240, + "groupName": "DEEBOT Mini2", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "SPA", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1640-0101", + "mid": "142", + "ota": False, + "supportVer": {"Android": "1.1.7", "IOS": "1.1.7"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ca1ca79e9e9270001354b2d", + }, + { + "groupId": "5ca31e8212851900016858c2", + "sort": 140, + "groupName": "DEEBOT N79", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "SPA", + "failCount": 0, + "checkTips": 0, + "materialNo": "702-0000-0136", + "mid": "126", + "ota": False, + "supportVer": {"Android": "1.1.7", "IOS": "1.1.7"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ca32ab2e9e9270001354b3d", + }, + { + "groupId": "5ca31e8212851900016858c2", + "sort": 140, + "groupName": "DEEBOT N79S/SE", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "SPA", + "failCount": 0, + "checkTips": 0, + "materialNo": "702-0000-0136", + "mid": "126", + "ota": False, + "supportVer": {"Android": "1.1.7", "IOS": "1.1.7"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5cd4ca505b032200015a455d", + }, + { + "groupId": "5ca31e8212851900016858c2", + "sort": 140, + "groupName": "DEEBOT N79T/W", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "SPA", + "failCount": 0, + "checkTips": 0, + "materialNo": "702-0000-0136", + "mid": "126", + "ota": False, + "supportVer": {"Android": "1.1.7", "IOS": "1.1.7"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ca32a1012851900016858c6", + }, + { + "groupId": "5ae197be7ccd1a0001e1f6a1", + "sort": 120, + "groupName": "DEEBOT 711", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "702-0000-0205", + "mid": "uv242z", + "ota": False, + "supportVer": {"Android": "1.0.5", "IOS": "1.0.5"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b7fc85976f7f10001e9a0d0", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8e5d5d85b4d0001776484", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5b5ac4cc8d5a56000111e769", + }, + { + "groupId": "5ae197be7ccd1a0001e1f6a1", + "sort": 120, + "groupName": "DEEBOT 710", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "702-0000-0205", + "mid": "uv242z", + "ota": False, + "supportVer": {"Android": "1.0.5", "IOS": "1.0.5"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b7fc85976f7f10001e9a0d0", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8e5d5d85b4d0001776484", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5b5ac4e45f21100001882bb9", + }, + { + "groupId": "5ae197be7ccd1a0001e1f6a1", + "sort": 120, + "groupName": "DEEBOT 715", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "702-0000-0205", + "mid": "uv242z", + "ota": False, + "supportVer": {"Android": "1.0.5", "IOS": "1.0.5"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b7fc85976f7f10001e9a0d0", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8e5d5d85b4d0001776484", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5b7b65f176f7f10001e9a0c2", + }, + { + "groupId": "5ae197be7ccd1a0001e1f6a1", + "sort": 120, + "groupName": "DEEBOT 711s", + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "702-0000-0205", + "mid": "uv242z", + "ota": False, + "supportVer": {"Android": "1.0.5", "IOS": "1.0.5"}, + "smartTypes": [], + "steps": [ + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b7fc85976f7f10001e9a0d0", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8e5d5d85b4d0001776484", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d157f9f77a3a60001051f69", + }, + ], + "msg": "success", + "configFAQ": { + "wifiFAQUrl": "https://portal-ww.ecouser.net/api/pim/wififaq.html?lang=en&defaultLang=en", + "notFoundAPUrl": "https://portal-ww.ecouser.net/api/pim/findWifi.html?lang=en&defaultLang=en", + "configFailedUrl": "https://portal-ww.ecouser.net/api/pim/configfail.html?lang=en&defaultLang=en", + "contactUS": "helper", }, - { - "groupId": "5ca32b5de9e9270001354b3f", - "sort": 80, - "groupName": "DEEBOT OZMO 601", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "SPA", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1629-0203", - "mid": "159", - "ota": False, - "supportVer": { - "Android": "1.1.7", - "IOS": "1.1.7" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d4b7606de51dd0001fee12d" - }, - { - "groupId": "5ca32b5de9e9270001354b3f", - "sort": 80, - "groupName": "DEEBOT", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "SPA", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1629-0203", - "mid": "159", - "ota": False, - "supportVer": { - "Android": "1.1.7", - "IOS": "1.1.7" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d4b7628de51dd0001fee12f" - }, - { - "groupId": "5ca32b5de9e9270001354b3f", - "sort": 80, - "groupName": "DEEBOT OZMO 610 Series", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "SPA", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1629-0203", - "mid": "159", - "ota": False, - "supportVer": { - "Android": "1.1.7", - "IOS": "1.1.7" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d4b7640de51dd0001fee131" - }, - { - "groupId": "5cae9662e9e9270001354b55", - "sort": 150, - "groupName": "DEEBOT M80 Pro", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "SPA", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1638-0102", - "mid": "125", - "ota": False, - "supportVer": { - "Android": "1.1.7", - "IOS": "1.1.7" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c14414d60de0001eaf1f2" - }, - { - "groupId": "5bbedcd922d57f00018c13b6", - "sort": 30, - "groupName": "DEEBOT OZMO/PRO 930 Series", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "HK_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1803-0101", - "mid": "115", - "ota": False, - "supportVer": { - "Android": "1.1.8", - "IOS": "1.1.8" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5cf711aeb0acfc000179ff8a" - }, - { - "groupId": "5b6560760506b100015c8867", - "sort": 130, - "groupName": "DEEBOT 900 Series", - "isPopular": False, - "belongApp": [ - "ecoglobal", - "ecodeebot" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1711-0201", - "mid": "ls1ok3", - "ota": False, - "supportVer": { - "Android": "1.0.7", - "IOS": "1.0.7" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ba4a2cb6c2f120001c32839" - }, - { - "groupId": "5b6560760506b100015c8867", - "sort": 130, - "groupName": "DEEBOT 910", - "isPopular": False, - "belongApp": [ - "ecoglobal", - "ecodeebot" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1711-0201", - "mid": "ls1ok3", - "ota": False, - "supportVer": { - "Android": "1.0.7", - "IOS": "1.0.7" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5c778731280fda0001770ba0" - }, - { - "groupId": "5cae9793e9e9270001354b57", - "sort": 160, - "groupName": "DEEBOT M81 Pro", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "SPA", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1638-0101", - "mid": "141", - "ota": False, - "supportVer": { - "Android": "1.1.7", - "IOS": "1.1.7" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c2aa64d60de0001eaf1f6" - }, - { - "groupId": "5c19a835a1e6ee0001782245", - "sort": 20, - "groupName": "DEEBOT OZMO 920 Series", - "isPopular": True, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 1, - "materialNo": "110-1819-0101", - "mid": "vi829v", - "ota": False, - "supportVer": { - "Android": "1.1.5", - "IOS": "1.1.5" - }, - "smartTypes": [ - "MQ_AP" - ], - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "Briefly press the RESET button for 1 second and then release. You will hear that DEEBOT is ready for network setup.", - "retryText": "", - "confirmText": "I've heard the sound", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5c9c7995e9e9270001354ab4" - }, - { - "groupId": "5bc8187422d57f00018c13ba", - "sort": 50, - "groupName": "DEEBOT OZMO 960", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1803-0101", - "mid": "gd4uut", - "ota": False, - "supportVer": { - "Android": "1.1.5", - "IOS": "1.1.5" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5c7384767b93c700013f12e7" - }, - { - "groupId": "5b04bf1d7ccd1a0001e1f6a6", - "sort": 10, - "groupName": "DEEBOT OZMO 900 Series", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1810-0101", - "mid": "y79a7u", - "ota": False, - "supportVer": { - "Android": "1.0.0", - "IOS": "1.0.0" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5b04c0217ccd1a0001e1f6a7" - }, - { - "groupId": "5b04bf1d7ccd1a0001e1f6a6", - "sort": 10, - "groupName": "DEEBOT OZMO 905", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1810-0101", - "mid": "y79a7u", - "ota": False, - "supportVer": { - "Android": "1.0.0", - "IOS": "1.0.0" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d1474632a6bd50001b5b6f3" - }, - { - "groupId": "5c763de8280fda0001770b7f", - "sort": 100, - "groupName": "DEEBOT 500", - "isPopular": True, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "702-0000-0163", - "mid": "vsc5ia", - "ota": False, - "supportVer": { - "Android": "1.1.5", - "IOS": "1.1.5" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5df9d6b7c783810001d3d06b", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5dfc34d15d21490001700e14", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5c874326280fda0001770d2a" - }, - { - "groupId": "5c763de8280fda0001770b7f", - "sort": 100, - "groupName": "DEEBOT 501", - "isPopular": True, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "702-0000-0163", - "mid": "vsc5ia", - "ota": False, - "supportVer": { - "Android": "1.1.5", - "IOS": "1.1.5" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5df9d6b7c783810001d3d06b", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5dfc34d15d21490001700e14", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5c931fef280fda0001770d7e" - }, - { - "groupId": "5c763de8280fda0001770b7f", - "sort": 100, - "groupName": "DEEBOT 502", - "isPopular": True, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "702-0000-0163", - "mid": "vsc5ia", - "ota": False, - "supportVer": { - "Android": "1.1.5", - "IOS": "1.1.5" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5df9d6b7c783810001d3d06b", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5dfc34d15d21490001700e14", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5c93204b63023c0001e7faa7" - }, - { - "groupId": "5c763de8280fda0001770b7f", - "sort": 100, - "groupName": "DEEBOT 505", - "isPopular": True, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "702-0000-0163", - "mid": "vsc5ia", - "ota": False, - "supportVer": { - "Android": "1.1.5", - "IOS": "1.1.5" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5df9d6b7c783810001d3d06b", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5dfc34d15d21490001700e14", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5c932067280fda0001770d7f" - }, - { - "groupId": "5cae9aa5e9e9270001354b5d", - "sort": 230, - "groupName": "DEEBOT Slim2 Series", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "SPA", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1639-0102", - "mid": "123", - "ota": False, - "supportVer": { - "Android": "1.1.7", - "IOS": "1.1.7" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c150dba13eb00013feaae" - }, - { - "groupId": "5cae9aa5e9e9270001354b5d", - "sort": 230, - "groupName": "DEEBOT Slim Neo", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "SPA", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1639-0102", - "mid": "123", - "ota": False, - "supportVer": { - "Android": "1.1.7", - "IOS": "1.1.7" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c152f4d60de0001eaf1f4" - }, - { - "groupId": "5c19a8a0ddfc1f0001ede8e0", - "sort": 40, - "groupName": "DEEBOT OZMO 950 Series", - "isPopular": True, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 1, - "materialNo": "110-1820-0101", - "mid": "yna5xi", - "ota": False, - "supportVer": { - "Android": "1.1.5", - "IOS": "1.1.5" - }, - "smartTypes": [ - "MQ_AP" - ], - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "Briefly press the RESET button for 1 second and then release. You will hear that DEEBOT is ready for network setup.", - "retryText": "", - "confirmText": "I've heard the sound", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5caafd7e1285190001685965" - }, - { - "groupId": "5ca4711412851900016858cb", - "sort": 70, - "groupName": "DEEBOT OZMO 700", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 1, - "materialNo": "110-1825-0201", - "mid": "0xyhhr", - "ota": False, - "supportVer": { - "Android": "1.1.5", - "IOS": "1.1.5" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "Briefly press the RESET button for 1 second and then release. You will hear that DEEBOT is ready for network setup.", - "retryText": "", - "confirmText": "I've heard the sound", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d117d4f0ac6ad00012b792d" - }, - { - "groupId": "5ca4711412851900016858cb", - "sort": 70, - "groupName": "DEEBOT OZMO 750", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 1, - "materialNo": "110-1825-0201", - "mid": "0xyhhr", - "ota": False, - "supportVer": { - "Android": "1.1.5", - "IOS": "1.1.5" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "Briefly press the RESET button for 1 second and then release. You will hear that DEEBOT is ready for network setup.", - "retryText": "", - "confirmText": "I've heard the sound", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d3aa309ba13eb00013feb69" - }, - { - "groupId": "5acb0f2e7c295c0001876eb4", - "sort": 110, - "groupName": "DEEBOT 600 Series", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "702-0000-0170", - "mid": "dl8fht", - "ota": False, - "supportVer": { - "Android": "1.0.0", - "IOS": "1.0.0" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8e36591fd5d0001892541", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b8cd75f76f7f10001e9a0de", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5acc32067c295c0001876eea" - }, - { - "groupId": "5acb0f2e7c295c0001876eb4", - "sort": 110, - "groupName": "DEEBOT 661", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "702-0000-0170", - "mid": "dl8fht", - "ota": False, - "supportVer": { - "Android": "1.0.0", - "IOS": "1.0.0" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8e36591fd5d0001892541", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b8cd75f76f7f10001e9a0de", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d280ce3350e7a0001e84c95" - }, - { - "groupId": "5ca31ce8e9e9270001354b33", - "sort": 170, - "groupName": "DEEBOT M86", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "SPA", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1628-0101", - "mid": "129", - "ota": False, - "supportVer": { - "Android": "1.1.7", - "IOS": "1.1.7" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ca31df112851900016858c0" - }, - { - "groupId": "5d2460f244af360001383992", - "sort": 9, - "groupName": "DEEBOT U3", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "939393939393", - "mid": "xb83mv", - "ota": False, - "supportVer": { - "Android": "1.1.8", - "IOS": "1.1.8" - }, - "smartTypes": [ - "MQ_AP" - ], - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "Briefly press the RESET button for 1 second and then release. You will hear that DEEBOT is ready for network setup.", - "retryText": "", - "confirmText": "I've heard the sound", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d3fe649de51dd0001fee0de" - }, - { - "groupId": "5d2460f244af360001383992", - "sort": 9, - "groupName": "DEEBOT U3 LINE FRIENDS", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "939393939393", - "mid": "xb83mv", - "ota": False, - "supportVer": { - "Android": "1.1.8", - "IOS": "1.1.8" - }, - "smartTypes": [ - "MQ_AP" - ], - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "Briefly press the RESET button for 1 second and then release. You will hear that DEEBOT is ready for network setup.", - "retryText": "", - "confirmText": "I've heard the sound", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5da834a8d66cd10001f58265" - }, - { - "groupId": "5ca1c9a3e9e9270001354b2b", - "sort": 240, - "groupName": "DEEBOT Mini2", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "SPA", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1640-0101", - "mid": "142", - "ota": False, - "supportVer": { - "Android": "1.1.7", - "IOS": "1.1.7" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ca1ca79e9e9270001354b2d" - }, - { - "groupId": "5ca31e8212851900016858c2", - "sort": 140, - "groupName": "DEEBOT N79", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "SPA", - "failCount": 0, - "checkTips": 0, - "materialNo": "702-0000-0136", - "mid": "126", - "ota": False, - "supportVer": { - "Android": "1.1.7", - "IOS": "1.1.7" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ca32ab2e9e9270001354b3d" - }, - { - "groupId": "5ca31e8212851900016858c2", - "sort": 140, - "groupName": "DEEBOT N79S/SE", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "SPA", - "failCount": 0, - "checkTips": 0, - "materialNo": "702-0000-0136", - "mid": "126", - "ota": False, - "supportVer": { - "Android": "1.1.7", - "IOS": "1.1.7" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5cd4ca505b032200015a455d" - }, - { - "groupId": "5ca31e8212851900016858c2", - "sort": 140, - "groupName": "DEEBOT N79T/W", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "SPA", - "failCount": 0, - "checkTips": 0, - "materialNo": "702-0000-0136", - "mid": "126", - "ota": False, - "supportVer": { - "Android": "1.1.7", - "IOS": "1.1.7" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ca32a1012851900016858c6" - }, - { - "groupId": "5ae197be7ccd1a0001e1f6a1", - "sort": 120, - "groupName": "DEEBOT 711", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "702-0000-0205", - "mid": "uv242z", - "ota": False, - "supportVer": { - "Android": "1.0.5", - "IOS": "1.0.5" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b7fc85976f7f10001e9a0d0", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8e5d5d85b4d0001776484", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5b5ac4cc8d5a56000111e769" - }, - { - "groupId": "5ae197be7ccd1a0001e1f6a1", - "sort": 120, - "groupName": "DEEBOT 710", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "702-0000-0205", - "mid": "uv242z", - "ota": False, - "supportVer": { - "Android": "1.0.5", - "IOS": "1.0.5" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b7fc85976f7f10001e9a0d0", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8e5d5d85b4d0001776484", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5b5ac4e45f21100001882bb9" - }, - { - "groupId": "5ae197be7ccd1a0001e1f6a1", - "sort": 120, - "groupName": "DEEBOT 715", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "702-0000-0205", - "mid": "uv242z", - "ota": False, - "supportVer": { - "Android": "1.0.5", - "IOS": "1.0.5" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b7fc85976f7f10001e9a0d0", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8e5d5d85b4d0001776484", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5b7b65f176f7f10001e9a0c2" - }, - { - "groupId": "5ae197be7ccd1a0001e1f6a1", - "sort": 120, - "groupName": "DEEBOT 711s", - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "702-0000-0205", - "mid": "uv242z", - "ota": False, - "supportVer": { - "Android": "1.0.5", - "IOS": "1.0.5" - }, - "smartTypes": [], - "steps": [ - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b7fc85976f7f10001e9a0d0", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8e5d5d85b4d0001776484", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d157f9f77a3a60001051f69" - } - ], - "msg": "success", - "configFAQ": { - "wifiFAQUrl": "https://portal-ww.ecouser.net/api/pim/wififaq.html?lang=en&defaultLang=en", - "notFoundAPUrl": "https://portal-ww.ecouser.net/api/pim/findWifi.html?lang=en&defaultLang=en", - "configFailedUrl": "https://portal-ww.ecouser.net/api/pim/configfail.html?lang=en&defaultLang=en", - "contactUS": "helper" - } } configGroupsResponse = { - "code": 0, - "data": [ - { - "sort": 1, - "id": "5c19a743e916ba00019a4e32", - "name": "DEEBOT OZMO", - "robots": [ + "code": 0, + "data": [ { - "groupId": "5d2460f244af360001383992", - "category": "", - "groupName": "DEEBOT U3", - "sort": 9, - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "939393939393", - "mid": "xb83mv", - "ota": False, - "supportVer": { - "Android": "1.1.8", - "IOS": "1.1.8" - }, - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "Briefly press the RESET button for 1 second and then release. You will hear that DEEBOT is ready for network setup.", - "retryText": "", - "confirmText": "I've heard the sound", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d3fe66cea1a2e0001d2f243", - "products": [ - "5d246180350e7a0001e84bea", - "5d78f4e878d8b60001e23edc" - ], - "smartTypes": [ - "MQ_AP", - "MQ_AP" - ], - "seriesId": "5c19a743e916ba00019a4e32" + "sort": 1, + "id": "5c19a743e916ba00019a4e32", + "name": "DEEBOT OZMO", + "robots": [ + { + "groupId": "5d2460f244af360001383992", + "category": "", + "groupName": "DEEBOT U3", + "sort": 9, + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "939393939393", + "mid": "xb83mv", + "ota": False, + "supportVer": {"Android": "1.1.8", "IOS": "1.1.8"}, + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "Briefly press the RESET button for 1 second and then release. You will hear that DEEBOT is ready for network setup.", + "retryText": "", + "confirmText": "I've heard the sound", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d3fe66cea1a2e0001d2f243", + "products": [ + "5d246180350e7a0001e84bea", + "5d78f4e878d8b60001e23edc", + ], + "smartTypes": ["MQ_AP", "MQ_AP"], + "seriesId": "5c19a743e916ba00019a4e32", + }, + { + "groupId": "5b04bf1d7ccd1a0001e1f6a6", + "category": "", + "groupName": "DEEBOT OZMO 900 Series", + "sort": 10, + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1810-0101", + "mid": "y79a7u", + "ota": False, + "supportVer": {"Android": "1.0.0", "IOS": "1.0.0"}, + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d1472a62a6bd50001b5b6f2", + "products": [ + "5b04c0227ccd1a0001e1f6a8", + "5d1474630ac6ad00012b7940", + ], + "smartTypes": [], + "seriesId": "5c19a743e916ba00019a4e32", + }, + { + "groupId": "5c19a835a1e6ee0001782245", + "category": "", + "groupName": "DEEBOT OZMO 920 Series", + "sort": 20, + "isPopular": True, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 1, + "materialNo": "110-1819-0101", + "mid": "vi829v", + "ota": False, + "supportVer": {"Android": "1.1.5", "IOS": "1.1.5"}, + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "Briefly press the RESET button for 1 second and then release. You will hear that DEEBOT is ready for network setup.", + "retryText": "", + "confirmText": "I've heard the sound", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5c9c79c1e9e9270001354ab5", + "products": ["5c19a8f3a1e6ee0001782247"], + "smartTypes": ["MQ_AP"], + "seriesId": "5c19a743e916ba00019a4e32", + }, + { + "groupId": "5bbedcd922d57f00018c13b6", + "category": "", + "groupName": "DEEBOT OZMO/PRO 930 Series", + "sort": 30, + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "HK_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1803-0101", + "mid": "115", + "ota": False, + "supportVer": {"Android": "1.1.8", "IOS": "1.1.8"}, + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5cf66652b0acfc000179ff83", + "products": [ + "5bbedd2822d57f00018c13b7", + "5cd4dd385b032200015a4561", + ], + "smartTypes": [], + "seriesId": "5c19a743e916ba00019a4e32", + }, + { + "groupId": "5c19a8a0ddfc1f0001ede8e0", + "category": "", + "groupName": "DEEBOT OZMO 950 Series", + "sort": 40, + "isPopular": True, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 1, + "materialNo": "110-1820-0101", + "mid": "yna5xi", + "ota": False, + "supportVer": {"Android": "1.1.5", "IOS": "1.1.5"}, + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "Briefly press the RESET button for 1 second and then release. You will hear that DEEBOT is ready for network setup.", + "retryText": "", + "confirmText": "I've heard the sound", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5caafd981285190001685966", + "products": ["5c19a91ca1e6ee000178224a"], + "smartTypes": ["MQ_AP"], + "seriesId": "5c19a743e916ba00019a4e32", + }, + { + "groupId": "5bc8187422d57f00018c13ba", + "category": "", + "groupName": "DEEBOT OZMO 960 Series", + "sort": 50, + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1803-0101", + "mid": "gd4uut", + "ota": False, + "supportVer": {"Android": "1.1.5", "IOS": "1.1.5"}, + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d0899b00ac6ad00012b78f7", + "products": ["5bc8189d68142800016a6937"], + "smartTypes": [], + "seriesId": "5c19a743e916ba00019a4e32", + }, + { + "groupId": "5ae147f27ccd1a0001e1f69c", + "category": "", + "groupName": "DEEBOT OZMO Slim10 Series", + "sort": 60, + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1715-0201", + "mid": "02uwxm", + "ota": False, + "supportVer": {"Android": "1.0.0", "IOS": "1.0.0"}, + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5b5ebc34822f0b00013a2e1a", + "products": ["5ae1481e7ccd1a0001e1f69e"], + "smartTypes": [], + "seriesId": "5c19a743e916ba00019a4e32", + }, + { + "groupId": "5ca4711412851900016858cb", + "category": "", + "groupName": "DEEBOT OZMO 700 Series", + "sort": 70, + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 1, + "materialNo": "110-1825-0201", + "mid": "0xyhhr", + "ota": False, + "supportVer": {"Android": "1.1.5", "IOS": "1.1.5"}, + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "Briefly press the RESET button for 1 second and then release. You will hear that DEEBOT is ready for network setup.", + "retryText": "", + "confirmText": "I've heard the sound", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d117d680ac6ad00012b792e", + "products": [ + "5ca4716312851900016858cd", + "5ce7870cd85b4d0001775db9", + ], + "smartTypes": [], + "seriesId": "5c19a743e916ba00019a4e32", + }, + { + "groupId": "5ca32b5de9e9270001354b3f", + "category": "", + "groupName": "DEEBOT OZMO 600 Series", + "sort": 80, + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "SPA", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1629-0203", + "mid": "159", + "ota": False, + "supportVer": {"Android": "1.1.7", "IOS": "1.1.7"}, + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d4b75a8ea1a2e0001d2f28e", + "products": [ + "5ca32bc2e9e9270001354b41", + "5cbd97b961526a00019799bd", + "5cae98d01285190001685974", + ], + "smartTypes": [], + "seriesId": "5c19a743e916ba00019a4e32", + }, + ], }, { - "groupId": "5b04bf1d7ccd1a0001e1f6a6", - "category": "", - "groupName": "DEEBOT OZMO 900 Series", - "sort": 10, - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1810-0101", - "mid": "y79a7u", - "ota": False, - "supportVer": { - "Android": "1.0.0", - "IOS": "1.0.0" - }, - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d1472a62a6bd50001b5b6f2", - "products": [ - "5b04c0227ccd1a0001e1f6a8", - "5d1474630ac6ad00012b7940" - ], - "smartTypes": [], - "seriesId": "5c19a743e916ba00019a4e32" + "sort": 2, + "id": "5c2599b6a1e6ee0001782328", + "name": "DEEBOT", + "robots": [ + { + "groupId": "5c763de8280fda0001770b7f", + "category": "", + "groupName": "DEEBOT 500 Series", + "sort": 100, + "isPopular": True, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "702-0000-0163", + "mid": "vsc5ia", + "ota": False, + "supportVer": {"Android": "1.1.5", "IOS": "1.1.5"}, + "steps": [ + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5df9d6b7c783810001d3d06b", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5dfc34d15d21490001700e14", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5cf6668bda73e90001dc3b98", + "products": [ + "5c763eba280fda0001770b81", + "5c763f35280fda0001770b84", + "5c763f63280fda0001770b88", + "5c763f8263023c0001e7f855", + ], + "smartTypes": [], + "seriesId": "5c2599b6a1e6ee0001782328", + }, + { + "groupId": "5acb0f2e7c295c0001876eb4", + "category": "", + "groupName": "DEEBOT 600 Series", + "sort": 110, + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "702-0000-0170", + "mid": "dl8fht", + "ota": False, + "supportVer": {"Android": "1.0.0", "IOS": "1.0.0"}, + "steps": [ + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8e36591fd5d0001892541", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b8cd75f76f7f10001e9a0de", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5cf6669ab0acfc000179ff85", + "products": [ + "5acb0fa87c295c0001876ecf", + "5d280ce344af3600013839ab", + ], + "smartTypes": [], + "seriesId": "5c2599b6a1e6ee0001782328", + }, + { + "groupId": "5ae197be7ccd1a0001e1f6a1", + "category": "", + "groupName": "DEEBOT 700 Series", + "sort": 120, + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "702-0000-0205", + "mid": "uv242z", + "ota": False, + "supportVer": {"Android": "1.0.5", "IOS": "1.0.5"}, + "steps": [ + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b7fc85976f7f10001e9a0d0", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "image", + "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8e5d5d85b4d0001776484", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5b5ec2b1822f0b00013a2e1c", + "products": [ + "5b43077b8bc457000140363e", + "5b5149b4ac0b87000148c128", + "5b7b65f364e1680001a08b54", + "5ceba1c6d85b4d0001776986", + ], + "smartTypes": [], + "seriesId": "5c2599b6a1e6ee0001782328", + }, + { + "groupId": "5b6560760506b100015c8867", + "category": "", + "groupName": "DEEBOT 900 Series", + "sort": 130, + "isPopular": False, + "belongApp": ["ecoglobal", "ecodeebot"], + "smartType": "MQ_AP", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1711-0201", + "mid": "ls1ok3", + "ota": False, + "supportVer": {"Android": "1.0.7", "IOS": "1.0.7"}, + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d1472b70ac6ad00012b793f", + "products": [ + "5b6561060506b100015c8868", + "5bf2596f23244a00013f2f13", + ], + "smartTypes": [], + "seriesId": "5c2599b6a1e6ee0001782328", + }, + { + "groupId": "5ca31e8212851900016858c2", + "category": "", + "groupName": "DEEBOT N79 Series", + "sort": 140, + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "SPA", + "failCount": 0, + "checkTips": 0, + "materialNo": "702-0000-0136", + "mid": "126", + "ota": False, + "supportVer": {"Android": "1.1.7", "IOS": "1.1.7"}, + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5cf666bdda73e90001dc3b9a", + "products": [ + "5ca32ab212851900016858c7", + "5cce893813afb7000195d6af", + "5ca32a11e9e9270001354b39", + ], + "smartTypes": [], + "seriesId": "5c2599b6a1e6ee0001782328", + }, + { + "groupId": "5cae9662e9e9270001354b55", + "category": "", + "groupName": "DEEBOT M80 Pro", + "sort": 150, + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "SPA", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1638-0102", + "mid": "125", + "ota": False, + "supportVer": {"Android": "1.1.7", "IOS": "1.1.7"}, + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c1000ba13eb00013feaa4", + "products": ["5cae9703128519000168596a"], + "smartTypes": [], + "seriesId": "5c2599b6a1e6ee0001782328", + }, + { + "groupId": "5cae9793e9e9270001354b57", + "category": "", + "groupName": "DEEBOT M81 Pro", + "sort": 160, + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "SPA", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1638-0101", + "mid": "141", + "ota": False, + "supportVer": {"Android": "1.1.7", "IOS": "1.1.7"}, + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c10224d60de0001eaf1ea", + "products": ["5cae97c9128519000168596f"], + "smartTypes": [], + "seriesId": "5c2599b6a1e6ee0001782328", + }, + { + "groupId": "5ca31ce8e9e9270001354b33", + "category": "", + "groupName": "DEEBOT M86", + "sort": 170, + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "SPA", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1628-0101", + "mid": "129", + "ota": False, + "supportVer": {"Android": "1.1.7", "IOS": "1.1.7"}, + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5cf66704da73e90001dc3b9b", + "products": ["5ca31df1e9e9270001354b35"], + "smartTypes": [], + "seriesId": "5c2599b6a1e6ee0001782328", + }, + { + "groupId": "5cd4df825b032200015a4567", + "category": "", + "groupName": "DEEBOT M87", + "sort": 180, + "isPopular": False, + "belongApp": ["ecodeebot"], + "smartType": "SCM0", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1517-1001", + "mid": "121", + "ota": False, + "supportVer": {"Android": "1.1.7", "IOS": "1.1.7"}, + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8ea21d85b4d0001776489", + "products": [], + "smartTypes": [], + "seriesId": "5c2599b6a1e6ee0001782328", + }, + { + "groupId": "5cd4dfc9f542e00001dc2df8", + "category": "", + "groupName": "DEEBOT M88", + "sort": 190, + "isPopular": False, + "belongApp": ["ecodeebot"], + "smartType": "SCM0", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1517-0701", + "mid": "107", + "ota": False, + "supportVer": {"Android": "1.1.7", "IOS": "1.1.7"}, + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8eaa5d85b4d000177648a", + "products": [], + "smartTypes": [], + "seriesId": "5c2599b6a1e6ee0001782328", + }, + { + "groupId": "5cd4e077f542e00001dc2dfb", + "category": "", + "groupName": "DEEBOT R95", + "sort": 200, + "isPopular": False, + "belongApp": ["ecodeebot"], + "smartType": "SCM0", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1412-0001", + "mid": "113", + "ota": False, + "supportVer": {"Android": "1.1.7", "IOS": "1.1.7"}, + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8eb7091fd5d000189254a", + "products": [], + "smartTypes": [], + "seriesId": "5c2599b6a1e6ee0001782328", + }, + { + "groupId": "5cd4e0b25b032200015a4569", + "category": "", + "groupName": "DEEBOT R96", + "sort": 210, + "isPopular": False, + "belongApp": ["ecodeebot"], + "smartType": "SCM0", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1510-0501", + "mid": "118", + "ota": False, + "supportVer": {"Android": "1.1.7", "IOS": "1.1.7"}, + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8ebd791fd5d000189254b", + "products": [], + "smartTypes": [], + "seriesId": "5c2599b6a1e6ee0001782328", + }, + { + "groupId": "5cd4e0e15b032200015a456c", + "category": "", + "groupName": "DEEBOT R98", + "sort": 220, + "isPopular": False, + "belongApp": ["ecodeebot"], + "smartType": "SCM0", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1510-0601", + "mid": "117", + "ota": False, + "supportVer": {"Android": "1.1.7", "IOS": "1.1.7"}, + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8ec13d85b4d000177648b", + "products": [], + "smartTypes": [], + "seriesId": "5c2599b6a1e6ee0001782328", + }, + { + "groupId": "5cae9aa5e9e9270001354b5d", + "category": "", + "groupName": "DEEBOT Slim Series", + "sort": 230, + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "SPA", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1639-0102", + "mid": "123", + "ota": False, + "supportVer": {"Android": "1.1.7", "IOS": "1.1.7"}, + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c11fa4d60de0001eaf1ef", + "products": [ + "5cae9b201285190001685977", + "5cd43b4cf542e00001dc2dec", + ], + "smartTypes": [], + "seriesId": "5c2599b6a1e6ee0001782328", + }, + { + "groupId": "5ca1c9a3e9e9270001354b2b", + "category": "", + "groupName": "DEEBOT Mini2", + "sort": 240, + "isPopular": False, + "belongApp": ["ecoglobal"], + "smartType": "SPA", + "failCount": 0, + "checkTips": 0, + "materialNo": "110-1640-0101", + "mid": "142", + "ota": False, + "supportVer": {"Android": "1.1.7", "IOS": "1.1.7"}, + "steps": [ + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + { + "guideImageType": "", + "guideImageUrl": "", + "title": "", + "guideText": "", + "retryText": "", + "confirmText": "", + "btnText": "", + }, + ], + "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ca1c901e9e9270001354b2a", + "products": ["5ca1ca7a12851900016858bd"], + "smartTypes": [], + "seriesId": "5c2599b6a1e6ee0001782328", + }, + ], }, - { - "groupId": "5c19a835a1e6ee0001782245", - "category": "", - "groupName": "DEEBOT OZMO 920 Series", - "sort": 20, - "isPopular": True, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 1, - "materialNo": "110-1819-0101", - "mid": "vi829v", - "ota": False, - "supportVer": { - "Android": "1.1.5", - "IOS": "1.1.5" - }, - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "Briefly press the RESET button for 1 second and then release. You will hear that DEEBOT is ready for network setup.", - "retryText": "", - "confirmText": "I've heard the sound", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5c9c79c1e9e9270001354ab5", - "products": [ - "5c19a8f3a1e6ee0001782247" - ], - "smartTypes": [ - "MQ_AP" - ], - "seriesId": "5c19a743e916ba00019a4e32" - }, - { - "groupId": "5bbedcd922d57f00018c13b6", - "category": "", - "groupName": "DEEBOT OZMO/PRO 930 Series", - "sort": 30, - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "HK_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1803-0101", - "mid": "115", - "ota": False, - "supportVer": { - "Android": "1.1.8", - "IOS": "1.1.8" - }, - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5cf66652b0acfc000179ff83", - "products": [ - "5bbedd2822d57f00018c13b7", - "5cd4dd385b032200015a4561" - ], - "smartTypes": [], - "seriesId": "5c19a743e916ba00019a4e32" - }, - { - "groupId": "5c19a8a0ddfc1f0001ede8e0", - "category": "", - "groupName": "DEEBOT OZMO 950 Series", - "sort": 40, - "isPopular": True, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 1, - "materialNo": "110-1820-0101", - "mid": "yna5xi", - "ota": False, - "supportVer": { - "Android": "1.1.5", - "IOS": "1.1.5" - }, - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "Briefly press the RESET button for 1 second and then release. You will hear that DEEBOT is ready for network setup.", - "retryText": "", - "confirmText": "I've heard the sound", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5caafd981285190001685966", - "products": [ - "5c19a91ca1e6ee000178224a" - ], - "smartTypes": [ - "MQ_AP" - ], - "seriesId": "5c19a743e916ba00019a4e32" - }, - { - "groupId": "5bc8187422d57f00018c13ba", - "category": "", - "groupName": "DEEBOT OZMO 960 Series", - "sort": 50, - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1803-0101", - "mid": "gd4uut", - "ota": False, - "supportVer": { - "Android": "1.1.5", - "IOS": "1.1.5" - }, - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d0899b00ac6ad00012b78f7", - "products": [ - "5bc8189d68142800016a6937" - ], - "smartTypes": [], - "seriesId": "5c19a743e916ba00019a4e32" - }, - { - "groupId": "5ae147f27ccd1a0001e1f69c", - "category": "", - "groupName": "DEEBOT OZMO Slim10 Series", - "sort": 60, - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1715-0201", - "mid": "02uwxm", - "ota": False, - "supportVer": { - "Android": "1.0.0", - "IOS": "1.0.0" - }, - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5b5ebc34822f0b00013a2e1a", - "products": [ - "5ae1481e7ccd1a0001e1f69e" - ], - "smartTypes": [], - "seriesId": "5c19a743e916ba00019a4e32" - }, - { - "groupId": "5ca4711412851900016858cb", - "category": "", - "groupName": "DEEBOT OZMO 700 Series", - "sort": 70, - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 1, - "materialNo": "110-1825-0201", - "mid": "0xyhhr", - "ota": False, - "supportVer": { - "Android": "1.1.5", - "IOS": "1.1.5" - }, - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "Briefly press the RESET button for 1 second and then release. You will hear that DEEBOT is ready for network setup.", - "retryText": "", - "confirmText": "I've heard the sound", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d117d680ac6ad00012b792e", - "products": [ - "5ca4716312851900016858cd", - "5ce7870cd85b4d0001775db9" - ], - "smartTypes": [], - "seriesId": "5c19a743e916ba00019a4e32" - }, - { - "groupId": "5ca32b5de9e9270001354b3f", - "category": "", - "groupName": "DEEBOT OZMO 600 Series", - "sort": 80, - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "SPA", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1629-0203", - "mid": "159", - "ota": False, - "supportVer": { - "Android": "1.1.7", - "IOS": "1.1.7" - }, - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d4b75a8ea1a2e0001d2f28e", - "products": [ - "5ca32bc2e9e9270001354b41", - "5cbd97b961526a00019799bd", - "5cae98d01285190001685974" - ], - "smartTypes": [], - "seriesId": "5c19a743e916ba00019a4e32" - } - ] + ], + "msg": "success", + "configFAQ": { + "wifiFAQUrl": "https://portal-ww.ecouser.net/api/pim/wififaq.html?lang=en&defaultLang=en", + "notFoundAPUrl": "https://portal-ww.ecouser.net/api/pim/findWifi.html?lang=en&defaultLang=en", + "configFailedUrl": "https://portal-ww.ecouser.net/api/pim/configfail.html?lang=en&defaultLang=en", + "contactUS": "helper", }, - { - "sort": 2, - "id": "5c2599b6a1e6ee0001782328", - "name": "DEEBOT", - "robots": [ - { - "groupId": "5c763de8280fda0001770b7f", - "category": "", - "groupName": "DEEBOT 500 Series", - "sort": 100, - "isPopular": True, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "702-0000-0163", - "mid": "vsc5ia", - "ota": False, - "supportVer": { - "Android": "1.1.5", - "IOS": "1.1.5" - }, - "steps": [ - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5df9d6b7c783810001d3d06b", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5dfc34d15d21490001700e14", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5cf6668bda73e90001dc3b98", - "products": [ - "5c763eba280fda0001770b81", - "5c763f35280fda0001770b84", - "5c763f63280fda0001770b88", - "5c763f8263023c0001e7f855" - ], - "smartTypes": [], - "seriesId": "5c2599b6a1e6ee0001782328" - }, - { - "groupId": "5acb0f2e7c295c0001876eb4", - "category": "", - "groupName": "DEEBOT 600 Series", - "sort": 110, - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "702-0000-0170", - "mid": "dl8fht", - "ota": False, - "supportVer": { - "Android": "1.0.0", - "IOS": "1.0.0" - }, - "steps": [ - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8e36591fd5d0001892541", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b8cd75f76f7f10001e9a0de", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5cf6669ab0acfc000179ff85", - "products": [ - "5acb0fa87c295c0001876ecf", - "5d280ce344af3600013839ab" - ], - "smartTypes": [], - "seriesId": "5c2599b6a1e6ee0001782328" - }, - { - "groupId": "5ae197be7ccd1a0001e1f6a1", - "category": "", - "groupName": "DEEBOT 700 Series", - "sort": 120, - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "702-0000-0205", - "mid": "uv242z", - "ota": False, - "supportVer": { - "Android": "1.0.5", - "IOS": "1.0.5" - }, - "steps": [ - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b7fc85976f7f10001e9a0d0", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "image", - "guideImageUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8e5d5d85b4d0001776484", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5b5ec2b1822f0b00013a2e1c", - "products": [ - "5b43077b8bc457000140363e", - "5b5149b4ac0b87000148c128", - "5b7b65f364e1680001a08b54", - "5ceba1c6d85b4d0001776986" - ], - "smartTypes": [], - "seriesId": "5c2599b6a1e6ee0001782328" - }, - { - "groupId": "5b6560760506b100015c8867", - "category": "", - "groupName": "DEEBOT 900 Series", - "sort": 130, - "isPopular": False, - "belongApp": [ - "ecoglobal", - "ecodeebot" - ], - "smartType": "MQ_AP", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1711-0201", - "mid": "ls1ok3", - "ota": False, - "supportVer": { - "Android": "1.0.7", - "IOS": "1.0.7" - }, - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d1472b70ac6ad00012b793f", - "products": [ - "5b6561060506b100015c8868", - "5bf2596f23244a00013f2f13" - ], - "smartTypes": [], - "seriesId": "5c2599b6a1e6ee0001782328" - }, - { - "groupId": "5ca31e8212851900016858c2", - "category": "", - "groupName": "DEEBOT N79 Series", - "sort": 140, - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "SPA", - "failCount": 0, - "checkTips": 0, - "materialNo": "702-0000-0136", - "mid": "126", - "ota": False, - "supportVer": { - "Android": "1.1.7", - "IOS": "1.1.7" - }, - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5cf666bdda73e90001dc3b9a", - "products": [ - "5ca32ab212851900016858c7", - "5cce893813afb7000195d6af", - "5ca32a11e9e9270001354b39" - ], - "smartTypes": [], - "seriesId": "5c2599b6a1e6ee0001782328" - }, - { - "groupId": "5cae9662e9e9270001354b55", - "category": "", - "groupName": "DEEBOT M80 Pro", - "sort": 150, - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "SPA", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1638-0102", - "mid": "125", - "ota": False, - "supportVer": { - "Android": "1.1.7", - "IOS": "1.1.7" - }, - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c1000ba13eb00013feaa4", - "products": [ - "5cae9703128519000168596a" - ], - "smartTypes": [], - "seriesId": "5c2599b6a1e6ee0001782328" - }, - { - "groupId": "5cae9793e9e9270001354b57", - "category": "", - "groupName": "DEEBOT M81 Pro", - "sort": 160, - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "SPA", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1638-0101", - "mid": "141", - "ota": False, - "supportVer": { - "Android": "1.1.7", - "IOS": "1.1.7" - }, - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c10224d60de0001eaf1ea", - "products": [ - "5cae97c9128519000168596f" - ], - "smartTypes": [], - "seriesId": "5c2599b6a1e6ee0001782328" - }, - { - "groupId": "5ca31ce8e9e9270001354b33", - "category": "", - "groupName": "DEEBOT M86", - "sort": 170, - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "SPA", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1628-0101", - "mid": "129", - "ota": False, - "supportVer": { - "Android": "1.1.7", - "IOS": "1.1.7" - }, - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5cf66704da73e90001dc3b9b", - "products": [ - "5ca31df1e9e9270001354b35" - ], - "smartTypes": [], - "seriesId": "5c2599b6a1e6ee0001782328" - }, - { - "groupId": "5cd4df825b032200015a4567", - "category": "", - "groupName": "DEEBOT M87", - "sort": 180, - "isPopular": False, - "belongApp": [ - "ecodeebot" - ], - "smartType": "SCM0", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1517-1001", - "mid": "121", - "ota": False, - "supportVer": { - "Android": "1.1.7", - "IOS": "1.1.7" - }, - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8ea21d85b4d0001776489", - "products": [], - "smartTypes": [], - "seriesId": "5c2599b6a1e6ee0001782328" - }, - { - "groupId": "5cd4dfc9f542e00001dc2df8", - "category": "", - "groupName": "DEEBOT M88", - "sort": 190, - "isPopular": False, - "belongApp": [ - "ecodeebot" - ], - "smartType": "SCM0", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1517-0701", - "mid": "107", - "ota": False, - "supportVer": { - "Android": "1.1.7", - "IOS": "1.1.7" - }, - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8eaa5d85b4d000177648a", - "products": [], - "smartTypes": [], - "seriesId": "5c2599b6a1e6ee0001782328" - }, - { - "groupId": "5cd4e077f542e00001dc2dfb", - "category": "", - "groupName": "DEEBOT R95", - "sort": 200, - "isPopular": False, - "belongApp": [ - "ecodeebot" - ], - "smartType": "SCM0", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1412-0001", - "mid": "113", - "ota": False, - "supportVer": { - "Android": "1.1.7", - "IOS": "1.1.7" - }, - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8eb7091fd5d000189254a", - "products": [], - "smartTypes": [], - "seriesId": "5c2599b6a1e6ee0001782328" - }, - { - "groupId": "5cd4e0b25b032200015a4569", - "category": "", - "groupName": "DEEBOT R96", - "sort": 210, - "isPopular": False, - "belongApp": [ - "ecodeebot" - ], - "smartType": "SCM0", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1510-0501", - "mid": "118", - "ota": False, - "supportVer": { - "Android": "1.1.7", - "IOS": "1.1.7" - }, - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8ebd791fd5d000189254b", - "products": [], - "smartTypes": [], - "seriesId": "5c2599b6a1e6ee0001782328" - }, - { - "groupId": "5cd4e0e15b032200015a456c", - "category": "", - "groupName": "DEEBOT R98", - "sort": 220, - "isPopular": False, - "belongApp": [ - "ecodeebot" - ], - "smartType": "SCM0", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1510-0601", - "mid": "117", - "ota": False, - "supportVer": { - "Android": "1.1.7", - "IOS": "1.1.7" - }, - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ce8ec13d85b4d000177648b", - "products": [], - "smartTypes": [], - "seriesId": "5c2599b6a1e6ee0001782328" - }, - { - "groupId": "5cae9aa5e9e9270001354b5d", - "category": "", - "groupName": "DEEBOT Slim Series", - "sort": 230, - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "SPA", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1639-0102", - "mid": "123", - "ota": False, - "supportVer": { - "Android": "1.1.7", - "IOS": "1.1.7" - }, - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5d2c11fa4d60de0001eaf1ef", - "products": [ - "5cae9b201285190001685977", - "5cd43b4cf542e00001dc2dec" - ], - "smartTypes": [], - "seriesId": "5c2599b6a1e6ee0001782328" - }, - { - "groupId": "5ca1c9a3e9e9270001354b2b", - "category": "", - "groupName": "DEEBOT Mini2", - "sort": 240, - "isPopular": False, - "belongApp": [ - "ecoglobal" - ], - "smartType": "SPA", - "failCount": 0, - "checkTips": 0, - "materialNo": "110-1640-0101", - "mid": "142", - "ota": False, - "supportVer": { - "Android": "1.1.7", - "IOS": "1.1.7" - }, - "steps": [ - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - }, - { - "guideImageType": "", - "guideImageUrl": "", - "title": "", - "guideText": "", - "retryText": "", - "confirmText": "", - "btnText": "" - } - ], - "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ca1c901e9e9270001354b2a", - "products": [ - "5ca1ca7a12851900016858bd" - ], - "smartTypes": [], - "seriesId": "5c2599b6a1e6ee0001782328" - } - ] - } - ], - "msg": "success", - "configFAQ": { - "wifiFAQUrl": "https://portal-ww.ecouser.net/api/pim/wififaq.html?lang=en&defaultLang=en", - "notFoundAPUrl": "https://portal-ww.ecouser.net/api/pim/findWifi.html?lang=en&defaultLang=en", - "configFailedUrl": "https://portal-ww.ecouser.net/api/pim/configfail.html?lang=en&defaultLang=en", - "contactUS": "helper" - } } productConfigBatch = [ @@ -2717,9 +2416,9 @@ productConfigBatch = [ "video": False, "battery": True, "clean": True, - "charge": True + "charge": True, } - } + }, }, { "pid": "5e8e8d8a032edd8457c66bfb", @@ -2729,9 +2428,9 @@ productConfigBatch = [ "video": False, "battery": True, "clean": True, - "charge": True + "charge": True, } - } + }, }, { "pid": "5c19a91ca1e6ee000178224a", @@ -2741,9 +2440,9 @@ productConfigBatch = [ "video": False, "battery": True, "clean": True, - "charge": True + "charge": True, } - } + }, }, { "pid": "5e8e8d2a032edd3c03c66bf7", @@ -2753,9 +2452,9 @@ productConfigBatch = [ "video": False, "battery": True, "clean": True, - "charge": True + "charge": True, } - } + }, }, { "pid": "5de0d86ed88546000195239a", @@ -2765,9 +2464,9 @@ productConfigBatch = [ "video": True, "battery": True, "clean": True, - "charge": True + "charge": True, } - } + }, }, { "pid": "5c19a8f3a1e6ee0001782247", @@ -2777,9 +2476,9 @@ productConfigBatch = [ "video": False, "battery": True, "clean": True, - "charge": True + "charge": True, } - } + }, }, { "pid": "5e698a6306f6de52c264c61b", @@ -2789,9 +2488,9 @@ productConfigBatch = [ "video": False, "battery": True, "clean": True, - "charge": True + "charge": True, } - } + }, }, { "pid": "5e699a4106f6de83ea64c620", @@ -2801,9 +2500,9 @@ productConfigBatch = [ "video": False, "battery": True, "clean": True, - "charge": True + "charge": True, } - } + }, }, { "pid": "5edd998afdd6a30008da039b", @@ -2813,9 +2512,9 @@ productConfigBatch = [ "video": True, "battery": True, "clean": True, - "charge": True + "charge": True, } - } + }, }, { "pid": "5edd9a4075f2fc000636086c", @@ -2825,9 +2524,9 @@ productConfigBatch = [ "video": True, "battery": True, "clean": True, - "charge": True + "charge": True, } - } + }, }, { "pid": "5ed5e4d3a719ea460ec3216c", @@ -2837,9 +2536,9 @@ productConfigBatch = [ "video": False, "battery": True, "clean": True, - "charge": True + "charge": True, } - } + }, }, { "pid": "5f88195e6cf8de0008ed7c11", @@ -2849,9 +2548,9 @@ productConfigBatch = [ "video": False, "battery": True, "clean": True, - "charge": True + "charge": True, } - } + }, }, { "pid": "5f8819156cf8de0008ed7c0d", @@ -2861,9 +2560,9 @@ productConfigBatch = [ "video": False, "battery": True, "clean": True, - "charge": True + "charge": True, } - } + }, }, { "pid": "5fa105c6d16a99000667eb54", @@ -2873,8 +2572,8 @@ productConfigBatch = [ "video": False, "battery": True, "clean": True, - "charge": True + "charge": True, } - } - } + }, + }, ] diff --git a/bumper/plugins/bumper_confserver_portal_rapp.py b/bumper/plugins/bumper_confserver_portal_rapp.py index 1cd8627..44ca250 100644 --- a/bumper/plugins/bumper_confserver_portal_rapp.py +++ b/bumper/plugins/bumper_confserver_portal_rapp.py @@ -6,29 +6,27 @@ 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"), + 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 + 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" + "data": {"data": {"name": "My Home"}, "tag": None}, + "message": "success", } return web.json_response(body) @@ -36,4 +34,5 @@ class api_rapp(plugins.ConfServerApp): except Exception as e: logging.exception(f"{e}") + plugin = api_rapp() diff --git a/bumper/plugins/bumper_confserver_portal_users.py b/bumper/plugins/bumper_confserver_portal_users.py index 254fdbf..3c20182 100644 --- a/bumper/plugins/bumper_confserver_portal_users.py +++ b/bumper/plugins/bumper_confserver_portal_users.py @@ -10,20 +10,24 @@ from datetime import datetime, timedelta class portal_api_users(plugins.ConfServerApp): - def __init__(self): self.name = "portal_api_users" - self.plugin_type = "sub_api" + self.plugin_type = "sub_api" self.sub_api = "portal_api" - + self.routes = [ - - web.route("*", "/users/user.do", self.handle_usersapi, name="portal_api_users_user"), - + web.route( + "*", + "/users/user.do", + self.handle_usersapi, + name="portal_api_users_user", + ), ] - self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time - + self.get_milli_time = ( + bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + ) + async def handle_usersapi(self, request): if not request.method == "GET": # Skip GET for now try: @@ -116,5 +120,5 @@ class portal_api_users(plugins.ConfServerApp): body = {"result": "fail", "todo": "result"} return web.json_response(body) -plugin = portal_api_users() +plugin = portal_api_users() diff --git a/bumper/plugins/bumper_confserver_upload_global.py b/bumper/plugins/bumper_confserver_upload_global.py index 8f6fa11..ed7447e 100644 --- a/bumper/plugins/bumper_confserver_upload_global.py +++ b/bumper/plugins/bumper_confserver_upload_global.py @@ -9,32 +9,38 @@ from bumper import plugins from datetime import datetime, timedelta import os -class upload_global(plugins.ConfServerApp): +class upload_global(plugins.ConfServerApp): def __init__(self): self.name = "upload_global" - self.plugin_type = "sub_api" + self.plugin_type = "sub_api" self.sub_api = "upload_api" - - self.routes = [ - - web.route("*", "/global/{year}/{month}/{day}/{fileid}", self.handle_upload_global_file, name="upload_global_getFile"), + self.routes = [ + web.route( + "*", + "/global/{year}/{month}/{day}/{fileid}", + self.handle_upload_global_file, + name="upload_global_getFile", + ), ] - self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time - - + self.get_milli_time = ( + bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + ) async def handle_upload_global_file(self, request): try: fileID = request.match_info.get("id", "") - return web.FileResponse(os.path.join(bumper.bumper_dir,"bumper","web","images","robotvac_image.jpg")) - - except Exception as e: - logging.exception(f"{e}") + return web.FileResponse( + os.path.join( + bumper.bumper_dir, "bumper", "web", "images", "robotvac_image.jpg" + ) + ) + + except Exception as e: + logging.exception(f"{e}") + - - plugin = upload_global() diff --git a/bumper/plugins/bumper_confserver_v1_global_auth.py b/bumper/plugins/bumper_confserver_v1_global_auth.py index bbceb70..ffb94b5 100644 --- a/bumper/plugins/bumper_confserver_v1_global_auth.py +++ b/bumper/plugins/bumper_confserver_v1_global_auth.py @@ -10,19 +10,24 @@ from datetime import datetime, timedelta class v1_global_auth(plugins.ConfServerApp): - def __init__(self): self.name = "v1_global_auth" self.plugin_type = "sub_api" self.sub_api = "api_v1" - + authhandler = bumper.ConfServer.ConfServer_AuthHandler() self.routes = [ - web.route("*", "/global/auth/getAuthCode", authhandler.get_AuthCode, name="v1_global_auth_getAuthCode"), + web.route( + "*", + "/global/auth/getAuthCode", + authhandler.get_AuthCode, + name="v1_global_auth_getAuthCode", + ), ] - self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + self.get_milli_time = ( + bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + ) plugin = v1_global_auth() - diff --git a/bumper/plugins/bumper_confserver_v1_private_ad.py b/bumper/plugins/bumper_confserver_v1_private_ad.py index 530057c..000d85b 100644 --- a/bumper/plugins/bumper_confserver_v1_private_ad.py +++ b/bumper/plugins/bumper_confserver_v1_private_ad.py @@ -10,20 +10,30 @@ from datetime import datetime, timedelta class v1_private_ad(plugins.ConfServerApp): - def __init__(self): self.name = "v1_private_ad" - self.plugin_type = "sub_api" + self.plugin_type = "sub_api" self.sub_api = "api_v1" - - self.routes = [ - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/ad/getAdByPositionType", self.handle_getAdByPositionType, name="v1_ad_getAdByPositionType"), - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/ad/getBootScreen", self.handle_getBootScreen, name="v1_ad_getBootScreen"), + self.routes = [ + web.route( + "*", + "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/ad/getAdByPositionType", + self.handle_getAdByPositionType, + name="v1_ad_getAdByPositionType", + ), + web.route( + "*", + "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/ad/getBootScreen", + self.handle_getBootScreen, + name="v1_ad_getBootScreen", + ), ] - self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time - + self.get_milli_time = ( + bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + ) + async def handle_getAdByPositionType(self, request): # EcoVacs Home try: body = { @@ -54,5 +64,5 @@ class v1_private_ad(plugins.ConfServerApp): except Exception as e: logging.exception(f"{e}") -plugin = v1_private_ad() +plugin = v1_private_ad() diff --git a/bumper/plugins/bumper_confserver_v1_private_campaign.py b/bumper/plugins/bumper_confserver_v1_private_campaign.py index c388e25..da097a3 100644 --- a/bumper/plugins/bumper_confserver_v1_private_campaign.py +++ b/bumper/plugins/bumper_confserver_v1_private_campaign.py @@ -10,20 +10,24 @@ from datetime import datetime, timedelta class v1_private_campaign(plugins.ConfServerApp): - def __init__(self): self.name = "v1_private_campaign" - self.plugin_type = "sub_api" + self.plugin_type = "sub_api" self.sub_api = "api_v1" - + self.routes = [ - - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/campaign/homePageAlert", self.handle_homePageAlert, name="v1_campaign_homePageAlert"), - + web.route( + "*", + "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/campaign/homePageAlert", + self.handle_homePageAlert, + name="v1_campaign_homePageAlert", + ), ] - self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time - + self.get_milli_time = ( + bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + ) + async def handle_homePageAlert(self, request): try: nextAlert = self.get_milli_time( @@ -47,8 +51,7 @@ class v1_private_campaign(plugins.ConfServerApp): return web.json_response(body) except Exception as e: - logging.exception(f"{e}") - + logging.exception(f"{e}") + plugin = v1_private_campaign() - diff --git a/bumper/plugins/bumper_confserver_v1_private_common.py b/bumper/plugins/bumper_confserver_v1_private_common.py index ec2010d..9727828 100644 --- a/bumper/plugins/bumper_confserver_v1_private_common.py +++ b/bumper/plugins/bumper_confserver_v1_private_common.py @@ -8,25 +8,65 @@ from bumper.models import * class v1_private_common(plugins.ConfServerApp): - def __init__(self): self.name = "v1_private_common" self.plugin_type = "sub_api" self.sub_api = "api_v1" self.routes = [ - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/common/checkAPPVersion", self.handle_checkAPPVersion, name="v1_common_checkAppVersion"), - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/common/checkVersion", self.handle_checkVersion, name="v1_common_checkVersion"), - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/common/uploadDeviceInfo", self.handle_uploadDeviceInfo, name="v1_common_uploadDeviceInfo"), - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/common/getSystemReminder", self.handle_getSystemReminder, name="v1_common_getSystemReminder"), - 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/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", + ), ] - self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + self.get_milli_time = ( + bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + ) async def handle_checkVersion(self, request): try: @@ -115,11 +155,8 @@ class v1_private_common(plugins.ConfServerApp): async def handle_getConfig(self, request): try: data = [] - for key in request.query["keys"].split(','): - data.append({ - "key": key, - "value": "Y" - }) + for key in request.query["keys"].split(","): + data.append({"key": key, "value": "Y"}) body = { "code": bumper.RETURN_API_SUCCESS, @@ -160,7 +197,7 @@ class v1_private_common(plugins.ConfServerApp): "id": "20180804040641_7d746faf18b8cb22a50d145598fe4c90", "type": "USER", "url": "https://gl-eu-wap.ecovacs.com/content/agreement?id=20180804040641_7d746faf18b8cb22a50d145598fe4c90&language=EN", - "version": "1.03" + "version": "1.03", }, { "acceptTime": None, @@ -168,8 +205,8 @@ class v1_private_common(plugins.ConfServerApp): "id": "20180804040245_4e7c56dfb7ebd3b81b1f2747d0859fac", "type": "PRIVACY", "url": "https://gl-eu-wap.ecovacs.com/content/agreement?id=20180804040245_4e7c56dfb7ebd3b81b1f2747d0859fac&language=EN", - "version": "1.03" - } + "version": "1.03", + }, ], "msg": "操作成功", "success": True, @@ -186,9 +223,7 @@ class v1_private_common(plugins.ConfServerApp): time = self.get_milli_time(datetime.utcnow().timestamp()) body = { "code": bumper.RETURN_API_SUCCESS, - "data": { - "timestamp": time - }, + "data": {"timestamp": time}, "msg": "操作成功", "success": True, "time": time, @@ -202,261 +237,442 @@ class v1_private_common(plugins.ConfServerApp): 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"}]} +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_message.py b/bumper/plugins/bumper_confserver_v1_private_message.py index 596ad67..796a7f7 100644 --- a/bumper/plugins/bumper_confserver_v1_private_message.py +++ b/bumper/plugins/bumper_confserver_v1_private_message.py @@ -10,21 +10,30 @@ from datetime import datetime, timedelta class v1_private_message(plugins.ConfServerApp): - def __init__(self): self.name = "v1_private_message" - self.plugin_type = "sub_api" + self.plugin_type = "sub_api" self.sub_api = "api_v1" - + self.routes = [ - - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/message/hasUnreadMsg", self.handle_hasUnreadMessage, name="v1_message_hasUnreadMsg"), - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/message/getMsgList", self.handle_getMsgList, name="v1_message_getMsgList"), - + web.route( + "*", + "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/message/hasUnreadMsg", + self.handle_hasUnreadMessage, + name="v1_message_hasUnreadMsg", + ), + web.route( + "*", + "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/message/getMsgList", + self.handle_getMsgList, + name="v1_message_getMsgList", + ), ] - self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time - + self.get_milli_time = ( + bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + ) + async def handle_hasUnreadMessage(self, request): # EcoVacs Home try: body = { @@ -53,7 +62,7 @@ class v1_private_message(plugins.ConfServerApp): return web.json_response(body) except Exception as e: - logging.exception(f"{e}") + logging.exception(f"{e}") + plugin = v1_private_message() - diff --git a/bumper/plugins/bumper_confserver_v1_private_shop.py b/bumper/plugins/bumper_confserver_v1_private_shop.py index 4697b8c..855570a 100644 --- a/bumper/plugins/bumper_confserver_v1_private_shop.py +++ b/bumper/plugins/bumper_confserver_v1_private_shop.py @@ -10,20 +10,24 @@ from datetime import datetime, timedelta class v1_private_shop(plugins.ConfServerApp): - def __init__(self): self.name = "v1_private_shop" - self.plugin_type = "sub_api" + self.plugin_type = "sub_api" self.sub_api = "api_v1" - - self.routes = [ - - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/shop/getCnWapShopConfig", self.handle_getCnWapShopConfig, name="v1_shop_getCnWapShopConfig"), + self.routes = [ + web.route( + "*", + "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/shop/getCnWapShopConfig", + self.handle_getCnWapShopConfig, + name="v1_shop_getCnWapShopConfig", + ), ] - self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time - + self.get_milli_time = ( + bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + ) + async def handle_getCnWapShopConfig(self, request): # EcoVacs Home try: body = { @@ -42,7 +46,7 @@ class v1_private_shop(plugins.ConfServerApp): return web.json_response(body) except Exception as e: - logging.exception(f"{e}") + logging.exception(f"{e}") + plugin = v1_private_shop() - diff --git a/bumper/plugins/bumper_confserver_v1_private_user.py b/bumper/plugins/bumper_confserver_v1_private_user.py index 8787d5b..a69bb4e 100644 --- a/bumper/plugins/bumper_confserver_v1_private_user.py +++ b/bumper/plugins/bumper_confserver_v1_private_user.py @@ -10,34 +10,89 @@ from datetime import datetime, timedelta class v1_private_user(plugins.ConfServerApp): - def __init__(self): self.name = "v1_private_user" - self.plugin_type = "sub_api" + self.plugin_type = "sub_api" self.sub_api = "api_v1" authhandler = bumper.ConfServer.ConfServer_AuthHandler() self.routes = [ - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/login", authhandler.login, name="v1_user_login"), - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkLogin", authhandler.login, name="v1_user_checkLogin"), - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/getAuthCode", authhandler.get_AuthCode, name="v1_user_getAuthCode"), - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/logout", authhandler.logout, name="v1_user_logout"), - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkAgreement", self.handle_checkAgreement,name="v1_user_checkAgreement"), - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkAgreementBatch", self.handle_checkAgreement,name="v1_user_checkAgreementBatch"), - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/getUserAccountInfo", authhandler.getUserAccountInfo,name="v1_user_getUserAccountInfo"), - 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 - # /registerByEmail - + web.route( + "*", + "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/login", + authhandler.login, + name="v1_user_login", + ), + web.route( + "*", + "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkLogin", + authhandler.login, + name="v1_user_checkLogin", + ), + web.route( + "*", + "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/getAuthCode", + authhandler.get_AuthCode, + name="v1_user_getAuthCode", + ), + web.route( + "*", + "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/logout", + authhandler.logout, + name="v1_user_logout", + ), + web.route( + "*", + "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkAgreement", + self.handle_checkAgreement, + name="v1_user_checkAgreement", + ), + web.route( + "*", + "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkAgreementBatch", + self.handle_checkAgreement, + name="v1_user_checkAgreementBatch", + ), + web.route( + "*", + "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/getUserAccountInfo", + authhandler.getUserAccountInfo, + name="v1_user_getUserAccountInfo", + ), + 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 + # /registerByEmail ] - self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + self.get_milli_time = ( + bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + ) async def handle_checkAgreement(self, request): try: @@ -76,84 +131,82 @@ class v1_private_user(plugins.ConfServerApp): return web.json_response(body) except Exception as e: - logging.exception(f"{e}") + logging.exception(f"{e}") async def handle_getUserMenuInfo(self, request): try: apptype = request.match_info.get("apptype", "") body = { - "code": "0000", - "data": [ - { + "code": "0000", + "data": [ + { "menuItems": [ { - "clickAction": 1, - "clickUri": "https://ecovacs.zendesk.com/hc/en-us", - "menuIconUrl": "https://gl-us-pub.ecovacs.com/upload/global/2019/12/16/2019121603180741b73907046e742b80e8fe4a90fe2498.png", - "menuId": "20191216031849_4d744630f7ad2f5208a4b8051be61d10", - "menuName": "Help & Feedback", - "paramsJson": "" + "clickAction": 1, + "clickUri": "https://ecovacs.zendesk.com/hc/en-us", + "menuIconUrl": "https://gl-us-pub.ecovacs.com/upload/global/2019/12/16/2019121603180741b73907046e742b80e8fe4a90fe2498.png", + "menuId": "20191216031849_4d744630f7ad2f5208a4b8051be61d10", + "menuName": "Help & Feedback", + "paramsJson": "", } ], - "menuPositionKey": "A_FIRST" - }, - { + "menuPositionKey": "A_FIRST", + }, + { "menuItems": [ { - "clickAction": 3, - "clickUri": "robotShare", - "menuIconUrl": "https://gl-us-pub.ecovacs.com/upload/global/2019/12/16/2019121603284185e632ec6c5da10bd82119d7047a1f9e.png", - "menuId": "20191216032853_5fac4cc9cbd0e166dfa951485d1d8cc4", - "menuName": "Share Robot", - "paramsJson": "" + "clickAction": 3, + "clickUri": "robotShare", + "menuIconUrl": "https://gl-us-pub.ecovacs.com/upload/global/2019/12/16/2019121603284185e632ec6c5da10bd82119d7047a1f9e.png", + "menuId": "20191216032853_5fac4cc9cbd0e166dfa951485d1d8cc4", + "menuName": "Share Robot", + "paramsJson": "", } ], - "menuPositionKey": "B_SECOND" - }, - { + "menuPositionKey": "B_SECOND", + }, + { "menuItems": [ { - "clickAction": 3, - "clickUri": "config", - "menuIconUrl": "https://gl-us-pub.ecovacs.com/upload/global/2019/12/16/201912160325324068da4e4a09b8c3973db162e84784d5.png", - "menuId": "20191216032545_ebea0fbb4cb02d9c2fec5bdf3371bc2d", - "menuName": "Settings", - "paramsJson": "" + "clickAction": 3, + "clickUri": "config", + "menuIconUrl": "https://gl-us-pub.ecovacs.com/upload/global/2019/12/16/201912160325324068da4e4a09b8c3973db162e84784d5.png", + "menuId": "20191216032545_ebea0fbb4cb02d9c2fec5bdf3371bc2d", + "menuName": "Settings", + "paramsJson": "", } ], - "menuPositionKey": "C_THIRD" - }, - { + "menuPositionKey": "C_THIRD", + }, + { "menuItems": [ { - "clickAction": 1, - "clickUri": "https://bumper.ecovacs.com/", - "menuIconUrl": "https://gl-us-pub.ecovacs.com/upload/global/2019/12/16/201912160325324068da4e4a09b8c3973db162e84784d5.png", - "menuId": "20191216032545_ebea0fbb4cb02d9c2fec5bdf3371bc2c", - "menuName": "Bumper Status", - "paramsJson": "" + "clickAction": 1, + "clickUri": "https://bumper.ecovacs.com/", + "menuIconUrl": "https://gl-us-pub.ecovacs.com/upload/global/2019/12/16/201912160325324068da4e4a09b8c3973db162e84784d5.png", + "menuId": "20191216032545_ebea0fbb4cb02d9c2fec5bdf3371bc2c", + "menuName": "Bumper Status", + "paramsJson": "", } ], - "menuPositionKey": "D_FOURTH" - } - ], - "msg": "操作成功", - "success": True, - "time": self.get_milli_time(datetime.utcnow().timestamp()) - } + "menuPositionKey": "D_FOURTH", + }, + ], + "msg": "操作成功", + "success": True, + "time": self.get_milli_time(datetime.utcnow().timestamp()), + } return web.json_response(body) except Exception as e: - logging.exception(f"{e}") + logging.exception(f"{e}") async def handle_changeArea(self, request): try: body = { "code": bumper.RETURN_API_SUCCESS, - "data": { - "isNeedReLogin": "N" - }, + "data": {"isNeedReLogin": "N"}, "msg": "操作成功", "success": True, "time": self.get_milli_time(datetime.utcnow().timestamp()), @@ -179,5 +232,5 @@ class v1_private_user(plugins.ConfServerApp): except Exception as e: logging.exception(f"{e}") -plugin = v1_private_user() +plugin = v1_private_user() diff --git a/bumper/plugins/bumper_confserver_v1_private_userSetting.py b/bumper/plugins/bumper_confserver_v1_private_userSetting.py index 9822545..2e78594 100644 --- a/bumper/plugins/bumper_confserver_v1_private_userSetting.py +++ b/bumper/plugins/bumper_confserver_v1_private_userSetting.py @@ -7,18 +7,24 @@ from datetime import datetime class v1_private_userSetting(plugins.ConfServerApp): - def __init__(self): self.name = "v1_private_userSetting" - 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}/userSetting/getSuggestionSetting", self.handle_getSuggestionSetting,name="v1_userSetting_getSuggestionSetting"), + 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 + self.get_milli_time = ( + bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + ) async def handle_getSuggestionSetting(self, request): try: @@ -31,19 +37,19 @@ class v1_private_userSetting(plugins.ConfServerApp): { "name": "Aktionen/Angebote/Ereignisse", "settingKey": "MARKETING", - "val": "Y" + "val": "Y", }, { "name": "Benutzerbefragung", "settingKey": "QUESTIONNAIRE", - "val": "Y" + "val": "Y", }, { "name": "Produkt-Upgrade/Hilfe für Benutzer", "settingKey": "INTRODUCTION", - "val": "Y" - } - ] + "val": "Y", + }, + ], }, "msg": "操作成功", "time": self.get_milli_time(datetime.utcnow().timestamp()), @@ -52,7 +58,7 @@ class v1_private_userSetting(plugins.ConfServerApp): return web.json_response(body) except Exception as e: - logging.exception(f"{e}") + logging.exception(f"{e}") plugin = v1_private_userSetting() diff --git a/bumper/plugins/bumper_confserver_v2_private_user.py b/bumper/plugins/bumper_confserver_v2_private_user.py index 5a3c7a3..981fb03 100644 --- a/bumper/plugins/bumper_confserver_v2_private_user.py +++ b/bumper/plugins/bumper_confserver_v2_private_user.py @@ -10,20 +10,25 @@ from datetime import datetime, timedelta class v2_private_user(plugins.ConfServerApp): - def __init__(self): self.name = "v2_private_user" - self.plugin_type = "sub_api" + self.plugin_type = "sub_api" self.sub_api = "api_v2" authhandler = bumper.ConfServer.ConfServer_AuthHandler() self.routes = [ - web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkLogin", authhandler.login, name="v2_user_checkLogin"), + web.route( + "*", + "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkLogin", + authhandler.login, + name="v2_user_checkLogin", + ), ] - self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time - + self.get_milli_time = ( + bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time + ) + plugin = v2_private_user() - diff --git a/bumper/util.py b/bumper/util.py index a2aabcf..ce1359b 100644 --- a/bumper/util.py +++ b/bumper/util.py @@ -22,7 +22,9 @@ def get_logger(name: str, rotate: RotatingFileHandler = None) -> logging.Logger: if not log_to_stdout: if not rotate: - rotate = RotatingFileHandler(f"logs/{name}.log", maxBytes=5000000, backupCount=5) + rotate = RotatingFileHandler( + f"logs/{name}.log", maxBytes=5000000, backupCount=5 + ) rotate.setFormatter(logformat) logger.addHandler(rotate) else: @@ -31,8 +33,12 @@ def get_logger(name: str, rotate: RotatingFileHandler = None) -> logging.Logger: __loggers[name] = logger if name == "mqttserver": - get_logger("transitions", rotate).setLevel(logging.CRITICAL + 1) # Ignore this logger - get_logger("passlib", rotate).setLevel(logging.CRITICAL + 1) # Ignore this logger + get_logger("transitions", rotate).setLevel( + logging.CRITICAL + 1 + ) # Ignore this logger + get_logger("passlib", rotate).setLevel( + logging.CRITICAL + 1 + ) # Ignore this logger get_logger("hbmqtt.broker", rotate) get_logger("hbmqtt.mqtt.protocol", rotate) get_logger("hbmqtt.client", rotate) diff --git a/bumper/xmppserver.py b/bumper/xmppserver.py index 802320a..fbced1a 100644 --- a/bumper/xmppserver.py +++ b/bumper/xmppserver.py @@ -70,9 +70,7 @@ class XMPPServer_Protocol(asyncio.Protocol): def connection_made(self, transport): if self.aclient: # Existing client... upgrading to TLS - xmppserverlog.debug( - f"Upgraded connection for {self.aclient.address}" - ) + xmppserverlog.debug(f"Upgraded connection for {self.aclient.address}") self.aclient.transport = transport else: aclient = XMPPAsyncClient(transport) @@ -254,9 +252,7 @@ class XMPPAsyncClient: if client.type == self.BOT: if client.uid.lower() in ctl_to.lower(): - xmppserverlog.debug( - f"Sending ctl to bot: {rxmlstring}" - ) + xmppserverlog.debug(f"Sending ctl to bot: {rxmlstring}") client.send(rxmlstring) except Exception as e: @@ -310,17 +306,21 @@ class XMPPAsyncClient: if not "from" in xml.attrib: xml.attrib["from"] = f"{self.bumper_jid}" if "errno" in data: - xmppserverlog.error(f"Error from bot - {data}") + xmppserverlog.error(f"Error from bot - {data}") if ( "errno='103'" in data ): # No permissions, usually if bot was last on Ecovac network, Bumper will try to add fuid user as owner if self.type == self.BOT: - xmppserverlog.info("Bot reported user has no permissions, Bumper will attempt to add user to bot. This is typical if bot was last on Ecovacs Network.") + xmppserverlog.info( + "Bot reported user has no permissions, Bumper will attempt to add user to bot. This is typical if bot was last on Ecovacs Network." + ) xquery = xml.getchildren() ctl = xquery[0].getchildren() if "error" in ctl[0].attrib: ctlerr = ctl[0].attrib["error"] - adminuser = ctlerr.replace("permission denied, please contact ", "") + adminuser = ctlerr.replace( + "permission denied, please contact ", "" + ) adminuser = adminuser.replace(" ", "") elif "admin" in ctl[0].attrib: adminuser = ctl[0].attrib["admin"] @@ -473,9 +473,7 @@ class XMPPAsyncClient: async def _handle_starttls(self, data): try: if self.TLSUpgraded == False: - self.TLSUpgraded = ( - True - ) # Set TLSUpgraded true to prevent further attempts to upgrade connection + self.TLSUpgraded = True # Set TLSUpgraded true to prevent further attempts to upgrade connection xmppserverlog.debug( "Upgrading connection with STARTTLS for {}:{}".format( self.address[0], self.address[1] @@ -662,9 +660,7 @@ class XMPPAsyncClient: ) # Send dummy return - self.send( - f' dummy ' - ) + self.send(f' dummy ') elif xml.get("type") == "unavailable": xmppserverlog.debug( "client presence unavailable (DISCONNECT) - {} ".format( @@ -681,9 +677,7 @@ class XMPPAsyncClient: ) ) # Send dummy return - self.send( - f' dummy ' - ) + self.send(f' dummy ') def _parse_data(self, data): @@ -768,9 +762,7 @@ class XMPPAsyncClient: self._handle_connect(newdata.encode("utf-8")) else: if not (newdata == "" or newdata == " "): - xmppserverlog.error( - f"xml parse error - {newdata} - {e}" - ) + xmppserverlog.error(f"xml parse error - {newdata} - {e}") elif "not well-formed (invalid token)" in e.msg: # If a lone - client is signalling end of session/disconnect @@ -782,15 +774,11 @@ class XMPPAsyncClient: else: if "" in newdata: - xmppserverlog.error( - f"xml parse error - {newdata} - {e}" - ) + xmppserverlog.error(f"xml parse error - {newdata} - {e}") else: self.send("") # Close stream self._set_state("DISCONNECT") diff --git a/tests/const.py b/tests/const.py index f2a229f..143d88a 100644 --- a/tests/const.py +++ b/tests/const.py @@ -1,2 +1,2 @@ -HOST= "127.0.0.1" +HOST = "127.0.0.1" MQTT_PORT = 8883 diff --git a/tests/test_confserver.py b/tests/test_confserver.py index 058e40a..9673618 100644 --- a/tests/test_confserver.py +++ b/tests/test_confserver.py @@ -42,29 +42,39 @@ async def test_confserver_ssl(): conf_server.confserver_app() asyncio.create_task(conf_server.start_server()) + async def test_confserver_exceptions(): with LogCapture() as l: - conf_server = bumper.ConfServer((HOST, 8007), usessl=True) - conf_server.confserver_app() - conf_server.site = web.TCPSite + conf_server = bumper.ConfServer((HOST, 8007), usessl=True) + conf_server.confserver_app() + conf_server.site = web.TCPSite - #bind permission - conf_server.site.start = mock.Mock(side_effect=OSError(1, "error while attempting to bind on address ('127.0.0.1', 8007): permission denied")) - await conf_server.start_server() + # bind permission + conf_server.site.start = mock.Mock( + side_effect=OSError( + 1, + "error while attempting to bind on address ('127.0.0.1', 8007): permission denied", + ) + ) + await conf_server.start_server() - #asyncio Cancel - conf_server.site = web.TCPSite - conf_server.site.start = mock.Mock(side_effect=asyncio.CancelledError) - await conf_server.start_server() + # asyncio Cancel + conf_server.site = web.TCPSite + conf_server.site.start = mock.Mock(side_effect=asyncio.CancelledError) + await conf_server.start_server() + + # general exception + conf_server.site = web.TCPSite + conf_server.site.start = mock.Mock(side_effect=Exception(1, "general")) + await conf_server.start_server() - #general exception - conf_server.site = web.TCPSite - conf_server.site.start = mock.Mock(side_effect=Exception(1, "general")) - await conf_server.start_server() - l.check_present( - ("confserver", "ERROR", "error while attempting to bind on address ('127.0.0.1', 8007): permission denied") + ( + "confserver", + "ERROR", + "error while attempting to bind on address ('127.0.0.1', 8007): permission denied", + ) ) @@ -89,7 +99,7 @@ def test_get_milli_time(): async def test_base(aiohttp_client): remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing - + # Start MQTT mqtt_server = bumper.MQTTServer(HOST, MQTT_PORT, password_file="tests/passwd") bumper.mqtt_server = mqtt_server @@ -100,7 +110,7 @@ async def test_base(aiohttp_client): xmpp_server = bumper.XMPPServer(xmpp_address) bumper.xmpp_server = xmpp_server await xmpp_server.start_async_server() - + # Start Helperbot mqtt_helperbot = bumper.MQTTHelperBot(HOST, MQTT_PORT) bumper.mqtt_helperbot = mqtt_helperbot @@ -108,7 +118,7 @@ async def test_base(aiohttp_client): client = await aiohttp_client(create_app) resp = await client.get("/") - assert resp.status == 200 + assert resp.status == 200 mqtt_helperbot.Client.disconnect() @@ -120,7 +130,7 @@ async def test_base(aiohttp_client): async def test_restartService(aiohttp_client): remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing - + # Start MQTT mqtt_server = bumper.MQTTServer(HOST, MQTT_PORT, password_file="tests/passwd") bumper.mqtt_server = mqtt_server @@ -131,37 +141,39 @@ async def test_restartService(aiohttp_client): xmpp_server = bumper.XMPPServer(xmpp_address) bumper.xmpp_server = xmpp_server await xmpp_server.start_async_server() - + # Start Helperbot mqtt_helperbot = bumper.MQTTHelperBot(HOST, MQTT_PORT) bumper.mqtt_helperbot = mqtt_helperbot await mqtt_helperbot.start_helper_bot() client = await aiohttp_client(create_app) - + resp = await client.get("/restart_Helperbot") - assert resp.status == 200 + assert resp.status == 200 resp = await client.get("/restart_MQTTServer") - assert resp.status == 200 + assert resp.status == 200 resp = await client.get("/restart_XMPPServer") - assert resp.status == 200 + assert resp.status == 200 mqtt_helperbot.Client.disconnect() await mqtt_server.broker.shutdown() xmpp_server.disconnect() + async def test_RemoveBot(aiohttp_client): client = await aiohttp_client(create_app) resp = await client.get("/bot/remove/test_did") - assert resp.status == 200 + assert resp.status == 200 + async def test_RemoveClient(aiohttp_client): client = await aiohttp_client(create_app) resp = await client.get("/client/remove/test_resource") - assert resp.status == 200 + assert resp.status == 200 async def test_login(aiohttp_client): @@ -180,7 +192,7 @@ async def test_login(aiohttp_client): assert "username" in jsonresp["data"] remove_existing_db() - bumper.db = "tests/tmp.db" # Set db location for testing + bumper.db = "tests/tmp.db" # Set db location for testing # Test global_e without user resp = await client.get("/v1/private/us/en/dev_1234/global_e/1/0/0/user/login") @@ -216,11 +228,11 @@ async def test_login(aiohttp_client): # Add a bot to db that doesn't have a did newbot = { - "class": "dev_1234", - "company": "com_123", - #"did": self.did, - "name": "sn_1234", - "resource": "res_1234", + "class": "dev_1234", + "company": "com_123", + # "did": self.did, + "name": "sn_1234", + "resource": "res_1234", } bumper.bot_full_upsert(newbot) @@ -592,11 +604,10 @@ async def test_getProductIotMap(aiohttp_client): jsonresp = json.loads(text) assert jsonresp["code"] == bumper.RETURN_API_SUCCESS - # Test getPimFile resp = await client.get("/api/pim/file/get/123") assert resp.status == 200 - + async def test_getUsersAPI(aiohttp_client): remove_existing_db() @@ -1006,6 +1017,3 @@ async def test_dim_devmanager(aiohttp_client): text = await resp.text() test_resp = json.loads(text) assert test_resp["ret"] == "fail" - - - diff --git a/tests/test_init.py b/tests/test_init.py index d84848f..e8e18a5 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -59,4 +59,3 @@ async def test_start_stop_debug(): ("bumper", "INFO", "Shutting down"), ("bumper", "INFO", "Shutdown complete") ) assert b.shutting_down == True - diff --git a/tests/test_mqttserver.py b/tests/test_mqttserver.py index aebbd8d..87eab72 100644 --- a/tests/test_mqttserver.py +++ b/tests/test_mqttserver.py @@ -14,6 +14,7 @@ import time from tests.const import HOST, MQTT_PORT + async def test_helperbot_message(): mqtt_server = bumper.MQTTServer(HOST, MQTT_PORT, password_file="tests/passwd") await mqtt_server.broker_coro() @@ -51,13 +52,11 @@ async def test_helperbot_message(): mqtt_helperbot.Client._connected_state._value == True ) # Check helperbot is connected msg_payload = "{}" - msg_topic_name = ( - "iot/p2p/GetWKVer/helperbot/bumper/helperbot/bot_serial/ls1ok3/wC3g/q/iCmuqp/j" - ) + msg_topic_name = "iot/p2p/GetWKVer/helperbot/bumper/helperbot/bot_serial/ls1ok3/wC3g/q/iCmuqp/j" await mqtt_helperbot.Client.publish( msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0 ) - + await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1) l.check_present( @@ -77,9 +76,7 @@ async def test_helperbot_message(): mqtt_helperbot.Client._connected_state._value == True ) # Check helperbot is connected msg_payload = '{"ret":"ok","ver":"0.13.5"}' - msg_topic_name = ( - "iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/helperbot/bumper/helperbot/p/iCmuqp/j" - ) + msg_topic_name = "iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/helperbot/bumper/helperbot/p/iCmuqp/j" await mqtt_helperbot.Client.publish( msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0 ) @@ -103,16 +100,13 @@ async def test_helperbot_message(): mqtt_helperbot.Client._connected_state._value == True ) # Check helperbot is connected msg_payload = "test" - msg_topic_name = ( - "iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/TESTBAD/bumper/helperbot/p/iCmuqp/j" - ) + msg_topic_name = "iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/TESTBAD/bumper/helperbot/p/iCmuqp/j" await mqtt_helperbot.Client.publish( msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0 ) await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1) - l.check_present( ( "helperbot", @@ -134,7 +128,7 @@ async def test_helperbot_message(): await mqtt_helperbot.Client.publish( msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0 ) - + await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1) l.check_present( @@ -156,7 +150,7 @@ async def test_helperbot_expire_message(): timeout = 0.1 # Test broadcast message - mqtt_helperbot = bumper.MQTTHelperBot(HOST, MQTT_PORT,timeout) + mqtt_helperbot = bumper.MQTTHelperBot(HOST, MQTT_PORT, timeout) bumper.mqtt_helperbot = mqtt_helperbot await mqtt_helperbot.start_helper_bot() assert ( @@ -167,14 +161,13 @@ async def test_helperbot_expire_message(): expire_msg_topic_name = "iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/helperbot/bumper/helperbot/p/testgood/j" currenttime = time.time() request_id = "ABC" - data = { - "time": currenttime, - "topic": expire_msg_topic_name, - "payload": expire_msg_payload, - } + data = { + "time": currenttime, + "topic": expire_msg_topic_name, + "payload": expire_msg_payload, + } - - mqtt_helperbot.commands[request_id]= data + mqtt_helperbot.commands[request_id] = data assert mqtt_helperbot.commands[request_id] == data @@ -185,13 +178,12 @@ async def test_helperbot_expire_message(): msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0 ) # Send another message to force get_msg - await asyncio.sleep(timeout*2) + await asyncio.sleep(timeout * 2) assert mqtt_helperbot.commands.get(request_id, None) == None await mqtt_helperbot.Client.disconnect() await mqtt_server.broker.shutdown() - async def test_helperbot_sendcommand(): @@ -233,9 +225,7 @@ async def test_helperbot_sendcommand(): # Send response beforehand msg_payload = '{"ret":"ok","ver":"0.13.5"}' - msg_topic_name = ( - "iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/helperbot/bumper/helperbot/p/testgood/j" - ) + msg_topic_name = "iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/helperbot/bumper/helperbot/p/testgood/j" await mqtt_helperbot.Client.publish( msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0 ) @@ -247,7 +237,7 @@ async def test_helperbot_sendcommand(): "ret": "ok", } - #mqtt_helperbot.Client.disconnect() + # mqtt_helperbot.Client.disconnect() # Test GetLifeSpan (xml command) cmdjson = { @@ -269,9 +259,7 @@ async def test_helperbot_sendcommand(): # Send response beforehand msg_payload = "" - msg_topic_name = ( - "iot/p2p/GetLifeSpan/bot_serial/ls1ok3/wC3g/helperbot/bumper/helperbot/p/testx/q" - ) + msg_topic_name = "iot/p2p/GetLifeSpan/bot_serial/ls1ok3/wC3g/helperbot/bumper/helperbot/p/testx/q" await mqtt_helperbot.Client.publish( msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0 ) @@ -289,12 +277,7 @@ async def test_helperbot_sendcommand(): "payloadType": "j", "toRes": "wC3g", "payload": { - "header": { - "pri": 1, - "ts": "1569380075887", - "tzm": -240, - "ver": "0.0.50" - } + "header": {"pri": 1, "ts": "1569380075887", "tzm": -240, "ver": "0.0.50"} }, "td": "q", "toId": "bot_serial", @@ -310,7 +293,7 @@ async def test_helperbot_sendcommand(): # Send response beforehand msg_payload = '{"body":{"code":0,"data":{"area":0,"cid":"111","start":"1569378657","time":6,"type":"auto"},"msg":"ok"},"header":{"fwVer":"1.6.4","hwVer":"0.1.1","pri":1,"ts":"1569380074036","tzm":480,"ver":"0.0.1"}}' - + msg_topic_name = ( "iot/p2p/getStats/bot_serial/ls1ok3/wC3g/helperbot/bumper/helperbot/p/testj/j" ) @@ -322,14 +305,33 @@ async def test_helperbot_sendcommand(): assert commandresult == { "id": "testj", - "resp": {'body':{'code':0,'data':{'area':0,'cid':'111','start':'1569378657','time':6,'type':'auto'},'msg':'ok'},'header':{'fwVer':'1.6.4','hwVer':'0.1.1','pri':1,'ts':'1569380074036','tzm':480,'ver':'0.0.1'}}, + "resp": { + "body": { + "code": 0, + "data": { + "area": 0, + "cid": "111", + "start": "1569378657", + "time": 6, + "type": "auto", + }, + "msg": "ok", + }, + "header": { + "fwVer": "1.6.4", + "hwVer": "0.1.1", + "pri": 1, + "ts": "1569380074036", + "tzm": 480, + "ver": "0.0.1", + }, + }, "ret": "ok", } mqtt_helperbot.Client.disconnect() await mqtt_server.broker.shutdown() - async def test_mqttserver(): @@ -338,9 +340,10 @@ async def test_mqttserver(): bumper.db = "tests/tmp.db" # Set db location for testing + mqtt_server = bumper.MQTTServer( + HOST, MQTT_PORT, password_file="tests/passwd", allow_anonymous=True + ) - mqtt_server = bumper.MQTTServer(HOST, MQTT_PORT, password_file="tests/passwd", allow_anonymous=True) - await mqtt_server.broker_coro() # Test helperbot connect @@ -387,13 +390,19 @@ async def test_mqttserver(): test_client.client_id = "test-file-auth" # await test_client.start_helper_bot() test_client.Client = hbmqtt.client.MQTTClient( - client_id=test_client.client_id, config={"check_hostname": False, "auto_reconnect": False, "reconnect_retries": 1} + client_id=test_client.client_id, + config={ + "check_hostname": False, + "auto_reconnect": False, + "reconnect_retries": 1, + }, ) # good user/pass await test_client.Client.connect( f"mqtts://test-client:abc123!@{HOST}:{MQTT_PORT}/", - cafile=bumper.ca_cert, cleansession=True + cafile=bumper.ca_cert, + cleansession=True, ) assert ( @@ -403,42 +412,57 @@ async def test_mqttserver(): assert ( test_client.Client._connected_state._value == False ) # Check client is disconnected - + # bad password with LogCapture() as l: - + await test_client.Client.connect( f"mqtts://test-client:notvalid!@{HOST}:{MQTT_PORT}/", - cafile=bumper.ca_cert, cleansession=True + cafile=bumper.ca_cert, + cleansession=True, ) l.check_present( - ("mqttserver", "INFO", "File Authentication Failed - Username: test-client - ClientID: test-file-auth"), - order_matters=False - ) - # no username in file + ( + "mqttserver", + "INFO", + "File Authentication Failed - Username: test-client - ClientID: test-file-auth", + ), + order_matters=False, + ) + # no username in file await test_client.Client.connect( f"mqtts://test-client-noexist:notvalid!@{HOST}:{MQTT_PORT}/", - cafile=bumper.ca_cert, cleansession=True + cafile=bumper.ca_cert, + cleansession=True, ) - l.check_present( - ("mqttserver", "INFO", 'File Authentication Failed - No Entry for Username: test-client-noexist - ClientID: test-file-auth'), - order_matters=False + ( + "mqttserver", + "INFO", + "File Authentication Failed - No Entry for Username: test-client-noexist - ClientID: test-file-auth", + ), + order_matters=False, ) - + await mqtt_server.broker.shutdown() - + async def test_nofileauth_mqttserver(): with LogCapture() as l: - mqtt_server = bumper.MQTTServer(HOST, MQTT_PORT, password_file="tests/passwd-notfound") + mqtt_server = bumper.MQTTServer( + HOST, MQTT_PORT, password_file="tests/passwd-notfound" + ) await mqtt_server.broker_coro() - await mqtt_server.broker.shutdown() + await mqtt_server.broker.shutdown() l.check_present( - ("hbmqtt.broker.plugins.bumper", "WARNING", 'Password file tests/passwd-notfound not found'), - order_matters=False + ( + "hbmqtt.broker.plugins.bumper", + "WARNING", + "Password file tests/passwd-notfound not found", + ), + order_matters=False, ) diff --git a/tests/test_xmppserver.py b/tests/test_xmppserver.py index 776cd0d..cc5521c 100644 --- a/tests/test_xmppserver.py +++ b/tests/test_xmppserver.py @@ -513,7 +513,9 @@ async def test_client_send_iq(*args, **kwargs): bumper.xmppserver.XMPPServer.clients.append(xmppclient2) # Roster IQ - Only seen from Android app so far - test_data = b'' + test_data = ( + b'' + ) xmppclient._parse_data(test_data) assert ( @@ -594,4 +596,3 @@ async def test_client_send_iq(*args, **kwargs): # Reset mock calls mock_send.reset_mock() -