add new endpoints for app v2 #116
21 changed files with 2778 additions and 904 deletions
|
|
@ -1,20 +1,3 @@
|
||||||
# Logs
|
|
||||||
logs
|
|
||||||
|
|
||||||
# Tests
|
|
||||||
tests
|
|
||||||
|
|
||||||
# Examples
|
|
||||||
examples
|
|
||||||
|
|
||||||
# Docs
|
|
||||||
docs
|
|
||||||
|
|
||||||
# Certs
|
|
||||||
certs
|
|
||||||
|
|
||||||
.coverage
|
|
||||||
|
|
||||||
# Markdown
|
# Markdown
|
||||||
*.md
|
*.md
|
||||||
|
|
||||||
|
|
|
||||||
10
Dockerfile
10
Dockerfile
|
|
@ -22,11 +22,15 @@ RUN apk add build-base
|
||||||
|
|
||||||
FROM base
|
FROM base
|
||||||
|
|
||||||
COPY . /bumper
|
COPY requirements.txt /requirements.txt
|
||||||
|
|
||||||
WORKDIR /bumper
|
|
||||||
|
|
||||||
# install required python packages
|
# install required python packages
|
||||||
RUN pip3 install -r requirements.txt
|
RUN pip3 install -r requirements.txt
|
||||||
|
|
||||||
|
WORKDIR /bumper
|
||||||
|
|
||||||
|
# Copy only required folders instead of all
|
||||||
|
COPY create_certs/ create_certs/
|
||||||
|
COPY bumper/ bumper/
|
||||||
|
|
||||||
ENTRYPOINT ["python3", "-m", "bumper"]
|
ENTRYPOINT ["python3", "-m", "bumper"]
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ bumper_announce_ip = os.environ.get("BUMPER_ANNOUNCE_IP") or bumper_listen
|
||||||
bumper_debug = strtobool(os.environ.get("BUMPER_DEBUG")) or False
|
bumper_debug = strtobool(os.environ.get("BUMPER_DEBUG")) or False
|
||||||
use_auth = False
|
use_auth = False
|
||||||
token_validity_seconds = 3600 # 1 hour
|
token_validity_seconds = 3600 # 1 hour
|
||||||
|
oauth_validity_days = 15
|
||||||
db = None
|
db = None
|
||||||
|
|
||||||
mqtt_server = None
|
mqtt_server = None
|
||||||
|
|
@ -236,7 +237,8 @@ async def start():
|
||||||
xmpp_server = XMPPServer((bumper_listen, xmpp_listen_port))
|
xmpp_server = XMPPServer((bumper_listen, xmpp_listen_port))
|
||||||
|
|
||||||
# Start MQTT Server
|
# Start MQTT Server
|
||||||
asyncio.create_task(mqtt_server.broker_coro())
|
# await start otherwise we get an error connecting the helper bot
|
||||||
|
await asyncio.create_task(mqtt_server.broker_coro())
|
||||||
|
|
||||||
# Start MQTT Helperbot
|
# Start MQTT Helperbot
|
||||||
asyncio.create_task(mqtt_helperbot.start_helper_bot())
|
asyncio.create_task(mqtt_helperbot.start_helper_bot())
|
||||||
|
|
@ -264,6 +266,7 @@ async def start():
|
||||||
|
|
||||||
async def maintenance():
|
async def maintenance():
|
||||||
revoke_expired_tokens()
|
revoke_expired_tokens()
|
||||||
|
revoke_expired_oauths()
|
||||||
|
|
||||||
|
|
||||||
async def shutdown():
|
async def shutdown():
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,16 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
import ssl
|
|
||||||
import string
|
|
||||||
import random
|
|
||||||
import bumper
|
|
||||||
import os
|
|
||||||
from bumper.models import *
|
|
||||||
from bumper import plugins
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from aiohttp import web
|
import logging
|
||||||
|
import os
|
||||||
|
import ssl
|
||||||
|
|
||||||
import aiohttp_jinja2
|
import aiohttp_jinja2
|
||||||
import jinja2
|
import jinja2
|
||||||
import uuid
|
from aiohttp import web
|
||||||
import xml.etree.ElementTree as ET
|
|
||||||
|
from bumper import plugins
|
||||||
|
from bumper.models import *
|
||||||
|
|
||||||
|
|
||||||
class aiohttp_filter(logging.Filter):
|
class aiohttp_filter(logging.Filter):
|
||||||
|
|
@ -62,13 +57,12 @@ class ConfServer:
|
||||||
|
|
||||||
self.app.add_routes(
|
self.app.add_routes(
|
||||||
[
|
[
|
||||||
|
|
||||||
web.get("", self.handle_base, name="base"),
|
web.get("", self.handle_base, name="base"),
|
||||||
web.get("/bot/remove/{did}", self.handle_RemoveBot, name='remove-bot'),
|
web.get("/bot/remove/{did}", self.handle_RemoveBot, name='remove-bot'),
|
||||||
web.get("/client/remove/{resource}", self.handle_RemoveClient, name='remove-client'),
|
web.get("/client/remove/{resource}", self.handle_RemoveClient, name='remove-client'),
|
||||||
web.get("/restart_{service}", self.handle_RestartService, name='restart-service'),
|
web.get("/restart_{service}", self.handle_RestartService, name='restart-service'),
|
||||||
web.post("/lookup.do", self.handle_lookup),
|
web.post("/lookup.do", self.handle_lookup),
|
||||||
|
web.post("/newauth.do", self.handle_newauth),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -217,8 +211,18 @@ class ConfServer:
|
||||||
async def log_all_requests(self, request, handler):
|
async def log_all_requests(self, request, handler):
|
||||||
|
|
||||||
if request._match_info.route.name not in self.excludelogging:
|
if request._match_info.route.name not in self.excludelogging:
|
||||||
|
to_log = {
|
||||||
|
"request": {
|
||||||
|
"route_name": f"{request.match_info.route.name}",
|
||||||
|
"method": f"{request.method}",
|
||||||
|
"path": f"{request.path}",
|
||||||
|
"query_string": f"{request.query_string}",
|
||||||
|
"raw_path": f"{request.raw_path}",
|
||||||
|
"raw_headers": f'{",".join(map("{}".format, request.raw_headers))}',
|
||||||
|
}
|
||||||
|
}
|
||||||
try:
|
try:
|
||||||
|
postbody = None
|
||||||
if request.content_length:
|
if request.content_length:
|
||||||
if request.content_type == "application/x-www-form-urlencoded":
|
if request.content_type == "application/x-www-form-urlencoded":
|
||||||
postbody = await request.post()
|
postbody = await request.post()
|
||||||
|
|
@ -232,78 +236,33 @@ class ConfServer:
|
||||||
|
|
||||||
else:
|
else:
|
||||||
postbody = await request.post()
|
postbody = await request.post()
|
||||||
else:
|
|
||||||
postbody = None
|
to_log["request"]["body"] = f"{postbody}"
|
||||||
|
|
||||||
response = await handler(request)
|
response = await handler(request)
|
||||||
|
if response is None:
|
||||||
|
confserverlog.warning("Response was null!")
|
||||||
|
confserverlog.warning(json.dumps(to_log))
|
||||||
|
return response
|
||||||
|
|
||||||
|
to_log["response"] = {
|
||||||
|
"status": f"{response.status}",
|
||||||
|
}
|
||||||
if not "application/octet-stream" in response.content_type:
|
if not "application/octet-stream" in response.content_type:
|
||||||
logall = {
|
to_log["response"]["body"] = f"{json.loads(response.body)}"
|
||||||
"request": {
|
|
||||||
"route_name": f"{request.match_info.route.name}",
|
|
||||||
"method": f"{request.method}",
|
|
||||||
"path": f"{request.path}",
|
|
||||||
"query_string": f"{request.query_string}",
|
|
||||||
"raw_path": f"{request.raw_path}",
|
|
||||||
"raw_headers": f'{",".join(map("{}".format, request.raw_headers))}',
|
|
||||||
"body": f"{postbody}",
|
|
||||||
},
|
|
||||||
|
|
||||||
"response": {
|
confserverlog.debug(json.dumps(to_log))
|
||||||
"response_body": f"{json.loads(response.body)}",
|
|
||||||
"status": f"{response.status}",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
logall = {
|
|
||||||
"request": {
|
|
||||||
"route_name": f"{request.match_info.route.name}",
|
|
||||||
"method": f"{request.method}",
|
|
||||||
"path": f"{request.path}",
|
|
||||||
"query_string": f"{request.query_string}",
|
|
||||||
"raw_path": f"{request.raw_path}",
|
|
||||||
"raw_headers": f'{",".join(map("{}".format, request.raw_headers))}',
|
|
||||||
"body": f"{postbody}",
|
|
||||||
},
|
|
||||||
|
|
||||||
"response": {
|
|
||||||
"status": f"{response.status}",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
confserverlog.debug(json.dumps(logall))
|
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
except web.HTTPNotFound as notfound:
|
except web.HTTPNotFound as notfound:
|
||||||
confserverlog.debug("Request path {} not found".format(request.raw_path))
|
confserverlog.debug("Request path {} not found".format(request.raw_path))
|
||||||
requestlog = {
|
confserverlog.debug(json.dumps(to_log))
|
||||||
"request": {
|
|
||||||
"route_name": f"{request.match_info.route.name}",
|
|
||||||
"method": f"{request.method}",
|
|
||||||
"path": f"{request.path}",
|
|
||||||
"query_string": f"{request.query_string}",
|
|
||||||
"raw_path": f"{request.raw_path}",
|
|
||||||
"raw_headers": f'{",".join(map("{}".format, request.raw_headers))}',
|
|
||||||
"body": f"{postbody}",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
confserverlog.debug(json.dumps(requestlog))
|
|
||||||
return notfound
|
return notfound
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
confserverlog.exception("{}".format(e))
|
confserverlog.exception("{}".format(e))
|
||||||
requestlog = {
|
confserverlog.error(json.dumps(to_log))
|
||||||
"request": {
|
|
||||||
"route_name": f"{request.match_info.route.name}",
|
|
||||||
"method": f"{request.method}",
|
|
||||||
"path": f"{request.path}",
|
|
||||||
"query_string": f"{request.query_string}",
|
|
||||||
"raw_path": f"{request.raw_path}",
|
|
||||||
"raw_headers": f'{",".join(map("{}".format, request.raw_headers))}',
|
|
||||||
"body": f"{postbody}",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
confserverlog.debug(json.dumps(requestlog))
|
|
||||||
return e
|
return e
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
@ -507,6 +466,26 @@ class ConfServer:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
confserverlog.exception("{}".format(e))
|
confserverlog.exception("{}".format(e))
|
||||||
|
|
||||||
|
async def handle_newauth(self, request):
|
||||||
|
# Bumper is only returning the submitted token. No reason yet to create another new token
|
||||||
|
try:
|
||||||
|
if request.content_type == "application/x-www-form-urlencoded":
|
||||||
|
postbody = await request.post()
|
||||||
|
else:
|
||||||
|
postbody = json.loads(await request.text())
|
||||||
|
|
||||||
|
confserverlog.debug(postbody)
|
||||||
|
|
||||||
|
body = {
|
||||||
|
"authCode": postbody["itToken"],
|
||||||
|
"result": "ok",
|
||||||
|
"todo": "result"
|
||||||
|
}
|
||||||
|
|
||||||
|
return web.json_response(body)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
confserverlog.exception("{}".format(e))
|
||||||
|
|
||||||
async def disconnect(self):
|
async def disconnect(self):
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
79
bumper/db.py
79
bumper/db.py
|
|
@ -1,13 +1,13 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import bumper
|
|
||||||
from bumper.models import VacBotClient, VacBotDevice, BumperUser, EcoVacsHomeProducts
|
|
||||||
from tinydb import TinyDB, Query
|
|
||||||
from tinydb.storages import MemoryStorage
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
import os
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
from tinydb import TinyDB, Query
|
||||||
|
|
||||||
|
import bumper
|
||||||
|
from bumper.models import VacBotClient, VacBotDevice, BumperUser, EcoVacsHomeProducts, OAuth
|
||||||
|
|
||||||
bumperlog = logging.getLogger("bumper")
|
bumperlog = logging.getLogger("bumper")
|
||||||
|
|
||||||
|
|
@ -32,6 +32,7 @@ def db_get():
|
||||||
db.table("clients", cache_size=0)
|
db.table("clients", cache_size=0)
|
||||||
db.table("bots", cache_size=0)
|
db.table("bots", cache_size=0)
|
||||||
db.table("tokens", cache_size=0)
|
db.table("tokens", cache_size=0)
|
||||||
|
db.table("oauth", cache_size=0)
|
||||||
|
|
||||||
return db
|
return db
|
||||||
|
|
||||||
|
|
@ -202,6 +203,55 @@ def user_revoke_authcode(userid, token, authcode):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def revoke_expired_oauths():
|
||||||
|
opendb = db_get()
|
||||||
|
with opendb:
|
||||||
|
table = opendb.table("oauth")
|
||||||
|
entries = table.all()
|
||||||
|
|
||||||
|
for i in entries:
|
||||||
|
oauth = OAuth(**i)
|
||||||
|
if datetime.now() >= datetime.fromisoformat(oauth.expire_at):
|
||||||
|
bumperlog.debug(
|
||||||
|
"Removing oauth {} due to expiration".format(oauth.access_token)
|
||||||
|
)
|
||||||
|
table.remove(doc_ids=[i.doc_id])
|
||||||
|
|
||||||
|
|
||||||
|
def user_revoke_expired_oauths(userid):
|
||||||
|
opendb = db_get()
|
||||||
|
with opendb:
|
||||||
|
table = opendb.table("oauth")
|
||||||
|
search = table.search(Query().userid == userid)
|
||||||
|
for i in search:
|
||||||
|
oauth = OAuth(**i)
|
||||||
|
if datetime.now() >= datetime.fromisoformat(oauth.expire_at):
|
||||||
|
bumperlog.debug(
|
||||||
|
"Removing oauth {} due to expiration".format(oauth.access_token)
|
||||||
|
)
|
||||||
|
table.remove(doc_ids=[i.doc_id])
|
||||||
|
|
||||||
|
|
||||||
|
def user_add_oauth(userid) -> OAuth:
|
||||||
|
user_revoke_expired_oauths(userid)
|
||||||
|
opendb = db_get()
|
||||||
|
with opendb:
|
||||||
|
table = opendb.table("oauth")
|
||||||
|
entry = table.get(Query().userid == userid)
|
||||||
|
if entry:
|
||||||
|
return OAuth(**entry)
|
||||||
|
else:
|
||||||
|
oauth = OAuth.create_new(userid)
|
||||||
|
bumperlog.debug("Adding oauth {} for userid {}".format(oauth.access_token, userid))
|
||||||
|
table.insert(oauth.toDB())
|
||||||
|
return oauth
|
||||||
|
|
||||||
|
|
||||||
|
def token_by_authcode(authcode):
|
||||||
|
tokens = db_get().table("tokens")
|
||||||
|
return tokens.get(Query().authcode == authcode)
|
||||||
|
|
||||||
|
|
||||||
def get_disconnected_xmpp_clients():
|
def get_disconnected_xmpp_clients():
|
||||||
clients = db_get().table("clients")
|
clients = db_get().table("clients")
|
||||||
Client = Query()
|
Client = Query()
|
||||||
|
|
@ -302,6 +352,21 @@ def bot_toEcoVacsHome_JSON(bot): # EcoVacs Home
|
||||||
bot["UILogicId"] = botprod["product"]["UILogicId"]
|
bot["UILogicId"] = botprod["product"]["UILogicId"]
|
||||||
bot["ota"] = botprod["product"]["ota"]
|
bot["ota"] = botprod["product"]["ota"]
|
||||||
bot["icon"] = botprod["product"]["iconUrl"]
|
bot["icon"] = botprod["product"]["iconUrl"]
|
||||||
|
bot["model"] = botprod["product"]["model"]
|
||||||
|
bot["pip"] = botprod["product"]["_id"]
|
||||||
|
bot["deviceName"] = botprod["product"]["name"]
|
||||||
|
bot["materialNo"] = botprod["product"]["materialNo"]
|
||||||
|
bot["product_category"] = "DEEBOT" if botprod["product"]["name"].startswith("DEEBOT") else "UNKNOWN"
|
||||||
|
# bot["updateInfo"] = {
|
||||||
|
# "changeLog": "",
|
||||||
|
# "needUpdate": False
|
||||||
|
# }
|
||||||
|
# bot["service"] = {
|
||||||
|
# "jmq": "jmq-ngiot-eu.dc.ww.ecouser.net",
|
||||||
|
# "mqs": "api-ngiot.dc-as.ww.ecouser.net"
|
||||||
|
# }
|
||||||
|
bot["status"] = 1 if bot["mqtt_connection"] or bot["xmpp_connection"] else 0
|
||||||
|
|
||||||
return json.dumps(
|
return json.dumps(
|
||||||
bot, default=lambda o: o.__dict__, sort_keys=False
|
bot, default=lambda o: o.__dict__, sort_keys=False
|
||||||
) # , indent=4)
|
) # , indent=4)
|
||||||
|
|
@ -345,12 +410,14 @@ def client_add(userid, realm, resource):
|
||||||
bumperlog.info("Adding new client with resource {}".format(newclient.resource))
|
bumperlog.info("Adding new client with resource {}".format(newclient.resource))
|
||||||
client_full_upsert(newclient.asdict())
|
client_full_upsert(newclient.asdict())
|
||||||
|
|
||||||
|
|
||||||
def client_remove(resource):
|
def client_remove(resource):
|
||||||
clients = db_get().table("clients")
|
clients = db_get().table("clients")
|
||||||
client = client_get(resource)
|
client = client_get(resource)
|
||||||
if client:
|
if client:
|
||||||
clients.remove(doc_ids=[client.doc_id])
|
clients.remove(doc_ids=[client.doc_id])
|
||||||
|
|
||||||
|
|
||||||
def client_get(resource):
|
def client_get(resource):
|
||||||
clients = db_get().table("clients")
|
clients = db_get().table("clients")
|
||||||
Client = Query()
|
Client = Query()
|
||||||
|
|
|
||||||
2011
bumper/models.py
2011
bumper/models.py
File diff suppressed because it is too large
Load diff
|
|
@ -1,12 +1,10 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import asyncio
|
|
||||||
from aiohttp import web
|
|
||||||
from bumper import plugins
|
|
||||||
import logging
|
import logging
|
||||||
import bumper
|
|
||||||
from bumper.models import *
|
from aiohttp import web
|
||||||
|
|
||||||
from bumper import plugins
|
from bumper import plugins
|
||||||
from datetime import datetime, timedelta
|
from bumper.models import *
|
||||||
|
|
||||||
|
|
||||||
class portal_api_appsvr(plugins.ConfServerApp):
|
class portal_api_appsvr(plugins.ConfServerApp):
|
||||||
|
|
@ -17,14 +15,14 @@ class portal_api_appsvr(plugins.ConfServerApp):
|
||||||
self.sub_api = "portal_api"
|
self.sub_api = "portal_api"
|
||||||
|
|
||||||
self.routes = [
|
self.routes = [
|
||||||
|
web.route("*", "/appsvr/app.do", self.handle_appsvr_app, name="portal_api_appsvr_app"),
|
||||||
web.route("*", "/appsvr/app.do", self.handle_appsvr_api, 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_api(self, request):
|
async def handle_appsvr_app(self, request):
|
||||||
if not request.method == "GET": # Skip GET for now
|
if not request.method == "GET": # Skip GET for now
|
||||||
try:
|
try:
|
||||||
|
|
||||||
|
|
@ -157,5 +155,57 @@ class portal_api_appsvr(plugins.ConfServerApp):
|
||||||
body = {"result": "fail", "todo": "result"}
|
body = {"result": "fail", "todo": "result"}
|
||||||
return web.json_response(body)
|
return web.json_response(body)
|
||||||
|
|
||||||
plugin = portal_api_appsvr()
|
async def handle_appsvr_service_list(self, request):
|
||||||
|
try:
|
||||||
|
# original urls comment out as they are sub sub domain, which the current certificate is not valid
|
||||||
|
# using url, where the certs is valid
|
||||||
|
# data = {
|
||||||
|
# "account": "users-base.dc-eu.ww.ecouser.net",
|
||||||
|
# "jmq": "jmq-ngiot-eu.dc.ww.ecouser.net",
|
||||||
|
# "lb": "lbo.ecouser.net",
|
||||||
|
# "magw": "api-app.dc-eu.ww.ecouser.net",
|
||||||
|
# "msgcloud": "msg-eu.ecouser.net:5223",
|
||||||
|
# "ngiotLb": "jmq-ngiot-eu.area.ww.ecouser.net",
|
||||||
|
# "rop": "api-rop.dc-eu.ww.ecouser.net"
|
||||||
|
# }
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"account": "users-base.ecouser.net",
|
||||||
|
"jmq": "jmq-ngiot-eu.ecouser.net",
|
||||||
|
"lb": "lbo.ecouser.net",
|
||||||
|
"magw": "api-app.ecouser.net",
|
||||||
|
"msgcloud": "msg-eu.ecouser.net:5223",
|
||||||
|
"ngiotLb": "jmq-ngiot-eu.ecouser.net",
|
||||||
|
"rop": "api-rop.ecouser.net"
|
||||||
|
}
|
||||||
|
|
||||||
|
body = {
|
||||||
|
"code": 0,
|
||||||
|
"data": data,
|
||||||
|
"ret": "ok",
|
||||||
|
"todo": "result"
|
||||||
|
}
|
||||||
|
|
||||||
|
return web.json_response(body)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("{}".format(e))
|
||||||
|
|
||||||
|
async def handle_appsvr_oauth_callback(self, request):
|
||||||
|
try:
|
||||||
|
token = bumper.token_by_authcode(request.query["code"])
|
||||||
|
oauth = bumper.user_add_oauth(token["userid"])
|
||||||
|
body = {
|
||||||
|
"code": 0,
|
||||||
|
"data": oauth.toResponse(),
|
||||||
|
"ret": "ok",
|
||||||
|
"todo": "result"
|
||||||
|
}
|
||||||
|
|
||||||
|
return web.json_response(body)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("{}".format(e))
|
||||||
|
|
||||||
|
|
||||||
|
plugin = portal_api_appsvr()
|
||||||
|
|
|
||||||
38
bumper/plugins/bumper_confserver_portal_ecms.py
Normal file
38
bumper/plugins/bumper_confserver_portal_ecms.py
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from aiohttp import web
|
||||||
|
|
||||||
|
from bumper import plugins
|
||||||
|
from bumper.models import *
|
||||||
|
|
||||||
|
|
||||||
|
class portal_api_ecms(plugins.ConfServerApp):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.name = "portal_api_ecms"
|
||||||
|
self.plugin_type = "sub_api"
|
||||||
|
self.sub_api = "portal_api"
|
||||||
|
|
||||||
|
self.routes = [
|
||||||
|
web.route("*", "/ecms/app/ad/res", self.handle_ad_res, name="portal_api_ecms_ad_res"),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
|
||||||
|
|
||||||
|
async def handle_ad_res(self, request):
|
||||||
|
try:
|
||||||
|
body = {
|
||||||
|
"code": 0,
|
||||||
|
"data": [],
|
||||||
|
"message": "success",
|
||||||
|
"success": True
|
||||||
|
}
|
||||||
|
|
||||||
|
return web.json_response(body)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("{}".format(e))
|
||||||
|
|
||||||
|
|
||||||
|
plugin = portal_api_ecms()
|
||||||
|
|
@ -1,17 +1,15 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import asyncio
|
|
||||||
from aiohttp import web
|
|
||||||
from bumper import plugins
|
|
||||||
import logging
|
import logging
|
||||||
import bumper
|
|
||||||
from bumper.models import *
|
|
||||||
from bumper import plugins
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
import os
|
|
||||||
import string
|
|
||||||
import random
|
import random
|
||||||
|
import string
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
|
from aiohttp import web
|
||||||
|
|
||||||
|
from bumper import plugins
|
||||||
|
from bumper.models import *
|
||||||
|
|
||||||
|
|
||||||
class portal_api_lg(plugins.ConfServerApp):
|
class portal_api_lg(plugins.ConfServerApp):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
@ -28,10 +26,11 @@ class portal_api_lg(plugins.ConfServerApp):
|
||||||
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
|
async def handle_lg_log(self, request): # EcoVacs Home
|
||||||
|
randomid = "".join(random.sample(string.ascii_letters, 6))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
json_body = json.loads(await request.text())
|
json_body = json.loads(await request.text())
|
||||||
|
|
||||||
randomid = "".join(random.sample(string.ascii_letters, 6))
|
|
||||||
did = json_body["did"]
|
did = json_body["did"]
|
||||||
|
|
||||||
botdetails = bumper.bot_get(did)
|
botdetails = bumper.bot_get(did)
|
||||||
|
|
@ -53,7 +52,7 @@ class portal_api_lg(plugins.ConfServerApp):
|
||||||
json_body["payloadType"] = "x"
|
json_body["payloadType"] = "x"
|
||||||
|
|
||||||
if not "payload" in json_body:
|
if not "payload" in json_body:
|
||||||
#json_body["payload"] = ""
|
# json_body["payload"] = ""
|
||||||
if json_body["td"] == "GetCleanLogs":
|
if json_body["td"] == "GetCleanLogs":
|
||||||
json_body["td"] = "q"
|
json_body["td"] = "q"
|
||||||
json_body["payload"] = '<ctl count="30"/>'
|
json_body["payload"] = '<ctl count="30"/>'
|
||||||
|
|
@ -77,8 +76,8 @@ class portal_api_lg(plugins.ConfServerApp):
|
||||||
"area": l.attrib['a'],
|
"area": l.attrib['a'],
|
||||||
"last": l.attrib['l'],
|
"last": l.attrib['l'],
|
||||||
"cleanType": l.attrib['t'],
|
"cleanType": l.attrib['t'],
|
||||||
#imageUrl allows for providing images of cleanings, something to look into later
|
# imageUrl allows for providing images of cleanings, something to look into later
|
||||||
#"imageUrl": "https://localhost:8007",
|
# "imageUrl": "https://localhost:8007",
|
||||||
}
|
}
|
||||||
logs.append(cleanlog)
|
logs.append(cleanlog)
|
||||||
body = {
|
body = {
|
||||||
|
|
@ -98,11 +97,12 @@ class portal_api_lg(plugins.ConfServerApp):
|
||||||
json_body["toId"]
|
json_body["toId"]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
body = {"id": randomid, "errno": bumper.ERR_COMMON, "ret": "fail"}
|
|
||||||
return web.json_response(body)
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("{}".format(e))
|
logging.exception("{}".format(e))
|
||||||
|
|
||||||
plugin = portal_api_lg()
|
body = {"id": randomid, "errno": bumper.ERR_COMMON, "ret": "fail"}
|
||||||
|
return web.json_response(body)
|
||||||
|
|
||||||
|
|
||||||
|
plugin = portal_api_lg()
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import asyncio
|
|
||||||
from aiohttp import web
|
|
||||||
from bumper import plugins
|
|
||||||
import logging
|
import logging
|
||||||
import bumper
|
|
||||||
from bumper.models import *
|
|
||||||
from bumper import plugins
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from aiohttp import web
|
||||||
|
|
||||||
|
from bumper import plugins
|
||||||
|
from bumper.models import *
|
||||||
|
|
||||||
|
|
||||||
class portal_api_pim(plugins.ConfServerApp):
|
class portal_api_pim(plugins.ConfServerApp):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
@ -17,13 +16,12 @@ class portal_api_pim(plugins.ConfServerApp):
|
||||||
self.sub_api = "portal_api"
|
self.sub_api = "portal_api"
|
||||||
|
|
||||||
self.routes = [
|
self.routes = [
|
||||||
|
|
||||||
web.route("*", "/pim/product/getProductIotMap", self.handle_getProductIotMap, name="portal_api_pim_getProductIotMap"),
|
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/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/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/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/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
|
||||||
|
|
@ -43,7 +41,7 @@ class portal_api_pim(plugins.ConfServerApp):
|
||||||
try:
|
try:
|
||||||
fileID = request.match_info.get("id", "")
|
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:
|
except Exception as e:
|
||||||
logging.exception("{}".format(e))
|
logging.exception("{}".format(e))
|
||||||
|
|
@ -76,6 +74,34 @@ class portal_api_pim(plugins.ConfServerApp):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("{}".format(e))
|
logging.exception("{}".format(e))
|
||||||
|
|
||||||
|
async def handle_product_config_batch(self, request):
|
||||||
|
try:
|
||||||
|
json_body = json.loads(await request.text())
|
||||||
|
data = []
|
||||||
|
for pid in json_body["pids"]:
|
||||||
|
for productConfig in productConfigBatch:
|
||||||
|
if pid == productConfig["pid"]:
|
||||||
|
data.append(productConfig)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# not found in productConfigBatch
|
||||||
|
# some devices don't have any product configuration
|
||||||
|
data.append({
|
||||||
|
"cfg": {},
|
||||||
|
"pid": pid
|
||||||
|
})
|
||||||
|
|
||||||
|
body = {
|
||||||
|
"code": 200,
|
||||||
|
"data": data,
|
||||||
|
"message": "success"
|
||||||
|
}
|
||||||
|
return web.json_response(body)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("{}".format(e))
|
||||||
|
|
||||||
|
|
||||||
plugin = portal_api_pim()
|
plugin = portal_api_pim()
|
||||||
|
|
||||||
confignetAllResponse = {
|
confignetAllResponse = {
|
||||||
|
|
@ -2681,3 +2707,174 @@ configGroupsResponse = {
|
||||||
"contactUS": "helper"
|
"contactUS": "helper"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
productConfigBatch = [
|
||||||
|
{
|
||||||
|
"pid": "5e14196a6e71b80001b60fda",
|
||||||
|
"cfg": {
|
||||||
|
"supported": {
|
||||||
|
"tmallstand": False,
|
||||||
|
"video": False,
|
||||||
|
"battery": True,
|
||||||
|
"clean": True,
|
||||||
|
"charge": True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pid": "5e8e8d8a032edd8457c66bfb",
|
||||||
|
"cfg": {
|
||||||
|
"supported": {
|
||||||
|
"tmallstand": False,
|
||||||
|
"video": False,
|
||||||
|
"battery": True,
|
||||||
|
"clean": True,
|
||||||
|
"charge": True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pid": "5c19a91ca1e6ee000178224a",
|
||||||
|
"cfg": {
|
||||||
|
"supported": {
|
||||||
|
"tmallstand": False,
|
||||||
|
"video": False,
|
||||||
|
"battery": True,
|
||||||
|
"clean": True,
|
||||||
|
"charge": True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pid": "5e8e8d2a032edd3c03c66bf7",
|
||||||
|
"cfg": {
|
||||||
|
"supported": {
|
||||||
|
"tmallstand": False,
|
||||||
|
"video": False,
|
||||||
|
"battery": True,
|
||||||
|
"clean": True,
|
||||||
|
"charge": True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pid": "5de0d86ed88546000195239a",
|
||||||
|
"cfg": {
|
||||||
|
"supported": {
|
||||||
|
"tmallstand": False,
|
||||||
|
"video": True,
|
||||||
|
"battery": True,
|
||||||
|
"clean": True,
|
||||||
|
"charge": True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pid": "5c19a8f3a1e6ee0001782247",
|
||||||
|
"cfg": {
|
||||||
|
"supported": {
|
||||||
|
"tmallstand": False,
|
||||||
|
"video": False,
|
||||||
|
"battery": True,
|
||||||
|
"clean": True,
|
||||||
|
"charge": True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pid": "5e698a6306f6de52c264c61b",
|
||||||
|
"cfg": {
|
||||||
|
"supported": {
|
||||||
|
"tmallstand": False,
|
||||||
|
"video": False,
|
||||||
|
"battery": True,
|
||||||
|
"clean": True,
|
||||||
|
"charge": True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pid": "5e699a4106f6de83ea64c620",
|
||||||
|
"cfg": {
|
||||||
|
"supported": {
|
||||||
|
"tmallstand": False,
|
||||||
|
"video": False,
|
||||||
|
"battery": True,
|
||||||
|
"clean": True,
|
||||||
|
"charge": True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pid": "5edd998afdd6a30008da039b",
|
||||||
|
"cfg": {
|
||||||
|
"supported": {
|
||||||
|
"tmallstand": False,
|
||||||
|
"video": True,
|
||||||
|
"battery": True,
|
||||||
|
"clean": True,
|
||||||
|
"charge": True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pid": "5edd9a4075f2fc000636086c",
|
||||||
|
"cfg": {
|
||||||
|
"supported": {
|
||||||
|
"tmallstand": False,
|
||||||
|
"video": True,
|
||||||
|
"battery": True,
|
||||||
|
"clean": True,
|
||||||
|
"charge": True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pid": "5ed5e4d3a719ea460ec3216c",
|
||||||
|
"cfg": {
|
||||||
|
"supported": {
|
||||||
|
"tmallstand": False,
|
||||||
|
"video": False,
|
||||||
|
"battery": True,
|
||||||
|
"clean": True,
|
||||||
|
"charge": True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pid": "5f88195e6cf8de0008ed7c11",
|
||||||
|
"cfg": {
|
||||||
|
"supported": {
|
||||||
|
"tmallstand": False,
|
||||||
|
"video": False,
|
||||||
|
"battery": True,
|
||||||
|
"clean": True,
|
||||||
|
"charge": True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pid": "5f8819156cf8de0008ed7c0d",
|
||||||
|
"cfg": {
|
||||||
|
"supported": {
|
||||||
|
"tmallstand": False,
|
||||||
|
"video": False,
|
||||||
|
"battery": True,
|
||||||
|
"clean": True,
|
||||||
|
"charge": True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pid": "5fa105c6d16a99000667eb54",
|
||||||
|
"cfg": {
|
||||||
|
"supported": {
|
||||||
|
"tmallstand": False,
|
||||||
|
"video": False,
|
||||||
|
"battery": True,
|
||||||
|
"clean": True,
|
||||||
|
"charge": True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
|
||||||
39
bumper/plugins/bumper_confserver_portal_rapp.py
Normal file
39
bumper/plugins/bumper_confserver_portal_rapp.py
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
from aiohttp import web
|
||||||
|
import logging
|
||||||
|
from bumper.models import *
|
||||||
|
from bumper import plugins
|
||||||
|
|
||||||
|
|
||||||
|
class api_rapp(plugins.ConfServerApp):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.name = "api_rapp"
|
||||||
|
self.plugin_type = "sub_api"
|
||||||
|
self.sub_api = "portal_api"
|
||||||
|
|
||||||
|
self.routes = [
|
||||||
|
web.route("*", "/rapp/sds/user/data/map/get", self.handle_map_get, name="api_rapp"),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
|
||||||
|
|
||||||
|
async def handle_map_get(self, request):
|
||||||
|
try:
|
||||||
|
body = {
|
||||||
|
"code": 0,
|
||||||
|
"data": {
|
||||||
|
"data": {
|
||||||
|
"name": "My Home"
|
||||||
|
},
|
||||||
|
"tag": None
|
||||||
|
},
|
||||||
|
"message": "success"
|
||||||
|
}
|
||||||
|
|
||||||
|
return web.json_response(body)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("{}".format(e))
|
||||||
|
|
||||||
|
plugin = api_rapp()
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import asyncio
|
|
||||||
from aiohttp import web
|
|
||||||
from bumper import plugins
|
|
||||||
import logging
|
import logging
|
||||||
import bumper
|
|
||||||
from bumper.models import *
|
from aiohttp import web
|
||||||
|
|
||||||
from bumper import plugins
|
from bumper import plugins
|
||||||
from datetime import datetime, timedelta
|
from bumper.models import *
|
||||||
|
|
||||||
|
|
||||||
class v1_private_common(plugins.ConfServerApp):
|
class v1_private_common(plugins.ConfServerApp):
|
||||||
|
|
@ -21,6 +19,10 @@ class v1_private_common(plugins.ConfServerApp):
|
||||||
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/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/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/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"),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -110,6 +112,351 @@ class v1_private_common(plugins.ConfServerApp):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("{}".format(e))
|
logging.exception("{}".format(e))
|
||||||
|
|
||||||
|
async def handle_getConfig(self, request):
|
||||||
|
try:
|
||||||
|
data = []
|
||||||
|
for key in request.query["keys"].split(','):
|
||||||
|
data.append({
|
||||||
|
"key": key,
|
||||||
|
"value": "Y"
|
||||||
|
})
|
||||||
|
|
||||||
|
body = {
|
||||||
|
"code": bumper.RETURN_API_SUCCESS,
|
||||||
|
"data": data,
|
||||||
|
"msg": "操作成功",
|
||||||
|
"success": True,
|
||||||
|
"time": self.get_milli_time(datetime.utcnow().timestamp()),
|
||||||
|
}
|
||||||
|
|
||||||
|
return web.json_response(body)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("{}".format(e))
|
||||||
|
|
||||||
|
async def handle_getAreas(self, request):
|
||||||
|
try:
|
||||||
|
body = {
|
||||||
|
"code": bumper.RETURN_API_SUCCESS,
|
||||||
|
"data": AREA_LIST,
|
||||||
|
"msg": "操作成功",
|
||||||
|
"success": True,
|
||||||
|
"time": self.get_milli_time(datetime.utcnow().timestamp()),
|
||||||
|
}
|
||||||
|
|
||||||
|
return web.json_response(body)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("{}".format(e))
|
||||||
|
|
||||||
|
async def handle_getAgreementURLBatch(self, request): # EcoVacs Home
|
||||||
|
try:
|
||||||
|
body = {
|
||||||
|
"code": bumper.RETURN_API_SUCCESS,
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"acceptTime": None,
|
||||||
|
"force": None,
|
||||||
|
"id": "20180804040641_7d746faf18b8cb22a50d145598fe4c90",
|
||||||
|
"type": "USER",
|
||||||
|
"url": "https://gl-eu-wap.ecovacs.com/content/agreement?id=20180804040641_7d746faf18b8cb22a50d145598fe4c90&language=EN",
|
||||||
|
"version": "1.03"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"acceptTime": None,
|
||||||
|
"force": None,
|
||||||
|
"id": "20180804040245_4e7c56dfb7ebd3b81b1f2747d0859fac",
|
||||||
|
"type": "PRIVACY",
|
||||||
|
"url": "https://gl-eu-wap.ecovacs.com/content/agreement?id=20180804040245_4e7c56dfb7ebd3b81b1f2747d0859fac&language=EN",
|
||||||
|
"version": "1.03"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"msg": "操作成功",
|
||||||
|
"success": True,
|
||||||
|
"time": self.get_milli_time(datetime.utcnow().timestamp()),
|
||||||
|
}
|
||||||
|
|
||||||
|
return web.json_response(body)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("{}".format(e))
|
||||||
|
|
||||||
|
async def handle_getTimestamp(self, request): # EcoVacs Home
|
||||||
|
try:
|
||||||
|
time = self.get_milli_time(datetime.utcnow().timestamp())
|
||||||
|
body = {
|
||||||
|
"code": bumper.RETURN_API_SUCCESS,
|
||||||
|
"data": {
|
||||||
|
"timestamp": time
|
||||||
|
},
|
||||||
|
"msg": "操作成功",
|
||||||
|
"success": True,
|
||||||
|
"time": time,
|
||||||
|
}
|
||||||
|
|
||||||
|
return web.json_response(body)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("{}".format(e))
|
||||||
|
|
||||||
|
|
||||||
plugin = v1_private_common()
|
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"}]}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,9 @@ class v1_private_user(plugins.ConfServerApp):
|
||||||
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkAgreementBatch", self.handle_checkAgreement,name="v1_user_checkAgreementBatch"),
|
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/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/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/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:
|
# Direct register from app:
|
||||||
# /{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/directRegister
|
# /{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/directRegister
|
||||||
#Register by email
|
#Register by email
|
||||||
|
|
@ -144,5 +147,37 @@ class v1_private_user(plugins.ConfServerApp):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("{}".format(e))
|
logging.exception("{}".format(e))
|
||||||
|
|
||||||
|
async def handle_changeArea(self, request):
|
||||||
|
try:
|
||||||
|
body = {
|
||||||
|
"code": bumper.RETURN_API_SUCCESS,
|
||||||
|
"data": {
|
||||||
|
"isNeedReLogin": "N"
|
||||||
|
},
|
||||||
|
"msg": "操作成功",
|
||||||
|
"success": True,
|
||||||
|
"time": self.get_milli_time(datetime.utcnow().timestamp()),
|
||||||
|
}
|
||||||
|
|
||||||
|
return web.json_response(body)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("{}".format(e))
|
||||||
|
|
||||||
|
async def handle_acceptAgreementBatch(self, request):
|
||||||
|
try:
|
||||||
|
body = {
|
||||||
|
"code": bumper.RETURN_API_SUCCESS,
|
||||||
|
"data": None,
|
||||||
|
"msg": "操作成功",
|
||||||
|
"success": True,
|
||||||
|
"time": self.get_milli_time(datetime.utcnow().timestamp()),
|
||||||
|
}
|
||||||
|
|
||||||
|
return web.json_response(body)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("{}".format(e))
|
||||||
|
|
||||||
plugin = v1_private_user()
|
plugin = v1_private_user()
|
||||||
|
|
||||||
|
|
|
||||||
58
bumper/plugins/bumper_confserver_v1_private_userSetting.py
Normal file
58
bumper/plugins/bumper_confserver_v1_private_userSetting.py
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
from aiohttp import web
|
||||||
|
import logging
|
||||||
|
import bumper
|
||||||
|
from bumper import plugins
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
|
class v1_private_userSetting(plugins.ConfServerApp):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
self.name = "v1_private_userSetting"
|
||||||
|
self.plugin_type = "sub_api"
|
||||||
|
self.sub_api = "api_v1"
|
||||||
|
|
||||||
|
self.routes = [
|
||||||
|
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/userSetting/getSuggestionSetting", self.handle_getSuggestionSetting,name="v1_userSetting_getSuggestionSetting"),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
|
||||||
|
|
||||||
|
async def handle_getSuggestionSetting(self, request):
|
||||||
|
try:
|
||||||
|
|
||||||
|
body = {
|
||||||
|
"code": bumper.RETURN_API_SUCCESS,
|
||||||
|
"data": {
|
||||||
|
"acceptSuggestion": "Y",
|
||||||
|
"itemList": [
|
||||||
|
{
|
||||||
|
"name": "Aktionen/Angebote/Ereignisse",
|
||||||
|
"settingKey": "MARKETING",
|
||||||
|
"val": "Y"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Benutzerbefragung",
|
||||||
|
"settingKey": "QUESTIONNAIRE",
|
||||||
|
"val": "Y"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Produkt-Upgrade/Hilfe für Benutzer",
|
||||||
|
"settingKey": "INTRODUCTION",
|
||||||
|
"val": "Y"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"msg": "操作成功",
|
||||||
|
"time": self.get_milli_time(datetime.utcnow().timestamp()),
|
||||||
|
}
|
||||||
|
|
||||||
|
return web.json_response(body)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("{}".format(e))
|
||||||
|
|
||||||
|
|
||||||
|
plugin = v1_private_userSetting()
|
||||||
|
|
@ -4,3 +4,7 @@ ecouser.net
|
||||||
*.ecouser.net
|
*.ecouser.net
|
||||||
ecovacs.net
|
ecovacs.net
|
||||||
*.ecovacs.net
|
*.ecovacs.net
|
||||||
|
*.ww.ecouser.net
|
||||||
|
*.dc-eu.ww.ecouser.net
|
||||||
|
*.dc.ww.ecouser.net
|
||||||
|
*.area.ww.ecouser.net
|
||||||
|
If it's of any help, here are all of the DNS queries and TLS server names for requests the app makes when communicating with my older Deebot 901:
If it's of any help, here are all of the DNS queries and TLS server names for requests the app makes when communicating with my older Deebot 901:
```
bigdata-international.ecovacs.com
recommender.ecovacs.com
gl-us-api.ecovacs.com
api-app.ecouser.net
mq-ww.ecouser.net
```
@puddly your urls are already covered by the wildcards domain above @puddly your urls are already covered by the wildcards domain above
Indeed they are. I'm located in the USA and and your v2 changes work fine with the latest version of the app on Android (thank you!), so either the app "upgrades" to different domains when communicating with newer-generation bots, or these existing domains in your PR are enough. Indeed they are. I'm located in the USA and and your v2 changes work fine with the latest version of the app on Android (thank you!), so either the app "upgrades" to different domains when communicating with newer-generation bots, or these existing domains in your PR are enough.
|
|||||||
|
|
@ -53,9 +53,9 @@ I get it, you don't trust create_certs and want to do it manually. The easiest
|
||||||
|
|
||||||
### Create a Root CA
|
### Create a Root CA
|
||||||
|
|
||||||
1. Create csrconfig.txt for use in later commands
|
1. Create csrconfig_ca.txt for use in later commands
|
||||||
|
|
||||||
***csrconfig.txt***
|
***csrconfig_ca.txt***
|
||||||
````
|
````
|
||||||
[ req ]
|
[ req ]
|
||||||
default_md = sha256
|
default_md = sha256
|
||||||
|
|
@ -70,9 +70,9 @@ keyUsage=critical,keyCertSign,cRLSign
|
||||||
basicConstraints=critical,CA:true,pathlen:1
|
basicConstraints=critical,CA:true,pathlen:1
|
||||||
````
|
````
|
||||||
|
|
||||||
1. Create certconfig.txt for use in later commands
|
1. Create certconfig_ca.txt for use in later commands
|
||||||
|
|
||||||
***certconfig.txt***
|
***certconfig_ca.txt***
|
||||||
````
|
````
|
||||||
[ req ]
|
[ req ]
|
||||||
default_md = sha256
|
default_md = sha256
|
||||||
|
|
@ -91,21 +91,21 @@ basicConstraints=critical,CA:true,pathlen:1
|
||||||
|
|
||||||
1. Generate the RSA private key
|
1. Generate the RSA private key
|
||||||
|
|
||||||
`openssl genpkey -outform PEM -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out priv.key`
|
`openssl genrsa -out ca.key 4096`
|
||||||
|
|
||||||
1. Create the CSR
|
1. Create the CSR
|
||||||
|
|
||||||
`openssl req -new -nodes -key priv.key -config csrconfig.txt -out cert.csr`
|
`openssl req -new -nodes -key ca.key -config csrconfig_ca.txt -out ca.csr`
|
||||||
|
|
||||||
1. Self-sign your CSR
|
1. Self-sign your CSR
|
||||||
|
|
||||||
`openssl req -x509 -nodes -in cert.csr -days 3650 -key priv.key -config certconfig.txt -extensions req_ext -out cert.crt`
|
`openssl req -x509 -nodes -in ca.csr -days 1095 -key ca.key -config certconfig_ca.txt -extensions req_ext -out ca.crt`
|
||||||
|
|
||||||
### Create the Server Certificate
|
### Create the Server Certificate
|
||||||
|
|
||||||
1. Create csrconfig.txt for use in later commands
|
1. Create csrconfig_bumper.txt for use in later commands
|
||||||
|
|
||||||
***csrconfig.txt***
|
***csrconfig_bumper.txt***
|
||||||
````
|
````
|
||||||
[ req ]
|
[ req ]
|
||||||
default_md = sha256
|
default_md = sha256
|
||||||
|
|
@ -127,11 +127,15 @@ DNS.2 = ecouser.net
|
||||||
DNS.3 = *.ecouser.net
|
DNS.3 = *.ecouser.net
|
||||||
DNS.4 = ecovacs.net
|
DNS.4 = ecovacs.net
|
||||||
DNS.5 = *.ecovacs.net
|
DNS.5 = *.ecovacs.net
|
||||||
|
DNS.6 = *.ww.ecouser.net
|
||||||
|
DNS.7 = *.dc-eu.ww.ecouser.net
|
||||||
|
DNS.8 = *.dc.ww.ecouser.net
|
||||||
|
DNS.9 = *.area.ww.ecouser.net
|
||||||
````
|
````
|
||||||
|
|
||||||
1. Create certconfig.txt for use in later commands
|
1. Create certconfig_bumper.txt for use in later commands
|
||||||
|
|
||||||
***certconfig.txt***
|
***certconfig_bumper.txt***
|
||||||
````
|
````
|
||||||
[ req ]
|
[ req ]
|
||||||
default_md = sha256
|
default_md = sha256
|
||||||
|
|
@ -155,19 +159,23 @@ DNS.2 = ecouser.net
|
||||||
DNS.3 = *.ecouser.net
|
DNS.3 = *.ecouser.net
|
||||||
DNS.4 = ecovacs.net
|
DNS.4 = ecovacs.net
|
||||||
DNS.5 = *.ecovacs.net
|
DNS.5 = *.ecovacs.net
|
||||||
|
DNS.6 = *.ww.ecouser.net
|
||||||
|
DNS.7 = *.dc-eu.ww.ecouser.net
|
||||||
|
DNS.8 = *.dc.ww.ecouser.net
|
||||||
|
DNS.9 = *.area.ww.ecouser.net
|
||||||
````
|
````
|
||||||
|
|
||||||
1. Generate the RSA private key
|
1. Generate the RSA private key
|
||||||
|
|
||||||
`openssl genpkey -outform PEM -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out priv.key`
|
`openssl genrsa -out bumper.key 4096`
|
||||||
|
|
||||||
1. Create the CSR
|
1. Create the CSR
|
||||||
|
|
||||||
`openssl req -new -nodes -key priv.key -config csrconfig.txt -out cert.csr`
|
`openssl req -new -nodes -key bumper.key -config csrconfig_bumper.txt -out bumper.csr`
|
||||||
|
|
||||||
1. Sign your CSR with a root CA cert
|
1. Sign your CSR with a root CA cert
|
||||||
|
|
||||||
`openssl x509 -req -in cert.csr -days 3650 -CA ca.crt -CAkey priv.key -extfile certconfig.txt -extensions req_ext -CAserial /tmp/tmp-10593TSH1OlVSxC7C -CAcreateserial -out cert.crt`
|
`openssl x509 -req -in bumper.csr -days 365 -CA ca.crt -CAkey ca.key -extfile certconfig_bumper.txt -extensions req_ext -CAcreateserial -out bumper.crt`
|
||||||
|
|
||||||
## Using a Custom CA/Self
|
## Using a Custom CA/Self
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ If overriding DNS for the top-level domains isn't an option, you'll need to conf
|
||||||
**Note:** Depending on country, your phone/robot may be using a different domain. Most of these domains contain country-specific placeholders.
|
**Note:** Depending on country, your phone/robot may be using a different domain. Most of these domains contain country-specific placeholders.
|
||||||
|
|
||||||
Not all domains have been documented at this point, and this list will be updated as more are identified/seen. The preferred way to ensure Bumper works is to override the full domains as above.
|
Not all domains have been documented at this point, and this list will be updated as more are identified/seen. The preferred way to ensure Bumper works is to override the full domains as above.
|
||||||
|
**Note:** The app dynamically gets the required domains from the endpoint `api/appsvr/service/list` and therefore ecovacs can use different domains for different models.
|
||||||
|
|
||||||
Replacement Examples:
|
Replacement Examples:
|
||||||
|
|
||||||
|
|
@ -46,6 +47,7 @@ Replacement Examples:
|
||||||
| `eco-{countrycode}-api.ecovacs.com` | Used for Login |
|
| `eco-{countrycode}-api.ecovacs.com` | Used for Login |
|
||||||
| `gl-{countrycode}-api.ecovacs.com` | Used by EcoVacs Home app |
|
| `gl-{countrycode}-api.ecovacs.com` | Used by EcoVacs Home app |
|
||||||
| `gl-{countrycode}-openapi.ecovacs.com` | Used by EcoVacs Home app |
|
| `gl-{countrycode}-openapi.ecovacs.com` | Used by EcoVacs Home app |
|
||||||
|
| `portal.ecouser.net` | Used for Login and Rest API |
|
||||||
| `portal-{countrycode}.ecouser.net` | Used for Login and Rest API |
|
| `portal-{countrycode}.ecouser.net` | Used for Login and Rest API |
|
||||||
| `portal-{region}.ecouser.net` | Used for Login and Rest API |
|
| `portal-{region}.ecouser.net` | Used for Login and Rest API |
|
||||||
| `portal-ww.ecouser.net` | Used for various Rest APIs |
|
| `portal-ww.ecouser.net` | Used for various Rest APIs |
|
||||||
|
|
@ -59,4 +61,11 @@ Replacement Examples:
|
||||||
| `recommender.ecovacs.com` | Used by Ecovacs Home app |
|
| `recommender.ecovacs.com` | Used by Ecovacs Home app |
|
||||||
| `bigdata-international.ecovacs.com` | Telemetry/tracking |
|
| `bigdata-international.ecovacs.com` | Telemetry/tracking |
|
||||||
| `bigdata-northamerica.ecovacs.com` | Telemetry/tracking |
|
| `bigdata-northamerica.ecovacs.com` | Telemetry/tracking |
|
||||||
|
| `bigdata-europe.ecovacs.com` | Telemetry/tracking |
|
||||||
| `bigdata-{unknown regions}.ecovacs.com` | Telemetry/tracking |
|
| `bigdata-{unknown regions}.ecovacs.com` | Telemetry/tracking |
|
||||||
|
| `api-app.ww.ecouser.net` | Api for App (v2+) |
|
||||||
|
| `api-app.dc-{region}.ww.ecouser.net` | Api for App (v2+) |
|
||||||
|
| `users-base.dc-{region}.ww.ecouser.net` | Accounts for App (v2+) |
|
||||||
|
| `jmq-ngiot-{region}.dc.ww.ecouser.net` | MQTT for App (v2+) |
|
||||||
|
| `api-rop.dc-{region}.ww.ecouser.net` | App (v2+) |
|
||||||
|
| `jmq-ngiot-{region}.area.ww.ecouser.net`| App (v2+) |
|
||||||
|
|
@ -42,3 +42,13 @@ Optionally you can map existing directories for logs, data, and certs.
|
||||||
````
|
````
|
||||||
docker run -it -e "BUMPER_ANNOUNCE_IP=X.X.X.X" -p 443:443 -p 8007:8007 -p 8883:8883 -p 5223:5223 -v /home/user/bumper/data:/bumper/data --name bumper bmartin5692/bumper
|
docker run -it -e "BUMPER_ANNOUNCE_IP=X.X.X.X" -p 443:443 -p 8007:8007 -p 8883:8883 -p 5223:5223 -v /home/user/bumper/data:/bumper/data --name bumper bmartin5692/bumper
|
||||||
````
|
````
|
||||||
|
|
||||||
|
# Docker-compose
|
||||||
|
|
||||||
|
A docker-compose example can be found in the ["example" folder](https://github.com/bmartin5692/bumper/tree/master/example/docker-compose).
|
||||||
|
|
||||||
|
The docker-compose starts two services:
|
||||||
|
- bumper itself
|
||||||
|
- nginx proxy, which redirects MQTT traffic on port `443` to port `8883`
|
||||||
|
|
||||||
|
The redirection is required as the app v2+ and robots with a newer firmware are connecting to the mqtt server on port 433.
|
||||||
|
|
|
||||||
45
example/docker-compose/docker-compose.yaml
Normal file
45
example/docker-compose/docker-compose.yaml
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
---
|
||||||
|
version: "3.6"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
bumper:
|
||||||
|
internal: true
|
||||||
|
|
||||||
|
services:
|
||||||
|
nginx:
|
||||||
|
depends_on:
|
||||||
|
- bumper
|
||||||
|
image: nginx:alpine
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
bumper:
|
||||||
|
ports:
|
||||||
|
- 443:443
|
||||||
|
- 5223:5223
|
||||||
|
- 8007:8007
|
||||||
|
- 8883:8883
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- /etc/timezone:/etc/timezone:ro
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
- ./nginx/:/etc/nginx:ro # See config file below
|
||||||
|
|
||||||
|
bumper:
|
||||||
|
image: bmartin5692/bumper
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
bumper:
|
||||||
|
|
||||||
|
environment:
|
||||||
|
PUID: 1000
|
||||||
|
PGID: 1000
|
||||||
|
TZ: Europe/Rome
|
||||||
|
BUMPER_ANNOUNCE_IP: XXX # Insert your IP
|
||||||
|
BUMPER_LISTEN: 0.0.0.0
|
||||||
|
# BUMPER_DEBUG: "true"
|
||||||
|
LOG_TO_STDOUT: "true"
|
||||||
|
volumes:
|
||||||
|
- /etc/timezone:/etc/timezone:ro
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
- ./config:/bumper/data
|
||||||
|
- ./certs:/bumper/certs
|
||||||
41
example/docker-compose/nginx/nginx.conf
Normal file
41
example/docker-compose/nginx/nginx.conf
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
error_log stderr;
|
||||||
|
pid /var/run/nginx.pid;
|
||||||
|
|
||||||
|
events { }
|
||||||
|
|
||||||
|
stream {
|
||||||
|
resolver 127.0.0.11 ipv6=off; #docker dns server
|
||||||
|
map_hash_bucket_size 64;
|
||||||
|
|
||||||
|
map $ssl_preread_server_name $internalport {
|
||||||
|
# redirect all requests, which contain "mq" in the SNI -> MQTT
|
||||||
|
~^.*(mq).*\.eco(vacs|user)\.(net|com)$ 8883;
|
||||||
|
|
||||||
|
# the rest of eco(user|vacs) requests
|
||||||
|
~^.*eco(vacs|user)\.(net|com)$ 443;
|
||||||
|
|
||||||
|
# mapping default to MQTT as the bots are connecting directly to the ip without SNI
|
||||||
|
default 8883;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443;
|
||||||
|
ssl_preread on;
|
||||||
|
proxy_pass bumper:$internalport;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 5223;
|
||||||
|
proxy_pass bumper:5223;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 8007;
|
||||||
|
proxy_pass bumper:8007;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 8883;
|
||||||
|
proxy_pass bumper:8883;
|
||||||
|
}
|
||||||
|
}
|
||||||
14
tests/test_models.py
Normal file
14
tests/test_models.py
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
from bumper import OAuth
|
||||||
|
|
||||||
|
|
||||||
|
def test_oauth():
|
||||||
|
userId = "test"
|
||||||
|
o_auth = OAuth.create_new(userId)
|
||||||
|
assert o_auth is not None
|
||||||
|
assert o_auth.userId == userId
|
||||||
|
assert o_auth.access_token is not None
|
||||||
|
assert o_auth.expire_at is not None
|
||||||
|
assert o_auth.refresh_token is not None
|
||||||
|
|
||||||
|
data = o_auth.toResponse()
|
||||||
|
assert data is not None
|
||||||
Loading…
Add table
Add a link
Reference in a new issue
@bmartin5692 Can you please verify, which domains the app is using in the us? :)