Merge pull request #88 from bmartin5692/wip-confserver-jinja2

confserver revamp
This commit is contained in:
Brian Martin 2020-01-04 20:46:33 -05:00 committed by GitHub
commit a5883f8fa2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 2067 additions and 1310 deletions

View file

@ -11,6 +11,8 @@ pyyaml = "~=5.2"
atomicwrites = "~=1.3" atomicwrites = "~=1.3"
yarl = "~=1.3.0" yarl = "~=1.3.0"
multidict = "~=4.5.2" multidict = "~=4.5.2"
aiohttp-jinja2 = "*"
jinja2 = "*"
[dev-packages] [dev-packages]
black = "*" black = "*"

56
Pipfile.lock generated
View file

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "315b25b9f7d46a6ea88fb08170ad935b35aa2717743be4eca137e52e595066de" "sha256": "740f61688ce679591a1b01f41abc1fa979f8fb1552d598e68868bc47712ba937"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": {}, "requires": {},
@ -32,6 +32,14 @@
"index": "pypi", "index": "pypi",
"version": "==3.6.2" "version": "==3.6.2"
}, },
"aiohttp-jinja2": {
"hashes": [
"sha256:2dfe29cfd278d07cd0a851afb98471bc8ce2a830968443e40d67636f3c035d79",
"sha256:3b4dfe1bfd5542e254a769c18cb58d62f7f92755fec127e38d0da3436900b240"
],
"index": "pypi",
"version": "==1.2.0"
},
"async-timeout": { "async-timeout": {
"hashes": [ "hashes": [
"sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f", "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f",
@ -82,6 +90,47 @@
], ],
"version": "==2.8" "version": "==2.8"
}, },
"jinja2": {
"hashes": [
"sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f",
"sha256:9fe95f19286cfefaa917656583d020be14e7859c6b0252588391e47db34527de"
],
"index": "pypi",
"version": "==2.10.3"
},
"markupsafe": {
"hashes": [
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
"sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
"sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"
],
"version": "==1.1.1"
},
"multidict": { "multidict": {
"hashes": [ "hashes": [
"sha256:024b8129695a952ebd93373e45b5d341dbb87c17ce49637b34000093f243dd4f", "sha256:024b8129695a952ebd93373e45b5d341dbb87c17ce49637b34000093f243dd4f",
@ -636,10 +685,9 @@
}, },
"wcwidth": { "wcwidth": {
"hashes": [ "hashes": [
"sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", "sha256:8fd29383f539be45b20bd4df0dc29c20ba48654a41e661925e612311e9f3c603"
"sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
], ],
"version": "==0.1.7" "version": "==0.1.8"
}, },
"wrapt": { "wrapt": {
"hashes": [ "hashes": [

View file

@ -12,6 +12,9 @@ from logging.handlers import RotatingFileHandler
import socket import socket
import sys import sys
import importlib
import pkgutil
from pkgutil import extend_path
def strtobool(strbool): def strtobool(strbool):
if str(strbool).lower() in ["true", "1", "t", "y", "on", "yes"]: if str(strbool).lower() in ["true", "1", "t", "y", "on", "yes"]:
@ -32,6 +35,8 @@ os.makedirs(data_dir, exist_ok=True) # Ensure data directory exists or create
certs_dir = os.environ.get("BUMPER_CERTS") or os.path.join(bumper_dir, "certs") certs_dir = os.environ.get("BUMPER_CERTS") or os.path.join(bumper_dir, "certs")
os.makedirs(certs_dir, exist_ok=True) # Ensure data directory exists or create os.makedirs(certs_dir, exist_ok=True) # Ensure data directory exists or create
# Certs # Certs
ca_cert = os.environ.get("BUMPER_CA") or os.path.join(certs_dir, "ca.crt") ca_cert = os.environ.get("BUMPER_CA") or os.path.join(certs_dir, "ca.crt")
server_cert = os.environ.get("BUMPER_CERT") or os.path.join(certs_dir, "bumper.crt") server_cert = os.environ.get("BUMPER_CERT") or os.path.join(certs_dir, "bumper.crt")
@ -57,6 +62,16 @@ conf_server = None
conf_server_2 = None conf_server_2 = None
xmpp_server = None xmpp_server = None
# Plugins
sys.path.append(os.path.join(bumper_dir, "bumper", "plugins"))
sys.path.append(os.path.join(data_dir, "plugins"))
discovered_plugins = {
name: importlib.import_module(name)
for finder, name, ispkg in pkgutil.iter_modules()
if name.startswith('bumper_')
}
shutting_down = False shutting_down = False
# Set format for all logs # Set format for all logs
@ -206,10 +221,8 @@ async def start():
# Start web servers # Start web servers
conf_server.confserver_app() conf_server.confserver_app()
asyncio.create_task(conf_server.start_server()) asyncio.create_task(conf_server.start_site(conf_server.app, address=bumper_listen, port=conf1_listen_port, usessl=True))
asyncio.create_task(conf_server.start_site(conf_server.app, address=bumper_listen, port=conf2_listen_port, usessl=False))
conf_server_2.confserver_app()
asyncio.create_task(conf_server_2.start_server())
# Start maintenance # Start maintenance
while not shutting_down: while not shutting_down:

File diff suppressed because it is too large Load diff

View file

@ -286,6 +286,7 @@ def bot_add(sn, did, devclass, resource, company):
def bot_remove(did): def bot_remove(did):
bots = db_get().table("bots") bots = db_get().table("bots")
bot = bot_get(did) bot = bot_get(did)
if bot:
bots.remove(doc_ids=[bot.doc_id]) bots.remove(doc_ids=[bot.doc_id])
@ -344,6 +345,11 @@ def client_add(userid, realm, resource):
bumperlog.info("Adding new client with resource {}".format(newclient.resource)) bumperlog.info("Adding new client with resource {}".format(newclient.resource))
client_full_upsert(newclient.asdict()) client_full_upsert(newclient.asdict())
def client_remove(resource):
clients = db_get().table("clients")
client = client_get(resource)
if client:
clients.remove(doc_ids=[client.doc_id])
def client_get(resource): def client_get(resource):
clients = db_get().table("clients") clients = db_get().table("clients")

View file

@ -27,7 +27,7 @@ class MQTTHelperBot:
def __init__(self, address): def __init__(self, address):
self.address = address self.address = address
self.client_id = "helper1@bumper/helper1" self.client_id = "helperbot@bumper/helperbot"
self.command_responses = [] self.command_responses = []
async def start_helper_bot(self): async def start_helper_bot(self):
@ -35,7 +35,7 @@ class MQTTHelperBot:
try: try:
if self.Client is None: if self.Client is None:
self.Client = MQTTClient( self.Client = MQTTClient(
client_id=self.client_id, config={"check_hostname": False} client_id=self.client_id, config={"check_hostname": False, "reconnect_retries": 20}
) )
await self.Client.connect( await self.Client.connect(
@ -44,89 +44,26 @@ class MQTTHelperBot:
) )
await self.Client.subscribe( await self.Client.subscribe(
[ [
("iot/p2p/+/+/+/+/helper1/bumper/helper1/+/+/+", QOS_0), ("iot/p2p/+/+/+/+/helperbot/bumper/helperbot/+/+/+", QOS_0),
("iot/p2p/+", QOS_0), ("iot/p2p/+", QOS_0),
("iot/atr/+", QOS_0), ("iot/atr/+", QOS_0),
] ]
) )
asyncio.create_task(self.get_msg()) # except ConnectionRefusedError as e:
# helperbotlog.Error(e)
# pass
except ConnectionRefusedError as e: # except asyncio.CancelledError as e:
helperbotlog.Error(e) # pass
pass
except asyncio.CancelledError as e: # except hbmqtt.client.ConnectException as e:
pass # helperbotlog.Error(e)
# pass
except hbmqtt.client.ConnectException as e:
helperbotlog.Error(e)
pass
except Exception as e: except Exception as e:
helperbotlog.exception("{}".format(e)) helperbotlog.exception("{}".format(e))
async def get_msg(self):
while True:
message = await self.Client.deliver_message()
if str(message.topic).split("/")[6] == "helper1":
# Response to command
helperbotlog.debug(
"Received Response - Topic: {} - Message: {}".format(
message.topic, str(message.data.decode("utf-8"))
)
)
self.command_responses.append(
{
"time": time.time(),
"topic": message.topic,
"payload": str(message.data.decode("utf-8")),
}
)
elif str(message.topic).split("/")[3] == "helper1":
# Helperbot sending command
helperbotlog.debug(
"Send Command - Topic: {} - Message: {}".format(
message.topic, str(message.data.decode("utf-8"))
)
)
elif str(message.topic).split("/")[1] == "atr":
# Broadcast message received on atr
if str(message.topic).split("/")[2] == "errors":
boterrorlog.error(
"Received Error - Topic: {} - Message: {}".format(
message.topic, str(message.data.decode("utf-8"))
)
)
else:
helperbotlog.debug(
"Received Broadcast - Topic: {} - Message: {}".format(
message.topic, str(message.data.decode("utf-8"))
)
)
else:
helperbotlog.debug(
"Received Message - Topic: {} - Message: {}".format(
message.topic, str(message.data.decode("utf-8"))
)
)
# Cleanup "expired messages" > 60 seconds from time
for msg in self.command_responses:
expire_time = (
datetime.fromtimestamp(msg["time"])
+ timedelta(seconds=self.expire_msg_seconds)
).timestamp()
if time.time() > expire_time:
helperbotlog.debug(
"Pruning Message Due To Expiration - Message Topic: {}".format(
msg["topic"]
)
)
self.command_responses.remove(msg)
async def wait_for_resp(self, requestid): async def wait_for_resp(self, requestid):
try: try:
@ -139,8 +76,7 @@ class MQTTHelperBot:
if len(self.command_responses) > 0: if len(self.command_responses) > 0:
for msg in self.command_responses: for msg in self.command_responses:
topic = str(msg["topic"]).split("/") topic = str(msg["topic"]).split("/")
if topic[6] == "helper1" and topic[10] == requestid: if topic[6] == "helperbot" and topic[10] == requestid:
# helperbotlog.debug('VacBot MQTT Response: Topic: %s Payload: %s' % (msg['topic'], msg['payload']))
if topic[11] == "j": if topic[11] == "j":
resppayload = json.loads(msg["payload"]) resppayload = json.loads(msg["payload"])
else: else:
@ -175,7 +111,7 @@ class MQTTHelperBot:
async def send_command(self, cmdjson, requestid): async def send_command(self, cmdjson, requestid):
if not self.Client._handler.writer is None: if not self.Client._handler.writer is None:
try: try:
ttopic = "iot/p2p/{}/helper1/bumper/helper1/{}/{}/{}/q/{}/{}".format( ttopic = "iot/p2p/{}/helperbot/bumper/helperbot/{}/{}/{}/q/{}/{}".format(
cmdjson["cmdName"], cmdjson["cmdName"],
cmdjson["toId"], cmdjson["toId"],
cmdjson["toType"], cmdjson["toType"],
@ -207,7 +143,7 @@ class MQTTHelperBot:
class MQTTServer: class MQTTServer:
default_config = {} default_config = None
broker = None broker = None
async def broker_coro(self): async def broker_coro(self):
@ -215,24 +151,38 @@ class MQTTServer:
mqttserverlog.info( mqttserverlog.info(
"Starting MQTT Server at {}:{}".format(self.address[0], self.address[1]) "Starting MQTT Server at {}:{}".format(self.address[0], self.address[1])
) )
self.broker = hbmqtt.broker.Broker(config=self.default_config)
try: try:
await self.broker.start() await self.broker.start()
except hbmqtt.broker.BrokerException as e: except hbmqtt.broker.BrokerException as e:
mqttserverlog.exception(e) mqttserverlog.exception(e)
asyncio.create_task(bumper.shutdown()) #asyncio.create_task(bumper.shutdown())
pass pass
except Exception as e: except Exception as e:
mqttserverlog.exception("{}".format(e)) mqttserverlog.exception("{}".format(e))
asyncio.create_task(bumper.shutdown()) #asyncio.create_task(bumper.shutdown())
pass
def __init__(self, address): def __init__(self, address, **kwargs):
try: try:
self.address = address self.address = address
# Default config opts
passwd_file = os.path.join(
os.path.join(bumper.data_dir, "passwd")
) # For file auth, set user:hash in passwd file see (https://hbmqtt.readthedocs.io/en/latest/references/hbmqtt.html#configuration-example)
allow_anon = False
for key, value in kwargs.items():
if key == "password_file":
passwd_file = kwargs["password_file"]
elif key == "allow_anonymous":
allow_anon = kwargs["allow_anonymous"] # Set to True to allow anonymous authentication
# The below adds a plugin to the hbmqtt.broker.plugins without having to futz with setup.py # The below adds a plugin to the hbmqtt.broker.plugins without having to futz with setup.py
distribution = pkg_resources.Distribution("hbmqtt.broker.plugins") distribution = pkg_resources.Distribution("hbmqtt.broker.plugins")
bumper_plugin = pkg_resources.EntryPoint.parse( bumper_plugin = pkg_resources.EntryPoint.parse(
@ -252,17 +202,17 @@ class MQTTServer:
"keyfile": bumper.server_key, "keyfile": bumper.server_key,
}, },
}, },
"sys_interval": 10, "sys_interval": 0,
"auth": { "auth": {
"allow-anonymous": False, # Set to True to allow anonymous authentication "allow-anonymous": allow_anon,
"password-file": os.path.join( "password-file": passwd_file,
os.path.join(bumper.data_dir, "passwd")
), # For file auth, set user:hash in passwd file see (https://hbmqtt.readthedocs.io/en/latest/references/hbmqtt.html#configuration-example)
"plugins": ["bumper"], # Bumper plugin provides auth and handling of bots/clients connecting "plugins": ["bumper"], # Bumper plugin provides auth and handling of bots/clients connecting
}, },
"topic-check": {"enabled": False}, "topic-check": {"enabled": False},
} }
self.broker = hbmqtt.broker.Broker(config=self.default_config)
except Exception as e: except Exception as e:
mqttserverlog.exception("{}".format(e)) mqttserverlog.exception("{}".format(e))
@ -284,13 +234,6 @@ class BumperMQTTServer_Plugin:
async def authenticate(self, *args, **kwargs): async def authenticate(self, *args, **kwargs):
authenticated = False authenticated = False
if not self.auth_config:
# auth config section not found
self.context.logger.warning(
"'auth' section not found in context configuration"
)
return False
try: try:
session = kwargs.get("session", None) session = kwargs.get("session", None)
@ -320,7 +263,7 @@ class BumperMQTTServer_Plugin:
realm = tmpclientdetail[0] realm = tmpclientdetail[0]
resource = tmpclientdetail[1] resource = tmpclientdetail[1]
if userid == "helper1": if userid == "helperbot":
mqttserverlog.info(f"Bumper Authentication Success - Helperbot: {client_id}") mqttserverlog.info(f"Bumper Authentication Success - Helperbot: {client_id}")
authenticated = True authenticated = True
else: else:
@ -383,8 +326,6 @@ class BumperMQTTServer_Plugin:
self.context.logger.debug(f"{(len(self._users))} user(s) read from file {password_file}") self.context.logger.debug(f"{(len(self._users))} user(s) read from file {password_file}")
except FileNotFoundError: except FileNotFoundError:
self.context.logger.warning(f"Password file {password_file} not found") self.context.logger.warning(f"Password file {password_file} not found")
else:
self.context.logger.debug("Configuration parameter 'password_file' not found")
async def on_broker_client_connected(self, client_id): async def on_broker_client_connected(self, client_id):
@ -401,6 +342,69 @@ class BumperMQTTServer_Plugin:
bumper.client_set_mqtt(client["resource"], True) bumper.client_set_mqtt(client["resource"], True)
return return
async def on_broker_message_received(self, client_id, message):
self.handle_helperbot_msg(client_id, message)
def handle_helperbot_msg(self, client_id, message):
if str(message.topic).split("/")[6] == "helperbot":
# Response to command
helperbotlog.debug(
"Received Response - Topic: {} - Message: {}".format(
message.topic, str(message.data.decode("utf-8"))
)
)
bumper.mqtt_helperbot.command_responses.append(
{
"time": time.time(),
"topic": message.topic,
"payload": str(message.data.decode("utf-8")),
}
)
elif str(message.topic).split("/")[3] == "helperbot":
# Helperbot sending command
helperbotlog.debug(
"Send Command - Topic: {} - Message: {}".format(
message.topic, str(message.data.decode("utf-8"))
)
)
elif str(message.topic).split("/")[1] == "atr":
# Broadcast message received on atr
if str(message.topic).split("/")[2] == "errors":
boterrorlog.error(
"Received Error - Topic: {} - Message: {}".format(
message.topic, str(message.data.decode("utf-8"))
)
)
else:
helperbotlog.debug(
"Received Broadcast - Topic: {} - Message: {}".format(
message.topic, str(message.data.decode("utf-8"))
)
)
else:
helperbotlog.debug(
"Received Message - Topic: {} - Message: {}".format(
message.topic, str(message.data.decode("utf-8"))
)
)
# Cleanup "expired messages" > 60 seconds from time
for msg in bumper.mqtt_helperbot.command_responses:
expire_time = (
datetime.fromtimestamp(msg["time"])
+ timedelta(seconds=bumper.mqtt_helperbot.expire_msg_seconds)
).timestamp()
if time.time() > expire_time:
helperbotlog.debug(
"Pruning Message Due To Expiration - Message Topic: {}".format(
msg["topic"]
)
)
bumper.mqtt_helperbot.command_responses.remove(msg)
async def on_broker_client_disconnected(self, client_id): async def on_broker_client_disconnected(self, client_id):
didsplit = str(client_id).split("@") didsplit = str(client_id).split("@")

13
bumper/plugins.py Normal file
View file

@ -0,0 +1,13 @@
#!/usr/bin/env python3
import asyncio
from aiohttp import web
class ConfServerApp():
name = None
plugin_type = None
path_prefix = None
app = None
sub_api = None
routes = None

View file

@ -0,0 +1,69 @@
#!/usr/bin/env python3
import asyncio
from aiohttp import web
from bumper import plugins
import logging
import bumper
from bumper.models import *
from bumper import plugins
from datetime import datetime, timedelta
class portal_api_appsvr(plugins.ConfServerApp):
def __init__(self):
self.name = "portal_api_appsvr"
self.plugin_type = "sub_api"
self.sub_api = "portal_api"
self.routes = [
web.route("*", "/appsvr/app.do", self.handle_appsvr_api, name="portal_api_appsvr_app"),
]
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
async def handle_appsvr_api(self, request):
if not request.method == "GET": # Skip GET for now
try:
body = {}
postbody = {}
if request.content_type == "application/x-www-form-urlencoded":
postbody = await request.post()
else:
postbody = json.loads(await request.text())
todo = postbody["todo"]
if todo == "GetGlobalDeviceList": # EcoVacs Home
bots = bumper.db_get().table("bots").all()
botlist = []
for bot in bots:
if bot["class"] != "":
b = bumper.bot_toEcoVacsHome_JSON(bot)
if (
not b is None
): # Happens if the bot isn't on the EcoVacs Home list
botlist.append(json.loads(b))
body = {
"code": 0,
"devices": botlist,
"ret": "ok",
"todo": "result",
}
return web.json_response(body)
except Exception as e:
logging.exception("{}".format(e))
# Return fail for GET
body = {"result": "fail", "todo": "result"}
return web.json_response(body)
plugin = portal_api_appsvr()

View file

@ -0,0 +1,72 @@
#!/usr/bin/env python3
import asyncio
from aiohttp import web
from bumper import plugins
import logging
import bumper
from bumper.models import *
from bumper import plugins
from datetime import datetime, timedelta
import string
import random
class portal_api_dim(plugins.ConfServerApp):
def __init__(self):
self.name = "portal_api_dimr"
self.plugin_type = "sub_api"
self.sub_api = "portal_api"
self.routes = [
web.route("*", "/dim/devmanager.do", self.handle_dim_devmanager, name="portal_api_dim_devmanager"),
]
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
async def handle_dim_devmanager(self, request): # Used in EcoVacs Home App
try:
json_body = json.loads(await request.text())
randomid = "".join(random.sample(string.ascii_letters, 6))
did = ""
if "toId" in json_body: # Its a command
did = json_body["toId"]
if did != "":
bot = bumper.bot_get(did)
if bot["company"] == "eco-ng" and bot["mqtt_connection"] == True:
retcmd = await bumper.mqtt_helperbot.send_command(
json_body, randomid
)
body = retcmd
logging.debug("Send Bot - {}".format(json_body))
logging.debug("Bot Response - {}".format(body))
return web.json_response(body)
else:
# No response, send error back
logging.error(
"No bots with DID: {} connected to MQTT".format(
json_body["toId"]
)
)
body = {"id": randomid, "errno": bumper.ERR_COMMON, "ret": "fail"}
return web.json_response(body)
else:
if "td" in json_body: # Seen when doing initial wifi config
if json_body["td"] == "PollSCResult":
body = {"ret": "ok"}
return web.json_response(body)
if json_body["td"] == "HasUnreadMsg": # EcoVacs Home
body = {"ret": "ok", "unRead": False}
return web.json_response(body)
except Exception as e:
logging.exception("{}".format(e))
plugin = portal_api_dim()

View file

@ -0,0 +1,76 @@
#!/usr/bin/env python3
import asyncio
from aiohttp import web
from bumper import plugins
import logging
import bumper
from bumper.models import *
from bumper import plugins
from datetime import datetime, timedelta
import string
import random
class portal_api_iot(plugins.ConfServerApp):
def __init__(self):
self.name = "portal_api_iot"
self.plugin_type = "sub_api"
self.sub_api = "portal_api"
self.routes = [
web.route("*", "/iot/devmanager.do", self.handle_devmanager_botcommand, name="portal_api_iot_devmanager"),
]
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
async def handle_devmanager_botcommand(self, request):
try:
json_body = json.loads(await request.text())
randomid = "".join(random.sample(string.ascii_letters, 6))
did = ""
if "toId" in json_body: # Its a command
did = json_body["toId"]
if did != "":
bot = bumper.bot_get(did)
if bot["company"] == "eco-ng":
retcmd = await bumper.mqtt_helperbot.send_command(
json_body, randomid
)
body = retcmd
logging.debug("Send Bot - {}".format(json_body))
logging.debug("Bot Response - {}".format(body))
return web.json_response(body)
else:
# No response, send error back
logging.error(
"No bots with DID: {} connected to MQTT".format(
json_body["toId"]
)
)
body = {
"id": randomid,
"errno": 500,
"ret": "fail",
"debug": "wait for response timed out",
}
return web.json_response(body)
else:
if "td" in json_body: # Seen when doing initial wifi config
if json_body["td"] == "PollSCResult":
body = {"ret": "ok"}
return web.json_response(body)
if json_body["td"] == "HasUnreadMsg": # EcoVacs Home
body = {"ret": "ok", "unRead": False}
return web.json_response(body)
except Exception as e:
logging.exception("{}".format(e))
plugin = portal_api_iot()

View file

@ -0,0 +1,108 @@
#!/usr/bin/env python3
import asyncio
from aiohttp import web
from bumper import plugins
import logging
import bumper
from bumper.models import *
from bumper import plugins
from datetime import datetime, timedelta
import os
import string
import random
import xml.etree.ElementTree as ET
class portal_api_lg(plugins.ConfServerApp):
def __init__(self):
self.name = "portal_api_lg"
self.plugin_type = "sub_api"
self.sub_api = "portal_api"
self.routes = [
web.route("*", "/lg/log.do", self.handle_lg_log, name="portal_api_lg_log"),
]
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
async def handle_lg_log(self, request): # EcoVacs Home
try:
json_body = json.loads(await request.text())
randomid = "".join(random.sample(string.ascii_letters, 6))
did = json_body["did"]
botdetails = bumper.bot_get(did)
if botdetails:
if not "cmdName" in json_body:
if "td" in json_body:
json_body["cmdName"] = json_body["td"]
if not "toId" in json_body:
json_body["toId"] = did
if not "toType" in json_body:
json_body["toType"] = botdetails["class"]
if not "toRes" in json_body:
json_body["toRes"] = botdetails["resource"]
if not "payloadType" in json_body:
json_body["payloadType"] = "x"
if not "payload" in json_body:
#json_body["payload"] = ""
if json_body["td"] == "GetCleanLogs":
json_body["td"] = "q"
json_body["payload"] = '<ctl count="30"/>'
if did != "":
bot = bumper.bot_get(did)
if bot["company"] == "eco-ng":
retcmd = await bumper.mqtt_helperbot.send_command(
json_body, randomid
)
body = retcmd
logging.debug("Send Bot - {}".format(json_body))
logging.debug("Bot Response - {}".format(body))
logs = []
logsroot = ET.fromstring(retcmd["resp"])
if logsroot.attrib["ret"] == "ok":
cleanlogs = logsroot.getchildren()
for l in cleanlogs:
cleanlog = {
"ts": l.attrib['s'],
"area": l.attrib['a'],
"last": l.attrib['l'],
"cleanType": l.attrib['t'],
#imageUrl allows for providing images of cleanings, something to look into later
#"imageUrl": "https://localhost:8007",
}
logs.append(cleanlog)
body = {
"ret": "ok",
"logs": logs,
}
else:
body = {"ret": "ok", "logs": []}
logging.debug("lg logs return: {}".format(json.dumps(body)))
return web.json_response(body)
else:
# No response, send error back
logging.error(
"No bots with DID: {} connected to MQTT".format(
json_body["toId"]
)
)
body = {"id": randomid, "errno": bumper.ERR_COMMON, "ret": "fail"}
return web.json_response(body)
except Exception as e:
logging.exception("{}".format(e))
plugin = portal_api_lg()

View file

@ -0,0 +1,37 @@
#!/usr/bin/env python3
import asyncio
from aiohttp import web
from bumper import plugins
import logging
import bumper
from bumper.models import *
from bumper import plugins
from datetime import datetime, timedelta
class portal_api_neng(plugins.ConfServerApp):
def __init__(self):
self.name = "portal_api_neng"
self.plugin_type = "sub_api"
self.sub_api = "portal_api"
self.routes = [
web.route("*", "/neng/message/hasUnreadMsg", self.handle_neng_hasUnreadMessage, name="portal_api_neng_hasUnreadMessage"),
]
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
async def handle_neng_hasUnreadMessage(self, request): # EcoVacs Home
try:
body = {"code": 0, "data": {"hasUnRead": True}}
return web.json_response(body)
except Exception as e:
logging.exception("{}".format(e))
plugin = portal_api_neng()

View file

@ -0,0 +1,49 @@
#!/usr/bin/env python3
import asyncio
from aiohttp import web
from bumper import plugins
import logging
import bumper
from bumper.models import *
from bumper import plugins
from datetime import datetime, timedelta
import os
class portal_api_pim(plugins.ConfServerApp):
def __init__(self):
self.name = "portal_api_pim"
self.plugin_type = "sub_api"
self.sub_api = "portal_api"
self.routes = [
web.route("*", "/pim/product/getProductIotMap", self.handle_getProductIotMap, name="portal_api_pim_getProductIotMap"),
web.route("*", "/pim/file/get/{id}", self.handle_pimFile, name="portal_api_pim_file"),
]
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
async def handle_getProductIotMap(self, request):
try:
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": EcoVacsHomeProducts,
}
return web.json_response(body)
except Exception as e:
logging.exception("{}".format(e))
async def handle_pimFile(self, request):
try:
fileID = request.match_info.get("id", "")
return web.FileResponse(os.path.join(bumper.data_dir,"web","robotvac_image.jpg"))
except Exception as e:
logging.exception("{}".format(e))
plugin = portal_api_pim()

View file

@ -0,0 +1,120 @@
#!/usr/bin/env python3
import asyncio
from aiohttp import web
from bumper import plugins
import logging
import bumper
from bumper.models import *
from bumper import plugins
from datetime import datetime, timedelta
class portal_api_users(plugins.ConfServerApp):
def __init__(self):
self.name = "portal_api_users"
self.plugin_type = "sub_api"
self.sub_api = "portal_api"
self.routes = [
web.route("*", "/users/user.do", self.handle_usersapi, name="portal_api_users_user"),
]
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
async def handle_usersapi(self, request):
if not request.method == "GET": # Skip GET for now
try:
body = {}
postbody = {}
if request.content_type == "application/x-www-form-urlencoded":
postbody = await request.post()
else:
postbody = json.loads(await request.text())
todo = postbody["todo"]
if todo == "FindBest":
service = postbody["service"]
if service == "EcoMsgNew":
srvip = bumper.bumper_announce_ip
srvport = 5223
logging.info(
"Announcing EcoMsgNew Server to bot as: {}:{}".format(
srvip, srvport
)
)
msgserver = {"ip": srvip, "port": srvport, "result": "ok"}
msgserver = json.dumps(msgserver)
msgserver = msgserver.replace(
" ", ""
) # bot seems to be very picky about having no spaces, only way was with text
return web.json_response(text=msgserver)
elif service == "EcoUpdate":
srvip = "47.88.66.164" # EcoVacs Server
srvport = 8005
logging.info(
"Announcing EcoUpdate Server to bot as: {}:{}".format(
srvip, srvport
)
)
body = {"result": "ok", "ip": srvip, "port": srvport}
elif todo == "loginByItToken":
if "userId" in postbody:
if bumper.check_authcode(postbody["userId"], postbody["token"]):
body = {
"resource": postbody["resource"],
"result": "ok",
"todo": "result",
"token": postbody["token"],
"userId": postbody["userId"],
}
else: # EcoVacs Home LoginByITToken
loginToken = bumper.loginByItToken(postbody["token"])
if not loginToken == {}:
body = {
"resource": postbody["resource"],
"result": "ok",
"todo": "result",
"token": loginToken["token"],
"userId": loginToken["userid"],
}
else:
body = {"result": "fail", "todo": "result"}
elif todo == "GetDeviceList":
body = {
"devices": bumper.db_get().table("bots").all(),
"result": "ok",
"todo": "result",
}
elif todo == "SetDeviceNick":
bumper.bot_set_nick(postbody["did"], postbody["nick"])
body = {"result": "ok", "todo": "result"}
elif todo == "AddOneDevice":
bumper.bot_set_nick(postbody["did"], postbody["nick"])
body = {"result": "ok", "todo": "result"}
elif todo == "DeleteOneDevice":
bumper.bot_remove(postbody["did"])
body = {"result": "ok", "todo": "result"}
return web.json_response(body)
except Exception as e:
logging.exception("{}".format(e))
# Return fail for GET
body = {"result": "fail", "todo": "result"}
return web.json_response(body)
plugin = portal_api_users()

View file

@ -0,0 +1,28 @@
#!/usr/bin/env python3
import asyncio
from aiohttp import web
from bumper import plugins
import logging
import bumper
from bumper.models import *
from bumper import plugins
from datetime import datetime, timedelta
class v1_global_auth(plugins.ConfServerApp):
def __init__(self):
self.name = "v1_global_auth"
self.plugin_type = "sub_api"
self.sub_api = "api_v1"
authhandler = bumper.ConfServer.ConfServer_AuthHandler()
self.routes = [
web.route("*", "/global/auth/getAuthCode", authhandler.get_AuthCode, name="v1_global_auth_getAuthCode"),
]
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
plugin = v1_global_auth()

View file

@ -0,0 +1,58 @@
#!/usr/bin/env python3
import asyncio
from aiohttp import web
from bumper import plugins
import logging
import bumper
from bumper.models import *
from bumper import plugins
from datetime import datetime, timedelta
class v1_private_ad(plugins.ConfServerApp):
def __init__(self):
self.name = "v1_private_ad"
self.plugin_type = "sub_api"
self.sub_api = "api_v1"
self.routes = [
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/ad/getAdByPositionType", self.handle_getAdByPositionType, name="v1_ad_getAdByPositionType"),
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/ad/getBootScreen", self.handle_getBootScreen, name="v1_ad_getBootScreen"),
]
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
async def handle_getAdByPositionType(self, request): # EcoVacs Home
try:
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": None,
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
except Exception as e:
logging.exception("{}".format(e))
async def handle_getBootScreen(self, request): # EcoVacs Home
try:
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": None,
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
except Exception as e:
logging.exception("{}".format(e))
plugin = v1_private_ad()

View file

@ -0,0 +1,54 @@
#!/usr/bin/env python3
import asyncio
from aiohttp import web
from bumper import plugins
import logging
import bumper
from bumper.models import *
from bumper import plugins
from datetime import datetime, timedelta
class v1_private_campaign(plugins.ConfServerApp):
def __init__(self):
self.name = "v1_private_campaign"
self.plugin_type = "sub_api"
self.sub_api = "api_v1"
self.routes = [
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/campaign/homePageAlert", self.handle_homePageAlert, name="v1_campaign_homePageAlert"),
]
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
async def handle_homePageAlert(self, request):
try:
nextAlert = self.get_milli_time(
(datetime.now() + timedelta(hours=12)).timestamp()
)
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": {
"clickSchemeUrl": None,
"clickWebUrl": None,
"hasCampaign": "N",
"imageUrl": None,
"nextAlertTime": nextAlert,
"serverTime": self.get_milli_time(datetime.utcnow().timestamp()),
},
"msg": "操作成功",
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
except Exception as e:
logging.exception("{}".format(e))
plugin = v1_private_campaign()

View file

@ -0,0 +1,115 @@
#!/usr/bin/env python3
import asyncio
from aiohttp import web
from bumper import plugins
import logging
import bumper
from bumper.models import *
from bumper import plugins
from datetime import datetime, timedelta
class v1_private_common(plugins.ConfServerApp):
def __init__(self):
self.name = "v1_private_common"
self.plugin_type = "sub_api"
self.sub_api = "api_v1"
self.routes = [
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/common/checkAPPVersion", self.handle_checkAPPVersion, name="v1_common_checkAppVersion"),
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/common/checkVersion", self.handle_checkVersion, name="v1_common_checkVersion"),
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/common/uploadDeviceInfo", self.handle_uploadDeviceInfo, name="v1_common_uploadDeviceInfo"),
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/common/getSystemReminder", self.handle_getSystemReminder, name="v1_common_getSystemReminder"),
]
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
async def handle_checkVersion(self, request):
try:
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": {
"c": None,
"img": None,
"r": 0,
"t": None,
"u": None,
"ut": 0,
"v": None,
},
"msg": "操作成功",
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
except Exception as e:
logging.exception("{}".format(e))
async def handle_checkAPPVersion(self, request): # EcoVacs Home
try:
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": {
"c": None,
"downPageUrl": None,
"img": None,
"nextAlertTime": None,
"r": 0,
"t": None,
"u": None,
"ut": 0,
"v": None,
},
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
except Exception as e:
logging.exception("{}".format(e))
async def handle_uploadDeviceInfo(self, request): # EcoVacs Home
try:
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": None,
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
except Exception as e:
logging.exception("{}".format(e))
async def handle_getSystemReminder(self, request): # EcoVacs Home
try:
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": {
"iosGradeTime": {"iodGradeFlag": "N"},
"openNotification": {
"openNotificationContent": None,
"openNotificationFlag": "N",
"openNotificationTitle": None,
},
},
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
except Exception as e:
logging.exception("{}".format(e))
plugin = v1_private_common()

View file

@ -0,0 +1,59 @@
#!/usr/bin/env python3
import asyncio
from aiohttp import web
from bumper import plugins
import logging
import bumper
from bumper.models import *
from bumper import plugins
from datetime import datetime, timedelta
class v1_private_message(plugins.ConfServerApp):
def __init__(self):
self.name = "v1_private_message"
self.plugin_type = "sub_api"
self.sub_api = "api_v1"
self.routes = [
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/message/hasUnreadMsg", self.handle_hasUnreadMessage, name="v1_message_hasUnreadMsg"),
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/message/getMsgList", self.handle_getMsgList, name="v1_message_getMsgList"),
]
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
async def handle_hasUnreadMessage(self, request): # EcoVacs Home
try:
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": "N",
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
except Exception as e:
logging.exception("{}".format(e))
async def handle_getMsgList(self, request): # EcoVacs Home
try:
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": {"hasNextPage": 0, "items": []},
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
except Exception as e:
logging.exception("{}".format(e))
plugin = v1_private_message()

View file

@ -0,0 +1,48 @@
#!/usr/bin/env python3
import asyncio
from aiohttp import web
from bumper import plugins
import logging
import bumper
from bumper.models import *
from bumper import plugins
from datetime import datetime, timedelta
class v1_private_shop(plugins.ConfServerApp):
def __init__(self):
self.name = "v1_private_shop"
self.plugin_type = "sub_api"
self.sub_api = "api_v1"
self.routes = [
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/shop/getCnWapShopConfig", self.handle_getCnWapShopConfig, name="v1_shop_getCnWapShopConfig"),
]
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
async def handle_getCnWapShopConfig(self, request): # EcoVacs Home
try:
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": {
"myShopShowFlag": "N",
"myShopUrl": "",
"shopIndexShowFlag": "N",
"shopIndexUrl": "",
},
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
except Exception as e:
logging.exception("{}".format(e))
plugin = v1_private_shop()

