Session Handover — 2026-09-05

For the next agent

Long multi-day homelab session (2026-09-02 → 2026-09-05) spanning TV network control, a presence daemon, a bambuddy preheat bug + clog RCA, the Starlink WAN migration, and a 2.4 GHz interference hunt. Most work is done and verified. The headline open items are: (1) the TV is physically powered off and unreachable (and Auto Power Off has still never been disabled), (2) the Starlink dish mount is printed but not installed while obstruction climbed to 3.3%, and (3) the north camera is marginal due to multipath, which channel tuning cannot fix. Pull deeper context via the historian; the per-topic vault notes are linked below.

Status: Between tasks, 2026-09-05. Predecessor: 2026-09-02-session-handover.


What we accomplished

1. LG TV network control — built, working, currently OFF

See 2026-09-02-lg-tv-network-control-presence for the full write-up.

  • Device: LG 43UP75003LF, 192.168.1.171, MAC ac:5a:f0:8b:48:da, webOS fw p20.03.53.45. Paired 2026-09-02 16:13; client key at /opt/tv-control/client-key.json (0600).
  • CLI: /opt/tv-control/tvpair | status | on | off | camwall | input <id>. venv at /opt/tv-control/venv, aiowebostv 0.10.0.
  • HTTP service: /opt/tv-control/tv-http, stdlib-only, binds 127.0.0.2:8102, published tailnet-only at https://telep-mainframe.taild4189d.ts.net:8451. Routes POST /api/{on,off,camwall,pair}. Embedded in Homepage via a gethomepage iframe service widget (classes: h-56 ...).
  • Four bugs found and fixed (all detailed in the linked note):
    1. err or type(err).__name__ truthiness bug printing empty error strings.
    2. aiowebostv’s hardcoded 10-second pairing window — monkeypatched to 180 s inside cmd_pair only.
    3. Active Standby leaves port 3000 open, so the reachability probe succeeded and WoL was skipped entirely.
    4. webOS accepts TCP before SSAP is ready — fixed with a 6 × 3 s retry loop and connect_timeout=10.
  • Power-state debounce (added later): decides on a live get_power_state() rather than the cached value; only short-circuits on a settled reading (two consecutive agreeing reads); verifies the TV actually reached the target and exits 1 naming the real state if not; serialises on/off with fcntl.flock on /run/tv-control.lock.

    aiowebostv.power_off() has the same stale-cache bug if not self.tv_state.is_on: return — so the library's own power_off() silently no-ops. It is bypassed with client.command("request", ep.POWER_OFF).

THE HEADLINE GOTCHA — HDMI numbering is a lie

The GPU’s xrandr output is HDMI-1, but the mainframe is physically plugged into the TV’s HDMI 2. Those numbers are unrelated. Switching the TV to HDMI_1 blanks it to “no signal” while xrandr, the framebuffer and camwall all look perfectly healthy — there is no local symptom at all. The TV reports the truth via WebOsTvState.inputs (connected: True/False per input). The subcommand was renamed hdmi1camwall and the old name was REMOVED (exits 2) rather than repointed, so no stale caller silently blanks the wall.

2. TV presence daemon

See 2026-09-02-tv-presence-wifi-union-daemon.

  • /opt/tv-control/presence + tv-presence.service. Polls every 60 s, presence window 900 s.
  • Presence = union of the router’s iwinfo assoclist (phy0-ap0 / phy1-ap0, over SSH key /home/levander/.ssh/router_alarm) and arp-scan on enp5s0.
  • Tracked MACs: 38:7f:8b:df:2a:79 (spider-web iPhone), a4:40:e1:02:01:e9 (F_M).
  • The union is load-bearing and was proven live: a sleeping iPhone is ABSENT from arp-scan while PRESENT in the assoclist. arp-scan alone would switch the TV off with someone standing in front of it.
  • Acts on transitions only, never re-asserts, and does not actuate on startup. A failed poll is discarded, not aged out.
  • The camera VLAN AP phy1-ap1 is deliberately NOT queried — it would leak camera devices into presence.
  • ⚠️ The arrival path has still never fired for real — both tracked devices have been home throughout.

