Initial commit.
This commit is contained in:
commit
548f2e701f
7 files changed
+2978
No files matched your search
Executable
+150
@@ -0,0 +1,150 @@
|
||||
#!/usr/bin/env python3
|
||||
# Regenerate manufacturers.tsv from the Matter/CHIP manufacturer ID map.
|
||||
#
|
||||
# The TSV has two columns, "<decimal vendor id>\t<name>", sorted by vendor id. It
|
||||
# is consumed by matter-decode.sh and is mirrored by the embedded <map> in
|
||||
# matter-qr-decoder.html. It is derived from upstream project-chip/connectedhomeip's
|
||||
# manufacturers.xml -- refresh from there rather than hand-editing.
|
||||
#
|
||||
# https://raw.githubusercontent.com/project-chip/connectedhomeip/refs/heads/master/src/app/zap-templates/zcl/data-model/manufacturers.xml
|
||||
#
|
||||
# By default the script pulls the upstream file over the network (via curl) and
|
||||
# writes manufacturers.tsv:
|
||||
#
|
||||
# python3 gen-manufacturers.py
|
||||
#
|
||||
# A GitHub blob/ (HTML) URL is auto-rewritten to its raw.githubusercontent.com
|
||||
# form, so the URL from the repo's "From ..." comment works as-is.
|
||||
#
|
||||
# Override the source with a positional argument that is a URL or a local file:
|
||||
#
|
||||
# python3 gen-manufacturers.py <url-or-file>
|
||||
# python3 gen-manufacturers.py matter-qr-decoder.html # offline: embedded block
|
||||
# python3 gen-manufacturers.py -o out.tsv <source>
|
||||
#
|
||||
# The default network fetch uses curl (falling back to urllib if curl is
|
||||
# missing); a .html source and a plain XML file need no network. Requires only
|
||||
# the Python 3 standard library.
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import urllib.request
|
||||
import xml.etree.ElementTree as ET
|
||||
from pathlib import Path
|
||||
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
|
||||
# Pinned upstream source -- the same commit the HTML block was generated from, so
|
||||
# a fresh pull reproduces the committed TSV byte-for-byte until upstream moves.
|
||||
UPSTREAM_RAW = ("https://raw.githubusercontent.com/project-chip/connectedhomeip/"
|
||||
"7986ee3d82b26403fc2d0f70bdfbc9e8ff650df1/"
|
||||
"src/app/zap-templates/zcl/data-model/manufacturers.xml")
|
||||
|
||||
|
||||
def by_local_tag(root, local_name):
|
||||
for el in root.iter():
|
||||
tag = el.tag.rsplit("}", 1)[-1] if "}" in el.tag else el.tag
|
||||
if tag == local_name:
|
||||
yield el
|
||||
|
||||
|
||||
def mappings_from_xml_text(xml_text):
|
||||
m = re.search(r"<map\b.*?</map>", xml_text, re.DOTALL | re.IGNORECASE)
|
||||
frag = m.group(0) if m else xml_text
|
||||
root = ET.fromstring(frag)
|
||||
|
||||
rows = []
|
||||
for el in by_local_tag(root, "mapping"):
|
||||
code = el.get("code")
|
||||
name = el.get("translation", "")
|
||||
if code is None:
|
||||
continue
|
||||
# Upstream codes are hex with a 0x prefix ("0x1026"); strip it.
|
||||
hexpart = code[2:] if code[:2].lower() == "0x" else code
|
||||
try:
|
||||
dec = int(hexpart, 16)
|
||||
except ValueError:
|
||||
sys.stderr.write(f"warning: skipping unparseable code {code!r}\n")
|
||||
continue
|
||||
rows.append((dec, name))
|
||||
return rows
|
||||
|
||||
|
||||
def to_raw_url(url):
|
||||
m = re.match(r"https://github\.com/([^/]+)/([^/]+)/blob/(.+)", url)
|
||||
if m:
|
||||
owner, repo, path = m.groups()
|
||||
return f"https://raw.githubusercontent.com/{owner}/{repo}/{path}"
|
||||
return url
|
||||
|
||||
|
||||
def fetch_url(url):
|
||||
url = to_raw_url(url)
|
||||
if shutil.which("curl"):
|
||||
try:
|
||||
out = subprocess.run(["curl", "-fsSL", url],
|
||||
capture_output=True, check=True).stdout
|
||||
return out.decode("utf-8")
|
||||
except subprocess.CalledProcessError as e:
|
||||
sys.stderr.write(e.stderr.decode("utf-8", "replace"))
|
||||
sys.exit(f"error: failed to fetch {url}")
|
||||
req = urllib.request.Request(url, headers={"User-Agent": "gen-manufacturers.py"})
|
||||
try:
|
||||
with urllib.request.urlopen(req) as resp:
|
||||
return resp.read().decode("utf-8")
|
||||
except Exception as e:
|
||||
sys.exit(f"error: failed to fetch {url}: {e}")
|
||||
|
||||
|
||||
def read_source(source):
|
||||
if source.startswith(("http://", "https://")):
|
||||
return fetch_url(source)
|
||||
path = Path(source)
|
||||
if not path.exists():
|
||||
sys.exit(f"error: no such file: {path}")
|
||||
if path.suffix.lower() == ".html":
|
||||
html = path.read_text(encoding="utf-8")
|
||||
m = re.search(r'<script\s+type="text/xml"[^>]*>(.*?)</script>',
|
||||
html, re.DOTALL | re.IGNORECASE)
|
||||
if not m:
|
||||
sys.exit('error: no <script type="text/xml"> block in ' + str(path))
|
||||
return m.group(1)
|
||||
return path.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def write_tsv(path, rows):
|
||||
rows.sort(key=lambda r: r[0])
|
||||
with open(path, "w", encoding="utf-8", newline="") as f:
|
||||
for dec, name in rows:
|
||||
f.write(f"{dec}\t{name}\n")
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(
|
||||
description="Regenerate manufacturers.tsv from the Matter/CHIP "
|
||||
"manufacturer ID map (fetches upstream by default).")
|
||||
ap.add_argument(
|
||||
"source", nargs="?", default=UPSTREAM_RAW,
|
||||
help="a URL or local file (default: pinned upstream manufacturers.xml). "
|
||||
"A GitHub blob/ URL is auto-converted to its raw form; a .html path "
|
||||
'uses its embedded <script type="text/xml"> block.')
|
||||
ap.add_argument(
|
||||
"-o", "--output", default=str(SCRIPT_DIR / "manufacturers.tsv"),
|
||||
help="output path (default: manufacturers.tsv next to this script)")
|
||||
args = ap.parse_args()
|
||||
|
||||
xml_text = read_source(args.source)
|
||||
rows = mappings_from_xml_text(xml_text)
|
||||
if not rows:
|
||||
sys.exit("error: no <mapping> entries found in the source")
|
||||
|
||||
out = Path(args.output)
|
||||
write_tsv(out, rows)
|
||||
sys.stderr.write(f"wrote {len(rows)} mappings to {out} (from {args.source})\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user