View file

@ -0,0 +1,76 @@
#!/usr/bin/env python3
import asyncio
from aiohttp import web
from bumper import plugins
import logging
import bumper
from bumper.models import *
from bumper import plugins
from datetime import datetime, timedelta
class v1_private_user(plugins.ConfServerApp):
def __init__(self):
self.name = "v1_private_user"
self.plugin_type = "sub_api"
self.sub_api = "api_v1"
authhandler = bumper.ConfServer.ConfServer_AuthHandler()
self.routes = [
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/login", authhandler.login, name="v1_user_login"),
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkLogin", authhandler.login, name="v1_user_checkLogin"),
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/getAuthCode", authhandler.get_AuthCode, name="v1_user_getAuthCode"),
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/logout", authhandler.logout, name="v1_user_logout"),
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkAgreement", self.handle_checkAgreement,name="v1_user_checkAgreement"),
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkAgreementBatch", self.handle_checkAgreement,name="v1_user_checkAgreementBatch"),
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/getUserAccountInfo", authhandler.getUserAccountInfo,name="v1_user_getUserAccountInfo"),
# Direct register from app:
# /{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/directRegister
]
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
async def handle_checkAgreement(self, request):
try:
apptype = request.match_info.get("apptype", "")
if "global_" in apptype:
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": [
{
"force": "N",
"id": "20180804040641_7d746faf18b8cb22a50d145598fe4c90",
"type": "USER",
"url": "https://bumper.ecovacs.com/content/agreement?id=20180804040641_7d746faf18b8cb22a50d145598fe4c90&language=EN", # "https://gl-us-wap.ecovacs.com/content/agreement?id=20180804040641_7d746faf18b8cb22a50d145598fe4c90&language=EN
"version": "1.01",
},
{
"force": "N",
"id": "20180804040245_4e7c56dfb7ebd3b81b1f2747d0859fac",
"type": "PRIVACY",
"url": "https://bumper.ecovacs.com/content/agreement?id=20180804040245_4e7c56dfb7ebd3b81b1f2747d0859fac&language=EN", # "https://gl-us-wap.ecovacs.com/content/agreement?id=20180804040245_4e7c56dfb7ebd3b81b1f2747d0859fac&language=EN"
"version": "1.01",
},
],
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
else:
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": [],
"msg": "操作成功",
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
except Exception as e:
logging.exception("{}".format(e))
plugin = v1_private_user()

