#!/usr/bin/env python3 from bottle import route, run, request, HTTPResponse, response import hashlib import json import pdb import os updated = True filename = "firmware.bin" @route('/update') def updateRequest(): #for header in request.headers.keys(): # print("{}: {}".format(header, request.headers.get(header, ""))) if not "X-Esp8266-Sta-Mac" in request.headers: return "Hello fellow friend! Looking for updates?" readConfig() global config requestor_mac = request.headers.get("X-Esp8266-Sta-Mac", "") running_fw_md5 = request.headers.get("X-Esp8266-Sketch-Md5", "") role, version, fw_filename, fw_hash = getProperties(config, requestor_mac) if role == "" and version == "" and fw_filename == "" and fw_hash == "": print("No configuration for {} found. Serving 304 - No Firmware".format(requestor_mac)) return nofirmware() print("Update request from {} with role {} (running fw hash: {}).".format(requestor_mac, role, running_fw_md5)) fw_path = os.path.join("firmwares", role, version, fw_filename) try: latest_fw_md5 = md5(fw_path) except FileNotFoundError: print("Configured firmware not found at {}".format(fw_path)) return nofirmware() if latest_fw_md5 != fw_hash: print("Configured hash ({}) does not match calculeted one ({})".format(fw_hash, latest_fw_md5)) return nofirmware() print("Latest firmware for role \"{}\" is \"{}\" ({}) - ".format(role, fw_filename, latest_fw_md5), end="") if latest_fw_md5 == running_fw_md5: print("Same, skipping…") return nofirmware() print("Differs, updating…") response.set_header('Content-Type', 'application/octet-stream') response.set_header('Content-Disposition', ' attachment; filename={}'.format(filename)) with open(fw_path, "rb") as fw: firmware = fw.read() response.set_header('Content-Length', '{}'.format(len(firmware))) response.set_header('X-MD5', latest_fw_md5) print("Serving {} bytes to {} with role {}".format(len(firmware), requestor_mac, role)) return firmware def getProperties(config, mac): configured_devices = config.get("devices", {}).keys() devices = config.get("devices", {}) firmwares = config.get("firmwares", {}) match = devices.get(mac, {"role":"", "version":""}) role = match.get("role", "") version = match.get("version", "") role_firmwares = firmwares.get(role, {}) firmware = role_firmwares.get(version, {}) firmware_filename = firmware.get("filename", "") firmware_hash = firmware.get("hash", "") return role, version, firmware_filename, firmware_hash def nofirmware(): return HTTPResponse(status=304, body="") def md5(fname): hash_md5 = hashlib.md5() with open(fname, "rb") as f: for chunk in iter(lambda: f.read(4096), b""): hash_md5.update(chunk) return hash_md5.hexdigest() def readConfig(config_name="config.json"): global config config = "" with open(config_name, "r") as conf: config = json.loads(conf.read()) readConfig() run(host='localhost', port=8080, debug=True)