Resolve Serial macro and ESP-NOW wrapper compilation errors. Preserve queue, neighbor, advert-history and Room post capacities while using qualified RTC, PSRAM and word-only IRAM storage on constrained ESP32 targets. Reduce Full infrastructure flash through lossless Room web compression, compact crypto and unused exception metadata removal. Add focused native regressions for storage ownership, exact protocol output, OTA proofs and build policy. Keep physical flash sizes, OTA slots and RAM guard formulas unchanged.
94 lines
3.2 KiB
Python
94 lines
3.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Losslessly package the Room Server page for browser gzip decoding.
|
|
|
|
RoomWebHtml.h remains the reviewed page source and native UI fixture. Only
|
|
Room Server's ESP32 WebConfig callback references the compressed asset. Other
|
|
roles and nRF52 builds do not include either page in their firmware.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import gzip
|
|
import hashlib
|
|
from pathlib import Path
|
|
import re
|
|
import zlib
|
|
|
|
SOURCE = "src/helpers/esp32/RoomWebHtml.h"
|
|
OUTPUT = "src/helpers/esp32/RoomWebHtmlGzip.h"
|
|
MARKER = "// room-page-source-sha256: "
|
|
|
|
|
|
def source_page(header: str) -> bytes:
|
|
matches = re.findall(
|
|
r'static const char ROOM_WEB_HTML\[\] PROGMEM = R"roomhtml\(([\s\S]*?)\)roomhtml";',
|
|
header,
|
|
)
|
|
if len(matches) != 1:
|
|
raise ValueError("expected one RoomWebHtml.h page source")
|
|
return matches[0].encode("ascii")
|
|
|
|
|
|
def page_bytes(header: str) -> bytes:
|
|
matches = re.findall(
|
|
r"static const uint8_t ROOM_WEB_HTML_GZ\[\] PROGMEM = \{([\s\S]*?)\};", header
|
|
)
|
|
if len(matches) != 1:
|
|
raise ValueError("expected one compressed Room page")
|
|
return bytes(int(value, 16) for value in re.findall(r"0x([0-9a-f]{2})", matches[0]))
|
|
|
|
|
|
def render(raw: bytes, encoded: bytes) -> str:
|
|
if gzip.decompress(encoded) != raw:
|
|
raise ValueError("compressed Room page differs from source")
|
|
digest = hashlib.sha256(raw).hexdigest()
|
|
lines = [
|
|
"// Auto-generated by scripts/generate_room_web_html.py; DO NOT EDIT.",
|
|
"// Lossless gzip of RoomWebHtml.h, decoded by the browser.",
|
|
MARKER + digest,
|
|
"#pragma once",
|
|
"#include <stdint.h>",
|
|
"#include <pgmspace.h>",
|
|
"",
|
|
"static const uint8_t ROOM_WEB_HTML_GZ[] PROGMEM = {",
|
|
]
|
|
for index in range(0, len(encoded), 16):
|
|
lines.append(" " + "".join("0x%02x," % value for value in encoded[index:index + 16]))
|
|
lines.extend(["};", ""])
|
|
return "\n".join(lines)
|
|
|
|
|
|
def generate(root: Path, *, check: bool = False) -> bool:
|
|
raw = source_page((root / SOURCE).read_text(encoding="ascii"))
|
|
destination = root / OUTPUT
|
|
existing = destination.read_text(encoding="ascii") if destination.exists() else ""
|
|
# Validate the actual bytes rather than trusting a marker or file mtime.
|
|
try:
|
|
encoded = page_bytes(existing)
|
|
intact = gzip.decompress(encoded) == raw and render(raw, encoded) == existing
|
|
except (ValueError, OSError, EOFError, zlib.error):
|
|
intact = False
|
|
if intact:
|
|
return False
|
|
if check:
|
|
raise ValueError("compressed Room page is missing, stale or invalid; regenerate it")
|
|
from zopfli_compress import gzip_compress
|
|
encoded = gzip_compress(raw)
|
|
destination.write_text(render(raw, encoded), encoding="ascii")
|
|
print("Room Web HTML: %d source bytes -> %d gzip bytes" % (len(raw), len(encoded)))
|
|
return True
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--check", action="store_true")
|
|
arguments = parser.parse_args()
|
|
root = Path(__file__).resolve().parents[1] if "__file__" in globals() else Path.cwd()
|
|
generate(root, check=arguments.check)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
elif "Import" in globals():
|
|
generate(Path.cwd())
|