View file

@ -0,0 +1,29 @@
#!/usr/bin/env python3
import asyncio
from aiohttp import web
from bumper import plugins
import logging
import bumper
from bumper.models import *
from bumper import plugins
from datetime import datetime, timedelta
class v2_private_user(plugins.ConfServerApp):
def __init__(self):
self.name = "v2_private_user"
self.plugin_type = "sub_api"
self.sub_api = "api_v2"
authhandler = bumper.ConfServer.ConfServer_AuthHandler()
self.routes = [
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkLogin", authhandler.login, name="v2_user_checkLogin"),
]
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
plugin = v2_private_user()

View file

@ -0,0 +1,196 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<title>Bumper</title>
<meta http-equiv="refresh" content="5" />
<script>
function restartService(service) {
console.log("restart service", service);
fetch('restart_' + service)
.then((response) => {
return response.json();
})
.then((myJson) => {
alert("Restarting " + service + ": " + myJson["status"])
console.log("restart",service,"-",myJson["status"]);
});
}
function removeBot(did) {
console.log("remove bot",did);
fetch('bot/remove/' + did)
.then((response) => {
return response.json();
})
.then((myJson) => {
alert("Removing Bot (DID) - " + did + " - " + myJson["status"])
console.log("remove bot",did, myJson["status"]);
});
}
function removeClient(resource) {
console.log("remove client",resource);
fetch('client/remove/' + resource)
.then((response) => {
return response.json();
})
.then((myJson) => {
alert("Removing Client - resource - " + resource + " - " + myJson["status"])
console.log("remove client",resource, myJson["status"]);
});
}
</script>
</head>
<body>
<ul id="navigation">
{% for item in navigation %}
<li><a href="{{ item.href }}">{{ item.caption }}</a></li>
{% endfor %}
</ul>
<div class="col-8">
<h1>Bumper</h1>
<!-- Section Stats -->
<div class="card bg-light border-dark">
<div class="card-header">
<h2 class="card-title">Server Status</h2>
</div>
<div class="card-body">
<div class="card border border-dark">
<div class="card card-header">
<h3 class="card-title">MQTT Server</h3>
<div>Action: <button type="button" class="btn btn-outline-danger btn-sm" onclick="restartService('MQTTServer');">Restart Service</button></div>
</div>
<div class="card-body">
Status: {% if mqtt_server[0].state == "started" %} <span class="badge badge-success">{{ mqtt_server[0].state }}</span> {% else %} <span class="badge badge-danger">{{ mqtt_server[0].state }}</span> {% endif %}
Sessions: {{ mqtt_server[1].sessions[0].count }}
<table class="table table-striped table-bordered table-responsive-lg">
<thead class="thead-dark">
<TH>username</TH><TH>clientid</TH><TH>state</TH>
</thead>
{% for sess in mqtt_server[1].sessions[1].clients %}
<TR>
<TD>{{ sess[0].username }}</TD>
<TD>{{ sess[0].client_id }}</TD>
<TD {% if sess[0].state == "connected" %} class="table-success" {% endif %}> {{ sess[0].state }} </TD>
</TR>
{% endfor %}
</table>
</div>
</div>
</br>
<div class="card border border-dark">
<div class="card card-header">
<h3 class="card-title">XMPP Server</h3>
<div>Action: <button type="button" class="btn btn-outline-danger btn-sm" onclick="restartService('XMPPServer');">Restart Service</button></div>
</div>
<div class="card-body">
Status: {% if xmpp_server.server._serving == True %} <span class="badge badge-success">running</span> {% else %} <span class="badge badge-danger">not running</span> {% endif %}
Clients: {{ xmpp_server.clients | length }}
<table class="table table-striped table-bordered table-responsive-lg">
<thead class="thead-dark">
<TH>uid</TH><TH>jid</TH><TH>state</TH>
</thead>
{% for client in xmpp_server.clients %}
<TR>
<TD>{{ client.uid }}</TD>
<TD>{{ client.bumper_jid }}</TD>
<TD {% if client.state == 4 %} class="table-success" {% endif %}>{% if client.state == 4 %} connected {% else %} not connected {% endif %}</TD>
</TR>
{% endfor %}
</table>
</div>
</div>
</br>
<div class="card border border-dark">
<div class="card card-header">
<h3 class="card-title">Helperbot</h3>
<div>Action: <button type="button" class="btn btn-outline-danger btn-sm" onclick="restartService('Helperbot');">Restart Service</button></div>
</div>
<div class="card-body">
Status: {% if helperbot[0].state == "connected" %} <span class="badge badge-success">{{ helperbot[0].state }}</span> {% else %} <span class="badge badge-danger">{{ helperbot[0].state }}</span> {% endif %}
</div>
</div>
</div>
</div>
</br>
<!-- Section Bots -->
<div class="card border-dark">
<div class="card-header">
<h2 class="card-title">Bots</h2>
</div>
<div class="card-body">
<table class="table table-striped table-bordered table-responsive-lg">
<thead class="thead-dark">
<TH>SN</TH><TH>Nickname</TH><TH>Class</TH><TH>DID</TH><TH>Resource</TH><TH>Company</TH><TH>MQTT Connected</TH><TH>XMPP Connected</TH><TH>Action</TH>
</thead>
{% for bot in bots %}
<TR>
<TD>{{ bot.name }}</TD>
<TD>{{ bot.nick }}</TD>
<TD>{{ bot.class }}</TD>
<TD>{{ bot.did }}</TD>
<TD>{{ bot.resource }} </TD>
<TD>{{ bot.company}} </TD>
<TD {% if bot.mqtt_connection == True %} class="table-success" {% endif %}> {{ bot.mqtt_connection }} </TD>
<TD {% if bot.xmpp_connection == True %} class="table-success" {% endif %}> {{ bot.xmpp_connection }}</TD>
<TD> <button type="button" class="btn btn-outline-danger btn-sm" onclick="removeBot('{{ bot.did }}');">Remove</button> </TD>
</TR>
{% endfor %}
</table>
</div>
</div>
</br>
<!-- Section Clients -->
<div class="card border-dark">
<div class="card-header">
<h2 class="card-title">Clients</h2>
</div>
<div class="card-body">
<table class="table table-striped table-bordered table-responsive-lg">
<thead class="thead-dark">
<TH>User ID</TH><TH>Realm</TH><TH>Resource</TH><TH>MQTT Connected</TH><TH>XMPP Connected</TH><TH>Action</TH>
</thead>
{% for client in clients %}
<TR>
<TD>{{ client.userid }}</TD>
<TD>{{ client.realm }}</TD>
<TD>{{ client.resource }}</TD>
<TD {% if client.mqtt_connection == True %} class="table-success" {% endif %}> {{ client.mqtt_connection }} </TD>
<TD {% if client.xmpp_connection == True %} class="table-success" {% endif %}> {{ client.xmpp_connection }}</TD>
<TD> <button type="button" class="btn btn-outline-danger btn-sm" onclick="removeClient('{{ client.resource }}');">Remove</button> </TD>
</TR>
{% endfor %}
</table>
</div>
</div>
</div>
</body>
</html>

View file

@ -1,4 +1,5 @@
aiohttp==3.6.2 aiohttp==3.6.2
aiohttp-jinja2==1.2.0
async-timeout==3.0.1 async-timeout==3.0.1
atomicwrites==1.3.0 atomicwrites==1.3.0
attrs==19.3.0 attrs==19.3.0
@ -6,9 +7,11 @@ chardet==3.0.4
docopt==0.6.2 docopt==0.6.2
hbmqtt==0.9.5 hbmqtt==0.9.5
idna==2.8 idna==2.8
Jinja2==2.10.3
MarkupSafe==1.1.1
multidict==4.5.2 multidict==4.5.2
passlib==1.7.2 passlib==1.7.2
PyYAML==5.2 PyYAML==5.3b1
six==1.13.0 six==1.13.0
tinydb==3.15.2 tinydb==3.15.2
transitions==0.7.1 transitions==0.7.1

View file

@ -9,6 +9,9 @@ import pytest_aiohttp
import pytest_asyncio import pytest_asyncio
import datetime, time import datetime, time
from aiohttp import web from aiohttp import web
import logging
from testfixtures import LogCapture
from unittest.mock import MagicMock
def create_confserver(): def create_confserver():
@ -37,6 +40,31 @@ async def test_confserver_ssl():
conf_server.confserver_app() conf_server.confserver_app()
asyncio.create_task(conf_server.start_server()) asyncio.create_task(conf_server.start_server())
async def test_confserver_exceptions():
with LogCapture() as l:
conf_server = bumper.ConfServer(("127.0.0.1", 8007), usessl=True)
conf_server.confserver_app()
conf_server.site = web.TCPSite
#bind permission
conf_server.site.start = mock.Mock(side_effect=OSError(1, "error while attempting to bind on address ('127.0.0.1', 8007): permission denied"))
await conf_server.start_server()
#asyncio Cancel
conf_server.site = web.TCPSite
conf_server.site.start = mock.Mock(side_effect=asyncio.CancelledError)
await conf_server.start_server()
#general exception
conf_server.site = web.TCPSite
conf_server.site.start = mock.Mock(side_effect=Exception(1, "general"))
await conf_server.start_server()
l.check_present(
("confserver", "ERROR", "error while attempting to bind on address ('127.0.0.1', 8007): permission denied")
)
async def test_confserver_no_ssl(): async def test_confserver_no_ssl():
conf_server = bumper.ConfServer(("127.0.0.1", 111111), usessl=False) conf_server = bumper.ConfServer(("127.0.0.1", 111111), usessl=False)
@ -62,10 +90,16 @@ async def test_base(aiohttp_client):
# Start MQTT # Start MQTT
mqtt_address = ("127.0.0.1", 8883) mqtt_address = ("127.0.0.1", 8883)
mqtt_server = bumper.MQTTServer(mqtt_address) mqtt_server = bumper.MQTTServer(mqtt_address, password_file="tests/passwd")
bumper.mqtt_server = mqtt_server bumper.mqtt_server = mqtt_server
await mqtt_server.broker_coro() await mqtt_server.broker_coro()
# Start XMPP
xmpp_address = ("127.0.0.1", 5223)
xmpp_server = bumper.XMPPServer(xmpp_address)
bumper.xmpp_server = xmpp_server
await xmpp_server.start_async_server()
# Start Helperbot # Start Helperbot
mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address) mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address)
bumper.mqtt_helperbot = mqtt_helperbot bumper.mqtt_helperbot = mqtt_helperbot
@ -79,13 +113,16 @@ async def test_base(aiohttp_client):
await mqtt_server.broker.shutdown() await mqtt_server.broker.shutdown()
bumper.xmpp_server.disconnect()
async def test_restartService(aiohttp_client): async def test_restartService(aiohttp_client):
remove_existing_db() remove_existing_db()
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
# Start MQTT # Start MQTT
mqtt_address = ("127.0.0.1", 8883) mqtt_address = ("127.0.0.1", 8883)
mqtt_server = bumper.MQTTServer(mqtt_address) mqtt_server = bumper.MQTTServer(mqtt_address, password_file="tests/passwd")
bumper.mqtt_server = mqtt_server bumper.mqtt_server = mqtt_server
await mqtt_server.broker_coro() await mqtt_server.broker_coro()
@ -116,6 +153,16 @@ async def test_restartService(aiohttp_client):
xmpp_server.disconnect() xmpp_server.disconnect()
async def test_RemoveBot(aiohttp_client):
client = await aiohttp_client(create_app)
resp = await client.get("/bot/remove/test_did")
assert resp.status == 200
async def test_RemoveClient(aiohttp_client):
client = await aiohttp_client(create_app)
resp = await client.get("/client/remove/test_resource")
assert resp.status == 200
async def test_login(aiohttp_client): async def test_login(aiohttp_client):
remove_existing_db() remove_existing_db()