3. bambuddy — chamber-target bug, clog RCA, and hardening

Full detail in 2026-09-03-bambuddy-preheat-chamber-target-bug-and-clog-rca. Summary:

  • The bug: _derive_chamber_target takes the MAX chamber target over all loaded AMS trays, ignoring the print’s own ams_mapping. A PC spool parked in slot 1 forced chamber 50 °C + bed 90 °C onto every PLA print — ~6 minutes of preheat/soak on a 3-gram keychain, and PLA printing on a 90 °C bed.
  • Root cause of the 2026-09-01 nozzle replacement (half of it): cold-end heat creep from that soak. The other half is PC residue carbonising at PLA temps on frequent PC→PLA transitions.
  • Fix (counter-intuitive): PC and PC-FR set to 0 in preheat_filament_targets, because a per-item preheat_chamber_target_override beats the map.

    PC-FR jobs must now carry an override PC-FR jobs MUST pass preheat_chamber_target_override: 60 when queued or they get no chamber preheat at all. ABS and ASA are still 45 — the same trap returns the moment either is loaded alongside PLA.

    PLA is correct with zero action, but

  • Rejected option, recorded so it is not retried: pulling the PC spool out of the AMS. The AMS is the driest storage available (~40% RH with desiccant); removing PC worsens the moisture half of the clog.
  • print-guard daemon/opt/print-guard/print-guard + print-guard.service. Polls the queue every 30 s and sets manual_start: true on any pending job whose material is LOW-temp when its predecessor is HIGH-temp, halting the queue for a human — the purge window. bambuddy honours the flag; verified live (Queue skip summary: {'manual_start': 1}). One-way only — it never clears the flag. ⚠️ Has never fired on real data.
  • Maintenance: Clean Nozzle/Hotend counter reset twice with notes (nozzle replacement, then the 2026-09-04 cold pull). Two new types created and assigned: Cold End / Extruder Inspection (100 h) and PC / PC-FR Dryness Check (14 days).
  • Local backups enabled and VERIFIED — daily 03:00, 7-day retention. bambuddy-backup-20260903-030031.zip, 292 MB, actually written.
  • Per-filament humidity thresholds configured in ams_humidity_thresholds: PLA 45 · PETG/ABS/ASA 40 · PETG-CF/TPU 35 · PC/PC-FR/PET-CF 30 · PA/PA-CF/PAHT-CF/PPS/PPA-CF/PVA 25 · default 40. Previously everything fell back to ams_humidity_fair: 60.

    The PC humidity alert is correct behaviour, not a misconfiguration 37–42% RH, so PC will alert immediately. That is intended. The AMS physically cannot fix it — it tops out at 65 °C and PC needs 80 °C. The alert means "dry externally".

    The AMS sits at

  • Tapo P115 smart plug/opt/tapo-bridge/tapo-bridge (python-kasa 0.10.2 in a venv) adapts it to bambuddy’s generic rest backend, binding 127.0.0.2:8117. Plug at 192.168.1.167, registered as plug id 1 linked to printer 1, monitoring-only (auto_on: false, auto_off: false) — bambuddy does not control printer power yet. Live readings confirmed (176 W, 0.408 kWh). Units are W and kWh, all multipliers 1.0; rest_method must be GET.

    P115 exposes no lifetime energy counter locally consumption_total returns None for all Tapo devices in python-kasa, so the bridge synthesizes a lifetime total from daily values persisted to /opt/tapo-bridge/state.json. It is monotonic and restart-safe, but it starts at zero and under-counts if the bridge is down across midnight.

SECURITY: bambuddy's API is completely unauthenticated

The users table is EMPTY. GET and PATCH against 127.0.0.2:8000 succeed with no credentials. Anyone who reaches bambuddy.taild4189d.ts.net has full control of a machine that heats to 280 °C. The tailnet ACL is the only gate. Separately: the bambuddy API key was leaked into a chat transcript and must be rotated. It is the credential bambuddy-mcp-bridge uses — regenerate in bambuddy and update that container’s env.

