snmp: Better sensor support to resolve previous issues (#113624)

Co-authored-by: Christian Kühnel <christian.kuehnel@gmail.com>
Co-authored-by: jan iversen <jancasacondor@gmail.com>
This commit is contained in:
Lex Li
2024-03-16 17:56:21 -04:00
committed by GitHub
parent 86ccb99f4c
commit 2bc4a5067d
4 changed files with 201 additions and 8 deletions

View File

@@ -4,7 +4,9 @@ from __future__ import annotations
from datetime import timedelta
import logging
from struct import unpack
from pyasn1.codec.ber import decoder
from pysnmp.error import PySnmpError
import pysnmp.hlapi.asyncio as hlapi
from pysnmp.hlapi.asyncio import (
@@ -18,6 +20,8 @@ from pysnmp.hlapi.asyncio import (
UsmUserData,
getCmd,
)
from pysnmp.proto.rfc1902 import Opaque
from pysnmp.proto.rfc1905 import NoSuchObject
import voluptuous as vol
from homeassistant.components.sensor import CONF_STATE_CLASS, PLATFORM_SCHEMA
@@ -165,7 +169,10 @@ async def async_setup_platform(
errindication, _, _, _ = get_result
if errindication and not accept_errors:
_LOGGER.error("Please check the details in the configuration file")
_LOGGER.error(
"Please check the details in the configuration file: %s",
errindication,
)
return
name = config.get(CONF_NAME, Template(DEFAULT_NAME, hass))
@@ -248,10 +255,44 @@ class SnmpData:
_LOGGER.error(
"SNMP error: %s at %s",
errstatus.prettyPrint(),
errindex and restable[-1][int(errindex) - 1] or "?",
restable[-1][int(errindex) - 1] if errindex else "?",
)
elif (errindication or errstatus) and self._accept_errors:
self.value = self._default_value
else:
for resrow in restable:
self.value = resrow[-1].prettyPrint()
self.value = self._decode_value(resrow[-1])
def _decode_value(self, value):
"""Decode the different results we could get into strings."""
_LOGGER.debug(
"SNMP OID %s received type=%s and data %s",
self._baseoid,
type(value),
bytes(value),
)
if isinstance(value, NoSuchObject):
_LOGGER.error(
"SNMP error for OID %s: No Such Object currently exists at this OID",
self._baseoid,
)
return self._default_value
if isinstance(value, Opaque):
# Float data type is not supported by the pyasn1 library,
# so we need to decode this type ourselves based on:
# https://tools.ietf.org/html/draft-perkins-opaque-01
if bytes(value).startswith(b"\x9f\x78"):
return str(unpack("!f", bytes(value)[3:])[0])
# Otherwise Opaque types should be asn1 encoded
try:
decoded_value, _ = decoder.decode(bytes(value))
return str(decoded_value)
# pylint: disable=broad-except
except Exception as decode_exception:
_LOGGER.error(
"SNMP error in decoding opaque type: %s", decode_exception
)
return self._default_value
return str(value)