print("WS RECV TEXT : %s" % msg) webSocket.SendText("Reply for %s" % msg) def _recvBinaryCallback(webSocket, data): print("WS RECV DATA : %s" % data) def _closedCallback(webSocket): print("WS CLOSED") # ---------------------------------------------------------------------------- #routeHandlers = [ # ( "/test", "GET", _httpHandlerTestGet ), # ( "/test", "POST", _httpHandlerTestPost ) #] srv = MicroWebSrv(webPath='www/') srv.MaxWebSocketRecvLen = 256 srv.WebSocketThreaded = False srv.AcceptWebSocketCallback = _acceptWebSocketCallback print("*** Starting web server ***") srv.Start(threaded=True) raw_input("Press enter to stop the web server") srv.Stop() # ----------------------------------------------------------------------------
class HttpServer: def __init__(self, gy521_obj, wifi_obj, user_settings_dict): self.gy521 = gy521_obj self.wifi = wifi_obj self.settings = user_settings_dict self.app = None def start(self): gy521 = self.gy521 wifi = self.wifi settings = self.settings @MicroWebSrv.route('/connecttest') def test_get(httpClient, httpResponse): """ 测试前端和后端之间的网络连接 """ httpResponse.WriteResponseOk() # Define the web routes and functions @MicroWebSrv.route('/tilt') def tilt_get(httpClient, httpResponse): """ 读取比重计倾角,前端每10秒请求1次 """ try: _, tilt, _ = gy521.read_angles() except: httpResponse.WriteResponseInternalServerError() else: httpResponse.WriteResponseJSONOk(obj={'tilt': tilt}, headers=None) @MicroWebSrv.route('/calibration', 'POST') def calibration_post(httpClient, httpResponse): """ 前台进行倾角与比重的拟合计算后,将比重单位和拟合系数发回后台保存 """ result = httpClient.ReadRequestContentAsJSON() try: with open('regression.json', 'w') as f: ujson.dump(result, f) except: # throw 500 error code httpResponse.WriteResponseInternalServerError() else: httpResponse.WriteResponseOk() @MicroWebSrv.route('/calibration') def calibration_get(httpClient, httpResponse): """ 将上一次保存的校准参数从json文件中读取并发送到前台 """ try: with open('regression.json', 'r') as f: params = ujson.load(f) except: httpResponse.WriteResponseInternalServerError() else: httpResponse.WriteResponseJSONOk(obj={'params': params}, headers=None) @MicroWebSrv.route('/settings') def settings_get(httpClient, httpResponse): """ 从后台读取设置参数 """ wifi_list = wifi.scan_wifi_list() settings_added = {'wifiList': wifi_list} settings_combined = settings.copy() settings_combined.update(settings_added) httpResponse.WriteResponseJSONOk(obj=settings_combined, headers=None) @MicroWebSrv.route('/settings', 'POST') def settings_post(httpClient, httpResponse): """ 向后台保存设置参数 """ settings_dict = httpClient.ReadRequestContentAsJSON() try: with open('user_settings.json', 'w') as f: ujson.dump(settings_dict, f) except: httpResponse.WriteResponseInternalServerError() else: httpResponse.WriteResponseOk() @MicroWebSrv.route('/reboot') def reboot_get(httpClient, httpResponse): """ 重启ESP32 """ tim = machine.Timer(-1) try: tim.init(period=3000, mode=machine.Timer.ONE_SHOT, callback=lambda t: machine.reset()) except: httpResponse.WriteResponseInternalServerError() else: httpResponse.WriteResponseOk() @MicroWebSrv.route('/deepsleep') def deepsleep_get(httpClient, httpResponse): """ 使ESP32进入深度睡眠,唤醒后便进入工作模式 """ with open('hardware_config.json', 'r') as f: json = f.read() config = ujson.loads(json) FIRSTSLEEP_TRIGGER = config['firstsleep_trigger'] def first_sleep(): with open(FIRSTSLEEP_TRIGGER, 'w') as s: pass machine.reset() tim = machine.Timer(-1) try: tim.init(period=3000, mode=machine.Timer.ONE_SHOT, callback=lambda t: first_sleep()) except: httpResponse.WriteResponseInternalServerError() else: httpResponse.WriteResponseOk() @MicroWebSrv.route('/wifi') def wifi_get(httpClient, httpResponse): """ 获取WIFI热点列表,用于刷新热点列表 """ wifi_list = wifi.scan_wifi_list() wifi_dict = {'wifiList': wifi_list} httpResponse.WriteResponseJSONOk(obj=wifi_dict, headers=None) @MicroWebSrv.route('/wifi', 'POST') def wifi_post(httpClient, httpResponse): """ 连接WIFI热点 """ wifi_dict = httpClient.ReadRequestContentAsJSON() new_ip = wifi.sta_connect(wifi_dict['ssid'], wifi_dict['pass']) if new_ip: # 200 httpResponse.WriteResponseOk() else: # throw 500 error code httpResponse.WriteResponseInternalServerError() @MicroWebSrv.route('/ftp') def ftp_get(httpClient, httpResponse): """ Start FTP service """ with open('hardware_config.json', 'r') as f: json = f.read() config = ujson.loads(json) FTP_TRIGGER = config['ftp_trigger'] def start_ftp(): with open(FTP_TRIGGER, 'w') as s: pass machine.reset() tim = machine.Timer(-1) try: tim.init(period=3000, mode=machine.Timer.ONE_SHOT, callback=lambda t: start_ftp()) except: httpResponse.WriteResponseInternalServerError() else: httpResponse.WriteResponseOk() @MicroWebSrv.route('/mqtttest', 'POST') def mqtt_post(httpClient, httpResponse): """ Send test message to MQTT server """ settings_dict = httpClient.ReadRequestContentAsJSON() test_msg = {'test-message': 200} str_data = ujson.dumps(test_msg) from mqtt_client import MQTT try: client = MQTT(settings_dict) client.publish(str_data) except: print('Failed to send the message to the MQTT broker.') httpResponse.WriteResponseInternalServerError() else: print('The test message has been sent successfully.') httpResponse.WriteResponseOk() # Initialize the Web server self.app = MicroWebSrv(webPath='/www') self.app.Start(threaded=True) # Starts the server def stop(self): if self.app: self.app.Stop() def is_started(self): if self.app: return self.app.IsStarted() else: return False
class WebThingServer: """Server to represent a Web Thing over HTTP.""" def __init__(self, things, port=80, hostname=None, ssl_options=None, additional_routes=None): """ Initialize the WebThingServer. For documentation on the additional route format, see: https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo/wiki/microWebSrv things -- list of Things managed by this server port -- port to listen on (defaults to 80) hostname -- Optional host name, i.e. mything.com ssl_options -- dict of SSL options to pass to the tornado server additional_routes -- list of additional routes to add to the server """ self.ssl_suffix = '' if ssl_options is None else 's' self.things = things self.name = things.get_name() self.port = port self.hostname = hostname station = network.WLAN() mac = station.config('mac') self.system_hostname = 'esp32-upy-{:02x}{:02x}{:02x}'.format( mac[3], mac[4], mac[5]) self.hosts = [ 'localhost', 'localhost:{}'.format(self.port), '{}.local'.format(self.system_hostname), '{}.local:{}'.format(self.system_hostname, self.port), ] for address in get_addresses(): self.hosts.extend([ address, '{}:{}'.format(address, self.port), ]) if self.hostname is not None: self.hostname = self.hostname.lower() self.hosts.extend([ self.hostname, '{}:{}'.format(self.hostname, self.port), ]) if isinstance(self.things, MultipleThings): log.info('Registering multiple things') for idx, thing in enumerate(self.things.get_things()): thing.set_href_prefix('/{}'.format(idx)) handlers = [ ( '/', 'GET', self.thingsGetHandler ), ( '/<thing_id>', 'GET', self.thingGetHandler ), ( '/<thing_id>/properties', 'GET', self.propertiesGetHandler ), ( '/<thing_id>/properties/<property_name>', 'GET', self.propertyGetHandler ), ( '/<thing_id>/properties/<property_name>', 'PUT', self.propertyPutHandler ), ] else: log.info('Registering a single thing') handlers = [ ( '/', 'GET', self.thingGetHandler ), ( '/properties', 'GET', self.propertiesGetHandler ), ( '/properties/<property_name>', 'GET', self.propertyGetHandler ), ( '/properties/<property_name>', 'PUT', self.propertyPutHandler ), ] if isinstance(additional_routes, list): handlers = additional_routes + handlers self.server = MicroWebSrv(webPath='/flash/www', routeHandlers=handlers, port=port) self.server.MaxWebSocketRecvLen = 256 self.WebSocketThreaded = ws_run_in_thread self.server.WebSocketStackSize = 8 * 1024 self.server.AcceptWebSocketCallback = self._acceptWebSocketCallback def start(self): """Start listening for incoming connections.""" # If WebSocketS used and NOT running in thread, and WebServer IS # running in thread make shure WebServer has enough stack size to # handle also the WebSocket requests. log.info('Starting Web Server on port {}'.format(self.port)) self.server.Start(threaded=srv_run_in_thread, stackSize=12*1024) mdns = network.mDNS() mdns.start(self.system_hostname, 'MicroPython with mDNS') mdns.addService('_webthing', '_tcp', 80, self.system_hostname, { 'board': 'ESP32', 'path': '/', }) def stop(self): """Stop listening.""" self.server.Stop() def getThing(self, routeArgs): """Get the thing ID based on the route.""" thing_id = routeArgs['thing_id'] if 'thing_id' in routeArgs else None return self.things.get_thing(thing_id) def getProperty(self, routeArgs): """Get the property name based on the route.""" thing = self.getThing(routeArgs) if thing: property_name = routeArgs['property_name'] if thing.has_property(property_name): return thing, thing.find_property(property_name) return None, None def validateHost(self, headers): """Validate the Host header in the request.""" host = headers.get('host', None) if host is not None and host.lower() in self.hosts: return True return False @print_exc def thingsGetHandler(self, httpClient, httpResponse): """Handle a request to / when the server manages multiple things.""" if not self.validateHost(httpClient.GetRequestHeaders()): httpResponse.WriteResponseError(403) return ws_href = 'ws{}://{}'.format( self.ssl_suffix, httpClient.GetRequestHeaders().get('host', '') ) descriptions = [] for thing in self.things.get_things(): description = thing.as_thing_description() description['links'].append({ 'rel': 'alternate', 'href': '{}{}'.format(ws_href, thing.get_href()), }) descriptions.append(description) httpResponse.WriteResponseJSONOk( obj=descriptions, headers=_CORS_HEADERS, ) @print_exc def thingGetHandler(self, httpClient, httpResponse, routeArgs=None): """Handle a GET request for an individual thing.""" if not self.validateHost(httpClient.GetRequestHeaders()): httpResponse.WriteResponseError(403) return thing = self.getThing(routeArgs) if thing is None: httpResponse.WriteResponseNotFound() return ws_href = 'ws{}://{}'.format( self.ssl_suffix, httpClient.GetRequestHeaders().get('host', '') ) description = thing.as_thing_description() description['links'].append({ 'rel': 'alternate', 'href': '{}{}'.format(ws_href, thing.get_href()), }) httpResponse.WriteResponseJSONOk( obj=description, headers=_CORS_HEADERS, ) @print_exc def propertiesGetHandler(self, httpClient, httpResponse, routeArgs=None): """Handle a GET request for a property.""" thing = self.getThing(routeArgs) if thing is None: httpResponse.WriteResponseNotFound() return httpResponse.WriteResponseJSONOk(thing.get_properties()) @print_exc def propertyGetHandler(self, httpClient, httpResponse, routeArgs=None): """Handle a GET request for a property.""" if not self.validateHost(httpClient.GetRequestHeaders()): httpResponse.WriteResponseError(403) return thing, prop = self.getProperty(routeArgs) if thing is None or prop is None: httpResponse.WriteResponseNotFound() return httpResponse.WriteResponseJSONOk( obj={prop.get_name(): prop.get_value()}, headers=_CORS_HEADERS, ) @print_exc def propertyPutHandler(self, httpClient, httpResponse, routeArgs=None): """Handle a PUT request for a property.""" if not self.validateHost(httpClient.GetRequestHeaders()): httpResponse.WriteResponseError(403) return thing, prop = self.getProperty(routeArgs) if thing is None or prop is None: httpResponse.WriteResponseNotFound() return args = httpClient.ReadRequestContentAsJSON() if args is None: httpResponse.WriteResponseBadRequest() return try: prop.set_value(args[prop.get_name()]) except PropertyError: httpResponse.WriteResponseBadRequest() return httpResponse.WriteResponseJSONOk( obj={prop.get_name(): prop.get_value()}, headers=_CORS_HEADERS, ) # === MicroWebSocket callbacks === @print_exc def _acceptWebSocketCallback(self, webSocket, httpClient): reqPath = httpClient.GetRequestPath() if WS_messages: print('WS ACCEPT reqPath =', reqPath) if ws_run_in_thread or srv_run_in_thread: # Print thread list so that we can monitor maximum stack size # of WebServer thread and WebSocket thread if any is used _thread.list() webSocket.RecvTextCallback = self._recvTextCallback webSocket.RecvBinaryCallback = self._recvBinaryCallback webSocket.ClosedCallback = self._closedCallback things = self.things.get_things() if len(things) == 1: thing_id = 0 else: thing_id = int(reqPath.split('/')[1]) thing = things[thing_id] webSocket.thing = thing thing.add_subscriber(webSocket) @print_exc def _recvTextCallback(self, webSocket, msg): if WS_messages: print('WS RECV TEXT : %s' % msg) @print_exc def _recvBinaryCallback(self, webSocket, data): if WS_messages: print('WS RECV DATA : %s' % data) @print_exc def _closedCallback(self, webSocket): if WS_messages: if ws_run_in_thread or srv_run_in_thread: _thread.list() print('WS CLOSED')
class HttpServer: def __init__(self, process_obj, wifi_obj, rtc_obj, user_settings_dict): self.process = process_obj self.wifi = wifi_obj self.rtc = rtc_obj self.settings = user_settings_dict self.app = None self.gravity_sg = None self.set_temp = None self.chamber_temp = None self.wort_temp = None self.time_mark = None def start(self): process = self.process wifi = self.wifi rtc = self.rtc settings = self.settings this = self @MicroWebSrv.route('/connecttest') def test_get(httpClient, httpResponse): """ 测试前端和后端之间的网络连接 """ httpResponse.WriteResponseOk() # Define the web routes and functions @MicroWebSrv.route('/overview') def overview_get(httpClient, httpResponse): """ 提供首页所有数据显示,前端每1分钟请求1次 """ brewery_name = settings['breweryName'] wifi_connected = wifi.is_connected() real_date = rtc.get_localdate() real_time = rtc.get_localtime() basic_info = { 'breweryName': brewery_name, 'wifiIsConnected': wifi_connected, 'realDate': real_date, 'realTime': real_time, } process_info = process.get_process_info() overview = basic_info.copy() overview.update(process_info) # 以下数据更新用于前端绘制echarts折线图 this.set_temp = process_info.get('setTemp') this.chamber_temp = process_info.get('chamberTemp') this.wort_temp = process_info.get('wortTemp') this.gravity_sg = process_info.get('hydrometerData').get( 'currentGravity') this.time_mark = real_date + ' ' + real_time httpResponse.WriteResponseJSONOk(obj=overview, headers=None) @MicroWebSrv.route('/fermentation', 'POST') def fermentation_post(httpClient, httpResponse): """ 前台向后台提交发酵步骤数据,并且开始发酵过程 """ json = httpClient.ReadRequestContentAsJSON() beerName = json['beerName'] fermentationSteps = json['fermentationSteps'] try: process.set_beer_name(beerName) process.load_steps(fermentationSteps) process.start() except: # throw 500 error code httpResponse.WriteResponseInternalServerError() else: httpResponse.WriteResponseOk() @MicroWebSrv.route('/abort') def abort_get(httpClient, httpResponse): try: process.abort() except: # throw 500 error code httpResponse.WriteResponseInternalServerError() else: httpResponse.WriteResponseOk() @MicroWebSrv.route('/settings') def settings_get(httpClient, httpResponse): """ 从后台读取设置参数 """ wifi_list = wifi.scan_wifi_list() temp_sensor_list = process.fermenter_temp_ctrl.chamber_sensor.ds_obj.get_device_list( ) # open user_settings.json and read settings with open('user_settings.json', 'r') as f: settings_dict = ujson.load(f) wort_sensor_dev_num = settings_dict.get('wortSensorDev') chamber_sensor_dev_num = settings_dict.get('chamberSensorDev') wort_sensor_rom_code = '' chamber_sensor_rom_code = '' for sensor_dict in temp_sensor_list: detail_list = sensor_dict.values() if wort_sensor_dev_num in detail_list: wort_sensor_rom_code = sensor_dict.get('label') elif chamber_sensor_dev_num in detail_list: chamber_sensor_rom_code = sensor_dict.get('label') wort_sensor_dev = { 'value': wort_sensor_dev_num, 'label': wort_sensor_rom_code } chamber_sensor_dev = { 'value': chamber_sensor_dev_num, 'label': chamber_sensor_rom_code } settings_added = { 'wifiList': wifi_list, 'wortSensorDev': wort_sensor_dev, 'chamberSensorDev': chamber_sensor_dev, 'tempSensorList': temp_sensor_list } settings_combined = settings_dict.copy() settings_combined.update(settings_added) # settings_json = ujson.dumps(settings_combined) httpResponse.WriteResponseJSONOk(obj=settings_combined, headers=None) # httpResponse.WriteResponseOk( # headers=None, # contentType="application/json", # contentCharset="UTF-8", # content=settings_json # ) @MicroWebSrv.route('/settings', 'POST') def settings_post(httpClient, httpResponse): """ 向后台保存设置参数,并且重启ESP32 """ # json = httpClient.ReadRequestContentAsJSON() # settings_dict = ujson.loads(json) settings_dict = httpClient.ReadRequestContentAsJSON() settings_dict['wortSensorDev'] = settings_dict['wortSensorDev'][ 'value'] settings_dict['chamberSensorDev'] = settings_dict[ 'chamberSensorDev']['value'] try: with open('user_settings.json', 'w') as f: ujson.dump(settings_dict, f) except: httpResponse.WriteResponseInternalServerError() else: httpResponse.WriteResponseOk() @MicroWebSrv.route('/reboot') def reboot_get(httpClient, httpResponse): tim = machine.Timer(-1) try: tim.init(period=3000, mode=machine.Timer.ONE_SHOT, callback=lambda t: machine.reset()) except: httpResponse.WriteResponseInternalServerError() else: httpResponse.WriteResponseOk() @MicroWebSrv.route('/wifi') def wifi_get(httpClient, httpResponse): """ 获取WIFI热点列表,用于刷新热点列表 """ wifi_list = wifi.scan_wifi_list() wifi_dict = {'wifiList': wifi_list} httpResponse.WriteResponseJSONOk(obj=wifi_dict, headers=None) @MicroWebSrv.route('/wifi', 'POST') def wifi_post(httpClient, httpResponse): """ 连接WIFI热点 """ wifi_dict = httpClient.ReadRequestContentAsJSON() try: new_ip = wifi.sta_connect(wifi_dict['ssid'], wifi_dict['pass']) except: httpResponse.WriteResponseInternalServerError() else: if wifi.is_connected(): httpResponse.WriteResponseOk() else: httpResponse.WriteResponseInternalServerError() @MicroWebSrv.route('/ip') def ip_get(httpClient, httpResponse): """ 获取IP地址 """ ap_ip = wifi.get_ap_ip_addr() sta_ip = wifi.get_sta_ip_addr() sta_ssid = wifi.get_sta_ssid() ip_dict = {'apIp': ap_ip, 'staIp': sta_ip, 'staSsid': sta_ssid} httpResponse.WriteResponseJSONOk(obj=ip_dict, headers=None) @MicroWebSrv.route('/ftp') def ftp_get(httpClient, httpResponse): """ Start FTP service """ try: import uftpd except: httpResponse.WriteResponseInternalServerError() else: httpResponse.WriteResponseOk() @MicroWebSrv.route('/mqtttest', 'POST') def mqtt_post(httpClient, httpResponse): """ Send test message to MQTT server """ settings_dict = httpClient.ReadRequestContentAsJSON() test_msg = ujson.dumps({'test-message': 200}) from mqtt_client import MQTT try: test_mqtt = MQTT(settings_dict) test_mqtt.publish(test_msg) except: httpResponse.WriteResponseInternalServerError() else: httpResponse.WriteResponseOk() @MicroWebSrv.route('/actuator', 'POST') def actuactor_post(httpClient, httpResponse): """ 手动控制制冷压缩机或制热器,主要用于测试 """ actuactor_dict = httpClient.ReadRequestContentAsJSON() heater = process.fermenter_temp_ctrl.heater cooler = process.fermenter_temp_ctrl.cooler led = process.fermenter_temp_ctrl.led try: if actuactor_dict.get('element') == 'heater': element = heater else: element = cooler action = actuactor_dict.get('action') if action: element.force_on() else: element.force_off() if heater.is_on() and not cooler.is_on(): led_color = 'red' elif not heater.is_on() and cooler.is_on(): led_color = 'blue' elif not heater.is_on() and not cooler.is_on(): led_color = 'green' else: led_color = 'orange' led.set_color(led_color) except: httpResponse.WriteResponseInternalServerError() else: httpResponse.WriteResponseOk() @MicroWebSrv.route('/tempsensors', 'POST') def temp_post(httpClient, httpResponse): """ 获取温度传感器读数 """ # 获取前端发来的设备序号 sensor_dict = httpClient.ReadRequestContentAsJSON() new_wort_romcode = sensor_dict.get('wortSensorDev').get('value') new_chamber_romcode = sensor_dict.get('chamberSensorDev').get( 'value') # 获取温感对象实例 wort_sensor = process.fermenter_temp_ctrl.wort_sensor chamber_sensor = process.fermenter_temp_ctrl.chamber_sensor # 更新温感设备序号 wort_sensor.update_romcode(new_wort_romcode) chamber_sensor.update_romcode(new_chamber_romcode) try: # 测量温度 wort_temp = wort_sensor.read_temp() chamber_temp = chamber_sensor.read_temp() except: # throw 500 error code httpResponse.WriteResponseInternalServerError() else: temp_dict = { 'wortTemp': wort_temp, 'chamberTemp': chamber_temp } httpResponse.WriteResponseJSONOk(obj=temp_dict, headers=None) @MicroWebSrv.route('/hydrometer', 'POST') def gravity_post(httpClient, httpResponse): hydrometer_dict = httpClient.ReadRequestContentAsJSON() process.save_hydrometer_data(hydrometer_dict) httpResponse.WriteResponseOk() @MicroWebSrv.route('/chart') def chart_get(httpClient, httpResponse): data = { 'timeMark': this.time_mark, 'setTemp': this.set_temp, 'wortTemp': this.wort_temp, 'chamberTemp': this.chamber_temp, 'gravitySg': this.gravity_sg } httpResponse.WriteResponseJSONOk(obj=data, headers=None) # Initialize the Web server self.app = MicroWebSrv(webPath='/sd/www') self.app.Start(threaded=True) # Starts the server def stop(self): if self.app: self.app.Stop() def is_started(self): if self.app: return self.app.IsStarted() else: return False
dict['internal'] = machine.internal_temp()[1] # Read ESP32 internal temp dict['time'] = rtc.now() # Record current time # Convert data to JSON and send websocket.SendText(json.dumps(dict)) def cb_accept_ws(webSocket, httpClient): print("WS ACCEPT") webSocket.RecvTextCallback = cb_receive_text webSocket.RecvBinaryCallback = cb_receive_binary webSocket.ClosedCallback = cb_closed # Use lambda to pass websocket to timer callback cb = lambda timer: cb_timer(timer, webSocket) # Init and start timer to poll temperature sensor tm.init(period=3000, callback=cb) mws = MicroWebSrv() # TCP port 80 and files in /flash/www mws.MaxWebSocketRecvLen = 256 # Default is set to 1024 mws.WebSocketThreaded = True # WebSockets with new threads mws.WebSocketStackSize = 4096 mws.AcceptWebSocketCallback = cb_accept_ws # Function to receive WebSockets mws.Start(threaded=False) # Blocking call (CTRL-C to exit) print('Cleaning up and exiting.') mws.Stop() tm.deinit() rtc.clear() ds.deinit() ow.deinit()
class Webserver(): def __init__(self, dev_state): self.wlan_agent = dev_state.wlan_agent self.mws = MicroWebSrv(webPath='www/') self.dev_state = dev_state def start(self): log('Start webserver.') if not self.wlan_agent.isActive(): raise Exception("Cant start webserver without wlan.") self.mws.Start() now = time() while not self.mws.IsStarted(): sleep(0.1) if (time()-now > 10): raise Exception('Webserver start is failed.') log('Webserver is started.') def stop(self): log('Stop webserver.') self.mws.Stop() now = time() while self.mws.IsStarted(): sleep(0.1) if (time()-now > 10): raise Exception('Webserver stop is failed.') log('Webserver is stopped.') def init_webserver(self): def httpHandlerGETState(httpClient, httpResponse): httpResponse.WriteResponseJSONOk( headers = None, obj = self.dev_state.get_state()) def httpHandlerPOSTsearchOnewire(httpClient, httpResponse): if self.dev_state.onewire_interface is not None: try: log('Request new search for one wire devices.') one_wire_inf = self.dev_state.onewire_interface one_wire_inf.interfacereset() one_wire_inf.getallid() one_wire_inf.checkdevices() self.dev_state.clear_sensors() one_wire_inf.update_state(self.dev_state) log('Find {} devices.'.format(one_wire_inf.num_devices)) res = { "numDevices": one_wire_inf.num_devices } httpResponse.WriteResponseJSONOk( headers = None, obj = res) except Exception as e: sys.print_exception(e) httpResponse.WriteResponseBadRequest() else: httpResponse.WriteResponseBadRequest() self.routeHandlers = [ ( '/state', 'GET', httpHandlerGETState), ( '/search_onewire', 'POST', httpHandlerPOSTsearchOnewire) ] self.mws = MicroWebSrv( webPath='www/', routeHandlers=self.routeHandlers )
class WebThingServer: """Server to represent a Web Thing over HTTP.""" def __init__(self, things, port=80, hostname=None, ssl_options=None): """ Initialize the WebThingServer. things -- list of Things managed by this server port -- port to listen on (defaults to 80) hostname -- Optional host name, i.e. mything.com ssl_options -- dict of SSL options to pass to the tornado server """ self.ssl_suffix = '' if ssl_options is None else 's' self.things = things self.name = things.get_name() self.port = port self.hostname = hostname self.ip = get_ip() station = network.WLAN() mac = station.config('mac') self.system_hostname = 'esp32-upy-{:02x}{:02x}{:02x}'.format( mac[3], mac[4], mac[5]) self.hosts = [ '127.0.0.1', '127.0.0.1:{}'.format(self.port), 'localhost', 'localhost:{}'.format(self.port), self.ip, '{}:{}'.format(self.ip, self.port), '{}.local'.format(self.system_hostname), '{}.local:{}'.format(self.system_hostname, self.port), ] if self.hostname is not None: self.hosts.extend([ self.hostname, '{}:{}'.format(self.hostname, self.port), ]) if isinstance(self.things, MultipleThings): log.info('Registering multiple things') for idx, thing in enumerate(self.things.get_things()): thing.set_href_prefix('/{}'.format(idx)) handlers = [ ('/', 'GET', self.thingsGetHandler), ('/<thing_id>', 'GET', self.thingGetHandler), ('/<thing_id>/properties', 'GET', self.propertiesGetHandler), ('/<thing_id>/properties/<property_name>', 'GET', self.propertyGetHandler), ('/<thing_id>/properties/<property_name>', 'PUT', self.propertyPutHandler), ] else: log.info('Registering a single thing') handlers = [ ('/', 'GET', self.thingGetHandler), ('/properties', 'GET', self.propertiesGetHandler), ('/properties/<property_name>', 'GET', self.propertyGetHandler), ('/properties/<property_name>', 'PUT', self.propertyPutHandler), ] for idx, thing in enumerate(self.things.get_things()): href = '//{}:{}{}'.format( self.hostname if self.hostname is not None else self.ip, self.port, thing.href_prefix) thing.set_ws_href('ws{}:{}'.format(self.ssl_suffix, href)) self.server = MicroWebSrv(webPath='/flash/www', routeHandlers=handlers, port=port) self.server.MaxWebSocketRecvLen = 256 self.WebSocketThreaded = ws_run_in_thread self.server.WebSocketStackSize = 8 * 1024 self.server.AcceptWebSocketCallback = self._acceptWebSocketCallback def start(self): """Start listening for incoming connections.""" url = 'http{}://{}:{}/'.format(self.ssl_suffix, self.ip, self.port) # If WebSocketS used and NOT running in thread, and WebServer IS # running in thread make sure WebServer has enough stack size to # handle also the WebSocket requests. log.info('Starting Web Server on ' + url) self.server.Start(threaded=srv_run_in_thread) def stop(self): """Stop listening.""" self.server.Stop() def getThing(self, routeArgs): """Get the thing ID based on the route.""" try: thing_id = routeArgs['thing_id'] except: thing_id = None return self.things.get_thing(thing_id) def getProperty(self, routeArgs): """Get the property name based on the route.""" thing = self.getThing(routeArgs) if thing: property_name = routeArgs['property_name'] if thing.has_property(property_name): return thing, thing.find_property(property_name) return None, None def validateHost(self, headers): """Validate the Host header in the request.""" host = headers.get('host', None) if host is not None and host in self.hosts: return True return False @print_exc def thingsGetHandler(self, httpClient, httpResponse): """Handle a request to / when the server manages multiple things.""" if not self.validateHost(httpClient.GetRequestHeaders()): httpResponse.WriteResponseError(403) return httpResponse.WriteResponseJSONOk( obj=[ thing.as_thing_description() for idx, thing in enumerate(self.things.get_things()) ], headers=_CORS_HEADERS, ) @print_exc def thingGetHandler(self, httpClient, httpResponse, routeArgs=None): """Handle a GET request for an individual thing.""" if not self.validateHost(httpClient.GetRequestHeaders()): httpResponse.WriteResponseError(403) return thing = self.getThing(routeArgs) if thing is None: httpResponse.WriteResponseNotFound() return descr = thing.as_thing_description() httpResponse.WriteResponseJSONOk( obj=descr, headers=_CORS_HEADERS, ) @print_exc def propertiesGetHandler(self, httpClient, httpResponse, routeArgs=None): """Handle a GET request for a property.""" thing = self.getThing(routeArgs) if thing is None: httpResponse.WriteResponseNotFound() return httpResponse.WriteResponseJSONOk(thing.get_properties()) @print_exc def propertyGetHandler(self, httpClient, httpResponse, routeArgs=None): """Handle a GET request for a property.""" if not self.validateHost(httpClient.GetRequestHeaders()): httpResponse.WriteResponseError(403) return thing, prop = self.getProperty(routeArgs) if thing is None or prop is None: httpResponse.WriteResponseNotFound() return httpResponse.WriteResponseJSONOk( obj={prop.get_name(): prop.get_value()}, headers=_CORS_HEADERS, ) @print_exc def propertyPutHandler(self, httpClient, httpResponse, routeArgs=None): """Handle a PUT request for a property.""" if not self.validateHost(httpClient.GetRequestHeaders()): httpResponse.WriteResponseError(403) return thing, prop = self.getProperty(routeArgs) if thing is None or prop is None: httpResponse.WriteResponseNotFound() return args = httpClient.ReadRequestContentAsJSON() if args is None: httpResponse.WriteResponseBadRequest() return try: prop.set_value(args[prop.get_name()]) except AttributeError: httpResponse.WriteResponseForbidden() return httpResponse.WriteResponseJSONOk( obj={prop.get_name(): prop.get_value()}, headers=_CORS_HEADERS, ) # === MicroWebSocket callbacks === @print_exc def _acceptWebSocketCallback(self, webSocket, httpClient): reqPath = httpClient.GetRequestPath() if WS_messages: print('WS ACCEPT reqPath =', reqPath) if ws_run_in_thread or srv_run_in_thread: # Print thread list so that we can monitor maximum stack size # of WebServer thread and WebSocket thread if any is used _thread.list() webSocket.RecvTextCallback = self._recvTextCallback webSocket.RecvBinaryCallback = self._recvBinaryCallback webSocket.ClosedCallback = self._closedCallback things = self.things.get_things() if len(things) == 1: thing_id = 0 else: thing_id = int(reqPath.split('/')[1]) thing = things[thing_id] webSocket.thing = thing thing.add_subscriber(webSocket) @print_exc def _recvTextCallback(self, webSocket, msg): if WS_messages: print('WS RECV TEXT : %s' % msg) @print_exc def _recvBinaryCallback(self, webSocket, data): if WS_messages: print('WS RECV DATA : %s' % data) @print_exc def _closedCallback(self, webSocket): if WS_messages: if ws_run_in_thread or srv_run_in_thread: _thread.list() print('WS CLOSED')
class WebThingServer: """Server to represent a Web Thing over HTTP.""" def __init__(self, things, port=80, hostname=None, ssl_options=None, additional_routes=None): """ Initialize the WebThingServer. For documentation on the additional route format, see: https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo/wiki/microWebSrv things -- list of Things managed by this server port -- port to listen on (defaults to 80) hostname -- Optional host name, i.e. mything.com ssl_options -- dict of SSL options to pass to the tornado server additional_routes -- list of additional routes to add to the server """ self.ssl_suffix = '' if ssl_options is None else 's' self.things = things self.name = things.get_name() self.port = port self.hostname = hostname station = network.WLAN() mac = station.mac() import ubinascii self.system_hostname = 'esp32-upy-' + ubinascii.hexlify(mac.sta_mac).decode() #todo to check in STA thus 0 or AP thus 1 self.hosts = [ 'localhost', 'localhost:{}'.format(self.port), '{}.local'.format(self.system_hostname), '{}.local:{}'.format(self.system_hostname, self.port), ] for address in get_addresses(): self.hosts.extend([ address, '{}:{}'.format(address, self.port), ]) if self.hostname is not None: self.hostname = self.hostname.lower() self.hosts.extend([ self.hostname, '{}:{}'.format(self.hostname, self.port), ]) if isinstance(self.things, MultipleThings): log.info('Registering multiple things') for idx, thing in enumerate(self.things.get_things()): thing.set_href_prefix('/{}'.format(idx)) handlers = [ ( '/.*', 'OPTIONS', self.optionsHandler ), ( '/', 'GET', self.thingsGetHandler ), ( '/<thing_id>', 'GET', self.thingGetHandler ), ( '/<thing_id>/properties', 'GET', self.propertiesGetHandler ), ( '/<thing_id>/properties/<property_name>', 'GET', self.propertyGetHandler ), ( '/<thing_id>/properties/<property_name>', 'PUT', self.propertyPutHandler ), ( '/<thing_id>/actions/<action_name>', 'GET', self.actionGetHandler ), ( '/<thing_id>/actions/<action_name>', 'POST', self.actionPostHandler ), ] else: log.info('Registering a single thing') handlers = [ ( '/.*', 'OPTIONS', self.optionsHandler ), ( '/', 'GET', self.thingGetHandler ), ( '/properties', 'GET', self.propertiesGetHandler ), ( '/properties/<property_name>', 'GET', self.propertyGetHandler ), ( '/properties/<property_name>', 'PUT', self.propertyPutHandler ), ( '/<thing_id>/actions/<action_name>', 'GET', self.actionGetHandler ), ( '/<thing_id>/actions/<action_name>', 'POST', self.actionPostHandler ), ] if isinstance(additional_routes, list): handlers = additional_routes + handlers self.server = MicroWebSrv(webPath='/flash/www', routeHandlers=handlers, port=port) self.server.MaxWebSocketRecvLen = 512 #256 self.WebSocketThreaded = ws_run_in_thread self.server.WebSocketStackSize = 12 * 1024 #8* self.server.AcceptWebSocketCallback = self._acceptWebSocketCallback def start(self): """Start listening for incoming connections.""" # If WebSocketS used and NOT running in thread, and WebServer IS # running in thread make shure WebServer has enough stack size to # handle also the WebSocket requests. log.info('Starting Web Server on port {}'.format(self.port)) #self.server.Start(threaded=srv_run_in_thread, stackSize=12*1024) self.server.Start(threaded=srv_run_in_thread) from network import MDNS #mdns = network.mDNS() # Initialize the MDNS module MDNS.init() # Set the hostname and instance name of this device MDNS.set_name(hostname = self.system_hostname, instance_name = "Micropython with MDNS") #mdns.start(self.system_hostname, 'MicroPython with mDNS') MDNS.add_service('_webthing', MDNS.PROTO_TCP, 80,txt=(("board","esp32"),("path","/")) ) #{ # 'board': 'ESP32', # 'path': '/', #}) def stop(self): """Stop listening.""" self.server.Stop() def getThing(self, routeArgs): """Get the thing ID based on the route.""" if not routeArgs or 'thing_id' not in routeArgs: thing_id = None else: thing_id = routeArgs['thing_id'] return self.things.get_thing(thing_id) def getProperty(self, routeArgs): """Get the property name based on the route.""" thing = self.getThing(routeArgs) if thing: property_name = routeArgs['property_name'] if thing.has_property(property_name): return thing, thing.find_property(property_name) return None, None def getAction(self, routeArgs): """Get the property name based on the route.""" thing = self.getThing(routeArgs) if thing: action_name = routeArgs['action_name'] if thing.has_action(action_name): return thing, thing.find_action(action_name) return None, None def validateHost(self, headers): """Validate the Host header in the request.""" host = headers.get('host', None) if host is not None and host.lower() in self.hosts: return True return False @print_exc def optionsHandler(self, httpClient, httpResponse, routeArgs=None): """Handle an OPTIONS request to any path.""" if not self.validateHost(httpClient.GetRequestHeaders()): httpResponse.WriteResponseError(403) return httpResponse.WriteResponse(204, _CORS_HEADERS, None, None, None) @print_exc def thingsGetHandler(self, httpClient, httpResponse): """Handle a request to / when the server manages multiple things.""" if not self.validateHost(httpClient.GetRequestHeaders()): httpResponse.WriteResponseError(403) return base_href = 'http{}://{}'.format( self.ssl_suffix, httpClient.GetRequestHeaders().get('host', '') ) ws_href = 'ws{}://{}'.format( self.ssl_suffix, httpClient.GetRequestHeaders().get('host', '') ) descriptions = [] for thing in self.things.get_things(): description = thing.as_thing_description() description['links'].append({ 'rel': 'alternate', 'href': '{}{}'.format(ws_href, thing.get_href()), }) description['href'] = thing.get_href() description['base'] = '{}{}'.format(base_href, thing.get_href()) description['securityDefinitions'] = { 'nosec_sc': { 'scheme': 'nosec', }, } description['security'] = 'nosec_sc' descriptions.append(description) httpResponse.WriteResponseJSONOk( obj=descriptions, headers=_CORS_HEADERS, ) @print_exc def thingGetHandler(self, httpClient, httpResponse, routeArgs=None): """Handle a GET request for an individual thing.""" if not self.validateHost(httpClient.GetRequestHeaders()): httpResponse.WriteResponseError(403) return thing = self.getThing(routeArgs) if thing is None: httpResponse.WriteResponseNotFound() return base_href = 'http{}://{}'.format( self.ssl_suffix, httpClient.GetRequestHeaders().get('host', '') ) ws_href = 'ws{}://{}'.format( self.ssl_suffix, httpClient.GetRequestHeaders().get('host', '') ) description = thing.as_thing_description() description['links'].append({ 'rel': 'alternate', 'href': '{}{}'.format(ws_href, thing.get_href()), }) description['base'] = '{}{}'.format(base_href, thing.get_href()) description['securityDefinitions'] = { 'nosec_sc': { 'scheme': 'nosec', }, } description['security'] = 'nosec_sc' httpResponse.WriteResponseJSONOk( obj=description, headers=_CORS_HEADERS, ) @print_exc def propertiesGetHandler(self, httpClient, httpResponse, routeArgs=None): """Handle a GET request for a property.""" thing = self.getThing(routeArgs) if thing is None: httpResponse.WriteResponseNotFound() return httpResponse.WriteResponseJSONOk(thing.get_properties()) @print_exc def propertyGetHandler(self, httpClient, httpResponse, routeArgs=None): """Handle a GET request for a property.""" if not self.validateHost(httpClient.GetRequestHeaders()): httpResponse.WriteResponseError(403) return thing, prop = self.getProperty(routeArgs) if thing is None or prop is None: httpResponse.WriteResponseNotFound() return httpResponse.WriteResponseJSONOk( obj={prop.get_name(): prop.get_value()}, headers=_CORS_HEADERS, ) @print_exc def propertyPutHandler(self, httpClient, httpResponse, routeArgs=None): """Handle a PUT request for a property.""" if not self.validateHost(httpClient.GetRequestHeaders()): httpResponse.WriteResponseError(403) return thing, prop = self.getProperty(routeArgs) if thing is None or prop is None: httpResponse.WriteResponseNotFound() return args = httpClient.ReadRequestContentAsJSON() if args is None: httpResponse.WriteResponseBadRequest() return try: prop.set_value(args[prop.get_name()]) except PropertyError: httpResponse.WriteResponseBadRequest() return httpResponse.WriteResponseJSONOk( obj={prop.get_name(): prop.get_value()}, headers=_CORS_HEADERS, ) @print_exc def actionGetHandler(self, httpClient, httpResponse, routeArgs=None): """Handle a GET request for an action.""" if not self.validateHost(httpClient.GetRequestHeaders()): httpResponse.WriteResponseError(403) return thing, act = self.getAction(routeArgs) if thing is None or act is None: httpResponse.WriteResponseNotFound() return httpResponse.WriteResponseJSONOk( #obj={act.get_name(): act.get_id()}, obj={act.get_name(): 0}, headers=_CORS_HEADERS, ) @print_exc def actionPostHandler(self, httpClient, httpResponse, routeArgs=None): """Handle a POST request for an action.""" if not self.validateHost(httpClient.GetRequestHeaders()): httpResponse.WriteResponseError(403) return thing, act = self.getAction(routeArgs) if thing is None or act is None: httpResponse.WriteResponseNotFound() return args = httpClient.ReadRequestContentAsJSON() if args is None: httpResponse.WriteResponseBadRequest() return try: act.perform_action(args[act.get_name()]) except ActionError: httpResponse.WriteResponseBadRequest() return httpResponse.WriteResponseJSONOk( obj={act.get_name(): act.get_value()}, headers=_CORS_HEADERS, ) # === MicroWebSocket callbacks === @print_exc def _acceptWebSocketCallback(self, webSocket, httpClient): reqPath = httpClient.GetRequestPath() if WS_messages: print('WS ACCEPT reqPath =', reqPath) if ws_run_in_thread or srv_run_in_thread: # Print thread list so that we can monitor maximum stack size # of WebServer thread and WebSocket thread if any is used _thread.list() webSocket.RecvTextCallback = self._recvTextCallback webSocket.RecvBinaryCallback = self._recvBinaryCallback webSocket.ClosedCallback = self._closedCallback things = self.things.get_things() if len(things) == 1: thing_id = 0 else: thing_id = int(reqPath.split('/')[1]) thing = things[thing_id] webSocket.thing = thing thing.add_subscriber(webSocket) @print_exc def _recvTextCallback(self, webSocket, msg): if WS_messages: print('WS RECV TEXT : %s' % msg) @print_exc def _recvBinaryCallback(self, webSocket, data): if WS_messages: print('WS RECV DATA : %s' % data) @print_exc def _closedCallback(self, webSocket): if WS_messages: if ws_run_in_thread or srv_run_in_thread: _thread.list() print('WS CLOSED')
srv.Start(threaded=True) # start server pycom.rgbled(0x00ff00) #green print(">>local webServer started<<") time.sleep(0.25) mf = open("status.txt", "w") # use to determine mqtt switch mf.close() while True: time.sleep(1) condf = open("status.txt", "r") cond = condf.read() condf.close() if cond == "yes": # use to determine mqtt switch print(">>stopping server<<") pycom.rgbled(0xff0000) #red time.sleep(1) srv.Stop() #stop server print(">>local server stopped<<") time.sleep(1) break else: pass print(">>changing WIFI mode<<") # setup as a station wlan = network.WLAN(mode=network.WLAN.STA) print(">>attempting hotspot connection, FiPy is in station mode<<") wlan.connect('TPU4G_L3TN', auth=(network.WLAN.WPA2, '56156271')) time.sleep(5) while not wlan.isconnected(): #retry every 5 seconds wlan.connect('TPU4G_L3TN', auth=(network.WLAN.WPA2, '56156271')) time.sleep(5) print(">>waiting for IP and DNS configuration<<")