Files
MeshCore/scripts/optional_display_profiles.py
T

95 lines
4.8 KiB
Python

"""Bind optional-screen directions to the exact qualified application."""
import json
import re
import sys
from t114_profiles import runtime_metadata as t114_metadata
CAPABILITY = "display.autodetect"
SPECS = (
dict(flag="RC32_DISPLAY_AUTODETECT", board="heltec-rc32", guard="HELTEC_RC32",
driver="NV3001BDisplay", controller="nv3001b", marker="rc32",
targets=r"heltec_rc32_(?!without_display_).+"),
dict(flag="WIRELESS_TRACKER_DISPLAY_AUTODETECT", board="esp32-s3-devkitc-1",
guard="HELTEC_LORA_V3", driver="ST7735Display", controller="st7735", marker="tracker",
targets=r"heltec_wireless_tracker_.+"),
dict(flag="WIRELESS_TRACKER_DISPLAY_AUTODETECT", board="heltec_tracker_v1_1",
guard="HELTEC_TRACKER_V1_1", driver="ST7735Display", controller="st7735", marker="tracker",
targets=r"heltec_tracker_v1_1_.+"),
dict(flag="HELTEC_TFT_DISPLAY_AUTODETECT", board="heltec_v4_r8",
guard="HELTEC_V4_R8_TFT", driver="ST7789LCDDisplay", controller="st7789", marker="heltec_r8",
targets=r"heltec_v4_r8_tft_.+"),
dict(flag="MUZI_BASE_ACCESSORY_AUTODETECT", board="muzi_base", guard="MUZI_BASE",
driver="SH1107Display", controller="sh1107", marker="muzi_superio", platform="NRF52_PLATFORM",
defines={"RADIO_CLASS": "CustomLR1121", "USE_LR1121": "1"},
targets=r"muzi_base_duo_(?:superio_)?companion_radio_(?:usb|ble|full)"),
dict(flag="MUZI_BASE_ACCESSORY_AUTODETECT", board="muzi_base", guard="MUZI_BASE",
driver="SH1107Display", controller="sh1107", marker="muzi_superio", platform="NRF52_PLATFORM",
defines={"RADIO_CLASS": "CustomSX1262", "USE_SX1262": "1"},
targets=r"muzi_base_uno_(?:superio_)?companion_radio_(?:usb|ble|full)"),
dict(flag="TECHO_LITE_DISPLAY_AUTODETECT", board="t-echo", guard="LILYGO_TECHO_LITE",
driver="GxEPDDisplay", controller="ssd1681", marker="techo_lite", platform="NRF52_PLATFORM",
defines={"EINK_DISPLAY_MODEL": "GxEPD2_122_T61"},
targets=r"lilygo_t-echo-lite_(?:non_shell_)?(?:companion_radio_(?:usb|ble|full)|(?:repeater|room_server)(?:_lora_ota(?:_no_external_sensors)?)?)"),
)
def marker(spec):
return "meshcore.capability.display.autodetect." + spec["marker"] + ".v1"
def spec_for_defines(defines, board=None):
for spec in SPECS:
if (defines.get(spec["flag"]) == "1"
and defines.get(spec["guard"], "0") not in ("0", "false")
and defines.get("DISPLAY_CLASS") == spec["driver"]
and all(defines.get(key) == value for key, value in spec.get("defines", {}).items())
and (board is None or board == spec["board"])):
return spec
return None
def runtime_metadata(manifest, defines, release_source=None, board=None):
if CAPABILITY not in (manifest.get("capabilities") or []):
return {}
if str(manifest.get("target", "")).lower().startswith("heltec_t114_"):
return t114_metadata(manifest, defines, release_source)
spec = spec_for_defines(defines, board)
if (not spec or board is None or manifest.get("verified") is not True
or manifest.get("platform") != spec.get("platform", "ESP32_PLATFORM")
or not re.fullmatch(spec["targets"], str(manifest.get("target", "")).lower())
or not any(row.get("capability") == CAPABILITY and row.get("present") is True
and row.get("source") == "packaged application"
and row.get("evidence") == marker(spec)
for row in manifest.get("verification") or [])):
raise ValueError("Optional display metadata requires exact board, flags and packaged detection proof")
source = manifest.get("source_commit", release_source)
if not isinstance(source, str) or not re.fullmatch(r"[0-9a-f]{40}", source):
raise ValueError("Optional display metadata requires an exact source commit")
if release_source is not None and source != release_source:
raise ValueError("Optional display metadata source must match its release")
return dict(displayAuto=True, displayController=spec["controller"], displaySource=source)
def resolved_defines(flags):
result = {}
for flag in flags:
match = re.fullmatch(r"-([DU])\s*(\w+)(?:=(.*))?", str(flag).strip())
if match:
kind, key, value = match.groups()
if kind == "U":
result.pop(key, None)
else:
result[key] = "1" if value is None else value
return result
if __name__ == "__main__":
# Configuration resolution is owned by build.sh; this command never runs PIO.
envs = {name: dict(options) for name, options in json.load(sys.stdin)}
env = envs.get("env:" + sys.argv[1], {})
spec = spec_for_defines(resolved_defines(env.get("build_flags", [])), env.get("board"))
if not spec:
sys.exit(1)
print(marker(spec))