From 5309006494678671141b958794eb9b16e5c68453 Mon Sep 17 00:00:00 2001
From: Jack <dj.jack90@gmail.com>
Date: Mon, 14 Aug 2017 10:31:06 -0400
Subject: [PATCH] Added continue-on-errors, added value template (#8971)

* Added continue-on-errors, added value template

* Refactored long lines

* Fixed whitespace issues
---
 homeassistant/components/sensor/snmp.py | 49 ++++++++++++++++++++-----
 1 file changed, 39 insertions(+), 10 deletions(-)

diff --git a/homeassistant/components/sensor/snmp.py b/homeassistant/components/sensor/snmp.py
index 361ce551426..aeb4587f3df 100644
--- a/homeassistant/components/sensor/snmp.py
+++ b/homeassistant/components/sensor/snmp.py
@@ -13,7 +13,8 @@ import homeassistant.helpers.config_validation as cv
 from homeassistant.components.sensor import PLATFORM_SCHEMA
 from homeassistant.helpers.entity import Entity
 from homeassistant.const import (
-    CONF_HOST, CONF_NAME, CONF_PORT, CONF_UNIT_OF_MEASUREMENT)
+    CONF_HOST, CONF_NAME, CONF_PORT, CONF_UNIT_OF_MEASUREMENT, STATE_UNKNOWN,
+    CONF_VALUE_TEMPLATE)
 
 REQUIREMENTS = ['pysnmp==4.3.9']
 
@@ -22,6 +23,8 @@ _LOGGER = logging.getLogger(__name__)
 CONF_BASEOID = 'baseoid'
 CONF_COMMUNITY = 'community'
 CONF_VERSION = 'version'
+CONF_ACCEPT_ERRORS = 'accept_errors'
+CONF_DEFAULT_VALUE = 'default_value'
 
 DEFAULT_COMMUNITY = 'public'
 DEFAULT_HOST = 'localhost'
@@ -45,6 +48,9 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
     vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
     vol.Optional(CONF_VERSION, default=DEFAULT_VERSION):
         vol.In(SNMP_VERSIONS),
+    vol.Optional(CONF_ACCEPT_ERRORS, default=False): cv.boolean,
+    vol.Optional(CONF_DEFAULT_VALUE): cv.string,
+    vol.Optional(CONF_VALUE_TEMPLATE): cv.template
 })
 
 
@@ -61,6 +67,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
     baseoid = config.get(CONF_BASEOID)
     unit = config.get(CONF_UNIT_OF_MEASUREMENT)
     version = config.get(CONF_VERSION)
+    accept_errors = config.get(CONF_ACCEPT_ERRORS)
+    default_value = config.get(CONF_DEFAULT_VALUE)
+    value_template = config.get(CONF_VALUE_TEMPLATE)
+
+    if value_template is not None:
+        value_template.hass = hass
 
     errindication, _, _, _ = next(
         getCmd(SnmpEngine(),
@@ -69,23 +81,27 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
                ContextData(),
                ObjectType(ObjectIdentity(baseoid))))
 
-    if errindication:
+    if errindication and not accept_errors:
         _LOGGER.error("Please check the details in the configuration file")
         return False
     else:
-        data = SnmpData(host, port, community, baseoid, version)
-        add_devices([SnmpSensor(data, name, unit)], True)
+        data = SnmpData(
+            host, port, community, baseoid, version, accept_errors,
+            default_value)
+        add_devices([SnmpSensor(data, name, unit, value_template)], True)
 
 
 class SnmpSensor(Entity):
     """Representation of a SNMP sensor."""
 
-    def __init__(self, data, name, unit_of_measurement):
+    def __init__(self, data, name, unit_of_measurement,
+                 value_template):
         """Initialize the sensor."""
         self.data = data
         self._name = name
         self._state = None
         self._unit_of_measurement = unit_of_measurement
+        self._value_template = value_template
 
     @property
     def name(self):
@@ -105,19 +121,30 @@ class SnmpSensor(Entity):
     def update(self):
         """Get the latest data and updates the states."""
         self.data.update()
-        self._state = self.data.value
+        value = self.data.value
+
+        if value is None:
+            value = STATE_UNKNOWN
+        elif self._value_template is not None:
+            value = self._value_template.render_with_possible_json_value(
+                value, STATE_UNKNOWN)
+
+        self._state = value
 
 
 class SnmpData(object):
     """Get the latest data and update the states."""
 
-    def __init__(self, host, port, community, baseoid, version):
+    def __init__(self, host, port, community, baseoid, version, accept_errors,
+                 default_value):
         """Initialize the data object."""
         self._host = host
         self._port = port
         self._community = community
         self._baseoid = baseoid
         self._version = SNMP_VERSIONS[version]
+        self._accept_errors = accept_errors
+        self._default_value = default_value
         self.value = None
 
     def update(self):
@@ -133,11 +160,13 @@ class SnmpData(object):
                    ObjectType(ObjectIdentity(self._baseoid)))
             )
 
-        if errindication:
+        if errindication and not self._accept_errors:
             _LOGGER.error("SNMP error: %s", errindication)
-        elif errstatus:
+        elif errstatus and not self._accept_errors:
             _LOGGER.error("SNMP error: %s at %s", errstatus.prettyPrint(),
                           errindex and restable[-1][int(errindex) - 1] or '?')
+        elif (errindication or errstatus) and self._accept_errors:
+            self.value = self._default_value
         else:
             for resrow in restable:
-                self.value = resrow[-1]
+                self.value = str(resrow[-1])