What’s in it:
Xvf3800RuntimeConfig – simple dataclass for VID/PID, timeouts, etc.
Xvf3800Controller(EventHandler) – structured very similar to your other controllers:
On init:
Checks enabled and hid availability.
Uses hid.enumerate() to find the XVF3800 by VID/PID.
Opens the first HID interface (non-blocking).
Starts a background thread _poll_loop().
_poll_loop():
Reads HID reports (read(64, timeout_ms=...)).
For each non-empty report:
Calls _parse_hw_mute_from_report(report) → Optional[bool].
If it returns True/False and the value changed, it publishes:
self.loop.call_soon_threadsafe(
self.event_bus.publish,
"set_mic_mute",
{"state": new_state},
)
Exits cleanly when self._running is false or state.shutdown is true.
@subscribe def set_mic_mute(self, data: dict):
Listens to the same set_mic_mute event MQTT and ButtonController use.
Updates _last_hw_mute_state.
Calls _send_hw_mute_state(desired) to push mute+LED down to hardware.
Placeholders:
def _parse_hw_mute_from_report(self, report: bytes) -> Optional[bool]:
# TODO: implement once we know the report layout
_LOGGER.debug("XVF3800 HID report (len=%d): %s", len(report), report.hex())
return None
def _send_hw_mute_state(self, muted: bool) -> None:
# TODO: implement once we know output report format
_LOGGER.debug("(stub) Would send HID command to set XVF3800 mute=%s", muted)