View file

@ -141,5 +141,8 @@ def test_client_db():
bumper.client_get("resource_123")["xmpp_connection"] == False bumper.client_get("resource_123")["xmpp_connection"] == False
) # Test that xmpp was set False for client ) # Test that xmpp was set False for client
assert ( assert (
len(bumper.get_disconnected_xmpp_clients()) == 1 len(bumper.get_disconnected_xmpp_clients()) > 0
) # Test len of connected xmpp clients is 1 ) # Test len of connected xmpp clients is 1
bumper.client_remove("resource_123")
assert bumper.client_get("resource_123") == None

View file

@ -15,7 +15,7 @@ import time
async def test_helperbot_message(): async def test_helperbot_message():
mqtt_address = ("127.0.0.1", 8883) mqtt_address = ("127.0.0.1", 8883)
mqtt_server = bumper.MQTTServer(mqtt_address) mqtt_server = bumper.MQTTServer(mqtt_address, password_file="tests/passwd")
await mqtt_server.broker_coro() await mqtt_server.broker_coro()
with LogCapture() as l: with LogCapture() as l:
@ -31,10 +31,9 @@ async def test_helperbot_message():
await mqtt_helperbot.Client.publish( await mqtt_helperbot.Client.publish(
msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0 msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0
) )
try:
await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1) await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1)
except asyncio.TimeoutError:
pass
l.check_present( l.check_present(
( (
"helperbot", "helperbot",
@ -53,20 +52,19 @@ async def test_helperbot_message():
) # Check helperbot is connected ) # Check helperbot is connected
msg_payload = "{}" msg_payload = "{}"
msg_topic_name = ( msg_topic_name = (
"iot/p2p/GetWKVer/helper1/bumper/helper1/bot_serial/ls1ok3/wC3g/q/iCmuqp/j" "iot/p2p/GetWKVer/helperbot/bumper/helperbot/bot_serial/ls1ok3/wC3g/q/iCmuqp/j"
) )
await mqtt_helperbot.Client.publish( await mqtt_helperbot.Client.publish(
msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0 msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0
) )
try:
await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1) await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1)
except asyncio.TimeoutError:
pass
l.check_present( l.check_present(
( (
"helperbot", "helperbot",
"DEBUG", "DEBUG",
"Send Command - Topic: iot/p2p/GetWKVer/helper1/bumper/helper1/bot_serial/ls1ok3/wC3g/q/iCmuqp/j - Message: {}", "Send Command - Topic: iot/p2p/GetWKVer/helperbot/bumper/helperbot/bot_serial/ls1ok3/wC3g/q/iCmuqp/j - Message: {}",
) )
) # Check send command message was logged ) # Check send command message was logged
l.clear() l.clear()
@ -80,20 +78,19 @@ async def test_helperbot_message():
) # Check helperbot is connected ) # Check helperbot is connected
msg_payload = '{"ret":"ok","ver":"0.13.5"}' msg_payload = '{"ret":"ok","ver":"0.13.5"}'
msg_topic_name = ( msg_topic_name = (
"iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/helper1/bumper/helper1/p/iCmuqp/j" "iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/helperbot/bumper/helperbot/p/iCmuqp/j"
) )
await mqtt_helperbot.Client.publish( await mqtt_helperbot.Client.publish(
msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0 msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0
) )
try:
await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1) await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1)
except asyncio.TimeoutError:
pass
l.check_present( l.check_present(
( (
"helperbot", "helperbot",
"DEBUG", "DEBUG",
'Received Response - Topic: iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/helper1/bumper/helper1/p/iCmuqp/j - Message: {"ret":"ok","ver":"0.13.5"}', 'Received Response - Topic: iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/helperbot/bumper/helperbot/p/iCmuqp/j - Message: {"ret":"ok","ver":"0.13.5"}',
) )
) # Check received response message was logged ) # Check received response message was logged
l.clear() l.clear()
@ -107,21 +104,20 @@ async def test_helperbot_message():
) # Check helperbot is connected ) # Check helperbot is connected
msg_payload = "test" msg_payload = "test"
msg_topic_name = ( msg_topic_name = (
"iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/TESTBAD/bumper/helper1/p/iCmuqp/j" "iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/TESTBAD/bumper/helperbot/p/iCmuqp/j"
) )
await mqtt_helperbot.Client.publish( await mqtt_helperbot.Client.publish(
msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0 msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0
) )
try:
await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1) await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1)
except asyncio.TimeoutError:
pass
l.check_present( l.check_present(
( (
"helperbot", "helperbot",
"DEBUG", "DEBUG",
"Received Message - Topic: iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/TESTBAD/bumper/helper1/p/iCmuqp/j - Message: test", "Received Message - Topic: iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/TESTBAD/bumper/helperbot/p/iCmuqp/j - Message: test",
) )
) # Check received message was logged ) # Check received message was logged
l.clear() l.clear()
@ -138,10 +134,8 @@ async def test_helperbot_message():
await mqtt_helperbot.Client.publish( await mqtt_helperbot.Client.publish(
msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0 msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0
) )
try:
await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1) await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1)
except asyncio.TimeoutError:
pass
l.check_present( l.check_present(
( (
@ -158,20 +152,21 @@ async def test_helperbot_message():
async def test_helperbot_expire_message(): async def test_helperbot_expire_message():
mqtt_address = ("127.0.0.1", 8883) mqtt_address = ("127.0.0.1", 8883)
mqtt_server = bumper.MQTTServer(mqtt_address) mqtt_server = bumper.MQTTServer(mqtt_address, password_file="tests/passwd")
await mqtt_server.broker_coro() await mqtt_server.broker_coro()
with LogCapture("helperbot") as l: with LogCapture("helperbot") as l:
# Test broadcast message # Test broadcast message
mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address) mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address)
bumper.mqtt_helperbot = mqtt_helperbot
await mqtt_helperbot.start_helper_bot() await mqtt_helperbot.start_helper_bot()
assert ( assert (
mqtt_helperbot.Client._connected_state._value == True mqtt_helperbot.Client._connected_state._value == True
) # Check helperbot is connected ) # Check helperbot is connected
expire_msg_payload = '{"ret":"ok","ver":"0.13.5"}' expire_msg_payload = '{"ret":"ok","ver":"0.13.5"}'
expire_msg_topic_name = "iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/helper1/bumper/helper1/p/testgood/j" expire_msg_topic_name = "iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/helperbot/bumper/helperbot/p/testgood/j"
currenttime = time.time() currenttime = time.time()
mqtt_helperbot.command_responses.append( mqtt_helperbot.command_responses.append(
{ {
@ -187,7 +182,7 @@ async def test_helperbot_expire_message():
"payload": expire_msg_payload, "payload": expire_msg_payload,
} in mqtt_helperbot.command_responses # check message is in command_responses } in mqtt_helperbot.command_responses # check message is in command_responses
await asyncio.sleep(0.2) await asyncio.sleep(0.1)
mqtt_helperbot.expire_msg_seconds = ( mqtt_helperbot.expire_msg_seconds = (
0.1 0.1
) # Set expire message seconds to 0.1 so we don't wait 10 seconds ) # Set expire message seconds to 0.1 so we don't wait 10 seconds
@ -197,10 +192,9 @@ async def test_helperbot_expire_message():
msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0 msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0
) # Send another message to force get_msg ) # Send another message to force get_msg
try:
await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1) await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1)
except asyncio.TimeoutError:
pass
assert { assert {
"time": currenttime, "time": currenttime,
@ -222,12 +216,14 @@ async def test_helperbot_expire_message():
await mqtt_server.broker.shutdown() await mqtt_server.broker.shutdown()
async def test_helperbot_sendcommand(): async def test_helperbot_sendcommand():
mqtt_address = ("127.0.0.1", 8883) mqtt_address = ("127.0.0.1", 8883)
mqtt_server = bumper.MQTTServer(mqtt_address) mqtt_server = bumper.MQTTServer(mqtt_address, password_file="tests/passwd")
await mqtt_server.broker_coro() await mqtt_server.broker_coro()
mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address) mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address)
bumper.mqtt_helperbot = mqtt_helperbot
await mqtt_helperbot.start_helper_bot() await mqtt_helperbot.start_helper_bot()
assert ( assert (
mqtt_helperbot.Client._connected_state._value == True mqtt_helperbot.Client._connected_state._value == True
@ -267,7 +263,7 @@ async def test_helperbot_sendcommand():
# Send response beforehand # Send response beforehand
msg_payload = '{"ret":"ok","ver":"0.13.5"}' msg_payload = '{"ret":"ok","ver":"0.13.5"}'
msg_topic_name = ( msg_topic_name = (
"iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/helper1/bumper/helper1/p/testgood/j" "iot/p2p/GetWKVer/bot_serial/ls1ok3/wC3g/helperbot/bumper/helperbot/p/testgood/j"
) )
await mqtt_helperbot.Client.publish( await mqtt_helperbot.Client.publish(
msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0 msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0
@ -306,7 +302,7 @@ async def test_helperbot_sendcommand():
# Send response beforehand # Send response beforehand
msg_payload = "<ctl ret='ok' type='Brush' left='4142' total='18000'/>" msg_payload = "<ctl ret='ok' type='Brush' left='4142' total='18000'/>"
msg_topic_name = ( msg_topic_name = (
"iot/p2p/GetLifeSpan/bot_serial/ls1ok3/wC3g/helper1/bumper/helper1/p/testx/q" "iot/p2p/GetLifeSpan/bot_serial/ls1ok3/wC3g/helperbot/bumper/helperbot/p/testx/q"
) )
await mqtt_helperbot.Client.publish( await mqtt_helperbot.Client.publish(
msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0 msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0
@ -351,7 +347,7 @@ async def test_helperbot_sendcommand():
msg_payload = '{"body":{"code":0,"data":{"area":0,"cid":"111","start":"1569378657","time":6,"type":"auto"},"msg":"ok"},"header":{"fwVer":"1.6.4","hwVer":"0.1.1","pri":1,"ts":"1569380074036","tzm":480,"ver":"0.0.1"}}' msg_payload = '{"body":{"code":0,"data":{"area":0,"cid":"111","start":"1569378657","time":6,"type":"auto"},"msg":"ok"},"header":{"fwVer":"1.6.4","hwVer":"0.1.1","pri":1,"ts":"1569380074036","tzm":480,"ver":"0.0.1"}}'
msg_topic_name = ( msg_topic_name = (
"iot/p2p/getStats/bot_serial/ls1ok3/wC3g/helper1/bumper/helper1/p/testj/j" "iot/p2p/getStats/bot_serial/ls1ok3/wC3g/helperbot/bumper/helperbot/p/testj/j"
) )
await mqtt_helperbot.Client.publish( await mqtt_helperbot.Client.publish(
msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0 msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0
@ -370,6 +366,7 @@ async def test_helperbot_sendcommand():
await mqtt_server.broker.shutdown() await mqtt_server.broker.shutdown()
async def test_mqttserver(): async def test_mqttserver():
if os.path.exists("tests/tmp.db"): if os.path.exists("tests/tmp.db"):
os.remove("tests/tmp.db") # Remove existing db os.remove("tests/tmp.db") # Remove existing db
@ -378,26 +375,7 @@ async def test_mqttserver():
mqtt_address = ("127.0.0.1", 8883) mqtt_address = ("127.0.0.1", 8883)
mqtt_server = bumper.MQTTServer(mqtt_address) mqtt_server = bumper.MQTTServer(mqtt_address, password_file="tests/passwd", allow_anonymous=True)
mqtt_server.default_config = {
"listeners": {
"default": {"type": "tcp"},
"tls1": {
"bind": "{}:{}".format(mqtt_address[0], mqtt_address[1]),
"ssl": "on",
"certfile": bumper.server_cert,
"keyfile": bumper.server_key,
},
},
"sys_interval": 10,
"auth": {
"allow-anonymous": True, # Set to True to allow anonymous authentication
"password-file": "tests/passwd", # For file auth, set user:hash in passwd file see (https://hbmqtt.readthedocs.io/en/latest/references/hbmqtt.html#configuration-example)
"plugins": ["bumper"], # Bumper plugin provides auth and handling of bots/clients connecting
},
"topic-check": {"enabled": False},
}
await mqtt_server.broker_coro() await mqtt_server.broker_coro()
@ -463,147 +441,41 @@ async def test_mqttserver():
) # Check client is disconnected ) # Check client is disconnected
# bad password # bad password
try: with LogCapture() as l:
await test_client.Client.connect( await test_client.Client.connect(
f"mqtts://test-client:notvalid!@{test_client.address[0]}:{test_client.address[1]}/", f"mqtts://test-client:notvalid!@{test_client.address[0]}:{test_client.address[1]}/",
cafile=bumper.ca_cert, cleansession=True cafile=bumper.ca_cert, cleansession=True
) )
assert ( l.check_present(
test_client.Client._connected_state._value == False ("mqttserver", "INFO", "File Authentication Failed - Username: test-client - ClientID: test-file-auth"),
) # Check client is connected order_matters=False
)
except Exception as ae:
pass
# no username in file # no username in file
try:
await test_client.Client.connect( await test_client.Client.connect(
f"mqtts://test-client-noexist:notvalid!@{test_client.address[0]}:{test_client.address[1]}/", f"mqtts://test-client-noexist:notvalid!@{test_client.address[0]}:{test_client.address[1]}/",
cafile=bumper.ca_cert, cleansession=True cafile=bumper.ca_cert, cleansession=True
) )
assert (
test_client.Client._connected_state._value == False
) # Check client is connected
except Exception as ae: l.check_present(
pass ("mqttserver", "INFO", 'File Authentication Failed - No Entry for Username: test-client-noexist - ClientID: test-file-auth'),
order_matters=False
await asyncio.sleep(0.1)
await mqtt_server.broker.shutdown()
await asyncio.sleep(0.1)
async def test_passwordfile_badhash_mqttserver():
mqtt_address = ("127.0.0.1", 8883)
mqtt_server = bumper.MQTTServer(mqtt_address)
mqtt_server.default_config = {
"listeners": {
"default": {"type": "tcp"},
"tls1": {
"bind": "{}:{}".format(mqtt_address[0], mqtt_address[1]),
"ssl": "on",
"certfile": bumper.server_cert,
"keyfile": bumper.server_key,
},
},
"sys_interval": 10,
"auth": {
"allow-anonymous": True, # Set to True to allow anonymous authentication
"password-file": "tests/passwd_bad", # For file auth, set user:hash in passwd file see (https://hbmqtt.readthedocs.io/en/latest/references/hbmqtt.html#configuration-example)
"plugins": ["bumper"], # Bumper plugin provides auth and handling of bots/clients connecting
},
"topic-check": {"enabled": False},
}
await mqtt_server.broker_coro()
await asyncio.sleep(0.1)
# bad password
try:
test_client = bumper.MQTTHelperBot(mqtt_address)
await test_client.Client.connect(
f"mqtts://test-client:notvalid!@{test_client.address[0]}:{test_client.address[1]}/",
cafile=bumper.ca_cert, cleansession=True
) )
assert (
test_client.Client._connected_state._value == False
) # Check client is connected
except Exception as ae:
pass
await mqtt_server.broker.shutdown() await mqtt_server.broker.shutdown()
await asyncio.sleep(0.1)
async def test_nofileauth_mqttserver(): async def test_nofileauth_mqttserver():
try: with LogCapture() as l:
mqtt_address = ("127.0.0.1", 8883) mqtt_address = ("127.0.0.1", 8883)
mqtt_server = bumper.MQTTServer(mqtt_address, password_file="tests/passwd-notfound")
mqtt_server = bumper.MQTTServer(mqtt_address)
mqtt_server.default_config = {
"listeners": {
"default": {"type": "tcp"},
"tls1": {
"bind": "{}:{}".format(mqtt_address[0], mqtt_address[1]),
"ssl": "on",
"certfile": bumper.server_cert,
"keyfile": bumper.server_key,
},
},
"sys_interval": 10,
"auth": {
"allow-anonymous": True, # Set to True to allow anonymous authentication
"password-file": "tests/passwd-notfound", # For file auth, set user:hash in passwd file see (https://hbmqtt.readthedocs.io/en/latest/references/hbmqtt.html#configuration-example)
"plugins": ["bumper"], # Bumper plugin provides auth and handling of bots/clients connecting
},
"topic-check": {"enabled": False},
}
await mqtt_server.broker_coro()
except:
pass
async def test_passwordfile_opt_missing_mqttserver():
try:
mqtt_address = ("127.0.0.1", 8883)
mqtt_server = bumper.MQTTServer(mqtt_address)
mqtt_server.default_config = {
"listeners": {
"default": {"type": "tcp"},
"tls1": {
"bind": "{}:{}".format(mqtt_address[0], mqtt_address[1]),
"ssl": "on",
"certfile": bumper.server_cert,
"keyfile": bumper.server_key,
},
},
"sys_interval": 10,
"auth": {
"allow-anonymous": True, # Set to True to allow anonymous authentication
"plugins": ["bumper"], # Bumper plugin provides auth and handling of bots/clients connecting
},
"topic-check": {"enabled": False},
}
await mqtt_server.broker_coro() await mqtt_server.broker_coro()
await mqtt_server.broker.shutdown() await mqtt_server.broker.shutdown()
except: l.check_present(
pass ("hbmqtt.broker.plugins.bumper", "WARNING", 'Password file tests/passwd-notfound not found'),
order_matters=False
)