D901 #2
4 changed files with 172 additions and 104 deletions
14
bumper.py
14
bumper.py
|
|
@ -4,14 +4,20 @@ import logging
|
||||||
import bumper
|
import bumper
|
||||||
import sys, socket
|
import sys, socket
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO,
|
logging.basicConfig(level=logging.DEBUG,
|
||||||
format='%(asctime)s %(levelname)-8s %(message)s')
|
format='%(asctime)s %(levelname)-8s %(message)s')
|
||||||
|
|
||||||
conf_address = (socket.gethostbyname(socket.gethostname()), 8007)
|
#conf_address = (socket.gethostbyname(socket.gethostname()), 443)
|
||||||
xmpp_address = (socket.gethostbyname(socket.gethostname()), 5223)
|
conf_address = ("0.0.0.0", 443)
|
||||||
|
#xmpp_address = (socket.gethostbyname(socket.gethostname()), 5223)
|
||||||
|
xmpp_address = ("0.0.0.0", 5223)
|
||||||
|
#mqtt_address = (socket.gethostbyname(socket.gethostname()), 8883)
|
||||||
|
mqtt_address = ("0.0.0.0", 8883)
|
||||||
|
|
||||||
# start conf server (async)
|
# start conf server (async)
|
||||||
conf_server = bumper.ConfServer(conf_address, ssl=False, async=True)
|
conf_server = bumper.ConfServer(conf_address, usessl=True, run_async=True)
|
||||||
|
# start mqtt server (async)
|
||||||
|
mqtt_server = bumper.MQTTServer(mqtt_address, run_async=True)
|
||||||
|
|
||||||
# start xmpp server (sync)
|
# start xmpp server (sync)
|
||||||
xmpp_server = bumper.XMPPServer(xmpp_address)
|
xmpp_server = bumper.XMPPServer(xmpp_address)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from .confserver import ConfServer
|
from .confserver import ConfServer
|
||||||
|
from .mqttserver import MQTTServer
|
||||||
from .xmppserver import XMPPServer
|
from .xmppserver import XMPPServer
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,14 @@
|
||||||
|
|
||||||
from http.server import HTTPServer
|
from http.server import HTTPServer
|
||||||
from http.server import BaseHTTPRequestHandler
|
from http.server import BaseHTTPRequestHandler
|
||||||
|
#from http.server import SimpleHTTPRequestHandler
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
import socket, logging, ssl, json, sys
|
import socket, logging, ssl, json, sys
|
||||||
|
|
||||||
|
|
||||||
class RequestHandler(BaseHTTPRequestHandler):
|
class RequestHandler(BaseHTTPRequestHandler):
|
||||||
|
#class RequestHandler(SimpleHTTPRequestHandler):
|
||||||
def do_POST(self):
|
def do_POST(self):
|
||||||
try:
|
try:
|
||||||
self.protocol_version = 'HTTP/1.1'
|
self.protocol_version = 'HTTP/1.1'
|
||||||
|
|
@ -17,17 +19,44 @@ class RequestHandler(BaseHTTPRequestHandler):
|
||||||
request_body = post_data.decode('utf-8')
|
request_body = post_data.decode('utf-8')
|
||||||
logging.debug("Request: " + request_body)
|
logging.debug("Request: " + request_body)
|
||||||
json_body = json.loads(request_body)
|
json_body = json.loads(request_body)
|
||||||
|
if "getProductIotMap" in str(self.path):
|
||||||
|
body = {"code":0,"data":[{"classid":"dl8fht","product":{"_id":"5acb0fa87c295c0001876ecf","name":"DEEBOT 600 Series","icon":"5acc32067c295c0001876eea","UILogicId":"dl8fht","ota":False,"iconUrl":"https://portal-ww.ecouser.net/api/pim/file/get/5acc32067c295c0001876eea"}},{"classid":"02uwxm","product":{"_id":"5ae1481e7ccd1a0001e1f69e","name":"DEEBOT OZMO Slim10 Series","icon":"5b1dddc48bc45700014035a1","UILogicId":"02uwxm","ota":False,"iconUrl":"https://portal-ww.ecouser.net/api/pim/file/get/5b1dddc48bc45700014035a1"}},{"classid":"y79a7u","product":{"_id":"5b04c0227ccd1a0001e1f6a8","name":"DEEBOT OZMO 900","icon":"5b04c0217ccd1a0001e1f6a7","UILogicId":"y79a7u","ota":True,"iconUrl":"https://portal-ww.ecouser.net/api/pim/file/get/5b04c0217ccd1a0001e1f6a7"}},{"classid":"jr3pqa","product":{"_id":"5b43077b8bc457000140363e","name":"DEEBOT 711","icon":"5b5ac4cc8d5a56000111e769","UILogicId":"jr3pqa","ota":True,"iconUrl":"https://portal-ww.ecouser.net/api/pim/file/get/5b5ac4cc8d5a56000111e769"}},{"classid":"uv242z","product":{"_id":"5b5149b4ac0b87000148c128","name":"DEEBOT 710","icon":"5b5ac4e45f21100001882bb9","UILogicId":"uv242z","ota":True,"iconUrl":"https://portal-ww.ecouser.net/api/pim/file/get/5b5ac4e45f21100001882bb9"}},{"classid":"ls1ok3","product":{"_id":"5b6561060506b100015c8868","name":"DEEBOT 900 Series","icon":"5ba4a2cb6c2f120001c32839","UILogicId":"ls1ok3","ota":True,"iconUrl":"https://portal-ww.ecouser.net/api/pim/file/get/5ba4a2cb6c2f120001c32839"}}]}
|
||||||
|
elif "notify_engine.do" in str(self.path):
|
||||||
|
body = {"ret":"ok"}
|
||||||
|
else:
|
||||||
todo = json_body['todo']
|
todo = json_body['todo']
|
||||||
if todo == 'FindBest':
|
if todo == 'FindBest':
|
||||||
service = json_body['service']
|
service = json_body['service']
|
||||||
if service == 'EcoMsgNew':
|
if service == 'EcoMsgNew':
|
||||||
body = '{{"result":"ok","ip":"{}","port":5223}}'.format(socket.gethostbyname(socket.gethostname()))
|
body = {"result":"ok","ip":socket.gethostbyname(socket.gethostname()),"port":5223}
|
||||||
elif service == 'EcoUpdate':
|
elif service == 'EcoUpdate':
|
||||||
body = '{"result":"ok","ip":"47.88.66.164","port":8005}'
|
body = {"result":"ok","ip":"47.88.66.164","port":8005}
|
||||||
elif todo == 'loginByItToken':
|
elif todo == 'loginByItToken':
|
||||||
body = "{{'todo': 'result', 'result': 'ok', 'userId': '{}', 'resource': '{}', 'token': '{}'}}".format(json_body['userId'], json_body['resource'], json_body['token'])
|
body = {
|
||||||
|
"todo": "result",
|
||||||
|
"result": "ok",
|
||||||
|
"userId": json_body["userId"],
|
||||||
|
"resource": json_body["resource"],
|
||||||
|
"token": json_body["token"]
|
||||||
|
}
|
||||||
elif todo == 'GetDeviceList':
|
elif todo == 'GetDeviceList':
|
||||||
body = "{'todo': 'result', 'result': 'ok', 'devices': [{'did': '{}', 'name': '{}', 'class': '{}', 'resource': 'atom', 'nick': None, 'company': 'eco'}]}"
|
#Find a way to handle this automatically
|
||||||
|
#Maybe keep a list of devices from those that have checked into MQTT/XMPP/etc and return them
|
||||||
|
body = {
|
||||||
|
"devices": [
|
||||||
|
{
|
||||||
|
"class": "tmpClass",
|
||||||
|
"company": "eco-ng",
|
||||||
|
"did": "tmpDeviceID",
|
||||||
|
"name": "tmpName",
|
||||||
|
"nick": "tmpNick",
|
||||||
|
"resource": "tmpResource"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"result": "ok",
|
||||||
|
"todo": "result"
|
||||||
|
}
|
||||||
|
body = json.dumps(body)
|
||||||
logging.debug("Response: " + body)
|
logging.debug("Response: " + body)
|
||||||
body = body.encode()
|
body = body.encode()
|
||||||
self.send_response(HTTPStatus.OK)
|
self.send_response(HTTPStatus.OK)
|
||||||
|
|
@ -37,7 +66,73 @@ class RequestHandler(BaseHTTPRequestHandler):
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
self.wfile.write(body)
|
self.wfile.write(body)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error('ConfServer: {}'.format(e))
|
logging.error('ConfServer POST: {}'.format(e))
|
||||||
|
|
||||||
|
def do_GET(self):
|
||||||
|
try:
|
||||||
|
self.protocol_version = 'HTTP/1.1'
|
||||||
|
logging.debug("Path: " + self.path)
|
||||||
|
|
||||||
|
if "/login?" in str(self.path):
|
||||||
|
|
||||||
|
#Could implement basic auth if you wanted, or just accept anything
|
||||||
|
#Next up parse the path for variables
|
||||||
|
body = {
|
||||||
|
"code": "0000",
|
||||||
|
"data": {
|
||||||
|
"accessToken": "temp_accesstoken",
|
||||||
|
"country": "us",
|
||||||
|
"email": "null@null.com",
|
||||||
|
"uid": "fuid_1",
|
||||||
|
"username": "fusername_1"
|
||||||
|
},
|
||||||
|
"msg": "操作成功",
|
||||||
|
"time": 1547211900690
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
elif "/getAuthCode?" in str(self.path):
|
||||||
|
body = {
|
||||||
|
"code": "0000",
|
||||||
|
"data": {
|
||||||
|
"authCode": "temp_authcode",
|
||||||
|
"ecovacsUid": "fuid_1"
|
||||||
|
},
|
||||||
|
"msg": "操作成功",
|
||||||
|
"time": 1547211900808
|
||||||
|
}
|
||||||
|
|
||||||
|
elif "/checkVersion?" in str(self.path):
|
||||||
|
body = {
|
||||||
|
"code": "0000",
|
||||||
|
"data": {
|
||||||
|
"c": None,
|
||||||
|
"img": None,
|
||||||
|
"r": 0,
|
||||||
|
"t": None,
|
||||||
|
"u": None,
|
||||||
|
"ut": 0,
|
||||||
|
"v": None
|
||||||
|
},
|
||||||
|
"msg": "操作成功",
|
||||||
|
"time": 1548829850462
|
||||||
|
}
|
||||||
|
elif "/logout?" in str(self.path):
|
||||||
|
body = {"code": "0000","data": None,"msg": "操作成功","time": 1548830041460}
|
||||||
|
else:
|
||||||
|
body = {}
|
||||||
|
|
||||||
|
body = json.dumps(body)
|
||||||
|
logging.debug("Response: " + body)
|
||||||
|
body = body.encode()
|
||||||
|
self.send_response(HTTPStatus.OK)
|
||||||
|
self.send_header('Content-Type', 'application/json; charset=utf-8')
|
||||||
|
self.send_header('Connection', 'keep-alive')
|
||||||
|
self.send_header('Content-Length', len(body))
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(body)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error('ConfServer GET: {}'.format(e))
|
||||||
|
|
||||||
|
|
||||||
class HTTPServerThread(HTTPServer, Thread):
|
class HTTPServerThread(HTTPServer, Thread):
|
||||||
|
|
@ -46,11 +141,12 @@ class HTTPServerThread(HTTPServer, Thread):
|
||||||
self.server_address = server_address
|
self.server_address = server_address
|
||||||
self.handler = RequestHandler
|
self.handler = RequestHandler
|
||||||
self.exit_flag = False
|
self.exit_flag = False
|
||||||
|
HTTPServer.__init__(self, self.server_address, self.handler)
|
||||||
|
|
||||||
def handle_error(self, request, client_address):
|
def handle_error(self, request, client_address):
|
||||||
self.close_request(request)
|
self.close_request(request)
|
||||||
def run(self):
|
def run(self):
|
||||||
try:
|
try:
|
||||||
HTTPServer.__init__(self, self.server_address, self.handler)
|
|
||||||
logging.info('ConfServer: listening on {}:{}'.format(self.server_address[0], self.server_address[1]))
|
logging.info('ConfServer: listening on {}:{}'.format(self.server_address[0], self.server_address[1]))
|
||||||
while not self.exit_flag:
|
while not self.exit_flag:
|
||||||
self.handle_request()
|
self.handle_request()
|
||||||
|
|
@ -65,13 +161,13 @@ class HTTPServerThread(HTTPServer, Thread):
|
||||||
|
|
||||||
|
|
||||||
class ConfServer():
|
class ConfServer():
|
||||||
def __init__(self, address, ssl=False, async=True):
|
def __init__(self, address, usessl=False, run_async=True):
|
||||||
try:
|
self.run_async = run_async
|
||||||
self.async = async
|
|
||||||
self.server = HTTPServerThread(address)
|
self.server = HTTPServerThread(address)
|
||||||
if ssl:
|
try:
|
||||||
|
if usessl:
|
||||||
self.server.socket = ssl.wrap_socket(self.server.socket, keyfile='./certs/key.pem', certfile='./certs/cert.pem', server_side=True)
|
self.server.socket = ssl.wrap_socket(self.server.socket, keyfile='./certs/key.pem', certfile='./certs/cert.pem', server_side=True)
|
||||||
if self.async:
|
if self.run_async:
|
||||||
self.server.start()
|
self.server.start()
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
|
|
@ -83,6 +179,6 @@ class ConfServer():
|
||||||
def disconnect(self):
|
def disconnect(self):
|
||||||
logging.info('ConfServer: shutting down...')
|
logging.info('ConfServer: shutting down...')
|
||||||
self.server.disconnect()
|
self.server.disconnect()
|
||||||
if(self.async):
|
if(self.run_async):
|
||||||
self.server.join()
|
self.server.join()
|
||||||
logging.info('ConfServer: bye')
|
logging.info('ConfServer: bye')
|
||||||
|
|
|
||||||
|
|
@ -6,18 +6,28 @@ import os
|
||||||
from hbmqtt.broker import Broker
|
from hbmqtt.broker import Broker
|
||||||
import hbmqtt
|
import hbmqtt
|
||||||
|
|
||||||
default_config = {
|
class MQTTServer():
|
||||||
|
clients = []
|
||||||
|
exit_flag = False
|
||||||
|
default_config = {}
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def broker_coro(self):
|
||||||
|
broker = hbmqtt.broker.Broker(config=self.default_config, plugin_namespace=".")
|
||||||
|
|
||||||
|
yield from broker.start()
|
||||||
|
|
||||||
|
def __init__(self, address, run_async=False):
|
||||||
|
try:
|
||||||
|
# Initialize bot server
|
||||||
|
self.default_config = {
|
||||||
'listeners': {
|
'listeners': {
|
||||||
'default': {
|
'default': {
|
||||||
'type': 'tcp',
|
'type': 'tcp',
|
||||||
'bind': '0.0.0.0:1883',
|
|
||||||
},
|
},
|
||||||
'tls1': {
|
'tls1': {
|
||||||
'bind': '0.0.0.0:8883',
|
'bind': "{}:{}".format(address[0], address[1]),
|
||||||
'ssl': 'on',
|
'ssl': 'on',
|
||||||
#'cafile': '/some/cafile',
|
|
||||||
#'capath': '/some/folder',
|
|
||||||
#'capath': 'certificate data',
|
|
||||||
'certfile': './certs/cert.pem',
|
'certfile': './certs/cert.pem',
|
||||||
'keyfile': './certs/key.pem',
|
'keyfile': './certs/key.pem',
|
||||||
},
|
},
|
||||||
|
|
@ -34,59 +44,14 @@ default_config = {
|
||||||
'enabled': False
|
'enabled': False
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if run_async:
|
||||||
|
self.run()
|
||||||
|
finally:
|
||||||
|
print("Done")
|
||||||
|
|
||||||
class BaseAuthPlugin:
|
def run(self):
|
||||||
def __init__(self, context):
|
|
||||||
self.context = context
|
|
||||||
try:
|
|
||||||
self.auth_config = self.context.config['auth']
|
|
||||||
except KeyError:
|
|
||||||
self.context.logger.warning("'auth' section not found in context configuration")
|
|
||||||
|
|
||||||
def authenticate(self, *args, **kwargs):
|
|
||||||
if not self.auth_config:
|
|
||||||
# auth config section not found
|
|
||||||
self.context.logger.warning("'auth' section not found in context configuration")
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
class VacBotAuth(BaseAuthPlugin):
|
|
||||||
def __init__(self, context):
|
|
||||||
super().__init__(context)
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
def authenticate(self, *args, **kwargs):
|
|
||||||
authenticated = super().authenticate(*args, **kwargs)
|
|
||||||
if authenticated:
|
|
||||||
allow_anonymous = self.auth_config.get('allow-anonymous', True) # allow anonymous by default
|
|
||||||
if allow_anonymous:
|
|
||||||
authenticated = True
|
|
||||||
self.context.logger.debug("Authentication success: config allows anonymous")
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
session = kwargs.get('session', None)
|
|
||||||
authenticated = True if session.username else False
|
|
||||||
if self.context.logger.isEnabledFor(logging.DEBUG):
|
|
||||||
if authenticated:
|
|
||||||
self.context.logger.debug("Authentication success: session has a non empty username")
|
|
||||||
else:
|
|
||||||
self.context.logger.debug("Authentication failure: session has an empty username")
|
|
||||||
except KeyError:
|
|
||||||
self.context.logger.warning("Session informations not available")
|
|
||||||
authenticated = False
|
|
||||||
return authenticated
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
def broker_coro():
|
|
||||||
broker = hbmqtt.broker.Broker(config=default_config, plugin_namespace=".")
|
|
||||||
|
|
||||||
print(broker.plugins_manager)
|
|
||||||
yield from broker.start()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
formatter = "[%(asctime)s] :: %(levelname)s :: %(name)s :: %(message)s"
|
formatter = "[%(asctime)s] :: %(levelname)s :: %(name)s :: %(message)s"
|
||||||
logging.basicConfig(level=logging.DEBUG, format=formatter)
|
logging.basicConfig(level=logging.DEBUG, format=formatter)
|
||||||
eloop = asyncio.get_event_loop().run_until_complete(broker_coro())
|
eloop = asyncio.get_event_loop().run_until_complete(self.broker_coro())
|
||||||
|
|
||||||
asyncio.get_event_loop().run_forever()
|
asyncio.get_event_loop().run_forever()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue