#!/usr/bin/env python3 import struct from collections import namedtuple from datetime import datetime import subprocess import packaging.version from esptool.bin_image import LoadFirmwareImage # required to get hash and checksum right import os # locale.setlocale(locale.LC_ALL, 'en_US') APP_DESC_SIZE = 256 # sizeof(esp_app_desc_t) APP_DESC_STRUCT = " 0 if int(commits_since_tag) == 0: version_string = str(latest_release) else: next_version = increment_version(latest_release) version_string = version_format.format(version=next_version, commits=commits_since_tag, sha=sha) if is_dirty: version_string += ".dirty" return version_string def read_app_description_from_segment(segment): def process_bytes(b): if not isinstance(b, bytes): return b s = b.decode() return s[:s.find("\x00")] unpacked = struct.unpack(APP_DESC_STRUCT, segment.data[:APP_DESC_SIZE]) unpacked = tuple(process_bytes(e) for e in unpacked) magic_word, secure_version, _, _, version, project_name, time, date, idf_ver, app_elf_sha256, *_ = unpacked assert magic_word == 0xABCD5432 return AppDesc(secure_version, version, project_name, time, date, idf_ver, app_elf_sha256) def patch_app_description_in_segment(segment, version, project_name, time, date): #assert len(version) < 32 assert len(project_name) < 32 assert len(time) < 16 assert len(date) < 16 def fill_zeros(s, total_length): s += "\x00" * (total_length - len(s)) s = s.encode() assert len(s) == total_length return s raw_app_desc = segment.data[:APP_DESC_SIZE] unpacked = list(struct.unpack(APP_DESC_STRUCT, raw_app_desc)) unpacked[4] = fill_zeros(version, 32) unpacked[5] = fill_zeros(project_name, 32) unpacked[6] = fill_zeros(time, 16) unpacked[7] = fill_zeros(date, 16) packed = struct.pack(APP_DESC_STRUCT, *unpacked) original_data = segment.data segment.data = packed + original_data[APP_DESC_SIZE:] def patch_firmware(input_file, output_file): img = LoadFirmwareImage("esp32", input_file) version = version_number_from_git() now = datetime.now() time = now.strftime("%H:%M:%S") date = now.strftime("%b %d %Y") patch_app_description_in_segment(img.segments[0], version, "swimtracker.bauer.tech", time, date) img.save(output_file) if __name__ == "__main__": firmware_file = ".pio/build/esp32/firmware.bin" version_file_name = "VERSION" patch_firmware(firmware_file, firmware_file) # patch inplace version = version_number_from_git() print("Deploying ", version) with open(version_file_name, "w") as version_file: print(version, file=version_file) subprocess.run(["scp", firmware_file, "core@server:/docker/web/volumes/static-sites/swimtracker-update"]) subprocess.run(["scp", version_file_name, "core@server:/docker/web/volumes/static-sites/swimtracker-update"]) os.unlink(firmware_file) os.unlink(version_file_name)