Full detail in 2026-09-04-starlink-wan-migration-dish-telemetry. Summary:

  • NE200 5G CPE → Starlink Bypass. 235–279 Mbps, 18–35 ms, 0% loss, versus the NE200’s final logged RSRP −103 / SINR 9 / QPSK. This closes out the chronic episodic WAN degradation investigation.
  • Still CGNAT (100.64.0.0/10) → no inbound ports.
  • Dish reachable at 192.168.100.1 via a route on telep-router, persisted two ways: a uci config route stanza (unverified — the network was deliberately never reloaded) and /etc/hotplug.d/iface/99-starlink-dish (verified for real: route deleted → script run → route restored).

    NEVER run /etc/init.d/network reload|restart on telep-router It would drop the site's connectivity and the tailnet SSH used to manage it. Use the live-then-persist pattern: apply with ip route add, then write uci without reloading.

  • Telemetry: /home/levander/net-monitor/starlink_status.pystarlink.csv, cron */5, using grpcurl v1.9.4.

    -emit-defaults is mandatory Without it, proto3 omits zero-values and silently drops popPingDropRate / fractionObstructed exactly when everything is healthy. A logger without the flag looks fine and records nothing useful.

  • NE200 logger retired — its */15 cron line was removed, but the script and 342 rows of rflog.csv were deliberately preserved as the evidence base that justified the switch.
  • ⚠️ Obstruction climbed 0.12% → 3.3% over the first hours, with the prolonged-outage estimate falling from 6 h to 45 min. A raised mount was printed in PC-FR (2026-09-03, “Starlink Gen3 mounting bracket”) but is not yet installed. The CSV holds the before-baseline.

5. WiFi / camera diagnosis — 2026-09-04/05

Camwall was flashing; root cause was a watchdog re-roll loop driven by stalled camera streams.

  • The channel survey was decisive: 2.4 GHz ch11 was 93% busy while our own traffic was only ~20%, and the noise floor across ch8–13 sat at −60…−76 dBm versus −93…−98 dBm on ch1–7, with only 1–3 APs found in the entire scan.

    That is a non-WiFi emitter the user owns

    Not neighbours. Few APs + a raised noise floor confined to the upper band = a local device radiating on 2.4 GHz upper channels.

  • Moved radio1 ch11 → ch1 (non-overlapping with the HP printer’s WiFi-Direct on ch6 at −20 dBm). Channel busy fell 92.9% → 25.8%. Backup at /etc/config/wireless.bak-1788552343.
  • South camera recovered immediately. North (192.168.30.139) did not — it needed a reboot via the user’s own /home/levander/tapo-ctl/reboot-cams-onvif.py 192.168.30.139, after which all four cameras returned to ~5 fps and recording resumed.

    The north camera's real problem is multipath, not congestion −63 dBm / SNR 40 yet negotiates only 8.6–11 Mbit/s, where the south unit gets 114.7 Mbit/s at −47 dBm / SNR 57. Good RSSI with a collapsed rate is the signature of reflections in a metal-and-dense-concrete building. Channel tuning cannot fix this. It needs an AP closer with wired backhaul, a directional antenna to reject off-axis reflections, or moving either endpoint a metre or two to change the reflection geometry. More TX power and wider channels both make multipath worse.

    It sits at

  • New logger: /home/levander/net-monitor/wifi_survey.pywifi_survey.csv, cron daily 04:00 (a scan briefly interrupts clients, hence once a day, at night). It only measures — it never changes a wireless setting. Runs from the mainframe over SSH to the router at 192.168.1.1 (the LAN address — the tailnet ACL blocks port 22 from mainframe → router).

    Two caveats for anyone reading wifi_survey.csv

    • The in-use row’s busy_pct is a lifetime average over monotonic counters, so day-to-day it barely moves. Real daily congestion needs deltas between consecutive rows, handling active_time_ms decreasing on interface restart.
    • Scanned (non-in-use) rows are a single ~150 ms sample and very noisy — ch2 read 98.67% then 78.0% nineteen seconds apart. active_time_ms is in the CSV precisely so high- and low-confidence rows can be told apart.
  • Parser trap worth recording: a naive Channel:\s+(\d+) regex on iwinfo scan also matches Primary Channel: inside HT/VHT blocks and double-counts every AP. Anchor on ^Mode: and key by frequency.
  • Two states only: Bypass (WiFi off, pure L2 bridge) or not bypass (it is the main router). There is no AP/bridge mode that joins a third-party network. Starlink mesh nodes have no Ethernet backhaul and pair only to a Starlink router as main router. Reversing bypass requires a factory reset. One spare unit reports “Not on account” and is unmanageable.

    Going non-bypass would be actively harmful telep-router and takes out br-cams (the 192.168.30.0/24 camera VLAN + the telep-cc SSID), dnsmasq leases, and the iwinfo assoclist that BOTH the intruder alarm and tv-presence depend on.

    It displaces

  • The wanted “one big seamless WiFi” is NOT mesh — it is same SSID + wired APs on different channels + 802.11r/k/v fast roaming. That recommendation stands on its own and is unaffected by the extender question below.

TL-WA850RE — RESOLVED 2026-09-05: it is already a WIRED AP, not a repeater

The “it is a repeater” claim in this session was wrong. It was inferred from the model name, never measured, and it propagated into 2026-09-04-starlink-wan-migration-dish-telemetry, LOG.md and TOPICS.md before being corrected in all four places. [[telep-router#tp-link-tl-wa850re-added-as-a-wired-ap-ssid-bandi|TP-Link TL-WA850RE added as a wired AP (SSID bandi)]] is authoritative: the unit was reconfigured repeater → Access Point and wired into br-lan, broadcasting SSID bandi. eth/mgmt MAC ac:84:c6:1b:04:30, WiFi BSSID ac:84:c6:1b:04:31, static reservation .101. That note already characterises it as a “coverage AP, not a fast-lane AP”. bandi-vip is NOT this device. That AP’s BSSID is 5E:6D:F7:27:10:E3 — locally-administered, nothing like ac:84:c6:1b:04:31. So the “extender may still be stuck on ch11” theory is also void; bandi-vip is an unidentified third-party AP. The real ~50 Mbps ceiling is structural: a 100 Mb Fast-Ethernet uplink port + a single-band 2.4 GHz N300 radio, on a channel that was 93% busy. Nothing to do with repeating. “Replace the repeater with a wired AP” is a no-op — that conversion already happened; the actual upgrade is a dual-band AP on a gigabit port. ⚠️ Live re-verification was not possible: 192.168.1.101 ARP FAILED after 6 probes (device offline), absent from bridge fdb show br br-lan, and not associated on any of the three APs. The conclusion above rests on the telep-router record plus the BSSID mismatch, not on a live probe.

7. Smaller items

  • Frigate config tuned 2026-09-02 (2026-09-02-frigate-resource-tuning): birdseye mode: continuousobjects, and 3840×1080 → 1920×1080; face_recognition disabled (library was empty, /api/faces returned {}, 0 of 500 events had a sub_label). VRAM 2148 → 1804 MiB, RAM 3.887 → 3.164 GiB. ⚠️ CPU improvement was NOT proven — samples were bursty and the “before” was measured during post-boot startup.
  • ⚠️ ps -eo pcpu reports a lifetime average, not instantaneous CPU — worthless on a freshly booted box. Use repeated docker stats --no-stream or top -bn2.
  • Camera recordings exported for 2026-09-04 07:23–07:25 local/home/levander/freecad/exports/cam-2026-09-04_0723/ (4 files, ~19 MB each), browsable at https://cad.taild4189d.ts.net:8080/cam-2026-09-04_0723/.

    Frigate stores recordings in UTC 07:23 lives in the 05/ hour directory. Frigate's own export API was abandoned — three of four exports stuck as orphaned in_progress rows with no worker process (plus an older lastnight_telep_cam3 stuck from a previous session). The files were built directly with ffmpeg -f concat -c copy instead.

    Local

  • reMarkable Paper Pure evaluation written — device not purchased. See 2026-09-03-remarkable-paper-pure-claude-integration.
  • tailcat client half installed on the mainframe (v0.4.0, client nodekey generated). /home/levander/tailcat-mac-setup.sh is staged for the offline company Mac ceges-mac (100.69.95.112) but has never been run.
  • Incidental: /home/levander/tapo-ctl/ already contains a venv with python-kasa 0.10.2 — the same version /opt/tapo-bridge/venv installed separately. Duplicate venvs, and TP-Link credentials likely already on the box.

Where we paused / open items

Not done yet

  1. 🔴 The TV is physically powered off and unreachable — WiFi radio off, no ARP, no association, tv on times out on WoL. It needs a physical power button press. And Auto Power Off has STILL never been disabled (All Settings → General → Timers → Auto Power Off → Off) — this is the third time the TV has gone dark. It cannot be set over the network: the settings surface 404s with aiowebostv’s permission manifest, and obtaining a wider manifest means re-pairing, which means a trip to the TV anyway. So: press the button, then change the setting while you’re standing there.
  2. 🔴 Install the printed Starlink mount. Obstruction was 3.3% and rising, prolonged-outage estimate down to 45 min. The bracket is printed (PC-FR). The CSV has the before-baseline — re-check fraction_obstructed after install.
  3. 🔴 North camera (192.168.30.139) is marginalmultipath, 8.6–11 Mbit/s at good RSSI. Needs a closer wired AP, a directional antenna, or repositioning. Do not attempt to fix it with channel/TX-power tuning.
  4. Rotate the bambuddy API key (leaked in a transcript) and decide what to do about bambuddy’s unauthenticated API / empty users table.
  5. Identify the 2.4 GHz upper-band interferer — the daily wifi survey will fingerprint it by time-of-day pattern.
  6. Frigate: unexplained external docker restart twice on 2026-09-04 (RestartCount=0, healthy, StartedAt moved) — source unknown. Also clear the orphaned in_progress export rows.
  7. bambuddy: allow_skip_parts still false; plate detection uncalibrated (require_plate_clear: false, no reference images). Calibration is a REMOTE call once the bed is empty with the chamber light on; brim needs a per-material process preset built in Bambu Studio.
  8. print-guard’s guard path and tv-presence’s arrival path have never fired on real data. Both are unproven in production. ⚠️ Clarified 2026-09-07 — do not read this as broken alerting. It refers to their action paths only. Neither is an alert producer: print-guard talks only to the bambuddy API on http://127.0.0.2:8000, and tv-presence has no HTTP client at all — its “arrival path” powers the TV on and messages nobody. So neither is a Telegram → Matrix migration target. Source-verified in 2026-09-07-alert-source-inventory.
  9. Route persistence on the router is unverified until the next reboot — the uci stanza was committed but never applied by netifd; the hotplug script IS verified.
  10. VERIFY (not do) the TL-WA850RE when it is back online — it was unreachable this session (ARP failed, absent from the br-lan fdb, not associated on any AP), so its mode was never confirmed. Check its actual mode and channel[[telep-router]] records it as an already-converted wired AP on br-lan (SSID bandi), which is better sourced than this session’s model-name assumption. If it is still on ch11, move it off — that is where the 93%-busy interference is, and the router has already escaped to ch1. Do not action a repeater→AP conversion until the mode is actually established.
  11. Tapo plug is monitoring-only — temperature-gated auto_off (70 °C) and power_alert_high are configured but not enabled.
  12. ceges-mac tailcat setup script never run.

Key endpoints, paths & gotchas

Endpoints (tailnet-only):

ServiceURL
Frigatehttps://telep-mainframe.taild4189d.ts.net
Homepage dashboardhttps://telep-mainframe.taild4189d.ts.net:8450
tv-http (TV control)https://telep-mainframe.taild4189d.ts.net:8451
bambuddy (UI + /mcp)https://bambuddy.taild4189d.ts.net
CAD / camera exportshttps://cad.taild4189d.ts.net:8080

Local bind convention: services bind 127.0.0.2:<port> and are published only via tailscale serve. See tailnet-service-exposure-convention. Verified 127.0.0.2 port map (ss -ltnp, 2026-09-05):

8000   bambuddy (uvicorn, UNAUTHENTICATED)
8091   bambuddy-mcp-bridge (mcp-proxy)
8102   tv-http            -> tailnet :8451
8117   tapo-bridge        (Tapo P115 -> bambuddy rest plug)
3001   bambu-studio-api   (slicer sidecar)
3010   homepage           -> tailnet :8450
11000  nextcloud   11001  tsauth-proxy   11002  onlyoffice

All four custom services confirmed active on 2026-09-05: tv-http, tapo-bridge, print-guard, tv-presence.

Paths:

  • /opt/tv-control/{tv,tv-http,presence,client-key.json,venv} · /run/tv-control.lock
  • /opt/print-guard/print-guard · /opt/tapo-bridge/{tapo-bridge,state.json,venv} · /etc/tapo-bridge.env
  • /home/levander/net-monitor/{starlink_status.py,starlink.csv,wifi_survey.py,wifi_survey.csv,ne200_signal.py,rflog.csv}
  • /home/levander/tapo-ctl/{reboot-cams-onvif.py,set-cam-time.py} · /home/levander/tailcat-mac-setup.sh
  • Router: /etc/hotplug.d/iface/99-starlink-dish · /etc/config/wireless.bak-1788552343

Reusable gotchas:

  • 🔴 NEVER run /etc/init.d/network reload|restart on telep-router — it drops the site and the tailnet SSH used to manage it. Live-then-persist instead: ip route add, then write uci without reloading.
  • 🔴 busybox lacks timeout, nc -z and ping -M lie. This produced FOUR false diagnoses in one session on the router. Test from the mainframe, not from busybox.
  • 🔴 Frigate stores recordings in UTC — local 07:23 is in the 05/ hour directory.
  • HDMI numbers are unrelated: xrandr HDMI-1 ≠ the TV’s HDMI 1. Trust WebOsTvState.inputs.connected.
  • grpcurl needs -emit-defaults or proto3 drops zero-values exactly when healthy.
  • aiowebostv caches power state — both power_off() and any is_on short-circuit are unreliable; read live and verify the result.
  • ps -eo pcpu is a lifetime average, not instantaneous CPU.
  • iwinfo scan parsing: anchor on ^Mode:, not Channel: (which also matches Primary Channel: in HT/VHT blocks and double-counts APs).
  • Good RSSI + collapsed PHY rate = multipath, not congestion. More power and wider channels make it worse.

Related notes: 2026-09-02-session-handover (predecessor) · 2026-09-02-lg-tv-network-control-presence · 2026-09-02-tv-presence-wifi-union-daemon · 2026-09-02-frigate-resource-tuning · 2026-09-03-bambuddy-preheat-chamber-target-bug-and-clog-rca · 2026-09-03-remarkable-paper-pure-claude-integration · 2026-09-04-starlink-wan-migration-dish-telemetry · telep-mainframe · telep-router · 2026-08-31-episodic-wan-degradation · tailnet-service-exposure-convention

Not yet in its own note

Section 5 (the WiFi channel move, the ch11→ch1 change, wifi_survey.py, and the north-camera multipath finding) has no dedicated vault note yet — this handover is currently the only record. Worth promoting to a standalone note.


Continuation prompt (copy-paste to resume)

Pick up the 2026-09-05 homelab session (read projects/homelab/2026-09-05-session-handover.md, and use the historian for deeper context). Priorities: (1) the TV is physically powered off and unreachable — press the physical power button, then disable Auto Power Off while standing at it (All Settings → General → Timers → Auto Power Off → Off; it cannot be done over the network, the settings surface 404s with the current permission manifest); (2) install the printed PC-FR Starlink mount and check whether fraction_obstructed drops from 3.3% in ~/net-monitor/starlink.csv (the CSV has the before-baseline); (3) address the north camera’s multipath (192.168.30.139, 8.6–11 Mbit/s at −63 dBm / SNR 40) — it needs a closer wired AP, a directional antenna, or repositioning; channel/TX-power tuning will NOT fix it. Also queued: rotate the leaked bambuddy API key and decide on its unauthenticated API. Everything else — TV control CLI/HTTP + presence daemon, the bambuddy chamber-target fix, print-guard, backups, humidity thresholds, the Tapo bridge, the Starlink WAN migration and telemetry logger, the 2.4 GHz ch11→ch1 move and the daily wifi survey — is done and verified. Never run /etc/init.d/network reload|restart on telep-router.