vibescoder

Friday Fixes: Battling Zigbee Dongles and Muse Confusion

·6 min read

I spent most of Tuesday chasing a Sonoff Zigbee dongle that vanished from Home Assistant. The serial device was just... gone. The logs showed nothing. I checked the Proxmox USB passthrough config and it looked correct. It was pointing at the right port. But nothing.

That was the first of four configuration drifts I found this week. None of them were broken in the obvious way. They were all quietly wrong and part of the joy of maintaining self-hosted infrastructure.

The Zigbee dongle was passed through to a port that no longer had it

The host has two Sonoff USB sticks, Zigbee and Z-Wave. They share the same vendor and product ID, so you cannot tell them apart by ID. You have to trust the port.

The VM config passed them by physical port. A few days ago I moved the Zigbee stick to a different USB port when I was relocating the ThinkCentre from one precariously perched shelf to another. That disconnected and re-enumerated the dongle on a different port. The VM config still pointed at the old port. That’s why Home Assistant saw nothing.

[!NOTE] Physical-port passthrough is brittle. When a stick moves, the config does not move with it. Check dmesg for where the device grew, not where you think it is.

I updated the VM config to the port where the stick actually was and hot-attached it via QEMU monitor:

# QEMU monitor hot attach, no guest reboot
(qemu) host_device_add usb,id=usb1
(qemu) device_add usb-host,hostbus=1,hostaddr=3,id=usb1

The dongle came back as a serial device. The same pattern shows up in the Smart Home Dumb Luck series where USB hardware moves silently between Proxmox hosts.

I’ll probably need a script or some kind of janitor service to catch drift like this.

Two Zigbee stacks were fighting for the same dongle

With the passthrough fixed, Zigbee2MQTT still would not start. It logged Cannot lock port and died with a fatal Ember error.

SymptomWhat it looked likeWhat it was
Cannot lock portSerial permissionCoordinator contention
Ember/EZSP host errorFirmwareZHA holding the device

Home Assistant’s ZHA integration had a config entry for the same adapter. Both stacks wanted the same serial device.

# ZHA config entry removed to free the dongle
homeassistant:
  config_entries:
    - domain: zha
      entry_id: <the one pointing at /dev/ttyACM0>

I removed the ZHA entry. Zigbee2MQTT connected cleanly and re-formed the network from backup. One coordinator, one stack.

The permit join window made this worse. I opened permit join, then powered the light before the window was open. The window auto-closes. Open the window, then put the device into pairing mode immediately.

Wake-on-LAN was a power state problem, not a packet problem

The Home Assistant wake_<computer> script would run and complete without error. But alas, the machine remained sleeping. I captured the magic packet on the LAN with tcpdump. It was valid. The MAC was right. The machine sat on the same flat 192.168.0.0/24 broadcast domain as Home Assistant through an unmanaged switch.

The NIC had link. The LED blinked. Wake-on-LAN was enabled in BIOS. And all of the NIC settings were confirmed in Windows Device Manager. The problem was Windows Fast Startup.

[!IMPORTANT] Fast Startup is a hybrid hibernation to S4, not a full S5 power off. Windows disarms the NIC for Wake-on-LAN in that state.

With Fast Startup enabled, a normal Windows shutdown is S4, not S5. Windows disarms the NIC for Wake-on-LAN when it reports an S5 target power state. The NIC looks ready. It is not armed.

# Disable Fast Startup via Power Options
powercfg -h off

Disabling Fast Startup turned shutdown into a real S5. The same script that had been failing all week woke the machine on the first try. I covered Wake-on-LAN plumbing in Teaching the Homelab Where It Lives and the original QoL setup.

Muse Glimmer was compacting because of hidden GitHub tool schemas

The Muse Glimmer 30B model on the RTX 5090 is the dedicated writing model. In fact, it wrote this blog (with human editing, of course)! It has a 131K context window. It was compacting after a few commands. The context limit was correct. The real conversation was only about 3.3K tokens.

Turn one was about 79K tokens. Forty-five bmcp_github_* GitHub bridge tool schemas were injected into every request. That was about 33K tokens.

MetricBeforeAfter
Turn 1 total tokens~79K~20.5K
Max session tokens~?~57K
Messages before compaction~few170
Compactionsyes0

The Coder server injects those schemas server-globally via CODER_EXTERNAL_AUTH_0_MCP_URL. They are not controllable per template. The MCP servers in ~/.mcp.json are not injected into Coder Agents chat at all. Only OAuth-wired MCP servers via AI Bridge get injected.

I commented out the MCP URL in the Coder env file and restarted the service. Turn one dropped from 79K to 20.5K. A real session ran 170 messages to about 57K tokens with zero compactions.

I also bumped the model’s compression_threshold from 70 to 90. The lean muse-glimmer template saved about 71 tokens. The real win was server side.

This builds on The Model Writing This Blog Wasn’t Who I Thought and the router work in Homelab Router Part 5: Seven Models, One Writing Job.

Git auth was doing manual work Coder already handles

The workspace templates wired git and gh CLI auth with a manual git credential.helper that shelled out to coder external-auth access-token github on every invocation, plus a ~/.profile GITHUB_TOKEN export. It worked. It was not the documented pattern.

Coder’s built-in GIT_ASKPASS handles git over HTTPS automatically and auto-refreshes.

# Coder docs aligned pattern
data "coder_external_auth" "github" {}
 
resource "coder_agent" "main" {
  env = {
    GITHUB_TOKEN = data.coder_external_auth.github.token
    GH_TOKEN     = data.coder_external_auth.github.token
  }
}

I removed the helper and verified git ls-remote on a private repo still worked. For gh, the documented approach is to inject tokens declaratively via coder_agent.env. The token is a build-time snapshot that does not auto-refresh. Rebuild the workspace to pick up a refreshed token.

Both templates moved to the docs-aligned pattern in PR #16.

By the Numbers

  • 1 Zigbee dongle recovered via QEMU hot-attach, no guest reboot
  • 1 ZHA config entry removed to stop coordinator contention
  • 1 Windows Fast Startup toggle that fixed Wake-on-LAN
  • 45 bmcp_github_* schemas costing ~33K tokens per request
  • 79K → 20.5K turn one token drop after unwiring GitHub MCP
  • 170 messages to ~57K tokens with 0 compactions on Muse Glimmer
  • 2 templates moved to Coder docs-aligned git auth

Comments