Skip to content

Commit e39b795

Browse files
committed
fix(scripts): fail loud on missing node/WS config; harden ota_convert_stock
ms_ws.py: read MS_WS/NODE_ID from the env or ota-release.env and raise a clear error if neither has them, instead of silently defaulting to ws://localhost:5580 / node 9 (both stale -- matter-server runs on the Pi, node ids are per-device). Rename drive9.py -> drive_ac.py (the name encoded the stale node). ota_convert_stock.sh: set -euo pipefail like its sibling scripts so a mid-sequence failure (e.g. a failed chip-tool call during a live conversion) halts instead of silently continuing; the informational greps + interactive teardown are || true-guarded. Assisted-by: AI
1 parent c2e7367 commit e39b795

4 files changed

Lines changed: 59 additions & 21 deletions

File tree

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44

55
async def main():
6-
nid = ms_ws.node_id(9)
6+
nid = ms_ws.node_id()
77
async with ms_ws.connect(heartbeat=30, hello_timeout=20) as (ws, _):
88
seq = [
99
("SystemMode=Off", "1/513/28", 0),

firmware/scripts/ms_ws.py

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,65 @@
22
33
One async call() (send a command, await the reply whose message_id matches) and
44
one async connect() context manager (open the WS, consume the server_info hello
5-
frame). Both read the matter-server WS endpoint from $MS_WS (default
6-
ws://localhost:5580/ws); node_id() reads the target node from $NODE_ID.
5+
frame). ws_url() and node_id() read $MS_WS / $NODE_ID, falling back to the same
6+
ota-release.env that ota-release.sh uses (so a bare `python3 <script>.py` targets
7+
the right Pi + node). There is NO silent default: matter-server runs on the Pi and
8+
the node id is per-device, so a wrong endpoint/node quietly drives nothing -- both
9+
raise a clear error instead (the old ws://localhost:5580 + node 9 defaults were
10+
stale and silently mistargeted).
711
8-
Factored out of mcli/mnodes/ota_go/drive9, which each previously carried their
12+
Factored out of mcli/mnodes/ota_go/drive_ac, which each previously carried their
913
own copy of call() plus a hardcoded ws://localhost:5580/ws + node 9.
1014
"""
1115

1216
import os
17+
import re
1318
import json
1419
from contextlib import asynccontextmanager
1520

1621
import aiohttp
1722

18-
DEFAULT_WS = "ws://localhost:5580/ws"
23+
# ota-release.env sits next to this module; it's the source of truth for MS_WS + NODE_ID.
24+
_ENV_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ota-release.env")
25+
26+
27+
def _env(key):
28+
"""$key from the environment, else the same key parsed from ota-release.env, else None.
29+
The env always wins; the file is only a fallback for bare invocation."""
30+
v = os.environ.get(key)
31+
if v:
32+
return v
33+
try:
34+
with open(_ENV_FILE) as f:
35+
for line in f:
36+
m = re.match(rf'\s*{key}\s*=\s*"?([^"#\n]+?)"?\s*(?:#.*)?$', line)
37+
if m:
38+
return m.group(1)
39+
except OSError:
40+
pass
41+
return None
1942

2043

2144
def ws_url():
22-
"""Matter-server WS endpoint ($MS_WS, default ws://localhost:5580/ws)."""
23-
return os.environ.get("MS_WS", DEFAULT_WS)
45+
"""Matter-server WS endpoint from $MS_WS or ota-release.env (required, no default)."""
46+
v = _env("MS_WS")
47+
if not v:
48+
raise SystemExit(
49+
"MS_WS not set -- export it or set it in firmware/scripts/ota-release.env "
50+
"(e.g. ws://<pi-host>:5580/ws). matter-server runs on the Pi, not localhost."
51+
)
52+
return v
2453

2554

26-
def node_id(default=9):
27-
"""Target node id ($NODE_ID if set, else the caller's default)."""
28-
v = os.environ.get("NODE_ID")
29-
return int(v) if v else default
55+
def node_id():
56+
"""Target node id from $NODE_ID or ota-release.env (required, no default)."""
57+
v = _env("NODE_ID")
58+
if not v:
59+
raise SystemExit(
60+
"NODE_ID not set -- export it or set it in firmware/scripts/ota-release.env "
61+
"(per-device; e.g. kitchen AmebaZ2=14, ESP32=28)."
62+
)
63+
return int(v)
3064

3165

3266
async def call(ws, cmd, args, mid, timeout=180):

firmware/scripts/ota_convert_stock.sh

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@
1515
# IOT_SSID your-iot-ssid IOT_PW_FILE ~/.iot_wifi_pw
1616
# NODE 100 (target on chip-tool fabric) PROV_NODE 1
1717
# NEWVER 23 CURVER_MAX 22 CODE 34970112332
18-
set -o pipefail
18+
# Hardened like its sibling scripts: -e halts a mid-sequence failure (e.g. a failed chip-tool
19+
# write) instead of silently continuing; -u catches unset vars; pipefail propagates pipe failures.
20+
# Informational `... | grep | tail` filters (which legitimately match nothing) and the interactive
21+
# teardown are explicitly `|| true`-guarded so they don't trip -e.
22+
set -euo pipefail
1923
CHIP="${CHIP:-$HOME/ameba-dev/connectedhomeip}"
2024
CT="$CHIP/examples/chip-tool/out/host/chip-tool"
2125
OP="$CHIP/examples/ota-provider-app/linux/out/host/chip-ota-provider-app"
@@ -37,10 +41,10 @@ case "${1:-}" in
3741
echo ">>> Commissioning (bypass). Retry this subcommand if you see IM 0x0501 (transient)."
3842
"$CT" pairing code-wifi "$NODE" "$IOT_SSID" "$(cat "$IOT_PW_FILE")" "$CODE" \
3943
--bypass-attestation-verifier 1 --storage-directory "$STORE" 2>&1 | \
40-
grep -iE "Commissioning complete|CHIP Error|IM Error|Timeout|success" | tail -6
44+
grep -iE "Commissioning complete|CHIP Error|IM Error|Timeout|success" | tail -6 || true
4145
echo ">>> Identity + OTA Requestor check:"
4246
for a in vendor-id product-id software-version; do
43-
"$CT" basicinformation read "$a" "$NODE" 0 --storage-directory "$STORE" 2>&1 | grep -iE "VendorID|ProductID|SoftwareVersion" | tail -1
47+
"$CT" basicinformation read "$a" "$NODE" 0 --storage-directory "$STORE" 2>&1 | grep -iE "VendorID|ProductID|SoftwareVersion" | tail -1 || true
4448
done
4549
"$CT" descriptor read server-list "$NODE" 0 --storage-directory "$STORE" 2>&1 | grep -iE "42 \(Ota" && \
4650
echo ">>> OTA Requestor present. Run: $0 ota <VID> <PID>" || echo ">>> NO OTA Requestor (42) — use CH341A flash instead."
@@ -52,7 +56,7 @@ case "${1:-}" in
5256
python3 "$TOOL" extract "$OTA_SRC" "$WORK/payload.bin" >/dev/null 2>&1 || die "extract failed"
5357
python3 "$TOOL" create -v "$VID" -p "$PID" -vn "$NEWVER" -vs "${NEWVER}.0" -mi 1 -ma "$CURVER_MAX" \
5458
-da sha256 "$WORK/payload.bin" "$TGT" >/dev/null 2>&1 || die "create failed"
55-
python3 "$TOOL" show "$TGT" | grep -iE "Vendor Id|Product Id|Version:"
59+
python3 "$TOOL" show "$TGT" | grep -iE "Vendor Id|Product Id|Version:" || true
5660
if printf '%s\n' "$@" | grep -qx -- --dry-run; then
5761
echo ">>> --dry-run: .ota repackaged + header verified (host-only); skipping provider/commission/OTA."
5862
exit 0
@@ -61,13 +65,13 @@ case "${1:-}" in
6165
rm -f /tmp/ota_prov.kvs
6266
"$OP" --discriminator 22 --secured-device-port 5560 --KVS /tmp/ota_prov.kvs --filepath "$TGT" > "$WORK/provider.log" 2>&1 &
6367
PP=$!; sleep 4
64-
"$CT" pairing onnetwork "$PROV_NODE" 20202021 --storage-directory "$STORE" 2>&1 | grep -iE "complete|Error" | tail -2
68+
"$CT" pairing onnetwork "$PROV_NODE" 20202021 --storage-directory "$STORE" 2>&1 | grep -iE "complete|Error" | tail -2 || true
6569
"$CT" accesscontrol write acl "[{\"fabricIndex\":1,\"privilege\":5,\"authMode\":2,\"subjects\":[112233],\"targets\":null},{\"fabricIndex\":1,\"privilege\":3,\"authMode\":2,\"subjects\":null,\"targets\":null}]" "$PROV_NODE" 0 --storage-directory "$STORE" >/dev/null 2>&1
6670
"$CT" otasoftwareupdaterequestor write default-otaproviders "[{\"fabricIndex\":1,\"providerNodeID\":$PROV_NODE,\"endpoint\":0}]" "$NODE" 0 --storage-directory "$STORE" >/dev/null 2>&1
67-
"$CT" otasoftwareupdaterequestor announce-otaprovider "$PROV_NODE" 0 0 0 "$NODE" 0 --storage-directory "$STORE" 2>&1 | grep -iE "Status=0x0|Error" | tail -1
71+
"$CT" otasoftwareupdaterequestor announce-otaprovider "$PROV_NODE" 0 0 0 "$NODE" 0 --storage-directory "$STORE" 2>&1 | grep -iE "Status=0x0|Error" | tail -1 || true
6872
echo ">>> Announced. Watching transfer (Ctrl-C when you see ApplyUpdateRequest)..."
69-
timeout 300 tail -f "$WORK/provider.log" | grep --line-buffered -iE "QueryImage|UpdateAvailable|BlockAckEOF|ApplyUpdateRequest"
70-
kill "$PP" 2>/dev/null
73+
timeout 300 tail -f "$WORK/provider.log" | grep --line-buffered -iE "QueryImage|UpdateAvailable|BlockAckEOF|ApplyUpdateRequest" || true
74+
kill "$PP" 2>/dev/null || true
7175
echo ">>> On ApplyUpdateRequest the unit reboots into our firmware. Then press 77 and add to HA with code $CODE."
7276
;;
7377
*) echo "usage: $0 {commission | ota <VID> <PID> [--dry-run]} (see firmware/docs/12-ota-convert-stock-unit.md)";;

firmware/scripts/ota_go.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44

55
async def main():
6-
nid = ms_ws.node_id(9)
6+
nid = ms_ws.node_id()
77
# wait for node available
88
for attempt in range(20):
99
try:
@@ -26,7 +26,7 @@ async def main():
2626
)
2727
print("update_node result:", json.dumps(r)[:400])
2828
return
29-
except Exception as e:
29+
except Exception:
3030
pass
3131
await asyncio.sleep(10)
3232
print(f"node{nid} never became available")

0 commit comments

Comments
 (0)