Skip to content

Fix Home Assistant SNMP blocking-call warnings - #80

Merged
partach merged 1 commit into
mainfrom
claude/snmp-errors-protocol-wizard-cq3n2n
Sep 5, 2026
Merged

Fix Home Assistant SNMP blocking-call warnings#80
partach merged 1 commit into
mainfrom
claude/snmp-errors-protocol-wizard-cq3n2n

Conversation

@partach

@partach partach commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Ports the SNMP engine warm-up technique from switch_port_card_pro (its fix-ha-2026-log-errors branch) into Protocol Wizard.

Problem

PySNMP loads bundled MIB modules from disk the first time an engine encodes a request and decodes the response. SNMPClient._ensure_engine created SnmpEngine() lazily on the event loop, so Home Assistant flagged those file reads as blocking calls inside the event loop.

Measured against pysnmp 7.1.29, counting open() calls during a single GET:

file reads on the event loop
Before 3PYSNMP-SOURCE-MIB.py, PYSNMP-MIB.py, SNMPv2-TM.py
After 0

Changes

custom_components/protocol_wizard/protocols/snmp/client.py

  • _create_engine() builds the engine and preloads its MIB modules — the well-known set plus every file-backed module PySNMP ships, discovered by _iter_pysnmp_mib_module_names(). Warm-up loads 34 modules vs. 13 bare, in ~0.17s.
  • _async_get_shared_engine() runs that in an executor and caches the result, so the engine is never built on the loop.
  • The warmed engine is shared process-wide rather than one per client. Protocol Wizard previously created an engine per SNMPClient; preloading all bundled MIBs per config entry would repeat expensive work. Each client still owns its own UdpTransportTarget.
  • disconnect() no longer calls close_dispatcher(). This follows from sharing — __init__.py calls disconnect() on config-entry unload, which would otherwise tear the engine out from under every other SNMP entry. The client still drops its transport and connection state.

__init__.py / config_flow.py

  • hass is threaded through to the client from both construction sites so the engine is built with async_add_executor_job; a loop executor is used as a fallback when no hass is available.

The other two parts of the upstream fix were checked and did not apply here: Protocol Wizard already calls async_config_entry_first_refresh() before async_forward_entry_setups in all three setup paths, and entity_base.py has no try/except around available.

Tests

New tests/test_snmp_client_engine.py (9 tests) covers MIB preloading, module discovery from file-backed sources, executor use, the loop-executor fallback, concurrent callers sharing one engine, and disconnect() leaving the shared engine open for other clients.

Full suite: 219 passed. ruff check . clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YcBZKHHnbLVbwmB95WS2uA


Generated by Claude Code

PySNMP loads bundled MIB modules from disk the first time an engine
encodes a request and decodes the response. Because the engine was
created lazily on the event loop, Home Assistant flagged those file
reads as blocking calls inside the event loop.

Port the warm-up technique used in switch_port_card_pro:

- Create the engine in an executor and preload the bundled MIB modules
  (both the well-known set and every file-backed module PySNMP ships)
  while still off the event loop. A GET now performs zero file reads on
  the loop, down from three.
- Share one warmed engine across all SNMP clients in the process, so the
  preload cost is paid once rather than per config entry.
- Stop closing the dispatcher in disconnect(): the engine is shared, so
  unloading one config entry must not tear it down for the others. Each
  client still drops its own transport and connection state.

hass is threaded through to the client from both construction sites so
the engine is built with async_add_executor_job; a loop executor is used
as a fallback when no hass is available.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YcBZKHHnbLVbwmB95WS2uA
@partach
partach merged commit b69ed6e into main Sep 5, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants