Compare commits
11 commits
master
...
wip-proxyR
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe8e4b0615 | ||
|
|
f97c07ac7b | ||
|
|
346895eff3 | ||
|
|
c2bcb13769 | ||
|
|
a4a77708e5 | ||
|
|
904167a8c5 | ||
|
|
bdc452b99c | ||
|
|
3b7b31af47 | ||
|
|
3e8ad68eb2 | ||
|
|
aca40e0e57 | ||
|
|
d428c5019b |
24 changed files with 1412 additions and 2859 deletions
|
|
@ -1,3 +1,20 @@
|
|||
# Logs
|
||||
logs
|
||||
|
||||
# Tests
|
||||
tests
|
||||
|
||||
# Examples
|
||||
examples
|
||||
|
||||
# Docs
|
||||
docs
|
||||
|
||||
# Certs
|
||||
certs
|
||||
|
||||
.coverage
|
||||
|
||||
# Markdown
|
||||
*.md
|
||||
|
||||
|
|
|
|||
10
Dockerfile
10
Dockerfile
|
|
@ -22,15 +22,11 @@ RUN apk add build-base
|
|||
|
||||
FROM base
|
||||
|
||||
COPY requirements.txt /requirements.txt
|
||||
COPY . /bumper
|
||||
|
||||
WORKDIR /bumper
|
||||
|
||||
# install required python packages
|
||||
RUN pip3 install -r requirements.txt
|
||||
|
||||
WORKDIR /bumper
|
||||
|
||||
# Copy only required folders instead of all
|
||||
COPY create_certs/ create_certs/
|
||||
COPY bumper/ bumper/
|
||||
|
||||
ENTRYPOINT ["python3", "-m", "bumper"]
|
||||
|
|
|
|||
|
|
@ -26,13 +26,10 @@ def strtobool(strbool):
|
|||
# os.environ['PYTHONASYNCIODEBUG'] = '1' # Uncomment to enable ASYNCIODEBUG
|
||||
bumper_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
|
||||
|
||||
log_to_stdout = os.environ.get("LOG_TO_STDOUT")
|
||||
|
||||
# Set defaults from environment variables first
|
||||
# Folders
|
||||
if not log_to_stdout:
|
||||
logs_dir = os.environ.get("BUMPER_LOGS") or os.path.join(bumper_dir, "logs")
|
||||
os.makedirs(logs_dir, exist_ok=True) # Ensure logs directory exists or create
|
||||
logs_dir = os.environ.get("BUMPER_LOGS") or os.path.join(bumper_dir, "logs")
|
||||
os.makedirs(logs_dir, exist_ok=True) # Ensure logs directory exists or create
|
||||
data_dir = os.environ.get("BUMPER_DATA") or os.path.join(bumper_dir, "data")
|
||||
os.makedirs(data_dir, exist_ok=True) # Ensure data directory exists or create
|
||||
certs_dir = os.environ.get("BUMPER_CERTS") or os.path.join(bumper_dir, "certs")
|
||||
|
|
@ -57,8 +54,8 @@ bumper_announce_ip = os.environ.get("BUMPER_ANNOUNCE_IP") or bumper_listen
|
|||
bumper_debug = strtobool(os.environ.get("BUMPER_DEBUG")) or False
|
||||
use_auth = False
|
||||
token_validity_seconds = 3600 # 1 hour
|
||||
oauth_validity_days = 15
|
||||
db = None
|
||||
bumper_proxy_mode = strtobool(os.environ.get("BUMPER_PROXY_MODE")) or False
|
||||
|
||||
mqtt_server = None
|
||||
mqtt_helperbot = None
|
||||
|
|
@ -84,102 +81,83 @@ logformat = logging.Formatter(
|
|||
)
|
||||
|
||||
bumperlog = logging.getLogger("bumper")
|
||||
if not log_to_stdout:
|
||||
bumper_rotate = RotatingFileHandler("logs/bumper.log", maxBytes=5000000, backupCount=5)
|
||||
bumper_rotate.setFormatter(logformat)
|
||||
bumperlog.addHandler(bumper_rotate)
|
||||
else:
|
||||
bumperlog.addHandler(logging.StreamHandler(sys.stdout))
|
||||
bumper_rotate = RotatingFileHandler("logs/bumper.log", maxBytes=5000000, backupCount=5)
|
||||
bumper_rotate.setFormatter(logformat)
|
||||
bumperlog.addHandler(bumper_rotate)
|
||||
# Override the logging level
|
||||
# bumperlog.setLevel(logging.INFO)
|
||||
|
||||
confserverlog = logging.getLogger("confserver")
|
||||
if not log_to_stdout:
|
||||
conf_rotate = RotatingFileHandler(
|
||||
"logs/confserver.log", maxBytes=5000000, backupCount=5
|
||||
)
|
||||
conf_rotate.setFormatter(logformat)
|
||||
confserverlog.addHandler(conf_rotate)
|
||||
else:
|
||||
confserverlog.addHandler(logging.StreamHandler(sys.stdout))
|
||||
conf_rotate = RotatingFileHandler(
|
||||
"logs/confserver.log", maxBytes=5000000, backupCount=5
|
||||
)
|
||||
conf_rotate.setFormatter(logformat)
|
||||
confserverlog.addHandler(conf_rotate)
|
||||
# Override the logging level
|
||||
# confserverlog.setLevel(logging.INFO)
|
||||
|
||||
mqttserverlog = logging.getLogger("mqttserver")
|
||||
if not log_to_stdout:
|
||||
mqtt_rotate = RotatingFileHandler(
|
||||
"logs/mqttserver.log", maxBytes=5000000, backupCount=5
|
||||
)
|
||||
mqtt_rotate.setFormatter(logformat)
|
||||
mqttserverlog.addHandler(mqtt_rotate)
|
||||
else:
|
||||
mqttserverlog.addHandler(logging.StreamHandler(sys.stdout))
|
||||
mqtt_rotate = RotatingFileHandler(
|
||||
"logs/mqttserver.log", maxBytes=5000000, backupCount=5
|
||||
)
|
||||
mqtt_rotate.setFormatter(logformat)
|
||||
mqttserverlog.addHandler(mqtt_rotate)
|
||||
# Override the logging level
|
||||
# mqttserverlog.setLevel(logging.INFO)
|
||||
|
||||
proxymodelog = logging.getLogger("proxymode")
|
||||
proxymode_rotate = RotatingFileHandler(
|
||||
"logs/proxymode.log", maxBytes=5000000, backupCount=5
|
||||
)
|
||||
proxymode_rotate.setFormatter(logformat)
|
||||
proxymodelog.addHandler(proxymode_rotate)
|
||||
|
||||
# Override the logging level
|
||||
# mqttserverlog.setLevel(logging.INFO)
|
||||
|
||||
### Additional MQTT Logs
|
||||
translog = logging.getLogger("transitions")
|
||||
if not log_to_stdout:
|
||||
translog.addHandler(mqtt_rotate)
|
||||
else:
|
||||
translog.addHandler(logging.StreamHandler(sys.stdout))
|
||||
translog.addHandler(mqtt_rotate)
|
||||
translog.setLevel(logging.CRITICAL + 1) # Ignore this logger
|
||||
logging.getLogger("passlib").setLevel(logging.CRITICAL + 1) # Ignore this logger
|
||||
brokerlog = logging.getLogger("hbmqtt.broker")
|
||||
#brokerlog.setLevel(
|
||||
# logging.CRITICAL + 1
|
||||
#) # Ignore this logger #There are some sublogs that could be set if needed (.plugins)
|
||||
if not log_to_stdout:
|
||||
brokerlog.addHandler(mqtt_rotate)
|
||||
else:
|
||||
brokerlog.addHandler(logging.StreamHandler(sys.stdout))
|
||||
brokerlog.addHandler(mqtt_rotate)
|
||||
protolog = logging.getLogger("hbmqtt.mqtt.protocol")
|
||||
#protolog.setLevel(
|
||||
# logging.CRITICAL + 1
|
||||
#) # Ignore this logger
|
||||
if not log_to_stdout:
|
||||
protolog.addHandler(mqtt_rotate)
|
||||
else:
|
||||
protolog.addHandler(logging.StreamHandler(sys.stdout))
|
||||
protolog.addHandler(mqtt_rotate)
|
||||
clientlog = logging.getLogger("hbmqtt.client")
|
||||
#clientlog.setLevel(logging.CRITICAL + 1) # Ignore this logger
|
||||
if not log_to_stdout:
|
||||
clientlog.addHandler(mqtt_rotate)
|
||||
else:
|
||||
clientlog.addHandler(logging.StreamHandler(sys.stdout))
|
||||
clientlog.addHandler(mqtt_rotate)
|
||||
|
||||
helperbotlog = logging.getLogger("helperbot")
|
||||
if not log_to_stdout:
|
||||
helperbot_rotate = RotatingFileHandler(
|
||||
"logs/helperbot.log", maxBytes=5000000, backupCount=5
|
||||
)
|
||||
helperbot_rotate.setFormatter(logformat)
|
||||
helperbotlog.addHandler(helperbot_rotate)
|
||||
else:
|
||||
helperbotlog.addHandler(logging.StreamHandler(sys.stdout))
|
||||
helperbot_rotate = RotatingFileHandler(
|
||||
"logs/helperbot.log", maxBytes=5000000, backupCount=5
|
||||
)
|
||||
helperbot_rotate.setFormatter(logformat)
|
||||
helperbotlog.addHandler(helperbot_rotate)
|
||||
# Override the logging level
|
||||
# helperbotlog.setLevel(logging.INFO)
|
||||
|
||||
boterrorlog = logging.getLogger("boterror")
|
||||
if not log_to_stdout:
|
||||
boterrorlog_rotate = RotatingFileHandler(
|
||||
"logs/boterror.log", maxBytes=5000000, backupCount=5
|
||||
)
|
||||
boterrorlog_rotate.setFormatter(logformat)
|
||||
boterrorlog.addHandler(boterrorlog_rotate)
|
||||
else:
|
||||
boterrorlog.addHandler(logging.StreamHandler(sys.stdout))
|
||||
boterrorlog_rotate = RotatingFileHandler(
|
||||
"logs/boterror.log", maxBytes=5000000, backupCount=5
|
||||
)
|
||||
boterrorlog_rotate.setFormatter(logformat)
|
||||
boterrorlog.addHandler(boterrorlog_rotate)
|
||||
# Override the logging level
|
||||
# boterrorlog.setLevel(logging.INFO)
|
||||
|
||||
xmppserverlog = logging.getLogger("xmppserver")
|
||||
if not log_to_stdout:
|
||||
xmpp_rotate = RotatingFileHandler(
|
||||
"logs/xmppserver.log", maxBytes=5000000, backupCount=5
|
||||
)
|
||||
xmpp_rotate.setFormatter(logformat)
|
||||
xmppserverlog.addHandler(xmpp_rotate)
|
||||
else:
|
||||
xmppserverlog.addHandler(logging.StreamHandler(sys.stdout))
|
||||
xmpp_rotate = RotatingFileHandler(
|
||||
"logs/xmppserver.log", maxBytes=5000000, backupCount=5
|
||||
)
|
||||
xmpp_rotate.setFormatter(logformat)
|
||||
xmppserverlog.addHandler(xmpp_rotate)
|
||||
# Override the logging level
|
||||
# xmppserverlog.setLevel(logging.INFO)
|
||||
|
||||
|
|
@ -193,6 +171,11 @@ xmpp_listen_port = 5223
|
|||
|
||||
|
||||
async def start():
|
||||
#config_proxyMode_deleteTable() #delete existing proxymode table
|
||||
|
||||
#Reset xmpp/mqtt to false in database for bots and clients
|
||||
bot_reset_connectionStatus()
|
||||
client_reset_connectionStatus()
|
||||
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
|
|
@ -237,8 +220,9 @@ async def start():
|
|||
xmpp_server = XMPPServer((bumper_listen, xmpp_listen_port))
|
||||
|
||||
# Start MQTT Server
|
||||
# await start otherwise we get an error connecting the helper bot
|
||||
await asyncio.create_task(mqtt_server.broker_coro())
|
||||
asyncio.create_task(mqtt_server.broker_coro())
|
||||
|
||||
await asyncio.sleep(0.5) #Wait half a sec for broker to start
|
||||
|
||||
# Start MQTT Helperbot
|
||||
asyncio.create_task(mqtt_helperbot.start_helper_bot())
|
||||
|
|
@ -254,7 +238,22 @@ async def start():
|
|||
await asyncio.sleep(0.1)
|
||||
|
||||
# Start web servers
|
||||
conf_server.confserver_app()
|
||||
if bumper_proxy_mode:
|
||||
bumperlog.info("Proxy Mode Enabled")
|
||||
if config_proxyMode_countEntries() == 0: # check if proxymode servers are entered
|
||||
bumperlog.info("Proxy Mode - No Servers, Loading Defaults (US)")
|
||||
config_proxyMode_defaults() # set defaults if 0
|
||||
|
||||
configproxy = config_proxyMode_getall()
|
||||
cntentries = len(configproxy)
|
||||
proxymodelog.info(f"Loaded {cntentries} entries from proxyconfig")
|
||||
for entry in configproxy:
|
||||
proxymodelog.info(f"Config Entry {entry}")
|
||||
|
||||
conf_server.confserver_proxy_app()
|
||||
else:
|
||||
conf_server.confserver_app()
|
||||
|
||||
asyncio.create_task(conf_server.start_site(conf_server.app, address=bumper_listen, port=conf1_listen_port, usessl=True))
|
||||
asyncio.create_task(conf_server.start_site(conf_server.app, address=bumper_listen, port=conf2_listen_port, usessl=False))
|
||||
|
||||
|
|
@ -266,7 +265,6 @@ async def start():
|
|||
|
||||
async def maintenance():
|
||||
revoke_expired_tokens()
|
||||
revoke_expired_oauths()
|
||||
|
||||
|
||||
async def shutdown():
|
||||
|
|
@ -385,12 +383,17 @@ def main(argv=None):
|
|||
help="announce address to bots on checkin",
|
||||
)
|
||||
parser.add_argument("--debug", action="store_true", help="enable debug logs")
|
||||
parser.add_argument("--proxy-mode", action="store_true", help="enable proxy mode")
|
||||
|
||||
args = parser.parse_args(args=argv)
|
||||
|
||||
if args.debug:
|
||||
bumper_debug = True
|
||||
|
||||
if args.proxy_mode:
|
||||
global bumper_proxy_mode
|
||||
bumper_proxy_mode = True
|
||||
|
||||
if args.listen:
|
||||
bumper_listen = args.listen
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,23 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import ssl
|
||||
|
||||
import string
|
||||
import random
|
||||
import bumper
|
||||
import os
|
||||
from bumper.models import *
|
||||
from bumper import plugins
|
||||
from datetime import datetime, timedelta
|
||||
import asyncio
|
||||
from aiohttp import web
|
||||
import aiohttp
|
||||
import aiohttp_jinja2
|
||||
import jinja2
|
||||
from aiohttp import web
|
||||
import uuid
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from bumper import plugins
|
||||
from bumper.models import *
|
||||
|
||||
|
||||
class aiohttp_filter(logging.Filter):
|
||||
|
|
@ -35,6 +42,7 @@ logging.getLogger("aiohttp.access").addFilter(
|
|||
aiohttp_filter()
|
||||
) # Add logging filter above to aiohttp.access
|
||||
|
||||
proxymodelog = logging.getLogger("proxymode")
|
||||
|
||||
class ConfServer:
|
||||
def __init__(self, address, usessl=False):
|
||||
|
|
@ -49,6 +57,115 @@ class ConfServer:
|
|||
def get_milli_time(self, timetoconvert):
|
||||
return int(round(timetoconvert * 1000))
|
||||
|
||||
def confserver_proxy_app(self):
|
||||
|
||||
self.app = web.Application(middlewares=[
|
||||
self.log_all_requests,
|
||||
])
|
||||
aiohttp_jinja2.setup(self.app, loader=jinja2.FileSystemLoader(os.path.join(bumper.bumper_dir,"bumper","web","templates")))
|
||||
|
||||
|
||||
self.app.add_routes(
|
||||
[
|
||||
|
||||
web.get("/bot/remove/{did}", self.handle_RemoveBot, name='remove-bot'),
|
||||
web.get("/client/remove/{resource}", self.handle_RemoveClient, name='remove-client'),
|
||||
web.get("/restart_{service}", self.handle_RestartService, name='restart-service'),
|
||||
web.route("*", "/{path:.*}", self.handle_proxy, name="confserver_proxy"),
|
||||
]
|
||||
)
|
||||
|
||||
return self.app
|
||||
|
||||
async def handle_proxy(self, request):
|
||||
|
||||
try:
|
||||
ecoresp = ""
|
||||
|
||||
server_port = 443 #default to 443
|
||||
if "_SSLProtocolTransport" != type(request.transport).__name__ and "_SelectorSocketTransport" != type(request.transport).__name__: #check not ssl transport class
|
||||
if "_extra" in request.transport:
|
||||
if "sockname" in request.transport._extra:
|
||||
server_port = request.transport._extra["sockname"][1]
|
||||
|
||||
if request.raw_path == "/":
|
||||
return await self.handle_base(request)
|
||||
if request.raw_path == "/lookup.do":
|
||||
return await self.handle_lookup(request) #use bumper to handle lookup so bot gets Bumper IP and not Ecovacs
|
||||
|
||||
matchproxy = bumper.config_proxyMode_getServerIP("app", request.host)
|
||||
|
||||
if matchproxy:
|
||||
proxymodelog.info(f"Matched {request.host} to entry in proxyconfig!")
|
||||
ecorequest = f"{request.scheme}://{matchproxy}"
|
||||
else:
|
||||
proxymodelog.info(f"No match for {request.host} in proxyconfig!")
|
||||
if "ecovacs.com" in request.host:
|
||||
proxymodelog.info(f"ecovacs.com in {request.host} defaulting to ecovacs.com IP!")
|
||||
matchproxy = bumper.config_proxyMode_getServerIP("app", "ecovacs.com")
|
||||
ecorequest = f"{request.scheme}://{matchproxy}"
|
||||
elif "ecouser.net" in request.host:
|
||||
proxymodelog.info(f"ecouser.net in {request.host} defaulting to ecouser.net IP!")
|
||||
matchproxy = bumper.config_proxyMode_getServerIP("app", "ecouser.net")
|
||||
ecorequest = f"{request.scheme}://{matchproxy}"
|
||||
else:
|
||||
proxymodelog.info(f"No matches for {request.host} defaulting to ecovacs.com IP!")
|
||||
matchproxy = bumper.config_proxyMode_getServerIP("app", "ecovacs.com")
|
||||
ecorequest = f"{request.scheme}://{matchproxy}"
|
||||
|
||||
if server_port != 443:
|
||||
ecorequest = f"{ecorequest}:{server_port}"
|
||||
|
||||
proxymodelog.info(f"{request.host} - {ecorequest}")
|
||||
ecorequest = f"{ecorequest}{request.path_qs}"
|
||||
requestheaders = {'host': request.host}
|
||||
async with aiohttp.ClientSession(headers=requestheaders, connector=aiohttp.TCPConnector(verify_ssl=False)) as session:
|
||||
if request.content.total_bytes > 0:
|
||||
proxymodelog.info(f"HTTP Proxy Request to EcoVacs (body=true) (host:{request.host}) - {ecorequest} - {request._read_bytes}")
|
||||
if request.content_type == "application/x-www-form-urlencoded": # android apps use form
|
||||
fdata = await request.post()
|
||||
async with session.request(request.method, ecorequest, data=fdata) as resp:
|
||||
ecoresp = await resp.text()
|
||||
proxymodelog.info(f"HTTP Proxy Response from EcoVacs (URL: {ecorequest}) - (Status: {resp.status}) - {ecoresp}")
|
||||
else: # handle json
|
||||
jdata = request._read_bytes.decode('utf8')
|
||||
jdata = json.loads(jdata)
|
||||
async with session.request(request.method, ecorequest, json=jdata) as resp:
|
||||
ecoresp = await resp.text()
|
||||
proxymodelog.info(f"HTTP Proxy Response from EcoVacs (URL: {ecorequest}) - (Status: {resp.status}) - {ecoresp}")
|
||||
|
||||
else:
|
||||
proxymodelog.info(f"HTTP Proxy Request to EcoVacs (body=false) (host:{request.host}) - {ecorequest}")
|
||||
async with session.request(request.method, ecorequest) as resp:
|
||||
if resp.content_type == "application/octet-stream":
|
||||
ecoresp = await resp.read()
|
||||
proxymodelog.info(f"HTTP Proxy Response from EcoVacs (URL: {ecorequest}) - (Status: {resp.status}) - <BYTES CONTENT>")
|
||||
else:
|
||||
ecoresp = await resp.text()
|
||||
proxymodelog.info(f"HTTP Proxy Response from EcoVacs (URL: {ecorequest}) - (Status: {resp.status}) - {ecoresp}")
|
||||
|
||||
if resp.status == 200:
|
||||
if resp.content_type == "application/json":
|
||||
ecoresp = json.loads(ecoresp)
|
||||
return web.json_response(ecoresp)
|
||||
elif resp.content_type == "application/octet-stream":
|
||||
return web.Response(body=ecoresp)
|
||||
else:
|
||||
return web.Response(text=ecoresp)
|
||||
|
||||
else:
|
||||
return web.Response(text=ecoresp)
|
||||
|
||||
except asyncio.CancelledError as e:
|
||||
proxymodelog.error(f"Request cancelled or timeout - {ecorequest} - {jdata}")
|
||||
return web.Response(text="")
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
proxymodelog.exception("{}".format(e))
|
||||
return web.Response(text="")
|
||||
|
||||
|
||||
def confserver_app(self):
|
||||
self.app = web.Application(loop=asyncio.get_event_loop(), middlewares=[
|
||||
self.log_all_requests,
|
||||
|
|
@ -57,12 +174,13 @@ class ConfServer:
|
|||
|
||||
self.app.add_routes(
|
||||
[
|
||||
|
||||
web.get("", self.handle_base, name="base"),
|
||||
web.get("/bot/remove/{did}", self.handle_RemoveBot, name='remove-bot'),
|
||||
web.get("/client/remove/{resource}", self.handle_RemoveClient, name='remove-client'),
|
||||
web.get("/restart_{service}", self.handle_RestartService, name='restart-service'),
|
||||
web.post("/lookup.do", self.handle_lookup),
|
||||
web.post("/newauth.do", self.handle_newauth),
|
||||
|
||||
]
|
||||
)
|
||||
|
||||
|
|
@ -104,9 +222,11 @@ class ConfServer:
|
|||
|
||||
|
||||
async def start_site(self, app, address='localhost', port=8080, usessl=False):
|
||||
|
||||
runner = web.AppRunner(app)
|
||||
self.runners.append(runner)
|
||||
await runner.setup()
|
||||
|
||||
if usessl:
|
||||
ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||
ssl_ctx.load_cert_chain(bumper.server_cert, bumper.server_key)
|
||||
|
|
@ -124,6 +244,36 @@ class ConfServer:
|
|||
|
||||
await site.start()
|
||||
|
||||
def start_site_thread(self, app, address='localhost', port=8080, usessl=False):
|
||||
#test for new thread and loop
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
|
||||
runner = web.AppRunner(app)
|
||||
self.runners.append(runner)
|
||||
#await runner.setup()
|
||||
loop.run_until_complete(runner.setup()) #for thread test
|
||||
|
||||
if usessl:
|
||||
ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||
ssl_ctx.load_cert_chain(bumper.server_cert, bumper.server_key)
|
||||
site = web.TCPSite(
|
||||
runner,
|
||||
host=address,
|
||||
port=port,
|
||||
ssl_context=ssl_ctx,
|
||||
)
|
||||
|
||||
else:
|
||||
site = web.TCPSite(
|
||||
runner, host=address, port=port
|
||||
)
|
||||
|
||||
#await site.start()
|
||||
#for thread test
|
||||
loop.run_until_complete(site.start())
|
||||
loop.run_forever()
|
||||
|
||||
async def start_server(self):
|
||||
try:
|
||||
confserverlog.info(
|
||||
|
|
@ -211,18 +361,8 @@ class ConfServer:
|
|||
async def log_all_requests(self, request, handler):
|
||||
|
||||
if request._match_info.route.name not in self.excludelogging:
|
||||
to_log = {
|
||||
"request": {
|
||||
"route_name": f"{request.match_info.route.name}",
|
||||
"method": f"{request.method}",
|
||||
"path": f"{request.path}",
|
||||
"query_string": f"{request.query_string}",
|
||||
"raw_path": f"{request.raw_path}",
|
||||
"raw_headers": f'{",".join(map("{}".format, request.raw_headers))}',
|
||||
}
|
||||
}
|
||||
|
||||
try:
|
||||
postbody = None
|
||||
if request.content_length:
|
||||
if request.content_type == "application/x-www-form-urlencoded":
|
||||
postbody = await request.post()
|
||||
|
|
@ -236,33 +376,100 @@ class ConfServer:
|
|||
|
||||
else:
|
||||
postbody = await request.post()
|
||||
|
||||
to_log["request"]["body"] = f"{postbody}"
|
||||
else:
|
||||
postbody = None
|
||||
|
||||
response = await handler(request)
|
||||
if response is None:
|
||||
confserverlog.warning("Response was null!")
|
||||
confserverlog.warning(json.dumps(to_log))
|
||||
return response
|
||||
if response:
|
||||
if not "application/octet-stream" in response.content_type:
|
||||
try:
|
||||
logall = {
|
||||
"request": {
|
||||
"route_name": f"{request.match_info.route.name}",
|
||||
"method": f"{request.method}",
|
||||
"path": f"{request.path}",
|
||||
"query_string": f"{request.query_string}",
|
||||
"raw_path": f"{request.raw_path}",
|
||||
"raw_headers": f'{",".join(map("{}".format, request.raw_headers))}',
|
||||
"body": f"{postbody}",
|
||||
},
|
||||
|
||||
to_log["response"] = {
|
||||
"status": f"{response.status}",
|
||||
}
|
||||
if not "application/octet-stream" in response.content_type:
|
||||
to_log["response"]["body"] = f"{json.loads(response.body)}"
|
||||
"response": {
|
||||
#"response_body": f"{json.loads(response.body)}",
|
||||
"response_body": f"{json.loads(response.text)}",
|
||||
"status": f"{response.status}",
|
||||
}
|
||||
}
|
||||
except Exception as e:
|
||||
logall = {
|
||||
"request": {
|
||||
"route_name": f"{request.match_info.route.name}",
|
||||
"method": f"{request.method}",
|
||||
"path": f"{request.path}",
|
||||
"query_string": f"{request.query_string}",
|
||||
"raw_path": f"{request.raw_path}",
|
||||
"raw_headers": f'{",".join(map("{}".format, request.raw_headers))}',
|
||||
"body": f"{postbody}",
|
||||
},
|
||||
|
||||
confserverlog.debug(json.dumps(to_log))
|
||||
"response": {
|
||||
#"response_body": f"{json.loads(response.body)}",
|
||||
"response_body": f"{(response.text)}",
|
||||
"status": f"{response.status}",
|
||||
}
|
||||
}
|
||||
|
||||
else:
|
||||
logall = {
|
||||
"request": {
|
||||
"route_name": f"{request.match_info.route.name}",
|
||||
"method": f"{request.method}",
|
||||
"path": f"{request.path}",
|
||||
"query_string": f"{request.query_string}",
|
||||
"raw_path": f"{request.raw_path}",
|
||||
"raw_headers": f'{",".join(map("{}".format, request.raw_headers))}',
|
||||
"body": f"{postbody}",
|
||||
},
|
||||
|
||||
"response": {
|
||||
"status": f"{response.status}",
|
||||
}
|
||||
}
|
||||
|
||||
confserverlog.debug(json.dumps(logall))
|
||||
|
||||
return response
|
||||
|
||||
except web.HTTPNotFound as notfound:
|
||||
confserverlog.debug("Request path {} not found".format(request.raw_path))
|
||||
confserverlog.debug(json.dumps(to_log))
|
||||
requestlog = {
|
||||
"request": {
|
||||
"route_name": f"{request.match_info.route.name}",
|
||||
"method": f"{request.method}",
|
||||
"path": f"{request.path}",
|
||||
"query_string": f"{request.query_string}",
|
||||
"raw_path": f"{request.raw_path}",
|
||||
"raw_headers": f'{",".join(map("{}".format, request.raw_headers))}',
|
||||
"body": f"{postbody}",
|
||||
}
|
||||
}
|
||||
confserverlog.debug(json.dumps(requestlog))
|
||||
return notfound
|
||||
|
||||
except Exception as e:
|
||||
confserverlog.exception("{}".format(e))
|
||||
confserverlog.error(json.dumps(to_log))
|
||||
requestlog = {
|
||||
"request": {
|
||||
"route_name": f"{request.match_info.route.name}",
|
||||
"method": f"{request.method}",
|
||||
"path": f"{request.path}",
|
||||
"query_string": f"{request.query_string}",
|
||||
"raw_path": f"{request.raw_path}",
|
||||
"raw_headers": f'{",".join(map("{}".format, request.raw_headers))}',
|
||||
"body": f"{postbody}",
|
||||
}
|
||||
}
|
||||
confserverlog.debug(json.dumps(requestlog))
|
||||
return e
|
||||
|
||||
else:
|
||||
|
|
@ -466,26 +673,6 @@ class ConfServer:
|
|||
except Exception as e:
|
||||
confserverlog.exception("{}".format(e))
|
||||
|
||||
async def handle_newauth(self, request):
|
||||
# Bumper is only returning the submitted token. No reason yet to create another new token
|
||||
try:
|
||||
if request.content_type == "application/x-www-form-urlencoded":
|
||||
postbody = await request.post()
|
||||
else:
|
||||
postbody = json.loads(await request.text())
|
||||
|
||||
confserverlog.debug(postbody)
|
||||
|
||||
body = {
|
||||
"authCode": postbody["itToken"],
|
||||
"result": "ok",
|
||||
"todo": "result"
|
||||
}
|
||||
|
||||
return web.json_response(body)
|
||||
|
||||
except Exception as e:
|
||||
confserverlog.exception("{}".format(e))
|
||||
|
||||
async def disconnect(self):
|
||||
try:
|
||||
|
|
|
|||
154
bumper/db.py
154
bumper/db.py
|
|
@ -1,13 +1,13 @@
|
|||
#!/usr/bin/env python3
|
||||
import bumper
|
||||
from bumper.models import VacBotClient, VacBotDevice, BumperUser, EcoVacsHomeProducts
|
||||
from tinydb import TinyDB, Query
|
||||
from tinydb.storages import MemoryStorage
|
||||
from datetime import datetime, timedelta
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from tinydb import TinyDB, Query
|
||||
|
||||
import bumper
|
||||
from bumper.models import VacBotClient, VacBotDevice, BumperUser, EcoVacsHomeProducts, OAuth
|
||||
|
||||
bumperlog = logging.getLogger("bumper")
|
||||
|
||||
|
|
@ -32,10 +32,65 @@ def db_get():
|
|||
db.table("clients", cache_size=0)
|
||||
db.table("bots", cache_size=0)
|
||||
db.table("tokens", cache_size=0)
|
||||
db.table("oauth", cache_size=0)
|
||||
db.table("config_proxymode", cache_size=0)
|
||||
|
||||
return db
|
||||
|
||||
def config_proxyMode_deleteTable():
|
||||
opendb = db_get()
|
||||
opendb.purge_table("config_proxymode")
|
||||
|
||||
def config_proxyMode_defaults():
|
||||
defaults = [
|
||||
{"type":"app","host":"gl-us-api.ecovacs.com","ip":"47.252.51.29","match":"gl-"},
|
||||
{"type":"app","host":"gl-us-openapi.ecovacs.com","ip":"47.252.51.29"},
|
||||
{"type":"app","host":"portal-ww.ecouser.net","ip":"47.88.66.164","match":"portal-"},
|
||||
{"type":"app","host":"bigdata-northamerica.ecovacs.com","ip":"47.88.66.111"},
|
||||
{"type":"app","host":"bigdata-international.ecovacs.com","ip":"47.88.132.151","match":"bigdata-"},
|
||||
{"type":"app","host":"eco-us-api.ecovacs.com","ip":"47.89.135.130","match":"eco-"},
|
||||
{"type":"app","host":"ecovacs.com","ip":"47.90.210.46"},
|
||||
{"type":"app","host":"ecouser.net","ip":"116.62.93.217"},
|
||||
{"type":"mqtt_server","host":"mq-ww.ecouser.net","ip":"47.254.52.46"},
|
||||
]
|
||||
opendb = db_get()
|
||||
with opendb:
|
||||
config = opendb.table("config_proxymode")
|
||||
config.insert_multiple(defaults)
|
||||
|
||||
def config_proxyMode_getServerIP(type, host):
|
||||
opendb = db_get()
|
||||
with opendb:
|
||||
proxyconfig = opendb.table("config_proxymode")
|
||||
proxy = Query()
|
||||
if type == "mqtt_server":
|
||||
entry = proxyconfig.get((proxy.type == type))
|
||||
|
||||
else:
|
||||
entry = proxyconfig.get((proxy.type == type) & (proxy.host == host))
|
||||
|
||||
if entry:
|
||||
return entry["ip"]
|
||||
|
||||
else:
|
||||
proxylist = proxyconfig.search(Query())
|
||||
for proxy in proxylist: # check for sub matches
|
||||
if "match" in proxy:
|
||||
if proxy["match"] in host:
|
||||
return proxy["ip"]
|
||||
return None
|
||||
|
||||
def config_proxyMode_countEntries():
|
||||
opendb = db_get()
|
||||
with opendb:
|
||||
config = opendb.table("config_proxymode")
|
||||
return len(config)
|
||||
|
||||
def config_proxyMode_getall():
|
||||
opendb = db_get()
|
||||
with opendb:
|
||||
config = opendb.table("config_proxymode")
|
||||
return config.search(Query())
|
||||
|
||||
|
||||
def user_add(userid):
|
||||
newuser = BumperUser()
|
||||
|
|
@ -203,55 +258,6 @@ def user_revoke_authcode(userid, token, authcode):
|
|||
)
|
||||
|
||||
|
||||
def revoke_expired_oauths():
|
||||
opendb = db_get()
|
||||
with opendb:
|
||||
table = opendb.table("oauth")
|
||||
entries = table.all()
|
||||
|
||||
for i in entries:
|
||||
oauth = OAuth(**i)
|
||||
if datetime.now() >= datetime.fromisoformat(oauth.expire_at):
|
||||
bumperlog.debug(
|
||||
"Removing oauth {} due to expiration".format(oauth.access_token)
|
||||
)
|
||||
table.remove(doc_ids=[i.doc_id])
|
||||
|
||||
|
||||
def user_revoke_expired_oauths(userid):
|
||||
opendb = db_get()
|
||||
with opendb:
|
||||
table = opendb.table("oauth")
|
||||
search = table.search(Query().userid == userid)
|
||||
for i in search:
|
||||
oauth = OAuth(**i)
|
||||
if datetime.now() >= datetime.fromisoformat(oauth.expire_at):
|
||||
bumperlog.debug(
|
||||
"Removing oauth {} due to expiration".format(oauth.access_token)
|
||||
)
|
||||
table.remove(doc_ids=[i.doc_id])
|
||||
|
||||
|
||||
def user_add_oauth(userid) -> OAuth:
|
||||
user_revoke_expired_oauths(userid)
|
||||
opendb = db_get()
|
||||
with opendb:
|
||||
table = opendb.table("oauth")
|
||||
entry = table.get(Query().userid == userid)
|
||||
if entry:
|
||||
return OAuth(**entry)
|
||||
else:
|
||||
oauth = OAuth.create_new(userid)
|
||||
bumperlog.debug("Adding oauth {} for userid {}".format(oauth.access_token, userid))
|
||||
table.insert(oauth.toDB())
|
||||
return oauth
|
||||
|
||||
|
||||
def token_by_authcode(authcode):
|
||||
tokens = db_get().table("tokens")
|
||||
return tokens.get(Query().authcode == authcode)
|
||||
|
||||
|
||||
def get_disconnected_xmpp_clients():
|
||||
clients = db_get().table("clients")
|
||||
Client = Query()
|
||||
|
|
@ -264,8 +270,8 @@ def check_authcode(uid, authcode):
|
|||
tmpauth = tokens.get(
|
||||
(Query().authcode == authcode)
|
||||
& ( # Match authcode
|
||||
(Query().userid == uid.replace("fuid_", ""))
|
||||
| (Query().userid == "fuid_{}".format(uid))
|
||||
(Query().userid == uid.replace("fuid_", ""))
|
||||
| (Query().userid == "fuid_{}".format(uid))
|
||||
) # Userid with or without fuid_
|
||||
)
|
||||
if tmpauth:
|
||||
|
|
@ -296,8 +302,8 @@ def check_token(uid, token):
|
|||
tmpauth = tokens.get(
|
||||
(Query().token == token)
|
||||
& ( # Match token
|
||||
(Query().userid == uid.replace("fuid_", ""))
|
||||
| (Query().userid == "fuid_{}".format(uid))
|
||||
(Query().userid == uid.replace("fuid_", ""))
|
||||
| (Query().userid == "fuid_{}".format(uid))
|
||||
) # Userid with or without fuid_
|
||||
)
|
||||
if tmpauth:
|
||||
|
|
@ -325,7 +331,7 @@ def bot_add(sn, did, devclass, resource, company):
|
|||
bot = bot_get(did)
|
||||
if not bot: # Not existing bot in database
|
||||
if (
|
||||
not devclass == "" or "@" not in sn or "tmp" not in sn
|
||||
not devclass == "" or "@" not in sn or "tmp" not in sn
|
||||
): # try to prevent bad additions to the bot list
|
||||
bumperlog.info(
|
||||
"Adding new bot with SN: {} DID: {}".format(newbot.name, newbot.did)
|
||||
|
|
@ -339,6 +345,11 @@ def bot_remove(did):
|
|||
if bot:
|
||||
bots.remove(doc_ids=[bot.doc_id])
|
||||
|
||||
def bot_reset_connectionStatus():
|
||||
bots = db_get().table("bots")
|
||||
for bot in bots:
|
||||
bot_set_mqtt(bot["did"], False)
|
||||
bot_set_xmpp(bot["did"], False)
|
||||
|
||||
def bot_get(did):
|
||||
bots = db_get().table("bots")
|
||||
|
|
@ -352,21 +363,6 @@ def bot_toEcoVacsHome_JSON(bot): # EcoVacs Home
|
|||
bot["UILogicId"] = botprod["product"]["UILogicId"]
|
||||
bot["ota"] = botprod["product"]["ota"]
|
||||
bot["icon"] = botprod["product"]["iconUrl"]
|
||||
bot["model"] = botprod["product"]["model"]
|
||||
bot["pip"] = botprod["product"]["_id"]
|
||||
bot["deviceName"] = botprod["product"]["name"]
|
||||
bot["materialNo"] = botprod["product"]["materialNo"]
|
||||
bot["product_category"] = "DEEBOT" if botprod["product"]["name"].startswith("DEEBOT") else "UNKNOWN"
|
||||
# bot["updateInfo"] = {
|
||||
# "changeLog": "",
|
||||
# "needUpdate": False
|
||||
# }
|
||||
# bot["service"] = {
|
||||
# "jmq": "jmq-ngiot-eu.dc.ww.ecouser.net",
|
||||
# "mqs": "api-ngiot.dc-as.ww.ecouser.net"
|
||||
# }
|
||||
bot["status"] = 1 if bot["mqtt_connection"] or bot["xmpp_connection"] else 0
|
||||
|
||||
return json.dumps(
|
||||
bot, default=lambda o: o.__dict__, sort_keys=False
|
||||
) # , indent=4)
|
||||
|
|
@ -410,6 +406,11 @@ def client_add(userid, realm, resource):
|
|||
bumperlog.info("Adding new client with resource {}".format(newclient.resource))
|
||||
client_full_upsert(newclient.asdict())
|
||||
|
||||
def client_reset_connectionStatus():
|
||||
clients = db_get().table("clients")
|
||||
for client in clients:
|
||||
client_set_mqtt(client["resource"], False)
|
||||
client_set_xmpp(client["resource"], False)
|
||||
|
||||
def client_remove(resource):
|
||||
clients = db_get().table("clients")
|
||||
|
|
@ -417,7 +418,6 @@ def client_remove(resource):
|
|||
if client:
|
||||
clients.remove(doc_ids=[client.doc_id])
|
||||
|
||||
|
||||
def client_get(resource):
|
||||
clients = db_get().table("clients")
|
||||
Client = Query()
|
||||
|
|
|
|||
2353
bumper/models.py
2353
bumper/models.py
File diff suppressed because it is too large
Load diff
|
|
@ -14,10 +14,21 @@ import json
|
|||
from datetime import datetime, timedelta
|
||||
import bumper
|
||||
from passlib.apps import custom_app_context as pwd_context
|
||||
import ssl
|
||||
import tempfile
|
||||
|
||||
from urllib.parse import urlparse, urlunparse
|
||||
from hbmqtt.mqtt.protocol.client_handler import ClientProtocolHandler
|
||||
from hbmqtt.adapters import StreamReaderAdapter, StreamWriterAdapter, WebSocketsReader, WebSocketsWriter
|
||||
from websockets.uri import InvalidURI
|
||||
from websockets.exceptions import InvalidHandshake
|
||||
from hbmqtt.mqtt.protocol.handler import ProtocolHandlerException
|
||||
from hbmqtt.mqtt.connack import CONNECTION_ACCEPTED
|
||||
|
||||
helperbotlog = logging.getLogger("helperbot")
|
||||
boterrorlog = logging.getLogger("boterror")
|
||||
mqttserverlog = logging.getLogger("mqttserver")
|
||||
proxymodelog = logging.getLogger("proxymode")
|
||||
|
||||
class MQTTHelperBot:
|
||||
|
||||
|
|
@ -44,9 +55,7 @@ class MQTTHelperBot:
|
|||
)
|
||||
await self.Client.subscribe(
|
||||
[
|
||||
("iot/p2p/+/+/+/+/helperbot/bumper/helperbot/+/+/+", QOS_0),
|
||||
("iot/p2p/+", QOS_0),
|
||||
("iot/atr/+", QOS_0),
|
||||
("iot/#", QOS_0),
|
||||
]
|
||||
)
|
||||
|
||||
|
|
@ -216,8 +225,118 @@ class MQTTServer:
|
|||
except Exception as e:
|
||||
mqttserverlog.exception("{}".format(e))
|
||||
|
||||
class BumperProxyModeMQTTClient(MQTTClient):
|
||||
ecohelpername = ""
|
||||
async def _connect_coro(self): #Override default to ignore ssl verification
|
||||
kwargs = dict()
|
||||
|
||||
# Decode URI attributes
|
||||
uri_attributes = urlparse(self.session.broker_uri)
|
||||
scheme = uri_attributes.scheme
|
||||
secure = True if scheme in ('mqtts', 'wss') else False
|
||||
self.session.username = self.session.username if self.session.username else uri_attributes.username
|
||||
self.session.password = self.session.password if self.session.password else uri_attributes.password
|
||||
self.session.remote_address = uri_attributes.hostname
|
||||
self.session.remote_port = uri_attributes.port
|
||||
if scheme in ('mqtt', 'mqtts') and not self.session.remote_port:
|
||||
self.session.remote_port = 8883 if scheme == 'mqtts' else 1883
|
||||
if scheme in ('ws', 'wss') and not self.session.remote_port:
|
||||
self.session.remote_port = 443 if scheme == 'wss' else 80
|
||||
if scheme in ('ws', 'wss'):
|
||||
# Rewrite URI to conform to https://tools.ietf.org/html/rfc6455#section-3
|
||||
uri = (scheme, self.session.remote_address + ":" + str(self.session.remote_port), uri_attributes[2],
|
||||
uri_attributes[3], uri_attributes[4], uri_attributes[5])
|
||||
self.session.broker_uri = urlunparse(uri)
|
||||
# Init protocol handler
|
||||
#if not self._handler:
|
||||
self._handler = ClientProtocolHandler(self.plugins_manager, loop=self._loop)
|
||||
|
||||
if secure:
|
||||
sc = ssl.create_default_context(
|
||||
ssl.Purpose.SERVER_AUTH,
|
||||
cafile=self.session.cafile,
|
||||
capath=self.session.capath,
|
||||
cadata=self.session.cadata)
|
||||
if 'certfile' in self.config and 'keyfile' in self.config:
|
||||
sc.load_cert_chain(self.config['certfile'], self.config['keyfile'])
|
||||
if 'check_hostname' in self.config and isinstance(self.config['check_hostname'], bool):
|
||||
sc.check_hostname = self.config['check_hostname']
|
||||
|
||||
sc.verify_mode = ssl.CERT_NONE #Ignore verify of cert
|
||||
kwargs['ssl'] = sc
|
||||
|
||||
try:
|
||||
reader = None
|
||||
writer = None
|
||||
self._connected_state.clear()
|
||||
# Open connection
|
||||
if scheme in ('mqtt', 'mqtts'):
|
||||
conn_reader, conn_writer = \
|
||||
await asyncio.open_connection(
|
||||
self.session.remote_address,
|
||||
self.session.remote_port, loop=self._loop, **kwargs)
|
||||
reader = StreamReaderAdapter(conn_reader)
|
||||
writer = StreamWriterAdapter(conn_writer)
|
||||
elif scheme in ('ws', 'wss'):
|
||||
websocket = await websockets.connect(
|
||||
self.session.broker_uri,
|
||||
subprotocols=['mqtt'],
|
||||
loop=self._loop,
|
||||
extra_headers=self.extra_headers,
|
||||
**kwargs)
|
||||
reader = WebSocketsReader(websocket)
|
||||
writer = WebSocketsWriter(websocket)
|
||||
# Start MQTT protocol
|
||||
self._handler.attach(self.session, reader, writer)
|
||||
return_code = await self._handler.mqtt_connect()
|
||||
if return_code is not CONNECTION_ACCEPTED:
|
||||
self.session.transitions.disconnect()
|
||||
self.logger.warning("Connection rejected with code '%s'" % return_code)
|
||||
exc = ConnectException("Connection rejected by broker")
|
||||
exc.return_code = return_code
|
||||
raise exc
|
||||
else:
|
||||
# Handle MQTT protocol
|
||||
await self._handler.start()
|
||||
self.session.transitions.connect()
|
||||
self._connected_state.set()
|
||||
self.logger.debug("connected to %s:%s" % (self.session.remote_address, self.session.remote_port))
|
||||
return return_code
|
||||
except InvalidURI as iuri:
|
||||
self.logger.warning("connection failed: invalid URI '%s'" % self.session.broker_uri)
|
||||
self.session.transitions.disconnect()
|
||||
raise ConnectException("connection failed: invalid URI '%s'" % self.session.broker_uri, iuri)
|
||||
except InvalidHandshake as ihs:
|
||||
self.logger.warning("connection failed: invalid websocket handshake")
|
||||
self.session.transitions.disconnect()
|
||||
raise ConnectException("connection failed: invalid websocket handshake", ihs)
|
||||
except (ProtocolHandlerException, ConnectionError, OSError) as e:
|
||||
self.logger.warning("MQTT connection failed: %r" % e)
|
||||
self.session.transitions.disconnect()
|
||||
raise ConnectException(e)
|
||||
|
||||
async def get_msg(self):
|
||||
try:
|
||||
while self._connected_state._value:
|
||||
message = await self.deliver_message()
|
||||
msgdata = str(message.data.decode("utf-8"))
|
||||
|
||||
proxymodelog.info(f"MQTT Proxy Client - Message Received From Ecovacs - Topic: {message.topic} - Message: {msgdata}")
|
||||
ttopic = message.topic.split("/")
|
||||
self.ecohelpername = ttopic[3]
|
||||
ttopic[3] = "proxyhelper"
|
||||
ttopic_comb = "/".join(ttopic)
|
||||
proxymodelog.info(f"MQTT Proxy Client - Converted Topic From {message.topic} TO {ttopic_comb}")
|
||||
proxymodelog.info(f"MQTT Proxy Client - Proxy Forward Message to Helperbot - Topic: {ttopic_comb} - Message: {msgdata.encode()}")
|
||||
await bumper.mqtt_helperbot.Client.publish(
|
||||
ttopic_comb, msgdata.encode(), QOS_0
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
proxymodelog.error(f"MQTT Proxy Client - get_msg Exception - {e}")
|
||||
|
||||
class BumperMQTTServer_Plugin:
|
||||
proxyclients = {}
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
try:
|
||||
|
|
@ -232,6 +351,8 @@ class BumperMQTTServer_Plugin:
|
|||
except Exception as e:
|
||||
mqttserverlog.exception("{}".format(e))
|
||||
|
||||
|
||||
|
||||
async def authenticate(self, *args, **kwargs):
|
||||
authenticated = False
|
||||
|
||||
|
|
@ -257,6 +378,32 @@ class BumperMQTTServer_Plugin:
|
|||
mqttserverlog.info(f"Bumper Authentication Success - Bot - SN: {username} - DID: {didsplit[0]} - Class: {tmpbotdetail[0]}")
|
||||
authenticated = True
|
||||
|
||||
if authenticated and bumper.bumper_proxy_mode:
|
||||
mqtt_server = bumper.config_proxyMode_getServerIP("mqtt_server","")
|
||||
if mqtt_server:
|
||||
proxymodelog.info(f"MQTT Proxy Mode - Using server {mqtt_server}")
|
||||
else:
|
||||
proxymodelog.error(f"MQTT Proxy Mode - No server found! Load defaults or set mqtt_server in config_proxymode table!")
|
||||
proxymodelog.exception(f"MQTT Proxy Mode - Exiting due to no MQTT Server configured!")
|
||||
exit(1)
|
||||
|
||||
proxymodelog.info(f"MQTT Proxy Mode - Proxy Bot to MQTT - Client_id: {client_id} - Username: {username}")
|
||||
|
||||
self.proxyclients[client_id] = BumperProxyModeMQTTClient(
|
||||
client_id=client_id, config={"check_hostname": False}
|
||||
)
|
||||
|
||||
try:
|
||||
await self.proxyclients[client_id].connect(
|
||||
f"mqtts://{username}:{password}@{mqtt_server}:8883",
|
||||
)
|
||||
except Exception as e:
|
||||
mqttserverlog.error(f"MQTT Proxy Mode - Exception connecting with proxy to ecovacs - {e}")
|
||||
pass
|
||||
proxymodelog.info(f"MQTT Proxy Mode - Proxy Bot Connected - Client_id: {client_id}")
|
||||
asyncio.create_task(self.proxyclients[client_id].get_msg())
|
||||
|
||||
|
||||
else:
|
||||
tmpclientdetail = str(didsplit[1]).split("/")
|
||||
userid = didsplit[0]
|
||||
|
|
@ -327,6 +474,20 @@ class BumperMQTTServer_Plugin:
|
|||
except FileNotFoundError:
|
||||
self.context.logger.warning(f"Password file {password_file} not found")
|
||||
|
||||
async def on_broker_client_subscribed(self, client_id, topic, qos):
|
||||
if bumper.bumper_proxy_mode: #if proxy mode, also subscribe on ecovacs server
|
||||
if client_id in self.proxyclients:
|
||||
await self.proxyclients[client_id].subscribe(
|
||||
[
|
||||
(topic, qos)
|
||||
]
|
||||
)
|
||||
else:
|
||||
proxymodelog.info(f"MQTT Proxy Mode - New MQTT Topic Subscription - Client: {client_id} - Topic: {topic}")
|
||||
|
||||
#return
|
||||
#pass
|
||||
|
||||
async def on_broker_client_connected(self, client_id):
|
||||
|
||||
didsplit = str(client_id).split("@")
|
||||
|
|
@ -336,16 +497,39 @@ class BumperMQTTServer_Plugin:
|
|||
bumper.bot_set_mqtt(bot["did"], True)
|
||||
return
|
||||
|
||||
clientresource = didsplit[1].split("/")[1]
|
||||
client = bumper.client_get(clientresource)
|
||||
if client:
|
||||
bumper.client_set_mqtt(client["resource"], True)
|
||||
return
|
||||
if len(didsplit) > 1:
|
||||
clientresource = didsplit[1].split("/")[1]
|
||||
client = bumper.client_get(clientresource)
|
||||
if client:
|
||||
bumper.client_set_mqtt(client["resource"], True)
|
||||
return
|
||||
|
||||
async def on_broker_message_received(self, client_id, message):
|
||||
self.handle_helperbot_msg(client_id, message)
|
||||
await self.handle_helperbot_msg(client_id, message)
|
||||
|
||||
async def handle_helperbot_msg(self, client_id, message):
|
||||
if bumper.bumper_proxy_mode:
|
||||
if client_id in self.proxyclients:
|
||||
msgdata = str(message.data.decode("utf-8"))
|
||||
if not str(message.topic).split("/")[3] == "proxyhelper": # if from proxyhelper, don't send back to ecovacs...yet
|
||||
if str(message.topic).split("/")[6] == "proxyhelper":
|
||||
ttopic = message.topic.split("/")
|
||||
ttopic[6] = self.proxyclients[client_id].ecohelpername
|
||||
ttopic_join = "/".join(ttopic)
|
||||
proxymodelog.info(f"MQTT Proxy Client - Bot Message Converted Topic From {message.topic} TO {ttopic_join} with message: {msgdata}")
|
||||
else:
|
||||
ttopic_join = message.topic
|
||||
proxymodelog.info(f"MQTT Proxy Client - Bot Message From {ttopic_join} with message: {msgdata}")
|
||||
|
||||
try:
|
||||
# Send back to ecovacs
|
||||
proxymodelog.info(f"MQTT Proxy Client - Proxy Forward Message to Ecovacs - Topic: {ttopic_join} - Message: {msgdata.encode()}")
|
||||
await self.proxyclients[client_id].publish(
|
||||
ttopic_join, msgdata.encode(), message.qos
|
||||
)
|
||||
except Exception as e:
|
||||
proxymodelog.error(f"MQTT Proxy Client - Forwarding to Ecovacs Exception - {e}")
|
||||
|
||||
def handle_helperbot_msg(self, client_id, message):
|
||||
|
||||
if str(message.topic).split("/")[6] == "helperbot":
|
||||
# Response to command
|
||||
|
|
@ -407,6 +591,10 @@ class BumperMQTTServer_Plugin:
|
|||
|
||||
async def on_broker_client_disconnected(self, client_id):
|
||||
|
||||
if bumper.bumper_proxy_mode:
|
||||
if client_id in self.proxyclients:
|
||||
await self.proxyclients[client_id].disconnect()
|
||||
|
||||
didsplit = str(client_id).split("@")
|
||||
|
||||
bot = bumper.bot_get(didsplit[0])
|
||||
|
|
@ -414,8 +602,9 @@ class BumperMQTTServer_Plugin:
|
|||
bumper.bot_set_mqtt(bot["did"], False)
|
||||
return
|
||||
|
||||
clientresource = didsplit[1].split("/")[1]
|
||||
client = bumper.client_get(clientresource)
|
||||
if client:
|
||||
bumper.client_set_mqtt(client["resource"], False)
|
||||
return
|
||||
if len(didsplit) > 1:
|
||||
clientresource = didsplit[1].split("/")[1]
|
||||
client = bumper.client_get(clientresource)
|
||||
if client:
|
||||
bumper.client_set_mqtt(client["resource"], False)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
#!/usr/bin/env python3
|
||||
import logging
|
||||
|
||||
import asyncio
|
||||
from aiohttp import web
|
||||
|
||||
from bumper import plugins
|
||||
import logging
|
||||
import bumper
|
||||
from bumper.models import *
|
||||
from bumper import plugins
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
||||
class portal_api_appsvr(plugins.ConfServerApp):
|
||||
|
|
@ -15,14 +17,14 @@ class portal_api_appsvr(plugins.ConfServerApp):
|
|||
self.sub_api = "portal_api"
|
||||
|
||||
self.routes = [
|
||||
web.route("*", "/appsvr/app.do", self.handle_appsvr_app, name="portal_api_appsvr_app"),
|
||||
web.route("*", "/appsvr/service/list", self.handle_appsvr_service_list, name="portal_api_appsvr_service_list"),
|
||||
web.route("*", "/appsvr/oauth_callback", self.handle_appsvr_oauth_callback, name="portal_api_appsvr_oauth_callback"),
|
||||
|
||||
web.route("*", "/appsvr/app.do", self.handle_appsvr_api, name="portal_api_appsvr_app"),
|
||||
|
||||
]
|
||||
|
||||
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
|
||||
|
||||
async def handle_appsvr_app(self, request):
|
||||
async def handle_appsvr_api(self, request):
|
||||
if not request.method == "GET": # Skip GET for now
|
||||
try:
|
||||
|
||||
|
|
@ -155,57 +157,5 @@ class portal_api_appsvr(plugins.ConfServerApp):
|
|||
body = {"result": "fail", "todo": "result"}
|
||||
return web.json_response(body)
|
||||
|
||||
async def handle_appsvr_service_list(self, request):
|
||||
try:
|
||||
# original urls comment out as they are sub sub domain, which the current certificate is not valid
|
||||
# using url, where the certs is valid
|
||||
# data = {
|
||||
# "account": "users-base.dc-eu.ww.ecouser.net",
|
||||
# "jmq": "jmq-ngiot-eu.dc.ww.ecouser.net",
|
||||
# "lb": "lbo.ecouser.net",
|
||||
# "magw": "api-app.dc-eu.ww.ecouser.net",
|
||||
# "msgcloud": "msg-eu.ecouser.net:5223",
|
||||
# "ngiotLb": "jmq-ngiot-eu.area.ww.ecouser.net",
|
||||
# "rop": "api-rop.dc-eu.ww.ecouser.net"
|
||||
# }
|
||||
|
||||
data = {
|
||||
"account": "users-base.ecouser.net",
|
||||
"jmq": "jmq-ngiot-eu.ecouser.net",
|
||||
"lb": "lbo.ecouser.net",
|
||||
"magw": "api-app.ecouser.net",
|
||||
"msgcloud": "msg-eu.ecouser.net:5223",
|
||||
"ngiotLb": "jmq-ngiot-eu.ecouser.net",
|
||||
"rop": "api-rop.ecouser.net"
|
||||
}
|
||||
|
||||
body = {
|
||||
"code": 0,
|
||||
"data": data,
|
||||
"ret": "ok",
|
||||
"todo": "result"
|
||||
}
|
||||
|
||||
return web.json_response(body)
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("{}".format(e))
|
||||
|
||||
async def handle_appsvr_oauth_callback(self, request):
|
||||
try:
|
||||
token = bumper.token_by_authcode(request.query["code"])
|
||||
oauth = bumper.user_add_oauth(token["userid"])
|
||||
body = {
|
||||
"code": 0,
|
||||
"data": oauth.toResponse(),
|
||||
"ret": "ok",
|
||||
"todo": "result"
|
||||
}
|
||||
|
||||
return web.json_response(body)
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("{}".format(e))
|
||||
|
||||
|
||||
plugin = portal_api_appsvr()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
import logging
|
||||
|
||||
from aiohttp import web
|
||||
|
||||
from bumper import plugins
|
||||
from bumper.models import *
|
||||
|
||||
|
||||
class portal_api_ecms(plugins.ConfServerApp):
|
||||
|
||||
def __init__(self):
|
||||
self.name = "portal_api_ecms"
|
||||
self.plugin_type = "sub_api"
|
||||
self.sub_api = "portal_api"
|
||||
|
||||
self.routes = [
|
||||
web.route("*", "/ecms/app/ad/res", self.handle_ad_res, name="portal_api_ecms_ad_res"),
|
||||
]
|
||||
|
||||
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
|
||||
|
||||
async def handle_ad_res(self, request):
|
||||
try:
|
||||
body = {
|
||||
"code": 0,
|
||||
"data": [],
|
||||
"message": "success",
|
||||
"success": True
|
||||
}
|
||||
|
||||
return web.json_response(body)
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("{}".format(e))
|
||||
|
||||
|
||||
plugin = portal_api_ecms()
|
||||
|
|
@ -29,7 +29,7 @@ class portal_api_iot(plugins.ConfServerApp):
|
|||
try:
|
||||
json_body = json.loads(await request.text())
|
||||
|
||||
randomid = "".join(random.sample(string.ascii_letters, 4))
|
||||
randomid = "".join(random.sample(string.ascii_letters, 6))
|
||||
did = ""
|
||||
if "toId" in json_body: # Its a command
|
||||
did = json_body["toId"]
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
import logging
|
||||
import random
|
||||
import string
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
import asyncio
|
||||
from aiohttp import web
|
||||
|
||||
from bumper import plugins
|
||||
import logging
|
||||
import bumper
|
||||
from bumper.models import *
|
||||
|
||||
from bumper import plugins
|
||||
from datetime import datetime, timedelta
|
||||
import os
|
||||
import string
|
||||
import random
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
class portal_api_lg(plugins.ConfServerApp):
|
||||
|
||||
|
|
@ -26,11 +28,10 @@ class portal_api_lg(plugins.ConfServerApp):
|
|||
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
|
||||
|
||||
async def handle_lg_log(self, request): # EcoVacs Home
|
||||
randomid = "".join(random.sample(string.ascii_letters, 6))
|
||||
|
||||
try:
|
||||
json_body = json.loads(await request.text())
|
||||
|
||||
randomid = "".join(random.sample(string.ascii_letters, 6))
|
||||
did = json_body["did"]
|
||||
|
||||
botdetails = bumper.bot_get(did)
|
||||
|
|
@ -52,7 +53,7 @@ class portal_api_lg(plugins.ConfServerApp):
|
|||
json_body["payloadType"] = "x"
|
||||
|
||||
if not "payload" in json_body:
|
||||
# json_body["payload"] = ""
|
||||
#json_body["payload"] = ""
|
||||
if json_body["td"] == "GetCleanLogs":
|
||||
json_body["td"] = "q"
|
||||
json_body["payload"] = '<ctl count="30"/>'
|
||||
|
|
@ -76,8 +77,8 @@ class portal_api_lg(plugins.ConfServerApp):
|
|||
"area": l.attrib['a'],
|
||||
"last": l.attrib['l'],
|
||||
"cleanType": l.attrib['t'],
|
||||
# imageUrl allows for providing images of cleanings, something to look into later
|
||||
# "imageUrl": "https://localhost:8007",
|
||||
#imageUrl allows for providing images of cleanings, something to look into later
|
||||
#"imageUrl": "https://localhost:8007",
|
||||
}
|
||||
logs.append(cleanlog)
|
||||
body = {
|
||||
|
|
@ -97,12 +98,11 @@ class portal_api_lg(plugins.ConfServerApp):
|
|||
json_body["toId"]
|
||||
)
|
||||
)
|
||||
body = {"id": randomid, "errno": bumper.ERR_COMMON, "ret": "fail"}
|
||||
return web.json_response(body)
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("{}".format(e))
|
||||
|
||||
body = {"id": randomid, "errno": bumper.ERR_COMMON, "ret": "fail"}
|
||||
return web.json_response(body)
|
||||
|
||||
|
||||
plugin = portal_api_lg()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
#!/usr/bin/env python3
|
||||
import logging
|
||||
import os
|
||||
|
||||
import asyncio
|
||||
from aiohttp import web
|
||||
|
||||
from bumper import plugins
|
||||
import logging
|
||||
import bumper
|
||||
from bumper.models import *
|
||||
|
||||
from bumper import plugins
|
||||
from datetime import datetime, timedelta
|
||||
import os
|
||||
|
||||
class portal_api_pim(plugins.ConfServerApp):
|
||||
|
||||
|
|
@ -16,12 +17,13 @@ class portal_api_pim(plugins.ConfServerApp):
|
|||
self.sub_api = "portal_api"
|
||||
|
||||
self.routes = [
|
||||
|
||||
web.route("*", "/pim/product/getProductIotMap", self.handle_getProductIotMap, name="portal_api_pim_getProductIotMap"),
|
||||
web.route("*", "/pim/file/get/{id}", self.handle_pimFile, name="portal_api_pim_file"),
|
||||
web.route("*", "/pim/product/getConfignetAll", self.handle_getConfignetAll, name="portal_api_pim_getConfignetAll"),
|
||||
web.route("*", "/pim/product/getConfigGroups", self.handle_getConfigGroups, name="portal_api_pim_getConfigGroups"),
|
||||
web.route("*", "/pim/dictionary/getErrDetail", self.handle_getErrDetail, name="portal_api_pim_getErrDetail"),
|
||||
web.route("*", "/pim/product/software/config/batch", self.handle_product_config_batch, name="portal_api_pim_product_config_batch"),
|
||||
|
||||
]
|
||||
|
||||
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
|
||||
|
|
@ -41,7 +43,7 @@ class portal_api_pim(plugins.ConfServerApp):
|
|||
try:
|
||||
fileID = request.match_info.get("id", "")
|
||||
|
||||
return web.FileResponse(os.path.join(bumper.bumper_dir, "bumper", "web", "images", "robotvac_image.jpg"))
|
||||
return web.FileResponse(os.path.join(bumper.bumper_dir,"bumper","web","images","robotvac_image.jpg"))
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("{}".format(e))
|
||||
|
|
@ -65,43 +67,15 @@ class portal_api_pim(plugins.ConfServerApp):
|
|||
async def handle_getErrDetail(self, request):
|
||||
try:
|
||||
body = {
|
||||
"code": -1,
|
||||
"data": [],
|
||||
"msg": "This errcode's detail is not exists"
|
||||
}
|
||||
"code": -1,
|
||||
"data": [],
|
||||
"msg": "This errcode's detail is not exists"
|
||||
}
|
||||
return web.json_response(body)
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("{}".format(e))
|
||||
|
||||
async def handle_product_config_batch(self, request):
|
||||
try:
|
||||
json_body = json.loads(await request.text())
|
||||
data = []
|
||||
for pid in json_body["pids"]:
|
||||
for productConfig in productConfigBatch:
|
||||
if pid == productConfig["pid"]:
|
||||
data.append(productConfig)
|
||||
continue
|
||||
|
||||
# not found in productConfigBatch
|
||||
# some devices don't have any product configuration
|
||||
data.append({
|
||||
"cfg": {},
|
||||
"pid": pid
|
||||
})
|
||||
|
||||
body = {
|
||||
"code": 200,
|
||||
"data": data,
|
||||
"message": "success"
|
||||
}
|
||||
return web.json_response(body)
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("{}".format(e))
|
||||
|
||||
|
||||
plugin = portal_api_pim()
|
||||
|
||||
confignetAllResponse = {
|
||||
|
|
@ -2707,174 +2681,3 @@ configGroupsResponse = {
|
|||
"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
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
from aiohttp import web
|
||||
import logging
|
||||
from bumper.models import *
|
||||
from bumper import plugins
|
||||
|
||||
|
||||
class api_rapp(plugins.ConfServerApp):
|
||||
|
||||
def __init__(self):
|
||||
self.name = "api_rapp"
|
||||
self.plugin_type = "sub_api"
|
||||
self.sub_api = "portal_api"
|
||||
|
||||
self.routes = [
|
||||
web.route("*", "/rapp/sds/user/data/map/get", self.handle_map_get, name="api_rapp"),
|
||||
]
|
||||
|
||||
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
|
||||
|
||||
async def handle_map_get(self, request):
|
||||
try:
|
||||
body = {
|
||||
"code": 0,
|
||||
"data": {
|
||||
"data": {
|
||||
"name": "My Home"
|
||||
},
|
||||
"tag": None
|
||||
},
|
||||
"message": "success"
|
||||
}
|
||||
|
||||
return web.json_response(body)
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("{}".format(e))
|
||||
|
||||
plugin = api_rapp()
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
#!/usr/bin/env python3
|
||||
import logging
|
||||
|
||||
import asyncio
|
||||
from aiohttp import web
|
||||
|
||||
from bumper import plugins
|
||||
import logging
|
||||
import bumper
|
||||
from bumper.models import *
|
||||
from bumper import plugins
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
||||
class v1_private_common(plugins.ConfServerApp):
|
||||
|
|
@ -19,10 +21,6 @@ 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/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"),
|
||||
|
||||
]
|
||||
|
||||
|
|
@ -112,351 +110,6 @@ class v1_private_common(plugins.ConfServerApp):
|
|||
except Exception as e:
|
||||
logging.exception("{}".format(e))
|
||||
|
||||
async def handle_getConfig(self, request):
|
||||
try:
|
||||
data = []
|
||||
for key in request.query["keys"].split(','):
|
||||
data.append({
|
||||
"key": key,
|
||||
"value": "Y"
|
||||
})
|
||||
|
||||
body = {
|
||||
"code": bumper.RETURN_API_SUCCESS,
|
||||
"data": data,
|
||||
"msg": "操作成功",
|
||||
"success": True,
|
||||
"time": self.get_milli_time(datetime.utcnow().timestamp()),
|
||||
}
|
||||
|
||||
return web.json_response(body)
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("{}".format(e))
|
||||
|
||||
async def handle_getAreas(self, request):
|
||||
try:
|
||||
body = {
|
||||
"code": bumper.RETURN_API_SUCCESS,
|
||||
"data": AREA_LIST,
|
||||
"msg": "操作成功",
|
||||
"success": True,
|
||||
"time": self.get_milli_time(datetime.utcnow().timestamp()),
|
||||
}
|
||||
|
||||
return web.json_response(body)
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("{}".format(e))
|
||||
|
||||
async def handle_getAgreementURLBatch(self, request): # EcoVacs Home
|
||||
try:
|
||||
body = {
|
||||
"code": bumper.RETURN_API_SUCCESS,
|
||||
"data": [
|
||||
{
|
||||
"acceptTime": None,
|
||||
"force": None,
|
||||
"id": "20180804040641_7d746faf18b8cb22a50d145598fe4c90",
|
||||
"type": "USER",
|
||||
"url": "https://gl-eu-wap.ecovacs.com/content/agreement?id=20180804040641_7d746faf18b8cb22a50d145598fe4c90&language=EN",
|
||||
"version": "1.03"
|
||||
},
|
||||
{
|
||||
"acceptTime": None,
|
||||
"force": None,
|
||||
"id": "20180804040245_4e7c56dfb7ebd3b81b1f2747d0859fac",
|
||||
"type": "PRIVACY",
|
||||
"url": "https://gl-eu-wap.ecovacs.com/content/agreement?id=20180804040245_4e7c56dfb7ebd3b81b1f2747d0859fac&language=EN",
|
||||
"version": "1.03"
|
||||
}
|
||||
],
|
||||
"msg": "操作成功",
|
||||
"success": True,
|
||||
"time": self.get_milli_time(datetime.utcnow().timestamp()),
|
||||
}
|
||||
|
||||
return web.json_response(body)
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("{}".format(e))
|
||||
|
||||
async def handle_getTimestamp(self, request): # EcoVacs Home
|
||||
try:
|
||||
time = self.get_milli_time(datetime.utcnow().timestamp())
|
||||
body = {
|
||||
"code": bumper.RETURN_API_SUCCESS,
|
||||
"data": {
|
||||
"timestamp": time
|
||||
},
|
||||
"msg": "操作成功",
|
||||
"success": True,
|
||||
"time": time,
|
||||
}
|
||||
|
||||
return web.json_response(body)
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("{}".format(e))
|
||||
|
||||
|
||||
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,9 +27,6 @@ class v1_private_user(plugins.ConfServerApp):
|
|||
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkAgreementBatch", self.handle_checkAgreement,name="v1_user_checkAgreementBatch"),
|
||||
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/getUserAccountInfo", authhandler.getUserAccountInfo,name="v1_user_getUserAccountInfo"),
|
||||
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/getUserMenuInfo", self.handle_getUserMenuInfo,name="v1_user_getUserMenuInfo"),
|
||||
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/changeArea", self.handle_changeArea, name="v1_user_changeArea"),
|
||||
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/queryChangeArea", self.handle_changeArea, name="v1_user_queryChangeArea"),
|
||||
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/acceptAgreementBatch", self.handle_acceptAgreementBatch, name="v1_user_acceptAgreementBatch"),
|
||||
# Direct register from app:
|
||||
# /{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/directRegister
|
||||
#Register by email
|
||||
|
|
@ -147,37 +144,5 @@ class v1_private_user(plugins.ConfServerApp):
|
|||
except Exception as e:
|
||||
logging.exception("{}".format(e))
|
||||
|
||||
async def handle_changeArea(self, request):
|
||||
try:
|
||||
body = {
|
||||
"code": bumper.RETURN_API_SUCCESS,
|
||||
"data": {
|
||||
"isNeedReLogin": "N"
|
||||
},
|
||||
"msg": "操作成功",
|
||||
"success": True,
|
||||
"time": self.get_milli_time(datetime.utcnow().timestamp()),
|
||||
}
|
||||
|
||||
return web.json_response(body)
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("{}".format(e))
|
||||
|
||||
async def handle_acceptAgreementBatch(self, request):
|
||||
try:
|
||||
body = {
|
||||
"code": bumper.RETURN_API_SUCCESS,
|
||||
"data": None,
|
||||
"msg": "操作成功",
|
||||
"success": True,
|
||||
"time": self.get_milli_time(datetime.utcnow().timestamp()),
|
||||
}
|
||||
|
||||
return web.json_response(body)
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("{}".format(e))
|
||||
|
||||
plugin = v1_private_user()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,58 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
from aiohttp import web
|
||||
import logging
|
||||
import bumper
|
||||
from bumper import plugins
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class v1_private_userSetting(plugins.ConfServerApp):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.name = "v1_private_userSetting"
|
||||
self.plugin_type = "sub_api"
|
||||
self.sub_api = "api_v1"
|
||||
|
||||
self.routes = [
|
||||
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/userSetting/getSuggestionSetting", self.handle_getSuggestionSetting,name="v1_userSetting_getSuggestionSetting"),
|
||||
]
|
||||
|
||||
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
|
||||
|
||||
async def handle_getSuggestionSetting(self, request):
|
||||
try:
|
||||
|
||||
body = {
|
||||
"code": bumper.RETURN_API_SUCCESS,
|
||||
"data": {
|
||||
"acceptSuggestion": "Y",
|
||||
"itemList": [
|
||||
{
|
||||
"name": "Aktionen/Angebote/Ereignisse",
|
||||
"settingKey": "MARKETING",
|
||||
"val": "Y"
|
||||
},
|
||||
{
|
||||
"name": "Benutzerbefragung",
|
||||
"settingKey": "QUESTIONNAIRE",
|
||||
"val": "Y"
|
||||
},
|
||||
{
|
||||
"name": "Produkt-Upgrade/Hilfe für Benutzer",
|
||||
"settingKey": "INTRODUCTION",
|
||||
"val": "Y"
|
||||
}
|
||||
]
|
||||
},
|
||||
"msg": "操作成功",
|
||||
"time": self.get_milli_time(datetime.utcnow().timestamp()),
|
||||
}
|
||||
|
||||
return web.json_response(body)
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("{}".format(e))
|
||||
|
||||
|
||||
plugin = v1_private_userSetting()
|
||||
|
|
@ -4,7 +4,3 @@ ecouser.net
|
|||
*.ecouser.net
|
||||
ecovacs.net
|
||||
*.ecovacs.net
|
||||
*.ww.ecouser.net
|
||||
*.dc-eu.ww.ecouser.net
|
||||
*.dc.ww.ecouser.net
|
||||
*.area.ww.ecouser.net
|
||||
|
|
@ -53,9 +53,9 @@ I get it, you don't trust create_certs and want to do it manually. The easiest
|
|||
|
||||
### Create a Root CA
|
||||
|
||||
1. Create csrconfig_ca.txt for use in later commands
|
||||
1. Create csrconfig.txt for use in later commands
|
||||
|
||||
***csrconfig_ca.txt***
|
||||
***csrconfig.txt***
|
||||
````
|
||||
[ req ]
|
||||
default_md = sha256
|
||||
|
|
@ -70,9 +70,9 @@ keyUsage=critical,keyCertSign,cRLSign
|
|||
basicConstraints=critical,CA:true,pathlen:1
|
||||
````
|
||||
|
||||
1. Create certconfig_ca.txt for use in later commands
|
||||
1. Create certconfig.txt for use in later commands
|
||||
|
||||
***certconfig_ca.txt***
|
||||
***certconfig.txt***
|
||||
````
|
||||
[ req ]
|
||||
default_md = sha256
|
||||
|
|
@ -91,21 +91,21 @@ basicConstraints=critical,CA:true,pathlen:1
|
|||
|
||||
1. Generate the RSA private key
|
||||
|
||||
`openssl genrsa -out ca.key 4096`
|
||||
`openssl genpkey -outform PEM -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out priv.key`
|
||||
|
||||
1. Create the CSR
|
||||
|
||||
`openssl req -new -nodes -key ca.key -config csrconfig_ca.txt -out ca.csr`
|
||||
`openssl req -new -nodes -key priv.key -config csrconfig.txt -out cert.csr`
|
||||
|
||||
1. Self-sign your CSR
|
||||
|
||||
`openssl req -x509 -nodes -in ca.csr -days 1095 -key ca.key -config certconfig_ca.txt -extensions req_ext -out ca.crt`
|
||||
`openssl req -x509 -nodes -in cert.csr -days 3650 -key priv.key -config certconfig.txt -extensions req_ext -out cert.crt`
|
||||
|
||||
### Create the Server Certificate
|
||||
|
||||
1. Create csrconfig_bumper.txt for use in later commands
|
||||
1. Create csrconfig.txt for use in later commands
|
||||
|
||||
***csrconfig_bumper.txt***
|
||||
***csrconfig.txt***
|
||||
````
|
||||
[ req ]
|
||||
default_md = sha256
|
||||
|
|
@ -127,15 +127,11 @@ DNS.2 = ecouser.net
|
|||
DNS.3 = *.ecouser.net
|
||||
DNS.4 = ecovacs.net
|
||||
DNS.5 = *.ecovacs.net
|
||||
DNS.6 = *.ww.ecouser.net
|
||||
DNS.7 = *.dc-eu.ww.ecouser.net
|
||||
DNS.8 = *.dc.ww.ecouser.net
|
||||
DNS.9 = *.area.ww.ecouser.net
|
||||
````
|
||||
|
||||
1. Create certconfig_bumper.txt for use in later commands
|
||||
1. Create certconfig.txt for use in later commands
|
||||
|
||||
***certconfig_bumper.txt***
|
||||
***certconfig.txt***
|
||||
````
|
||||
[ req ]
|
||||
default_md = sha256
|
||||
|
|
@ -159,23 +155,19 @@ DNS.2 = ecouser.net
|
|||
DNS.3 = *.ecouser.net
|
||||
DNS.4 = ecovacs.net
|
||||
DNS.5 = *.ecovacs.net
|
||||
DNS.6 = *.ww.ecouser.net
|
||||
DNS.7 = *.dc-eu.ww.ecouser.net
|
||||
DNS.8 = *.dc.ww.ecouser.net
|
||||
DNS.9 = *.area.ww.ecouser.net
|
||||
````
|
||||
|
||||
1. Generate the RSA private key
|
||||
|
||||
`openssl genrsa -out bumper.key 4096`
|
||||
`openssl genpkey -outform PEM -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out priv.key`
|
||||
|
||||
1. Create the CSR
|
||||
|
||||
`openssl req -new -nodes -key bumper.key -config csrconfig_bumper.txt -out bumper.csr`
|
||||
`openssl req -new -nodes -key priv.key -config csrconfig.txt -out cert.csr`
|
||||
|
||||
1. Sign your CSR with a root CA cert
|
||||
|
||||
`openssl x509 -req -in bumper.csr -days 365 -CA ca.crt -CAkey ca.key -extfile certconfig_bumper.txt -extensions req_ext -CAcreateserial -out bumper.crt`
|
||||
`openssl x509 -req -in cert.csr -days 3650 -CA ca.crt -CAkey priv.key -extfile certconfig.txt -extensions req_ext -CAserial /tmp/tmp-10593TSH1OlVSxC7C -CAcreateserial -out cert.crt`
|
||||
|
||||
## Using a Custom CA/Self
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ If overriding DNS for the top-level domains isn't an option, you'll need to conf
|
|||
**Note:** Depending on country, your phone/robot may be using a different domain. Most of these domains contain country-specific placeholders.
|
||||
|
||||
Not all domains have been documented at this point, and this list will be updated as more are identified/seen. The preferred way to ensure Bumper works is to override the full domains as above.
|
||||
**Note:** The app dynamically gets the required domains from the endpoint `api/appsvr/service/list` and therefore ecovacs can use different domains for different models.
|
||||
|
||||
Replacement Examples:
|
||||
|
||||
|
|
@ -47,7 +46,6 @@ Replacement Examples:
|
|||
| `eco-{countrycode}-api.ecovacs.com` | Used for Login |
|
||||
| `gl-{countrycode}-api.ecovacs.com` | Used by EcoVacs Home app |
|
||||
| `gl-{countrycode}-openapi.ecovacs.com` | Used by EcoVacs Home app |
|
||||
| `portal.ecouser.net` | Used for Login and Rest API |
|
||||
| `portal-{countrycode}.ecouser.net` | Used for Login and Rest API |
|
||||
| `portal-{region}.ecouser.net` | Used for Login and Rest API |
|
||||
| `portal-ww.ecouser.net` | Used for various Rest APIs |
|
||||
|
|
@ -61,11 +59,4 @@ Replacement Examples:
|
|||
| `recommender.ecovacs.com` | Used by Ecovacs Home app |
|
||||
| `bigdata-international.ecovacs.com` | Telemetry/tracking |
|
||||
| `bigdata-northamerica.ecovacs.com` | Telemetry/tracking |
|
||||
| `bigdata-europe.ecovacs.com` | Telemetry/tracking |
|
||||
| `bigdata-{unknown regions}.ecovacs.com` | Telemetry/tracking |
|
||||
| `api-app.ww.ecouser.net` | Api for App (v2+) |
|
||||
| `api-app.dc-{region}.ww.ecouser.net` | Api for App (v2+) |
|
||||
| `users-base.dc-{region}.ww.ecouser.net` | Accounts for App (v2+) |
|
||||
| `jmq-ngiot-{region}.dc.ww.ecouser.net` | MQTT for App (v2+) |
|
||||
| `api-rop.dc-{region}.ww.ecouser.net` | App (v2+) |
|
||||
| `jmq-ngiot-{region}.area.ww.ecouser.net`| App (v2+) |
|
||||
|
|
@ -42,13 +42,3 @@ Optionally you can map existing directories for logs, data, and certs.
|
|||
````
|
||||
docker run -it -e "BUMPER_ANNOUNCE_IP=X.X.X.X" -p 443:443 -p 8007:8007 -p 8883:8883 -p 5223:5223 -v /home/user/bumper/data:/bumper/data --name bumper bmartin5692/bumper
|
||||
````
|
||||
|
||||
# Docker-compose
|
||||
|
||||
A docker-compose example can be found in the ["example" folder](https://github.com/bmartin5692/bumper/tree/master/example/docker-compose).
|
||||
|
||||
The docker-compose starts two services:
|
||||
- bumper itself
|
||||
- nginx proxy, which redirects MQTT traffic on port `443` to port `8883`
|
||||
|
||||
The redirection is required as the app v2+ and robots with a newer firmware are connecting to the mqtt server on port 433.
|
||||
|
|
|
|||
|
|
@ -12,4 +12,3 @@ Bumper has a number of environment variables to help with custom deployments and
|
|||
| BUMPER_LOGS | {full path to logs directory} | The directory where logs should be stored |
|
||||
| BUMPER_DATA | {full path to data directory} | The directory where persistent data should be stored (bumper.db) |
|
||||
| BUMPER_DEBUG | true | Run Bumper with debug mode/logging |
|
||||
| LOG_TO_STDOUT | true | Instead of logging to logs/, logs to to STDOUT |
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
---
|
||||
version: "3.6"
|
||||
|
||||
networks:
|
||||
bumper:
|
||||
internal: true
|
||||
|
||||
services:
|
||||
nginx:
|
||||
depends_on:
|
||||
- bumper
|
||||
image: nginx:alpine
|
||||
networks:
|
||||
default:
|
||||
bumper:
|
||||
ports:
|
||||
- 443:443
|
||||
- 5223:5223
|
||||
- 8007:8007
|
||||
- 8883:8883
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ./nginx/:/etc/nginx:ro # See config file below
|
||||
|
||||
bumper:
|
||||
image: bmartin5692/bumper
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
bumper:
|
||||
|
||||
environment:
|
||||
PUID: 1000
|
||||
PGID: 1000
|
||||
TZ: Europe/Rome
|
||||
BUMPER_ANNOUNCE_IP: XXX # Insert your IP
|
||||
BUMPER_LISTEN: 0.0.0.0
|
||||
# BUMPER_DEBUG: "true"
|
||||
LOG_TO_STDOUT: "true"
|
||||
volumes:
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ./config:/bumper/data
|
||||
- ./certs:/bumper/certs
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
error_log stderr;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events { }
|
||||
|
||||
stream {
|
||||
resolver 127.0.0.11 ipv6=off; #docker dns server
|
||||
map_hash_bucket_size 64;
|
||||
|
||||
map $ssl_preread_server_name $internalport {
|
||||
# redirect all requests, which contain "mq" in the SNI -> MQTT
|
||||
~^.*(mq).*\.eco(vacs|user)\.(net|com)$ 8883;
|
||||
|
||||
# the rest of eco(user|vacs) requests
|
||||
~^.*eco(vacs|user)\.(net|com)$ 443;
|
||||
|
||||
# mapping default to MQTT as the bots are connecting directly to the ip without SNI
|
||||
default 8883;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443;
|
||||
ssl_preread on;
|
||||
proxy_pass bumper:$internalport;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 5223;
|
||||
proxy_pass bumper:5223;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8007;
|
||||
proxy_pass bumper:8007;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8883;
|
||||
proxy_pass bumper:8883;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
from bumper import OAuth
|
||||
|
||||
|
||||
def test_oauth():
|
||||
userId = "test"
|
||||
o_auth = OAuth.create_new(userId)
|
||||
assert o_auth is not None
|
||||
assert o_auth.userId == userId
|
||||
assert o_auth.access_token is not None
|
||||
assert o_auth.expire_at is not None
|
||||
assert o_auth.refresh_token is not None
|
||||
|
||||
data = o_auth.toResponse()
|
||||
assert data is not None
|
||||
Loading…
Add table
Add a link
Reference in a new issue