Merge pull request #47 from bmartin5692/wip_dev

Re-org and start of API
This commit is contained in:
Brian Martin 2019-06-20 20:26:06 -05:00 committed by GitHub
commit 52248d7c0e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 1323 additions and 1169 deletions

View file

@ -3,14 +3,12 @@
from bumper.confserver import ConfServer
from bumper.mqttserver import MQTTServer, MQTTHelperBot
from bumper.xmppserver import XMPPServer
from bumper.models import *
from bumper.db import *
import asyncio
import json
from datetime import datetime, timedelta
import os
import logging
from logging.handlers import RotatingFileHandler
from tinydb import TinyDB, Query
from tinydb.storages import MemoryStorage
import socket
import sys
@ -100,6 +98,15 @@ helperbotlog.addHandler(helperbot_rotate)
# Override the logging level
# helperbotlog.setLevel(logging.INFO)
boterrorlog = logging.getLogger("boterror")
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")
xmpp_rotate = RotatingFileHandler(
"logs/xmppserver.log", maxBytes=5000000, backupCount=5
@ -156,23 +163,12 @@ async def start():
global mqtt_helperbot
mqtt_helperbot = MQTTHelperBot((bumper_listen, mqtt_listen_port))
global conf_server
conf_server = ConfServer(
(bumper_listen, conf1_listen_port), usessl=True, helperbot=mqtt_helperbot
)
conf_server = ConfServer((bumper_listen, conf1_listen_port), usessl=True)
global conf_server_2
conf_server_2 = ConfServer(
(bumper_listen, conf2_listen_port), usessl=False, helperbot=mqtt_helperbot
)
conf_server_2 = ConfServer((bumper_listen, conf2_listen_port), usessl=False)
global xmpp_server
xmpp_server = XMPPServer((bumper_listen, xmpp_listen_port))
# Start web servers
conf_server.confserver_app()
asyncio.create_task(conf_server.start_server())
conf_server_2.confserver_app()
asyncio.create_task(conf_server_2.start_server())
# Start MQTT Server
asyncio.create_task(mqtt_server.broker_coro())
@ -182,6 +178,20 @@ async def start():
# Start XMPP Server
asyncio.create_task(xmpp_server.start_async_server())
# Wait for helperbot to connect first
while mqtt_helperbot.Client is None:
await asyncio.sleep(0.1)
while not mqtt_helperbot.Client.session.transitions.state == "connected":
await asyncio.sleep(0.1)
# Start web servers
conf_server.confserver_app()
asyncio.create_task(conf_server.start_server())
conf_server_2.confserver_app()
asyncio.create_task(conf_server_2.start_server())
# Start maintenance
while not shutting_down:
asyncio.create_task(maintenance())
@ -223,724 +233,6 @@ async def shutdown():
bumperlog.info("Shutdown complete")
def get_milli_time(timetoconvert):
return int(round(timetoconvert * 1000))
def db_file():
if db:
return db
return os_db_path()
def os_db_path(): # createdir=True):
return os.path.join(data_dir, "bumper.db")
def db_get():
try:
# Will create the database if it doesn't exist
db = TinyDB(db_file())
# Will create the tables if they don't exist
db.table("users", cache_size=0)
db.table("clients", cache_size=0)
db.table("bots", cache_size=0)
db.table("tokens", cache_size=0)
return db
except json.decoder.JSONDecodeError as jerr:
bumperlog.error("JsonErr: {} - Doc: {}".format(jerr.msg, jerr.doc))
except Exception as ex:
bumperlog.error(ex)
class BumperUser(object):
def __init__(self, userid=""):
self.userid = userid
self.devices = []
self.bots = []
def asdict(self):
return {"userid": self.userid, "devices": self.devices, "bots": self.bots}
def user_add(userid):
newuser = BumperUser()
newuser.userid = userid
user = user_get(userid)
if not user:
bumperlog.info("Adding new user with userid: {}".format(newuser.userid))
user_full_upsert(newuser.asdict())
def user_get(userid):
users = db_get().table("users")
User = Query()
return users.get(User.userid == userid)
def user_by_deviceid(deviceid):
users = db_get().table("users")
User = Query()
return users.get(User.devices.any([deviceid]))
def user_full_upsert(user):
users = db_get().table("users")
User = Query()
users.upsert(user, User.did == user["userid"])
def user_add_device(userid, devid):
users = db_get().table("users")
User = Query()
user = users.get(User.userid == userid)
userdevices = list(user["devices"])
if not devid in userdevices:
userdevices.append(devid)
users.upsert({"devices": userdevices}, User.userid == userid)
def user_remove_device(userid, devid):
users = db_get().table("users")
User = Query()
user = users.get(User.userid == userid)
userdevices = list(user["devices"])
if devid in userdevices:
userdevices.remove(devid)
users.upsert({"devices": userdevices}, User.userid == userid)
def user_add_bot(userid, did):
users = db_get().table("users")
User = Query()
user = users.get(User.userid == userid)
userbots = list(user["bots"])
if not did in userbots:
userbots.append(did)
users.upsert({"bots": userbots}, User.userid == userid)
def user_remove_bot(userid, did):
users = db_get().table("users")
User = Query()
user = users.get(User.userid == userid)
userbots = list(user["bots"])
if did in userbots:
userbots.remove(did)
users.upsert({"bots": userbots}, User.userid == userid)
def user_get_tokens(userid):
tokens = db_get().table("tokens")
return tokens.search((Query().userid == userid))
def user_get_token(userid, token):
tokens = db_get().table("tokens")
return tokens.get((Query().userid == userid) & (Query().token == token))
def user_add_token(userid, token):
tokens = db_get().table("tokens")
tmptoken = tokens.get((Query().userid == userid) & (Query().token == token))
if not tmptoken:
bumperlog.debug("Adding token {} for userid {}".format(token, userid))
tokens.insert(
{
"userid": userid,
"token": token,
"expiration": "{}".format(
datetime.now() + timedelta(seconds=token_validity_seconds)
),
}
)
def user_revoke_all_tokens(userid):
tokens = db_get().table("tokens")
tsearch = tokens.search(Query().userid == userid)
for i in tsearch:
tokens.remove(doc_ids=[i.doc_id])
def user_revoke_expired_tokens(userid):
tokens = db_get().table("tokens")
tsearch = tokens.search(Query().userid == userid)
for i in tsearch:
if datetime.now() >= datetime.fromisoformat(i["expiration"]):
bumperlog.debug("Removing token {} due to expiration".format(i["token"]))
tokens.remove(doc_ids=[i.doc_id])
def user_revoke_token(userid, token):
tokens = db_get().table("tokens")
tmptoken = tokens.get((Query().userid == userid) & (Query().token == token))
if tmptoken:
tokens.remove(doc_ids=[tmptoken.doc_id])
def user_add_authcode(userid, token, authcode):
tokens = db_get().table("tokens")
tmptoken = tokens.get((Query().userid == userid) & (Query().token == token))
if tmptoken:
tokens.upsert(
{"authcode": authcode},
((Query().userid == userid) & (Query().token == token)),
)
def user_revoke_authcode(userid, token, authcode):
tokens = db_get().table("tokens")
tmptoken = tokens.get((Query().userid == userid) & (Query().token == token))
if tmptoken:
tokens.upsert(
{"authcode": ""}, ((Query().userid == userid) & (Query().token == token))
)
class VacBotDevice(object):
def __init__(
self, did="", vac_bot_device_class="", resource="", name="", nick="", company=""
):
self.vac_bot_device_class = vac_bot_device_class
self.company = company
self.did = did
self.name = name
self.nick = nick
self.resource = resource
self.mqtt_connection = False
self.xmpp_connection = False
def asdict(self):
return {
"class": self.vac_bot_device_class,
"company": self.company,
"did": self.did,
"name": self.name,
"nick": self.nick,
"resource": self.resource,
"mqtt_connection": self.mqtt_connection,
"xmpp_connection": self.xmpp_connection,
}
class GlobalVacBotDevice(VacBotDevice): # EcoVacs Home
UILogicId = ""
ota = True
updateInfo = {"changeLog": "", "needUpdate": False}
icon = ""
deviceName = ""
# EcoVacs Home Product IOT Map - 2019-05-20
# https://portal-ww.ecouser.net/api/pim/product/getProductIotMap
EcoVacsHomeProducts = [
{
"classid": "dl8fht",
"product": {
"UILogicId": "D_600",
"_id": "5acb0fa87c295c0001876ecf",
"icon": "5acc32067c295c0001876eea",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5acc32067c295c0001876eea",
"materialNo": "702-0000-0170",
"name": "DEEBOT 600 Series",
"ota": False,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "02uwxm",
"product": {
"UILogicId": "D_OZMO_SLIM10",
"_id": "5ae1481e7ccd1a0001e1f69e",
"icon": "5b1dddc48bc45700014035a1",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b1dddc48bc45700014035a1",
"materialNo": "110-1715-0201",
"name": "DEEBOT OZMO Slim10 Series",
"ota": False,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "y79a7u",
"product": {
"UILogicId": "D_OZMO_900",
"_id": "5b04c0227ccd1a0001e1f6a8",
"icon": "5b04c0217ccd1a0001e1f6a7",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b04c0217ccd1a0001e1f6a7",
"materialNo": "110-1810-0101",
"name": "DEEBOT OZMO 900 Series",
"ota": True,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "jr3pqa",
"product": {
"UILogicId": "D_700",
"_id": "5b43077b8bc457000140363e",
"icon": "5b5ac4cc8d5a56000111e769",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b5ac4cc8d5a56000111e769",
"materialNo": "702-0000-0202",
"name": "DEEBOT 711",
"ota": True,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "uv242z",
"product": {
"UILogicId": "D_700",
"_id": "5b5149b4ac0b87000148c128",
"icon": "5b5ac4e45f21100001882bb9",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b5ac4e45f21100001882bb9",
"materialNo": "702-0000-0205",
"name": "DEEBOT 710",
"ota": True,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "ls1ok3",
"product": {
"UILogicId": "D_900",
"_id": "5b6561060506b100015c8868",
"icon": "5ba4a2cb6c2f120001c32839",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ba4a2cb6c2f120001c32839",
"materialNo": "110-1711-0201",
"name": "DEEBOT 900 Series",
"ota": True,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "eyi9jv",
"product": {
"UILogicId": "D_700",
"_id": "5b7b65f364e1680001a08b54",
"icon": "5b7b65f176f7f10001e9a0c2",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b7b65f176f7f10001e9a0c2",
"materialNo": "715",
"name": "DEEBOT 715",
"ota": True,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "4zfacv",
"product": {
"UILogicId": "DN_2G",
"_id": "5bf2596f23244a00013f2f13",
"icon": "5c778731280fda0001770ba0",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c778731280fda0001770ba0",
"materialNo": "910",
"name": "DEEBOT 910",
"ota": True,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "vi829v",
"product": {
"UILogicId": "DX_5G",
"_id": "5c19a8f3a1e6ee0001782247",
"icon": "5c9c7995e9e9270001354ab4",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c9c7995e9e9270001354ab4",
"materialNo": "920",
"name": "DEEBOT OZMO 920 Series",
"ota": True,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "gd4uut",
"product": {
"UILogicId": "DR_935G",
"_id": "5bc8189d68142800016a6937",
"icon": "5c7384767b93c700013f12e7",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c7384767b93c700013f12e7",
"materialNo": "960",
"name": "DEEBOT OZMO 960",
"ota": True,
"supportType": {
"alexa": True,
"assistant": True,
"share": False,
"tmjl": False,
},
},
},
{
"classid": "9akc61",
"product": {
"UILogicId": "D_500",
"_id": "5c763f8263023c0001e7f855",
"icon": "5c932067280fda0001770d7f",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c932067280fda0001770d7f",
"materialNo": "D505",
"name": "DEEBOT 505",
"ota": False,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "r8ead0",
"product": {
"UILogicId": "D_500",
"_id": "5c763f63280fda0001770b88",
"icon": "5c93204b63023c0001e7faa7",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c93204b63023c0001e7faa7",
"materialNo": "D502",
"name": "DEEBOT 502",
"ota": False,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "emzppx",
"product": {
"UILogicId": "D_500",
"_id": "5c763f35280fda0001770b84",
"icon": "5c931fef280fda0001770d7e",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c931fef280fda0001770d7e",
"materialNo": "D501",
"name": "DEEBOT 501",
"ota": False,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "vsc5ia",
"product": {
"UILogicId": "D_500",
"_id": "5c763eba280fda0001770b81",
"icon": "5c874326280fda0001770d2a",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c874326280fda0001770d2a",
"materialNo": "D500",
"name": "DEEBOT 500",
"ota": False,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "aqdd5p",
"product": {
"UILogicId": "D_900",
"_id": "5cb7cfba179839000114d762",
"icon": "5cb7cfbab72c4d00010e5fc7",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5cb7cfbab72c4d00010e5fc7",
"materialNo": "110-1711-0001",
"name": "DEEBOT DE55",
"ota": True,
"supportType": {
"alexa": False,
"assistant": False,
"share": False,
"tmjl": False,
},
},
},
]
class VacBotClient(object):
def __init__(self, userid="", realm="", token=""):
self.userid = userid
self.realm = realm
self.resource = token
self.mqtt_connection = False
self.xmpp_connection = False
def asdict(self):
return {
"userid": self.userid,
"realm": self.realm,
"resource": self.resource,
"mqtt_connection": self.mqtt_connection,
"xmpp_connection": self.xmpp_connection,
}
def get_disconnected_xmpp_clients():
clients = db_get().table("clients")
Client = Query()
return clients.search(Client.xmpp_connection == False)
def check_authcode(uid, authcode):
bumperlog.debug("Checking for authcode: {}".format(authcode))
tokens = db_get().table("tokens")
tmpauth = tokens.get(
(Query().authcode == authcode)
& ( # Match authcode
(Query().userid == uid.replace("fuid_", ""))
| (Query().userid == "fuid_{}".format(uid))
) # Userid with or without fuid_
)
if tmpauth:
return True
return False
def loginByItToken(authcode):
bumperlog.debug("Checking for authcode: {}".format(authcode))
tokens = db_get().table("tokens")
tmpauth = tokens.get(
(Query().authcode == authcode)
# & ( # Match authcode
# (Query().userid == uid.replace("fuid_", ""))
# | (Query().userid == "fuid_{}".format(uid))
# ) # Userid with or without fuid_
)
if tmpauth:
return {"token": tmpauth["token"], "userid": tmpauth["userid"]}
return {}
def check_token(uid, token):
bumperlog.debug("Checking for token: {}".format(token))
tokens = db_get().table("tokens")
tmpauth = tokens.get(
(Query().token == token)
& ( # Match token
(Query().userid == uid.replace("fuid_", ""))
| (Query().userid == "fuid_{}".format(uid))
) # Userid with or without fuid_
)
if tmpauth:
return True
return False
def revoke_expired_tokens():
tokens = db_get().table("tokens").all()
for i in tokens:
if datetime.now() >= datetime.fromisoformat(i["expiration"]):
bumperlog.debug("Removing token {} due to expiration".format(i["token"]))
db_get().table("tokens").remove(doc_ids=[i.doc_id])
def bot_add(sn, did, devclass, resource, company):
newbot = VacBotDevice()
newbot.did = did
newbot.name = sn
newbot.vac_bot_device_class = devclass
newbot.resource = resource
newbot.company = 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
): # try to prevent bad additions to the bot list
bumperlog.info(
"Adding new bot with SN: {} DID: {}".format(newbot.name, newbot.did)
)
bot_full_upsert(newbot.asdict())
def bot_remove(did):
bots = db_get().table("bots")
bot = bot_get(did)
bots.remove(doc_ids=[bot.doc_id])
def bot_get(did):
bots = db_get().table("bots")
Bot = Query()
return bots.get(Bot.did == did)
def bot_toEcoVacsHome_JSON(bot): # EcoVacs Home
for botprod in EcoVacsHomeProducts:
if botprod["classid"] == bot["class"]:
bot["UILogicId"] = botprod["product"]["UILogicId"]
bot["ota"] = botprod["product"]["ota"]
bot["icon"] = botprod["product"]["iconUrl"]
return json.dumps(
bot, default=lambda o: o.__dict__, sort_keys=False
) # , indent=4)
def bot_full_upsert(vacbot):
bots = db_get().table("bots")
Bot = Query()
bots.upsert(vacbot, Bot.did == vacbot["did"])
def bot_set_nick(did, nick):
bots = db_get().table("bots")
Bot = Query()
bots.upsert({"nick": nick}, Bot.did == did)
def bot_set_mqtt(did, mqtt):
bots = db_get().table("bots")
Bot = Query()
bots.upsert({"mqtt_connection": mqtt}, Bot.did == did)
def bot_set_xmpp(did, xmpp):
bots = db_get().table("bots")
Bot = Query()
bots.upsert({"xmpp_connection": xmpp}, Bot.did == did)
def client_add(userid, realm, resource):
newclient = VacBotClient()
newclient.userid = userid
newclient.realm = realm
newclient.resource = resource
client = client_get(resource)
if not client:
bumperlog.info("Adding new client with resource {}".format(newclient.resource))
client_full_upsert(newclient.asdict())
def client_get(resource):
clients = db_get().table("clients")
Client = Query()
return clients.get(Client.resource == resource)
def client_full_upsert(client):
clients = db_get().table("clients")
Client = Query()
clients.upsert(client, Client.resource == client["resource"])
def client_set_mqtt(resource, mqtt):
clients = db_get().table("clients")
Client = Query()
clients.upsert({"mqtt_connection": mqtt}, Client.resource == resource)
def client_set_xmpp(resource, xmpp):
clients = db_get().table("clients")
Client = Query()
clients.upsert({"xmpp_connection": xmpp}, Client.resource == resource)
RETURN_API_SUCCESS = "0000"
ERR_ACTIVATE_TOKEN_TIMEOUT = "1006"
ERR_COMMON = "0001"
ERR_DEFAULT = "9000"
ERR_EMAIL_NON_EXIST = "1002"
ERR_EMAIL_SEND_TIME_LIMIT = "1011"
ERR_EMAIL_USED = "1001"
ERR_INTERFACE_AUTH = "0002"
ERR_PARAM_INVALID = "0003"
ERR_PWD_WRONG = "1005"
ERR_RESET_PWD_TOKEN_TIMEOUT = "1007"
ERR_TIMESTAMP_INVALID = "0005"
ERR_TOKEN_INVALID = "0004"
ERR_USER_DISABLE = "1004"
ERR_USER_NOT_ACTIVATED = "1003"
ERR_WRONG_COMFIRM_PWD = "10010"
ERR_WRONG_EMAIL_ADDRESS = "1008"
ERR_WRONG_PWD_FROMATE = "1009"
API_ERRORS = {
RETURN_API_SUCCESS: "0000",
ERR_ACTIVATE_TOKEN_TIMEOUT: "1006",
ERR_COMMON: "0001",
ERR_DEFAULT: "9000",
ERR_EMAIL_NON_EXIST: "1002",
ERR_EMAIL_SEND_TIME_LIMIT: "1011",
ERR_EMAIL_USED: "1001",
ERR_INTERFACE_AUTH: "0002",
ERR_PARAM_INVALID: "0003",
ERR_PWD_WRONG: "1005",
ERR_RESET_PWD_TOKEN_TIMEOUT: "1007",
ERR_TIMESTAMP_INVALID: "0005",
ERR_TOKEN_INVALID: "0004",
ERR_USER_DISABLE: "1004",
ERR_USER_NOT_ACTIVATED: "1003",
ERR_WRONG_COMFIRM_PWD: "10010",
ERR_WRONG_EMAIL_ADDRESS: "1008",
ERR_WRONG_PWD_FROMATE: "1009",
}
def create_certs():
import platform
import os
@ -991,6 +283,7 @@ def create_certs():
def first_run():
create_certs()
def main(argv=None):
import argparse

View file

@ -6,6 +6,7 @@ import ssl
import string
import random
import bumper
from bumper.models import *
from datetime import datetime, timedelta
import asyncio
from aiohttp import web
@ -36,42 +37,24 @@ logging.getLogger("aiohttp.access").addFilter(
) # Add logging filter above to aiohttp.access
class EcoVacs_Login:
accessToken = ""
country = ""
email = ""
uid = ""
username = ""
def toJSON(self):
return json.dumps(
self, default=lambda o: o.__dict__, sort_keys=False
) # , indent=4)
class EcoVacsHome_Login(EcoVacs_Login):
loginName = ""
mobile = ""
ucUid = ""
class ConfServer:
def __init__(self, address, usessl=False, helperbot=None):
self.helperbot = helperbot
def __init__(self, address, usessl=False):
self.usessl = usessl
self.address = address
self.confthread = None
self.run_async = False
self.app = None
self.site = None
self.runner = None
def get_milli_time(self, timetoconvert):
return int(round(timetoconvert * 1000))
def confserver_app(self):
self.app = web.Application(loop=asyncio.get_event_loop())
self.app.add_routes(
[
web.get("", self.handle_base),
web.get("/restart_{service}", self.handle_RestartService),
web.get(
"/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/login",
self.handle_login,
@ -210,9 +193,82 @@ class ConfServer:
async def handle_base(self, request):
try:
# TODO - API Options here for viewing clients, tokens, restarting the server, etc.
text = "Bumper!"
# text = "Bumper!"
bots = bumper.db_get().table("bots").all()
clients = bumper.db_get().table("clients").all()
helperbot = bumper.mqtt_helperbot.Client.session.transitions.state
mqttserver = bumper.mqtt_server.broker
mq_sessions = []
for sess in mqttserver._sessions:
tmpsess = []
tmpsess.append({"client_id": mqttserver._sessions[sess][0].client_id})
tmpsess.append(
{"state": mqttserver._sessions[sess][0].transitions.state}
)
mq_sessions.append(tmpsess)
all = {
"bots": bots,
"clients": clients,
"helperbot": [{"state": helperbot}],
"mqtt_server": [
{"state": mqttserver.transitions.state},
{
"sessions": [
{"count": len(mqttserver._sessions)},
{"clients": mq_sessions},
]
},
],
}
return web.json_response(text)
return web.json_response(all)
except Exception as e:
confserverlog.exception("{}".format(e))
async def restart_Helper(self):
await bumper.mqtt_helperbot.Client.disconnect()
await bumper.mqtt_helperbot.start_helper_bot()
async def restart_MQTT(self):
mqttserver = bumper.mqtt_server.broker
for sess in list(mqttserver._sessions):
sessobj = mqttserver._sessions[sess][1]
await sessobj.writer.close()
mqttserver.delete_session(sess)
await bumper.mqtt_server.broker.shutdown()
while not bumper.mqtt_server.broker.transitions.state == "stopped":
await asyncio.sleep(0.1)
await bumper.mqtt_server.broker_coro()
while not bumper.mqtt_server.broker.transitions.state == "started":
await asyncio.sleep(0.1)
async def restart_XMPP(self):
bumper.xmpp_server.disconnect()
await bumper.xmpp_server.start_async_server()
async def handle_RestartService(self, request):
try:
service = request.match_info.get("service", "")
if service == "Helperbot":
await self.restart_Helper()
return web.json_response({"status": "complete"})
elif service == "MQTTServer":
await self.restart_MQTT()
aloop = asyncio.get_event_loop()
aloop.call_later(
2, lambda: asyncio.create_task(self.restart_Helper())
) # In 2 seconds restart Helperbot
return web.json_response({"status": "complete"})
elif service == "XMPPServer":
await self.restart_XMPP()
return web.json_response({"status": "complete"})
else:
return web.json_response({"status": "invalid service"})
except Exception as e:
confserverlog.exception("{}".format(e))
@ -256,7 +312,7 @@ class ConfServer:
login_details.email = "null@null.com"
body = {
"code": bumper.RETURN_API_SUCCESS,
"code": API_ERRORS[RETURN_API_SUCCESS],
"data": json.loads(login_details.toJSON()),
# {
# "accessToken": self.generate_token(tmpuser), # Generate a token
@ -266,9 +322,7 @@ class ConfServer:
# "username": "fusername_{}".format(tmpuser["userid"]),
# },
"msg": "操作成功",
"time": bumper.get_milli_time(
datetime.utcnow().timestamp()
),
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
@ -277,7 +331,7 @@ class ConfServer:
"code": bumper.ERR_USER_NOT_ACTIVATED,
"data": None,
"msg": "当前密码错误",
"time": bumper.get_milli_time(datetime.utcnow().timestamp()),
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
@ -323,7 +377,7 @@ class ConfServer:
"loginName": login_details.loginName,
},
"msg": "操作成功",
"time": bumper.get_milli_time(datetime.utcnow().timestamp()),
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
@ -359,7 +413,7 @@ class ConfServer:
# "username": "fusername_{}".format(tmpuser["userid"]),
# },
"msg": "操作成功",
"time": bumper.get_milli_time(datetime.utcnow().timestamp()),
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
@ -368,7 +422,7 @@ class ConfServer:
"code": bumper.ERR_TOKEN_INVALID,
"data": None,
"msg": "当前密码错误",
"time": bumper.get_milli_time(datetime.utcnow().timestamp()),
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
@ -459,7 +513,7 @@ class ConfServer:
# "username": "fusername_{}".format(tmpuser["userid"]),
# },
"msg": "操作成功",
"time": bumper.get_milli_time(datetime.utcnow().timestamp()),
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return body
@ -483,7 +537,7 @@ class ConfServer:
"code": bumper.RETURN_API_SUCCESS,
"data": None,
"msg": "操作成功",
"time": bumper.get_milli_time(datetime.utcnow().timestamp()),
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
@ -525,7 +579,7 @@ class ConfServer:
},
"msg": "操作成功",
"success": True,
"time": bumper.get_milli_time(
"time": self.get_milli_time(
datetime.utcnow().timestamp()
),
}
@ -537,7 +591,7 @@ class ConfServer:
"ecovacsUid": request.query["uid"],
},
"msg": "操作成功",
"time": bumper.get_milli_time(
"time": self.get_milli_time(
datetime.utcnow().timestamp()
),
}
@ -547,7 +601,7 @@ class ConfServer:
"code": bumper.ERR_TOKEN_INVALID,
"data": None,
"msg": "当前密码错误",
"time": bumper.get_milli_time(datetime.utcnow().timestamp()),
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
@ -569,7 +623,7 @@ class ConfServer:
"v": None,
},
"msg": "操作成功",
"time": bumper.get_milli_time(datetime.utcnow().timestamp()),
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
@ -594,7 +648,7 @@ class ConfServer:
},
"msg": "操作成功",
"success": True,
"time": bumper.get_milli_time(datetime.utcnow().timestamp()),
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
@ -609,7 +663,7 @@ class ConfServer:
"data": None,
"msg": "操作成功",
"success": True,
"time": bumper.get_milli_time(datetime.utcnow().timestamp()),
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
@ -624,7 +678,7 @@ class ConfServer:
"data": None,
"msg": "操作成功",
"success": True,
"time": bumper.get_milli_time(datetime.utcnow().timestamp()),
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
@ -639,7 +693,7 @@ class ConfServer:
"data": None,
"msg": "操作成功",
"success": True,
"time": bumper.get_milli_time(datetime.utcnow().timestamp()),
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
@ -654,7 +708,7 @@ class ConfServer:
"data": "N",
"msg": "操作成功",
"success": True,
"time": bumper.get_milli_time(datetime.utcnow().timestamp()),
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
@ -678,7 +732,7 @@ class ConfServer:
"data": {"hasNextPage": 0, "items": []},
"msg": "操作成功",
"success": True,
"time": bumper.get_milli_time(datetime.utcnow().timestamp()),
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
@ -698,7 +752,7 @@ class ConfServer:
},
"msg": "操作成功",
"success": True,
"time": bumper.get_milli_time(datetime.utcnow().timestamp()),
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
@ -720,7 +774,7 @@ class ConfServer:
},
"msg": "操作成功",
"success": True,
"time": bumper.get_milli_time(datetime.utcnow().timestamp()),
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
@ -752,14 +806,14 @@ class ConfServer:
],
"msg": "操作成功",
"success": True,
"time": bumper.get_milli_time(datetime.utcnow().timestamp()),
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
else:
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": [],
"msg": "操作成功",
"time": bumper.get_milli_time(datetime.utcnow().timestamp()),
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
@ -769,7 +823,7 @@ class ConfServer:
async def handle_homePageAlert(self, request):
try:
nextAlert = bumper.get_milli_time(
nextAlert = self.get_milli_time(
(datetime.now() + timedelta(hours=12)).timestamp()
)
@ -781,10 +835,10 @@ class ConfServer:
"hasCampaign": "N",
"imageUrl": None,
"nextAlertTime": nextAlert,
"serverTime": bumper.get_milli_time(datetime.utcnow().timestamp()),
"serverTime": self.get_milli_time(datetime.utcnow().timestamp()),
},
"msg": "操作成功",
"time": bumper.get_milli_time(datetime.utcnow().timestamp()),
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
@ -1088,9 +1142,11 @@ class ConfServer:
if did != "":
bot = bumper.bot_get(did)
if bot["company"] == "eco-ng" and bot["mqtt_connection"] == True:
if bot["company"] == "eco-ng":
body = ""
retcmd = await self.helperbot.send_command(json_body, randomid)
retcmd = await bumper.mqtt_helperbot.send_command(
json_body, randomid
)
confserverlog.debug("Send Bot - {}".format(json_body))
confserverlog.debug("Bot Response - {}".format(body))
logs = []
@ -1138,8 +1194,10 @@ class ConfServer:
if did != "":
bot = bumper.bot_get(did)
if bot["company"] == "eco-ng" and bot["mqtt_connection"] == True:
retcmd = await self.helperbot.send_command(json_body, randomid)
if bot["company"] == "eco-ng":
retcmd = await bumper.mqtt_helperbot.send_command(
json_body, randomid
)
body = retcmd
confserverlog.debug("Send Bot - {}".format(json_body))
confserverlog.debug("Bot Response - {}".format(body))
@ -1184,7 +1242,9 @@ class ConfServer:
if did != "":
bot = bumper.bot_get(did)
if bot["company"] == "eco-ng" and bot["mqtt_connection"] == True:
retcmd = await self.helperbot.send_command(json_body, randomid)
retcmd = await bumper.mqtt_helperbot.send_command(
json_body, randomid
)
body = retcmd
confserverlog.debug("Send Bot - {}".format(json_body))
confserverlog.debug("Bot Response - {}".format(body))
@ -1215,9 +1275,6 @@ class ConfServer:
async def disconnect(self):
try:
confserverlog.info("shutting down")
if self.run_async:
self.confthread.join()
else:
await self.app.shutdown()
except Exception as e:

366
bumper/db.py Normal file
View file

@ -0,0 +1,366 @@
#!/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
bumperlog = logging.getLogger("bumper")
def db_file():
if bumper.db:
return bumper.db
return os_db_path()
def os_db_path(): # createdir=True):
return os.path.join(bumper.data_dir, "bumper.db")
def db_get():
# Will create the database if it doesn't exist
db = TinyDB(db_file())
# Will create the tables if they don't exist
db.table("users", cache_size=0)
db.table("clients", cache_size=0)
db.table("bots", cache_size=0)
db.table("tokens", cache_size=0)
return db
def user_add(userid):
newuser = BumperUser()
newuser.userid = userid
user = user_get(userid)
if not user:
bumperlog.info("Adding new user with userid: {}".format(newuser.userid))
user_full_upsert(newuser.asdict())
def user_get(userid):
users = db_get().table("users")
User = Query()
return users.get(User.userid == userid)
def user_by_deviceid(deviceid):
users = db_get().table("users")
User = Query()
return users.get(User.devices.any([deviceid]))
def user_full_upsert(user):
opendb = db_get()
with opendb:
users = opendb.table("users")
User = Query()
users.upsert(user, User.did == user["userid"])
def user_add_device(userid, devid):
opendb = db_get()
with opendb:
users = opendb.table("users")
User = Query()
user = users.get(User.userid == userid)
userdevices = list(user["devices"])
if not devid in userdevices:
userdevices.append(devid)
users.upsert({"devices": userdevices}, User.userid == userid)
def user_remove_device(userid, devid):
opendb = db_get()
with opendb:
users = opendb.table("users")
User = Query()
user = users.get(User.userid == userid)
userdevices = list(user["devices"])
if devid in userdevices:
userdevices.remove(devid)
users.upsert({"devices": userdevices}, User.userid == userid)
def user_add_bot(userid, did):
opendb = db_get()
with opendb:
users = opendb.table("users")
User = Query()
user = users.get(User.userid == userid)
userbots = list(user["bots"])
if not did in userbots:
userbots.append(did)
users.upsert({"bots": userbots}, User.userid == userid)
def user_remove_bot(userid, did):
opendb = db_get()
with opendb:
users = opendb.table("users")
User = Query()
user = users.get(User.userid == userid)
userbots = list(user["bots"])
if did in userbots:
userbots.remove(did)
users.upsert({"bots": userbots}, User.userid == userid)
def user_get_tokens(userid):
tokens = db_get().table("tokens")
return tokens.search((Query().userid == userid))
def user_get_token(userid, token):
tokens = db_get().table("tokens")
return tokens.get((Query().userid == userid) & (Query().token == token))
def user_add_token(userid, token):
opendb = db_get()
with opendb:
tokens = opendb.table("tokens")
tmptoken = tokens.get((Query().userid == userid) & (Query().token == token))
if not tmptoken:
bumperlog.debug("Adding token {} for userid {}".format(token, userid))
tokens.insert(
{
"userid": userid,
"token": token,
"expiration": "{}".format(
datetime.now()
+ timedelta(seconds=bumper.token_validity_seconds)
),
}
)
def user_revoke_all_tokens(userid):
opendb = db_get()
with opendb:
tokens = opendb.table("tokens")
tsearch = tokens.search(Query().userid == userid)
for i in tsearch:
tokens.remove(doc_ids=[i.doc_id])
def user_revoke_expired_tokens(userid):
opendb = db_get()
with opendb:
tokens = opendb.table("tokens")
tsearch = tokens.search(Query().userid == userid)
for i in tsearch:
if datetime.now() >= datetime.fromisoformat(i["expiration"]):
bumperlog.debug(
"Removing token {} due to expiration".format(i["token"])
)
tokens.remove(doc_ids=[i.doc_id])
def user_revoke_token(userid, token):
opendb = db_get()
with opendb:
tokens = opendb.table("tokens")
tmptoken = tokens.get((Query().userid == userid) & (Query().token == token))
if tmptoken:
tokens.remove(doc_ids=[tmptoken.doc_id])
def user_add_authcode(userid, token, authcode):
opendb = db_get()
with opendb:
tokens = opendb.table("tokens")
tmptoken = tokens.get((Query().userid == userid) & (Query().token == token))
if tmptoken:
tokens.upsert(
{"authcode": authcode},
((Query().userid == userid) & (Query().token == token)),
)
def user_revoke_authcode(userid, token, authcode):
opendb = db_get()
with opendb:
tokens = opendb.table("tokens")
tmptoken = tokens.get((Query().userid == userid) & (Query().token == token))
if tmptoken:
tokens.upsert(
{"authcode": ""},
((Query().userid == userid) & (Query().token == token)),
)
def get_disconnected_xmpp_clients():
clients = db_get().table("clients")
Client = Query()
return clients.search(Client.xmpp_connection == False)
def check_authcode(uid, authcode):
bumperlog.debug("Checking for authcode: {}".format(authcode))
tokens = db_get().table("tokens")
tmpauth = tokens.get(
(Query().authcode == authcode)
& ( # Match authcode
(Query().userid == uid.replace("fuid_", ""))
| (Query().userid == "fuid_{}".format(uid))
) # Userid with or without fuid_
)
if tmpauth:
return True
return False
def loginByItToken(authcode):
bumperlog.debug("Checking for authcode: {}".format(authcode))
tokens = db_get().table("tokens")
tmpauth = tokens.get(
(Query().authcode == authcode)
# & ( # Match authcode
# (Query().userid == uid.replace("fuid_", ""))
# | (Query().userid == "fuid_{}".format(uid))
# ) # Userid with or without fuid_
)
if tmpauth:
return {"token": tmpauth["token"], "userid": tmpauth["userid"]}
return {}
def check_token(uid, token):
bumperlog.debug("Checking for token: {}".format(token))
tokens = db_get().table("tokens")
tmpauth = tokens.get(
(Query().token == token)
& ( # Match token
(Query().userid == uid.replace("fuid_", ""))
| (Query().userid == "fuid_{}".format(uid))
) # Userid with or without fuid_
)
if tmpauth:
return True
return False
def revoke_expired_tokens():
tokens = db_get().table("tokens").all()
for i in tokens:
if datetime.now() >= datetime.fromisoformat(i["expiration"]):
bumperlog.debug("Removing token {} due to expiration".format(i["token"]))
db_get().table("tokens").remove(doc_ids=[i.doc_id])
def bot_add(sn, did, devclass, resource, company):
newbot = VacBotDevice()
newbot.did = did
newbot.name = sn
newbot.vac_bot_device_class = devclass
newbot.resource = resource
newbot.company = 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
): # try to prevent bad additions to the bot list
bumperlog.info(
"Adding new bot with SN: {} DID: {}".format(newbot.name, newbot.did)
)
bot_full_upsert(newbot.asdict())
def bot_remove(did):
bots = db_get().table("bots")
bot = bot_get(did)
bots.remove(doc_ids=[bot.doc_id])
def bot_get(did):
bots = db_get().table("bots")
Bot = Query()
return bots.get(Bot.did == did)
def bot_toEcoVacsHome_JSON(bot): # EcoVacs Home
for botprod in EcoVacsHomeProducts:
if botprod["classid"] == bot["class"]:
bot["UILogicId"] = botprod["product"]["UILogicId"]
bot["ota"] = botprod["product"]["ota"]
bot["icon"] = botprod["product"]["iconUrl"]
return json.dumps(
bot, default=lambda o: o.__dict__, sort_keys=False
) # , indent=4)
def bot_full_upsert(vacbot):
bots = db_get().table("bots")
Bot = Query()
bots.upsert(vacbot, Bot.did == vacbot["did"])
def bot_set_nick(did, nick):
bots = db_get().table("bots")
Bot = Query()
bots.upsert({"nick": nick}, Bot.did == did)
def bot_set_mqtt(did, mqtt):
bots = db_get().table("bots")
Bot = Query()
bots.upsert({"mqtt_connection": mqtt}, Bot.did == did)
def bot_set_xmpp(did, xmpp):
bots = db_get().table("bots")
Bot = Query()
bots.upsert({"xmpp_connection": xmpp}, Bot.did == did)
def client_add(userid, realm, resource):
newclient = VacBotClient()
newclient.userid = userid
newclient.realm = realm
newclient.resource = resource
client = client_get(resource)
if not client:
bumperlog.info("Adding new client with resource {}".format(newclient.resource))
client_full_upsert(newclient.asdict())
def client_get(resource):
clients = db_get().table("clients")
Client = Query()
return clients.get(Client.resource == resource)
def client_full_upsert(client):
clients = db_get().table("clients")
Client = Query()
clients.upsert(client, Client.resource == client["resource"])
def client_set_mqtt(resource, mqtt):
clients = db_get().table("clients")
Client = Query()
clients.upsert({"mqtt_connection": mqtt}, Client.resource == resource)
def client_set_xmpp(resource, xmpp):
clients = db_get().table("clients")
Client = Query()
clients.upsert({"xmpp_connection": xmpp}, Client.resource == resource)

401
bumper/models.py Normal file
View file

@ -0,0 +1,401 @@
#!/usr/bin/env python3
import json
class VacBotDevice(object):
def __init__(
self, did="", vac_bot_device_class="", resource="", name="", nick="", company=""
):
self.vac_bot_device_class = vac_bot_device_class
self.company = company
self.did = did
self.name = name
self.nick = nick
self.resource = resource
self.mqtt_connection = False
self.xmpp_connection = False
def asdict(self):
return {
"class": self.vac_bot_device_class,
"company": self.company,
"did": self.did,
"name": self.name,
"nick": self.nick,
"resource": self.resource,
"mqtt_connection": self.mqtt_connection,
"xmpp_connection": self.xmpp_connection,
}
class BumperUser(object):
def __init__(self, userid=""):
self.userid = userid
self.devices = []
self.bots = []
def asdict(self):
return {"userid": self.userid, "devices": self.devices, "bots": self.bots}
class GlobalVacBotDevice(VacBotDevice): # EcoVacs Home
UILogicId = ""
ota = True
updateInfo = {"changeLog": "", "needUpdate": False}
icon = ""
deviceName = ""
class VacBotClient(object):
def __init__(self, userid="", realm="", token=""):
self.userid = userid
self.realm = realm
self.resource = token
self.mqtt_connection = False
self.xmpp_connection = False
def asdict(self):
return {
"userid": self.userid,
"realm": self.realm,
"resource": self.resource,
"mqtt_connection": self.mqtt_connection,
"xmpp_connection": self.xmpp_connection,
}
class EcoVacs_Login:
accessToken = ""
country = ""
email = ""
uid = ""
username = ""
def toJSON(self):
return json.dumps(
self, default=lambda o: o.__dict__, sort_keys=False
) # , indent=4)
class EcoVacsHome_Login(EcoVacs_Login):
loginName = ""
mobile = ""
ucUid = ""
# EcoVacs Home Product IOT Map - 2019-05-20
# https://portal-ww.ecouser.net/api/pim/product/getProductIotMap
EcoVacsHomeProducts = [
{
"classid": "dl8fht",
"product": {
"UILogicId": "D_600",
"_id": "5acb0fa87c295c0001876ecf",
"icon": "5acc32067c295c0001876eea",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5acc32067c295c0001876eea",
"materialNo": "702-0000-0170",
"name": "DEEBOT 600 Series",
"ota": False,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "02uwxm",
"product": {
"UILogicId": "D_OZMO_SLIM10",
"_id": "5ae1481e7ccd1a0001e1f69e",
"icon": "5b1dddc48bc45700014035a1",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b1dddc48bc45700014035a1",
"materialNo": "110-1715-0201",
"name": "DEEBOT OZMO Slim10 Series",
"ota": False,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "y79a7u",
"product": {
"UILogicId": "D_OZMO_900",
"_id": "5b04c0227ccd1a0001e1f6a8",
"icon": "5b04c0217ccd1a0001e1f6a7",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b04c0217ccd1a0001e1f6a7",
"materialNo": "110-1810-0101",
"name": "DEEBOT OZMO 900 Series",
"ota": True,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "jr3pqa",
"product": {
"UILogicId": "D_700",
"_id": "5b43077b8bc457000140363e",
"icon": "5b5ac4cc8d5a56000111e769",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b5ac4cc8d5a56000111e769",
"materialNo": "702-0000-0202",
"name": "DEEBOT 711",
"ota": True,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "uv242z",
"product": {
"UILogicId": "D_700",
"_id": "5b5149b4ac0b87000148c128",
"icon": "5b5ac4e45f21100001882bb9",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b5ac4e45f21100001882bb9",
"materialNo": "702-0000-0205",
"name": "DEEBOT 710",
"ota": True,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "ls1ok3",
"product": {
"UILogicId": "D_900",
"_id": "5b6561060506b100015c8868",
"icon": "5ba4a2cb6c2f120001c32839",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ba4a2cb6c2f120001c32839",
"materialNo": "110-1711-0201",
"name": "DEEBOT 900 Series",
"ota": True,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "eyi9jv",
"product": {
"UILogicId": "D_700",
"_id": "5b7b65f364e1680001a08b54",
"icon": "5b7b65f176f7f10001e9a0c2",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b7b65f176f7f10001e9a0c2",
"materialNo": "715",
"name": "DEEBOT 715",
"ota": True,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "4zfacv",
"product": {
"UILogicId": "DN_2G",
"_id": "5bf2596f23244a00013f2f13",
"icon": "5c778731280fda0001770ba0",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c778731280fda0001770ba0",
"materialNo": "910",
"name": "DEEBOT 910",
"ota": True,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "vi829v",
"product": {
"UILogicId": "DX_5G",
"_id": "5c19a8f3a1e6ee0001782247",
"icon": "5c9c7995e9e9270001354ab4",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c9c7995e9e9270001354ab4",
"materialNo": "920",
"name": "DEEBOT OZMO 920 Series",
"ota": True,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "gd4uut",
"product": {
"UILogicId": "DR_935G",
"_id": "5bc8189d68142800016a6937",
"icon": "5c7384767b93c700013f12e7",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c7384767b93c700013f12e7",
"materialNo": "960",
"name": "DEEBOT OZMO 960",
"ota": True,
"supportType": {
"alexa": True,
"assistant": True,
"share": False,
"tmjl": False,
},
},
},
{
"classid": "9akc61",
"product": {
"UILogicId": "D_500",
"_id": "5c763f8263023c0001e7f855",
"icon": "5c932067280fda0001770d7f",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c932067280fda0001770d7f",
"materialNo": "D505",
"name": "DEEBOT 505",
"ota": False,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "r8ead0",
"product": {
"UILogicId": "D_500",
"_id": "5c763f63280fda0001770b88",
"icon": "5c93204b63023c0001e7faa7",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c93204b63023c0001e7faa7",
"materialNo": "D502",
"name": "DEEBOT 502",
"ota": False,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "emzppx",
"product": {
"UILogicId": "D_500",
"_id": "5c763f35280fda0001770b84",
"icon": "5c931fef280fda0001770d7e",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c931fef280fda0001770d7e",
"materialNo": "D501",
"name": "DEEBOT 501",
"ota": False,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "vsc5ia",
"product": {
"UILogicId": "D_500",
"_id": "5c763eba280fda0001770b81",
"icon": "5c874326280fda0001770d2a",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5c874326280fda0001770d2a",
"materialNo": "D500",
"name": "DEEBOT 500",
"ota": False,
"supportType": {
"alexa": True,
"assistant": True,
"share": True,
"tmjl": False,
},
},
},
{
"classid": "aqdd5p",
"product": {
"UILogicId": "D_900",
"_id": "5cb7cfba179839000114d762",
"icon": "5cb7cfbab72c4d00010e5fc7",
"iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5cb7cfbab72c4d00010e5fc7",
"materialNo": "110-1711-0001",
"name": "DEEBOT DE55",
"ota": True,
"supportType": {
"alexa": False,
"assistant": False,
"share": False,
"tmjl": False,
},
},
},
]
RETURN_API_SUCCESS = "0000"
ERR_ACTIVATE_TOKEN_TIMEOUT = "1006"
ERR_COMMON = "0001"
ERR_DEFAULT = "9000"
ERR_EMAIL_NON_EXIST = "1002"
ERR_EMAIL_SEND_TIME_LIMIT = "1011"
ERR_EMAIL_USED = "1001"
ERR_INTERFACE_AUTH = "0002"
ERR_PARAM_INVALID = "0003"
ERR_PWD_WRONG = "1005"
ERR_RESET_PWD_TOKEN_TIMEOUT = "1007"
ERR_TIMESTAMP_INVALID = "0005"
ERR_TOKEN_INVALID = "0004"
ERR_USER_DISABLE = "1004"
ERR_USER_NOT_ACTIVATED = "1003"
ERR_WRONG_COMFIRM_PWD = "10010"
ERR_WRONG_EMAIL_ADDRESS = "1008"
ERR_WRONG_PWD_FROMATE = "1009"
API_ERRORS = {
RETURN_API_SUCCESS: "0000",
ERR_ACTIVATE_TOKEN_TIMEOUT: "1006",
ERR_COMMON: "0001",
ERR_DEFAULT: "9000",
ERR_EMAIL_NON_EXIST: "1002",
ERR_EMAIL_SEND_TIME_LIMIT: "1011",
ERR_EMAIL_USED: "1001",
ERR_INTERFACE_AUTH: "0002",
ERR_PARAM_INVALID: "0003",
ERR_PWD_WRONG: "1005",
ERR_RESET_PWD_TOKEN_TIMEOUT: "1007",
ERR_TIMESTAMP_INVALID: "0005",
ERR_TOKEN_INVALID: "0004",
ERR_USER_DISABLE: "1004",
ERR_USER_NOT_ACTIVATED: "1003",
ERR_WRONG_COMFIRM_PWD: "10010",
ERR_WRONG_EMAIL_ADDRESS: "1008",
ERR_WRONG_PWD_FROMATE: "1009",
}

View file

@ -15,6 +15,7 @@ from datetime import datetime, timedelta
import bumper
helperbotlog = logging.getLogger("helperbot")
boterrorlog = logging.getLogger("boterror")
mqttserverlog = logging.getLogger("mqttserver")
logging.getLogger("transitions").setLevel(logging.CRITICAL + 1) # Ignore this logger
@ -30,7 +31,7 @@ logging.getLogger("hbmqtt.client").setLevel(logging.CRITICAL + 1) # Ignore this
class MQTTHelperBot:
Client = MQTTClient()
Client = None
wait_resp_timeout_seconds = 10
expire_msg_seconds = 10
@ -38,11 +39,11 @@ class MQTTHelperBot:
self.address = address
self.client_id = "helper1@bumper/helper1"
self.command_responses = []
self.helperthread = None
async def start_helper_bot(self):
try:
if self.Client is None:
self.Client = MQTTClient(
client_id=self.client_id, config={"check_hostname": False}
)
@ -102,11 +103,19 @@ class MQTTHelperBot:
)
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(
@ -174,6 +183,7 @@ class MQTTHelperBot:
}
async def send_command(self, cmdjson, requestid):
if not self.Client._handler.writer is None:
try:
ttopic = "iot/p2p/{}/helper1/bumper/helper1/{}/{}/{}/q/{}/{}".format(
cmdjson["cmdName"],
@ -224,7 +234,6 @@ class MQTTServer:
def __init__(self, address):
try:
self.mqttserverthread = None
self.address = address
# The below adds a plugin to the hbmqtt.broker.plugins without having to futz with setup.py
@ -345,7 +354,7 @@ class BumperMQTTServer_Plugin:
return authenticated
async def on_broker_client_connected(self, client_id):
try:
didsplit = str(client_id).split("@")
bot = bumper.bot_get(didsplit[0])
@ -360,11 +369,8 @@ class BumperMQTTServer_Plugin:
bumper.client_set_mqtt(client["resource"], True)
return
except Exception as e:
mqttserverlog.exception("{}".format(e))
async def on_broker_client_disconnected(self, client_id):
try:
didsplit = str(client_id).split("@")
bot = bumper.bot_get(didsplit[0])
@ -378,7 +384,3 @@ class BumperMQTTServer_Plugin:
if client:
bumper.client_set_mqtt(client["resource"], False)
return
except Exception as e:
mqttserverlog.exception("{}".format(e))

View file

@ -10,6 +10,7 @@ import bumper
import asyncio
xmppserverlog = logging.getLogger("xmppserver")
boterrorlog = logging.getLogger("boterror")
class XMPPServer:
@ -50,8 +51,8 @@ class XMPPServer:
asyncio.create_task(bumper.shutdown())
def disconnect(self):
try:
xmppserverlog.debug("waiting for all client threads to exit")
xmppserverlog.debug("waiting for all clients to disconnect")
for client in self.clients:
client._disconnect()
@ -59,9 +60,6 @@ class XMPPServer:
xmppserverlog.debug("shutting down")
self.server_coro.cancel()
except Exception as e:
xmppserverlog.error("{}".format(e))
class XMPPServer_Protocol(asyncio.Protocol):
client_id = None
@ -614,17 +612,12 @@ class XMPPAsyncClient:
xmppserverlog.exception("{}".format(e))
def _handle_session(self, xml):
try:
res = '<iq type="result" id="{}" />'.format(xml.get("id"))
self._set_state("READY")
self.send(res)
asyncio.Task(self.schedule_ping(30))
except Exception as e:
xmppserverlog.exception("{}".format(e))
def _handle_presence(self, xml):
try:
if len(xml) and xml[0].tag == "status":
xmppserverlog.debug(
@ -635,9 +628,7 @@ class XMPPAsyncClient:
# Most likely a bot, possibly hello world in text
# Send dummy return
self.send(
'<presence to="{}"> dummy </presence>'.format(self.bumper_jid)
)
self.send('<presence to="{}"> dummy </presence>'.format(self.bumper_jid))
# If it is a BOT, send extras
if self.type == self.BOT:
@ -686,9 +677,6 @@ class XMPPAsyncClient:
'<presence to="{}"> dummy </presence>'.format(self.bumper_jid)
)
except Exception as e:
xmppserverlog.exception("{}".format(e))
def _parse_data(self, data):
if data.decode("utf-8").startswith(
@ -721,6 +709,19 @@ class XMPPAsyncClient:
).replace("ns0:", ""),
)
)
if (
'td="error"' in newdata
or "errs=" in newdata
or 'k="DeviceAlert' in newdata
):
boterrorlog.error(
"Received Error from ({}:{} | {}) - {}".format(
self.address[0],
self.address[1],
self.bumper_jid,
newdata,
)
)
self._handle_iq(item, newdata)
item.clear()
@ -790,7 +791,7 @@ class XMPPAsyncClient:
xmppserverlog.exception("{}".format(e))
def _handle_iq(self, xml, data):
try:
if len(xml):
child = self._tag_strip_uri(xml[0].tag)
else:
@ -818,6 +819,3 @@ class XMPPAsyncClient:
self._handle_result(xml, data)
else:
self._handle_result(xml, data)
except Exception as e:
xmppserverlog.exception("{}".format(e))

View file

@ -7,15 +7,16 @@ import json
import tinydb
import pytest_aiohttp
import pytest_asyncio
import datetime, time
from aiohttp import web
def create_confserver():
return bumper.ConfServer("127.0.0.1:11111", False, mock.MagicMock)
return bumper.ConfServer("127.0.0.1:11111", False)
def create_app(loop):
confserver = bumper.ConfServer("127.0.0.1:11111", False, mock.MagicMock)
confserver = bumper.ConfServer("127.0.0.1:11111", False)
confserver.confserver_app()
return confserver.app
@ -32,18 +33,31 @@ def remove_existing_db():
async def test_confserver_ssl():
conf_server = bumper.ConfServer(("127.0.0.1", 111111), usessl=True, helperbot=None)
conf_server = bumper.ConfServer(("127.0.0.1", 111111), usessl=True)
conf_server.confserver_app()
asyncio.create_task(conf_server.start_server())
async def test_confserver_no_ssl():
conf_server = bumper.ConfServer(("127.0.0.1", 111111), usessl=False, helperbot=None)
conf_server = bumper.ConfServer(("127.0.0.1", 111111), usessl=False)
conf_server.confserver_app()
asyncio.create_task(conf_server.start_server())
async def test_base(aiohttp_client):
def test_get_milli_time():
cserv = create_confserver()
assert (
cserv.get_milli_time(
datetime.datetime(
2018, 1, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc
).timestamp()
)
== 1514768400000
)
# Comment out test_base until api changes are complete
""" async def test_base(aiohttp_client):
remove_existing_db()
bumper.db = "tests/tmp.db" # Set db location for testing
client = await aiohttp_client(create_app)
@ -51,7 +65,7 @@ async def test_base(aiohttp_client):
resp = await client.get("/")
assert resp.status == 200
text = await resp.text()
assert "Bumper!" in text
assert "Bumper!" in text """
async def test_login(aiohttp_client):
@ -696,6 +710,7 @@ async def test_lg_logs(aiohttp_client):
bumper.bot_set_mqtt("did_1234", True)
confserver = create_confserver()
client = await aiohttp_client(create_app)
bumper.mqtt_helperbot = bumper.mqttserver.MQTTHelperBot("127.0.0.1")
# Test return get status
command_getstatus_resp = {
@ -703,7 +718,7 @@ async def test_lg_logs(aiohttp_client):
"resp": "<ctl ret='ok' status='idle'/>",
"ret": "ok",
}
confserver.helperbot.send_command = mock.MagicMock(
bumper.mqtt_helperbot.send_command = mock.MagicMock(
return_value=async_return(command_getstatus_resp)
)
@ -726,14 +741,6 @@ async def test_lg_logs(aiohttp_client):
jsonresp = json.loads(text)
assert jsonresp["ret"] == "ok"
# Not bot online
bumper.bot_set_mqtt("did_1234", False)
resp = await client.post("/api/lg/log.do", json=postbody)
assert resp.status == 200
text = await resp.text()
jsonresp = json.loads(text)
assert jsonresp["ret"] == "fail"
async def test_postLookup(aiohttp_client):
remove_existing_db()
@ -762,6 +769,7 @@ async def test_devmgr(aiohttp_client):
bumper.db = "tests/tmp.db" # Set db location for testing
confserver = create_confserver()
client = await aiohttp_client(create_app)
bumper.mqtt_helperbot = bumper.mqttserver.MQTTHelperBot("127.0.0.1")
# Test PollSCResult
postbody = {"td": "PollSCResult"}
@ -791,7 +799,7 @@ async def test_devmgr(aiohttp_client):
"resp": "<ctl ret='ok' status='idle'/>",
"ret": "ok",
}
confserver.helperbot.send_command = mock.MagicMock(
bumper.mqtt_helperbot.send_command = mock.MagicMock(
return_value=async_return(command_getstatus_resp)
)
resp = await client.post("/api/iot/devmanager.do", json=postbody)
@ -802,7 +810,7 @@ async def test_devmgr(aiohttp_client):
# Test return fail timeout
command_timeout_resp = {"id": "resp_1234", "errno": "timeout", "ret": "fail"}
confserver.helperbot.send_command = mock.MagicMock(
bumper.mqtt_helperbot.send_command = mock.MagicMock(
return_value=async_return(command_timeout_resp)
)
resp = await client.post("/api/iot/devmanager.do", json=postbody)
@ -811,23 +819,13 @@ async def test_devmgr(aiohttp_client):
test_resp = json.loads(text)
assert test_resp["ret"] == "fail"
# Set bot not on mqtt
bumper.bot_set_mqtt("did_1234", False)
confserver.helperbot.send_command = mock.MagicMock(
return_value=async_return(command_getstatus_resp)
)
resp = await client.post("/api/iot/devmanager.do", json=postbody)
assert resp.status == 200
text = await resp.text()
test_resp = json.loads(text)
assert test_resp["ret"] == "fail"
async def test_dim_devmanager(aiohttp_client):
remove_existing_db()
bumper.db = "tests/tmp.db" # Set db location for testing
confserver = create_confserver()
client = await aiohttp_client(create_app)
bumper.mqtt_helperbot = bumper.mqttserver.MQTTHelperBot("127.0.0.1")
# Test PollSCResult
postbody = {"td": "PollSCResult"}
@ -857,7 +855,7 @@ async def test_dim_devmanager(aiohttp_client):
"resp": "<ctl ret='ok' status='idle'/>",
"ret": "ok",
}
confserver.helperbot.send_command = mock.MagicMock(
bumper.mqtt_helperbot.send_command = mock.MagicMock(
return_value=async_return(command_getstatus_resp)
)
resp = await client.post("/api/dim/devmanager.do", json=postbody)
@ -868,7 +866,7 @@ async def test_dim_devmanager(aiohttp_client):
# Test return fail timeout
command_timeout_resp = {"id": "resp_1234", "errno": "timeout", "ret": "fail"}
confserver.helperbot.send_command = mock.MagicMock(
bumper.mqtt_helperbot.send_command = mock.MagicMock(
return_value=async_return(command_timeout_resp)
)
resp = await client.post("/api/dim/devmanager.do", json=postbody)
@ -880,7 +878,7 @@ async def test_dim_devmanager(aiohttp_client):
# Set bot not on mqtt
bumper.bot_set_mqtt("did_1234", False)
confserver.helperbot.send_command = mock.MagicMock(
bumper.mqtt_helperbot.send_command = mock.MagicMock(
return_value=async_return(command_getstatus_resp)
)
resp = await client.post("/api/dim/devmanager.do", json=postbody)

145
tests/test_db.py Normal file
View file

@ -0,0 +1,145 @@
#!/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
def test_db_path():
bumper.db = None
assert bumper.db_file() == os.path.join(bumper.data_dir, "bumper.db")
def test_user_db():
bumper.db = "tests/tmp.db" # Set db location for testing
bumper.user_add("testuser") # Add testuser
assert (
bumper.user_get("testuser")["userid"] == "testuser"
) # Test that testuser was created and returned
bumper.user_add_device("testuser", "dev_1234") # Add device to testuser
assert (
bumper.user_by_deviceid("dev_1234")["userid"] == "testuser"
) # Test that testuser was found by deviceid
bumper.user_remove_device("testuser", "dev_1234") # Remove device from testuser
assert "dev_1234" not in bumper.user_get("testuser")["devices"]
# Test that dev_1234 was not found in testuser devices
bumper.user_add_bot("testuser", "bot_1234") # Add bot did to testuser
assert "bot_1234" in bumper.user_get("testuser")["bots"]
# Test that bot was found in testuser's bot list
bumper.user_remove_bot("testuser", "bot_1234") # Remove bot did from testuser
assert "bot_1234" not in bumper.user_get("testuser")["bots"]
# Test that bot was not found in testuser's bot list
bumper.user_add_token("testuser", "token_1234") # Add token to testuser
assert bumper.check_token("testuser", "token_1234")
# Test that token was found for testuser
assert bumper.user_get_token("testuser", "token_1234")
# Test that token was returned for testuser
bumper.user_add_authcode(
"testuser", "token_1234", "auth_1234"
) # Add authcode to token_1234 for testuser
assert bumper.check_authcode("testuser", "auth_1234")
# Test that authcode was found for testuser
bumper.user_revoke_authcode(
"testuser", "token_1234", "auth_1234"
) # Remove authcode from testuser
assert bumper.check_authcode("testuser", "auth_1234") == False
# Test that authcode was not found for testuser
bumper.user_revoke_token("testuser", "token_1234") # Remove token from testuser
assert (
bumper.check_token("testuser", "token_1234") == False
) # Test that token was not found for testuser
bumper.user_add_token("testuser", "token_1234") # Add token_1234
bumper.user_add_token("testuser", "token_4321") # Add token_4321
assert len(bumper.user_get_tokens("testuser")) == 2 # Test 2 tokens are available
bumper.user_revoke_all_tokens("testuser") # Revoke all tokens
assert len(bumper.user_get_tokens("testuser")) == 0 # Test 0 tokens are available
db = TinyDB("tests/tmp.db")
tokens = db.table("tokens")
tokens.insert(
{
"userid": "testuser",
"token": "token_1234",
"expiration": "{}".format(datetime.now() + timedelta(seconds=-10)),
}
) # Add expired token
db.close()
assert len(bumper.user_get_tokens("testuser")) == 1 # Test 1 tokens are available
bumper.user_revoke_expired_tokens("testuser") # Revoke expired tokens
assert len(bumper.user_get_tokens("testuser")) == 0 # Test 0 tokens are available
db = TinyDB("tests/tmp.db")
tokens = db.table("tokens")
tokens.insert(
{
"userid": "testuser",
"token": "token_1234",
"expiration": "{}".format(datetime.now() + timedelta(seconds=-10)),
}
) # Add expired token
db.close()
assert len(bumper.user_get_tokens("testuser")) == 1 # Test 1 tokens are available
bumper.revoke_expired_tokens() # Revoke expired tokens
assert len(bumper.user_get_tokens("testuser")) == 0 # Test 0 tokens are available
def test_bot_db():
bumper.db = "tests/tmp.db" # Set db location for testing
bumper.bot_add("sn_123", "did_123", "dev_123", "res_123", "co_123")
assert bumper.bot_get("did_123") # Test that bot was added to db
bumper.bot_set_nick("did_123", "nick_123")
assert (
bumper.bot_get("did_123")["nick"] == "nick_123"
) # Test that nick was added to bot
bumper.bot_set_mqtt("did_123", True)
assert bumper.bot_get("did_123")[
"mqtt_connection"
] # Test that mqtt was set True for bot
bumper.bot_set_xmpp("did_123", True)
assert bumper.bot_get("did_123")[
"xmpp_connection"
] # Test that xmpp was set True for bot
bumper.bot_remove("did_123")
assert bumper.bot_get("did_123") == None # Test that bot is no longer in db
def test_client_db():
bumper.db = "tests/tmp.db" # Set db location for testing
bumper.client_add("user_123", "realm_123", "resource_123")
assert bumper.client_get("resource_123") # Test client was added
bumper.client_set_mqtt("resource_123", True)
assert bumper.client_get("resource_123")[
"mqtt_connection"
] # Test that mqtt was set True for client
bumper.client_set_xmpp("resource_123", False)
assert (
bumper.client_get("resource_123")["xmpp_connection"] == False
) # Test that xmpp was set False for client
assert (
len(bumper.get_disconnected_xmpp_clients()) == 1
) # Test len of connected xmpp clients is 1

View file

@ -5,29 +5,18 @@ from tinydb.storages import MemoryStorage
from tinydb import TinyDB, Query
import bumper
import os
import datetime, time
import platform
import json
import asyncio
from testfixtures import LogCapture
def test_get_milli_time():
assert (
bumper.get_milli_time(
datetime.datetime(
2018, 1, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc
).timestamp()
)
== 1514768400000
)
def test_strtobool():
assert bumper.strtobool("t") == True
assert bumper.strtobool("f") == False
assert bumper.strtobool(0) == False
async def test_start_stop():
with LogCapture() as l:
if os.path.exists("tests/tmp.db"):
@ -71,142 +60,3 @@ async def test_start_stop_debug():
)
assert b.shutting_down == True
def test_db_path():
bumper.db = None
assert bumper.db_file() == os.path.join(bumper.data_dir, "bumper.db")
def test_user_db():
bumper.db = "tests/tmp.db" # Set db location for testing
bumper.user_add("testuser") # Add testuser
assert (
bumper.user_get("testuser")["userid"] == "testuser"
) # Test that testuser was created and returned
bumper.user_add_device("testuser", "dev_1234") # Add device to testuser
assert (
bumper.user_by_deviceid("dev_1234")["userid"] == "testuser"
) # Test that testuser was found by deviceid
bumper.user_remove_device("testuser", "dev_1234") # Remove device from testuser
assert "dev_1234" not in bumper.user_get("testuser")["devices"]
# Test that dev_1234 was not found in testuser devices
bumper.user_add_bot("testuser", "bot_1234") # Add bot did to testuser
assert "bot_1234" in bumper.user_get("testuser")["bots"]
# Test that bot was found in testuser's bot list
bumper.user_remove_bot("testuser", "bot_1234") # Remove bot did from testuser
assert "bot_1234" not in bumper.user_get("testuser")["bots"]
# Test that bot was not found in testuser's bot list
bumper.user_add_token("testuser", "token_1234") # Add token to testuser
assert bumper.check_token("testuser", "token_1234")
# Test that token was found for testuser
assert bumper.user_get_token("testuser", "token_1234")
# Test that token was returned for testuser
bumper.user_add_authcode(
"testuser", "token_1234", "auth_1234"
) # Add authcode to token_1234 for testuser
assert bumper.check_authcode("testuser", "auth_1234")
# Test that authcode was found for testuser
bumper.user_revoke_authcode(
"testuser", "token_1234", "auth_1234"
) # Remove authcode from testuser
assert bumper.check_authcode("testuser", "auth_1234") == False
# Test that authcode was not found for testuser
bumper.user_revoke_token("testuser", "token_1234") # Remove token from testuser
assert (
bumper.check_token("testuser", "token_1234") == False
) # Test that token was not found for testuser
bumper.user_add_token("testuser", "token_1234") # Add token_1234
bumper.user_add_token("testuser", "token_4321") # Add token_4321
assert len(bumper.user_get_tokens("testuser")) == 2 # Test 2 tokens are available
bumper.user_revoke_all_tokens("testuser") # Revoke all tokens
assert len(bumper.user_get_tokens("testuser")) == 0 # Test 0 tokens are available
db = TinyDB("tests/tmp.db")
tokens = db.table("tokens")
tokens.insert(
{
"userid": "testuser",
"token": "token_1234",
"expiration": "{}".format(
datetime.datetime.now() + datetime.timedelta(seconds=-10)
),
}
) # Add expired token
db.close()
assert len(bumper.user_get_tokens("testuser")) == 1 # Test 1 tokens are available
bumper.user_revoke_expired_tokens("testuser") # Revoke expired tokens
assert len(bumper.user_get_tokens("testuser")) == 0 # Test 0 tokens are available
db = TinyDB("tests/tmp.db")
tokens = db.table("tokens")
tokens.insert(
{
"userid": "testuser",
"token": "token_1234",
"expiration": "{}".format(
datetime.datetime.now() + datetime.timedelta(seconds=-10)
),
}
) # Add expired token
db.close()
assert len(bumper.user_get_tokens("testuser")) == 1 # Test 1 tokens are available
bumper.revoke_expired_tokens() # Revoke expired tokens
assert len(bumper.user_get_tokens("testuser")) == 0 # Test 0 tokens are available
def test_bot_db():
bumper.db = "tests/tmp.db" # Set db location for testing
bumper.bot_add("sn_123", "did_123", "dev_123", "res_123", "co_123")
assert bumper.bot_get("did_123") # Test that bot was added to db
bumper.bot_set_nick("did_123", "nick_123")
assert (
bumper.bot_get("did_123")["nick"] == "nick_123"
) # Test that nick was added to bot
bumper.bot_set_mqtt("did_123", True)
assert bumper.bot_get("did_123")[
"mqtt_connection"
] # Test that mqtt was set True for bot
bumper.bot_set_xmpp("did_123", True)
assert bumper.bot_get("did_123")[
"xmpp_connection"
] # Test that xmpp was set True for bot
bumper.bot_remove("did_123")
assert bumper.bot_get("did_123") == None # Test that bot is no longer in db
def test_client_db():
bumper.db = "tests/tmp.db" # Set db location for testing
bumper.client_add("user_123", "realm_123", "resource_123")
assert bumper.client_get("resource_123") # Test client was added
bumper.client_set_mqtt("resource_123", True)
assert bumper.client_get("resource_123")[
"mqtt_connection"
] # Test that mqtt was set True for client
bumper.client_set_xmpp("resource_123", False)
assert (
bumper.client_get("resource_123")["xmpp_connection"] == False
) # Test that xmpp was set False for client
assert (
len(bumper.get_disconnected_xmpp_clients()) == 1
) # Test len of connected xmpp clients is 1

View file

@ -14,11 +14,12 @@ import time
async def test_helperbot_message():
with LogCapture("helperbot") as l:
mqtt_address = ("127.0.0.1", 8883)
mqtt_server = bumper.MQTTServer(mqtt_address)
await mqtt_server.broker_coro()
with LogCapture() as l:
# Test broadcast message
mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address)
await mqtt_helperbot.start_helper_bot()
@ -125,20 +126,42 @@ async def test_helperbot_message():
) # Check received message was logged
l.clear()
mqtt_helperbot.Client.disconnect()
# Received error message
mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address)
await mqtt_helperbot.start_helper_bot()
assert (
mqtt_helperbot.Client._connected_state._value == True
) # Check helperbot is connected
msg_payload = "<ctl ts='1560904925396' td='errors' old='' new='110'/>"
msg_topic_name = "iot/atr/errors/bot_serial/ls1ok3/wC3g/x"
await mqtt_helperbot.Client.publish(
msg_topic_name, msg_payload.encode(), hbmqtt.client.QOS_0
)
try:
await asyncio.wait_for(mqtt_helperbot.Client.deliver_message(), timeout=0.1)
except asyncio.TimeoutError:
pass
l.check_present(
(
"boterror",
"ERROR",
"Received Error - Topic: iot/atr/errors/bot_serial/ls1ok3/wC3g/x - Message: <ctl ts='1560904925396' td='errors' old='' new='110'/>",
)
) # Check received message was logged
l.clear()
mqtt_helperbot.Client.disconnect()
await mqtt_server.broker.shutdown()
async def test_helperbot_expire_message():
with LogCapture("helperbot") as l:
mqtt_address = ("127.0.0.1", 8883)
mqtt_server = bumper.MQTTServer(mqtt_address)
await mqtt_server.broker_coro()
# mqtt_address = ("127.0.0.1", 8883)
# mqtt_server = bumper.MQTTServer(mqtt_address)
# broker = hbmqtt.broker.Broker(
# mqtt_server.default_config, plugin_namespace="hbmqtt.test.plugins"
# )
# await broker.start()
with LogCapture("helperbot") as l:
# Test broadcast message
mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address)
@ -195,6 +218,7 @@ async def test_helperbot_expire_message():
)
) # Check received message was logged
mqtt_helperbot.Client.disconnect()
await mqtt_server.broker.shutdown()
@ -202,12 +226,6 @@ async def test_helperbot_sendcommand():
mqtt_address = ("127.0.0.1", 8883)
mqtt_server = bumper.MQTTServer(mqtt_address)
await mqtt_server.broker_coro()
# mqtt_address = ("127.0.0.1", 8883)
# mqtt_server = bumper.MQTTServer(mqtt_address)
# broker = hbmqtt.broker.Broker(
# mqtt_server.default_config, plugin_namespace="hbmqtt.test.plugins"
# )
# await broker.start()
mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address)
await mqtt_helperbot.start_helper_bot()

View file

@ -21,11 +21,12 @@ def mock_transport_extra_info(*args, **kwargs):
async def test_xmpp_server():
with LogCapture("xmppserver") as l:
xmpp_address = ("127.0.0.1", 5223)
xmpp_server = bumper.XMPPServer(xmpp_address)
await xmpp_server.start_async_server()
with LogCapture("xmppserver") as l:
reader, writer = await asyncio.open_connection("127.0.0.1", 5223)
writer.write(b"<stream:stream />") # Start stream
@ -52,10 +53,8 @@ async def test_xmpp_server():
await writer.drain()
await asyncio.sleep(0.1)
xmpp_server.disconnect()
await asyncio.sleep(0.1)
assert len(xmpp_server.clients) == 0 # Client count decreased
print(l)
async def test_client_connect_no_starttls(*args, **kwargs):
@ -209,6 +208,11 @@ async def test_client_connect_starttls_called(*args, **kwargs):
async def test_xmpp_server_client_tls():
xmpp_address = ("127.0.0.1", 5223)
xmpp_server = bumper.XMPPServer(xmpp_address)
await xmpp_server.start_async_server()
with LogCapture("xmppserver") as l:
async def do_stuff_after_start_tls(
@ -227,10 +231,6 @@ async def test_xmpp_server_client_tls():
await writer.drain()
xmpp_address = ("127.0.0.1", 5223)
xmpp_server = bumper.XMPPServer(xmpp_address)
await xmpp_server.start_async_server()
reader, writer = await asyncio.open_connection("127.0.0.1", 5223)
writer.write(
@ -616,3 +616,30 @@ async def test_client_send_iq(*args, **kwargs):
# Reset mock calls
mock_send.reset_mock()
# Bot error report
test_data = "<iq to='fuid_tmpuser@ecouser.net/IOSF53D07BA' type='set' id='631'><query xmlns='com:ctl'><ctl td='error' errs='102'/></query></iq>".encode(
"utf-8"
)
xmppclient2._parse_data(test_data)
assert (
mock_send.mock_calls[0].args[0]
== '<iq from="E0000000000000001234@159.ecorobot.net/atom" id="631" to="fuid_tmpuser@ecouser.net/IOSF53D07BA" type="set"><query xmlns="com:ctl"><ctl errs="102" td="error" /></query></iq>'
) # result sent to ecouser.net
# Reset mock calls
mock_send.reset_mock()
# Bot "DorpError" to all
test_data = "<iq to='rl.ecorobot.net' type='set' id='1234'><query xmlns='com:sf'><sf td='pub' t='log' ts='1559893796000' tp='p' k='DeviceAlert' v='DorpError' f='E0000000000000001234@159.ecorobot.net' g='fuid_tmpuser@ecouser.net'/></query></iq>".encode(
"utf-8"
)
xmppclient2._parse_data(test_data)
assert (
mock_send.mock_calls[0].args[0]
== '<iq xmlns="com:sf" from="E0000000000000001234@159.ecorobot.net/atom" id="1234" to="rl.ecorobot.net" type="set"><query xmlns="com:ctl"><sf f="E0000000000000001234@159.ecorobot.net" g="fuid_tmpuser@ecouser.net" k="DeviceAlert" t="log" td="pub" tp="p" ts="1559893796000" v="DorpError" /></query></iq>'
) # result sent to ecouser.net
# Reset mock calls
mock_send.reset_mock()

View file

@ -100,4 +100,3 @@ def test_main(mock_firstrun):
bumper.main()
assert mock_firstrun.called == True
bumper.ca_cert = "tests/test_certs/ca.crt"