diff --git a/.gitignore b/.gitignore index 6cf4a87..9dbc22b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,10 @@ *.log *.pyc *.pyo +.idea +/Release/build/* +/Release/dist/* + + +!empty diff --git a/README.md b/README.md index fa1462a..21a37b5 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ The included final Arduino Sketch binary is v5.9.1, defaulting `WIFI_CONFIG_TOOL ### Prerequisites **Windows users:** +Skip if you use the sonota.exe file * Download and install Python v3.5.x: https://www.python.org/downloads/windows/ (Python v3.5.x is recommended for Windows as the `netifaces` module has prebuilt wheels) * **All firewalls must be disabled** when running SonOTA @@ -32,11 +33,20 @@ The included final Arduino Sketch binary is v5.9.1, defaulting `WIFI_CONFIG_TOOL ### Running +Method 1) After setting up the prerequisites, download the repo (either using `git clone`, or downloading and extracting the .zip file). Then install the Python dependencies by changing into the SonOTA directory and running: `pip3 install --user -r requirements.txt` Once installed, you can run SonOTA (`./sonota.py`, you may need something like `python3 sonota.py` on Windows), and it will prompt you for the various settings it needs, and guide you through what to do for each step. +Method 2) +Download latest sonota.exe from the releases page + +Run the exe as administrator** + +A Console Windows will popup and it will prompt you for the various settings it needs, and guide you through what to do for each step. + + **Ensure all firewalls are disabled on all WiFi networks, including FinalStage when connected.** This is the most common reason things to not complete. Once complete and Sonoff-Tasmota is installed, you will have an AP called `sonoff-####` (note: this will be up for a minute then down for a minute until configured). @@ -85,6 +95,11 @@ This program is based on research documented here: The script is fairly well documented (I think) - apart from that above stated links should provide some context of what's been done how and why. Feel free to open issue tickets or contact me directly: sonota@nanl.de + + +### Building own Exe File +just run the built.bat in the Release folder to create your own sonota.exe. You will find the exe in the Release/dist folder. + # Compatibility Please see the [wiki](https://github.com/mirko/SonOTA/wiki) for known working configurations. diff --git a/Release/build.bat b/Release/build.bat new file mode 100644 index 0000000..6a6cbe1 --- /dev/null +++ b/Release/build.bat @@ -0,0 +1,2 @@ +pyinstaller sonota.spec --clean +pause \ No newline at end of file diff --git a/Release/build/empty b/Release/build/empty new file mode 100644 index 0000000..e69de29 diff --git a/Release/dist/empty b/Release/dist/empty new file mode 100644 index 0000000..e69de29 diff --git a/Release/sonota.spec b/Release/sonota.spec new file mode 100644 index 0000000..c08c99e --- /dev/null +++ b/Release/sonota.spec @@ -0,0 +1,35 @@ +# -*- mode: python -*- + +block_cipher = None + +added_files = [ + ('..\static','static'), + ('..\ssl','ssl') + ] + +a = Analysis(['..\\sonota.py'], + pathex=[], + binaries=[], + datas = added_files, + hiddenimports=[], + hookspath=[], + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher) +pyz = PYZ(a.pure, a.zipped_data, + cipher=block_cipher) +exe = EXE(pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + name='sonota', + debug=True, + strip=False, + upx=True, + console=True, + uac_admin=False, + icon='..\\res\\wifi.ico' ) + diff --git a/res/wifi.ico b/res/wifi.ico new file mode 100644 index 0000000..030c117 Binary files /dev/null and b/res/wifi.ico differ diff --git a/sonota.py b/sonota.py index edfe43a..f67128b 100755 --- a/sonota.py +++ b/sonota.py @@ -343,8 +343,8 @@ def on_message(self, message): if self.setup_completed and self.test and not self.upgrade: - hash_user1 = self.getFirmwareHash("static/%s" % upgrade_file_user1) - hash_user2 = self.getFirmwareHash("static/%s" % upgrade_file_user2) + hash_user1 = self.getFirmwareHash(resource_path(os.path.join("static", upgrade_file_user1))) + hash_user2 = self.getFirmwareHash(resource_path(os.path.join("static", upgrade_file_user2))) if not args.serving_host: raise ValueError('args.serving_host is required') @@ -408,7 +408,7 @@ def getFirmwareHash(self, filePath): with open(filePath, "rb") as firmware: hash_user = sha256(firmware.read()).hexdigest() except IOError as e: - log.warn(e) + log.warning(e) return hash_user @@ -419,7 +419,7 @@ def make_app(): # handling actual payload communication on WebSockets (r'/api/ws', WebSocketHandler), (r'/slowota/(.*)', SlowOTAUpdate), - (r'/ota/(.*)', OTAUpdate, {'path': "static/"}), + (r'/ota/(.*)', OTAUpdate, {'path': resource_path("static/")}), ] return tornado.web.Application(apps) @@ -474,10 +474,21 @@ def promptforval(msg): if val: return val +def resource_path(relative_path): + """ Get absolute path to resource, works for dev and for PyInstaller """ + try: + # PyInstaller creates a temp folder and stores path in _MEIPASS + base_path = sys._MEIPASS + except Exception: + base_path = os.path.dirname(sys.argv[0]) + + return os.path.join(base_path, relative_path) + def checkargs(): # Make sure all of the binary files that are needed are there for fn in [arduino_file, upgrade_file_user1, upgrade_file_user2]: fn = os.path.join('static', fn) + fn = resource_path(fn) if not os.path.isfile(fn): log.critical("Required file missing!", fn) sys.exit(1) @@ -642,8 +653,8 @@ def stage2(): app.listen(DEFAULT_PORT_HTTP) app_ssl = tornado.httpserver.HTTPServer(app, ssl_options={ - "certfile": "ssl/server.crt", - "keyfile": "ssl/server.key", + "certfile": resource_path("ssl/server.crt"), + "keyfile": resource_path("ssl/server.key"), }) # listening on HTTPS port to catch initial POST request to eu-disp.coolkit.cc app_ssl.listen(DEFAULT_PORT_HTTPS)