From 652c006cbce4302be0196a726ad988c29dbb9675 Mon Sep 17 00:00:00 2001 From: William Scanlon Date: Fri, 7 Jul 2017 01:39:11 -0400 Subject: [PATCH] Prevent errors on Octoprint sensors and binary_sensors when Octoprint and/or Printer are off (#8343) * Added platformnotready * Only log the first failure if octoprint/printer isn't up Remove blank line --- .../components/binary_sensor/octoprint.py | 10 ++-- homeassistant/components/octoprint.py | 53 +++++++++++++++---- homeassistant/components/sensor/octoprint.py | 15 ++---- 3 files changed, 50 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/binary_sensor/octoprint.py b/homeassistant/components/binary_sensor/octoprint.py index 6e278ccfccf..f4e4e04717d 100644 --- a/homeassistant/components/binary_sensor/octoprint.py +++ b/homeassistant/components/binary_sensor/octoprint.py @@ -12,13 +12,12 @@ import voluptuous as vol from homeassistant.const import CONF_NAME, CONF_MONITORED_CONDITIONS from homeassistant.components.binary_sensor import ( BinarySensorDevice, PLATFORM_SCHEMA) -from homeassistant.loader import get_component import homeassistant.helpers.config_validation as cv _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['octoprint'] - +DOMAIN = "octoprint" DEFAULT_NAME = 'OctoPrint' SENSOR_TYPES = { @@ -37,7 +36,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ # pylint: disable=unused-argument def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the available OctoPrint binary sensors.""" - octoprint = get_component('octoprint') + octoprint_api = hass.data[DOMAIN]["api"] name = config.get(CONF_NAME) monitored_conditions = config.get( CONF_MONITORED_CONDITIONS, SENSOR_TYPES.keys()) @@ -45,7 +44,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): devices = [] for octo_type in monitored_conditions: new_sensor = OctoPrintBinarySensor( - octoprint.OCTOPRINT, octo_type, SENSOR_TYPES[octo_type][2], + octoprint_api, octo_type, SENSOR_TYPES[octo_type][2], name, SENSOR_TYPES[octo_type][3], SENSOR_TYPES[octo_type][0], SENSOR_TYPES[octo_type][1], 'flags') devices.append(new_sensor) @@ -98,6 +97,3 @@ class OctoPrintBinarySensor(BinarySensorDevice): except requests.exceptions.ConnectionError: # Error calling the api, already logged in api.update() return - - if self._state is None: - _LOGGER.warning("Unable to locate value for %s", self.sensor_type) diff --git a/homeassistant/components/octoprint.py b/homeassistant/components/octoprint.py index 0a099fc7349..204490ce36c 100644 --- a/homeassistant/components/octoprint.py +++ b/homeassistant/components/octoprint.py @@ -17,8 +17,6 @@ _LOGGER = logging.getLogger(__name__) DOMAIN = 'octoprint' -OCTOPRINT = None - CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ vol.Required(CONF_API_KEY): cv.string, @@ -32,14 +30,15 @@ def setup(hass, config): base_url = 'http://{}/api/'.format(config[DOMAIN][CONF_HOST]) api_key = config[DOMAIN][CONF_API_KEY] - global OCTOPRINT + hass.data[DOMAIN] = {"api": None} + try: - OCTOPRINT = OctoPrintAPI(base_url, api_key) - OCTOPRINT.get('printer') - OCTOPRINT.get('job') + octoprint_api = OctoPrintAPI(base_url, api_key) + hass.data[DOMAIN]["api"] = octoprint_api + octoprint_api.get('printer') + octoprint_api.get('job') except requests.exceptions.RequestException as conn_err: _LOGGER.error("Error setting up OctoPrint API: %r", conn_err) - return False return True @@ -54,6 +53,11 @@ class OctoPrintAPI(object): 'X-Api-Key': key} self.printer_last_reading = [{}, None] self.job_last_reading = [{}, None] + self.job_available = False + self.printer_available = False + self.available = False + self.printer_error_logged = False + self.job_error_logged = False def get_tools(self): """Get the dynamic list of tools that temperature is monitored on.""" @@ -62,6 +66,7 @@ class OctoPrintAPI(object): def get(self, endpoint): """Send a get request, and return the response as a dict.""" + # Only query the API at most every 30 seconds now = time.time() if endpoint == "job": last_time = self.job_last_reading[1] @@ -73,28 +78,52 @@ class OctoPrintAPI(object): if last_time is not None: if now - last_time < 30.0: return self.printer_last_reading[0] + url = self.api_url + endpoint try: response = requests.get( - url, headers=self.headers, timeout=30) + url, headers=self.headers, timeout=9) response.raise_for_status() if endpoint == "job": self.job_last_reading[0] = response.json() self.job_last_reading[1] = time.time() + self.job_available = True elif endpoint == "printer": self.printer_last_reading[0] = response.json() self.printer_last_reading[1] = time.time() + self.printer_available = True + self.available = self.printer_available and self.job_available + if self.available: + self.job_error_logged = False + self.printer_error_logged = False return response.json() except (requests.exceptions.ConnectionError, - requests.exceptions.HTTPError) as conn_exc: - _LOGGER.error("Failed to update OctoPrint status. Error: %s", - conn_exc) + requests.exceptions.HTTPError, + requests.exceptions.ReadTimeout) as conn_exc: + log_string = "Failed to update OctoPrint status. " + \ + " Error: %s" % (conn_exc) + # Only log the first failure + if endpoint == "job": + log_string = "Endpoint: job " + log_string + if not self.job_error_logged: + _LOGGER.error(log_string) + self.job_error_logged = True + self.job_available = False + elif endpoint == "printer": + log_string = "Endpoint: printer " + log_string + if not self.printer_error_logged: + _LOGGER.error(log_string) + self.printer_error_logged = True + self.printer_available = False + self.available = False + return None def update(self, sensor_type, end_point, group, tool=None): """Return the value for sensor_type from the provided endpoint.""" response = self.get(end_point) if response is not None: return get_value_from_json(response, sensor_type, group, tool) + return response # pylint: disable=unused-variable @@ -111,3 +140,5 @@ def get_value_from_json(json_dict, sensor_type, group, tool): elif tool is not None: if sensor_type in json_dict[group][tool]: return json_dict[group][tool][sensor_type] + + return None diff --git a/homeassistant/components/sensor/octoprint.py b/homeassistant/components/sensor/octoprint.py index b029451bd5e..150a97288cc 100644 --- a/homeassistant/components/sensor/octoprint.py +++ b/homeassistant/components/sensor/octoprint.py @@ -13,13 +13,12 @@ from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( TEMP_CELSIUS, CONF_NAME, CONF_MONITORED_CONDITIONS) from homeassistant.helpers.entity import Entity -from homeassistant.loader import get_component import homeassistant.helpers.config_validation as cv _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['octoprint'] - +DOMAIN = "octoprint" DEFAULT_NAME = 'OctoPrint' SENSOR_TYPES = { @@ -38,7 +37,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ # pylint: disable=unused-argument def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the available OctoPrint sensors.""" - octoprint = get_component('octoprint') + octoprint_api = hass.data[DOMAIN]["api"] name = config.get(CONF_NAME) monitored_conditions = config.get(CONF_MONITORED_CONDITIONS) @@ -46,16 +45,16 @@ def setup_platform(hass, config, add_devices, discovery_info=None): types = ["actual", "target"] for octo_type in monitored_conditions: if octo_type == "Temperatures": - for tool in octoprint.OCTOPRINT.get_tools(): + for tool in octoprint_api.get_tools(): for temp_type in types: new_sensor = OctoPrintSensor( - octoprint.OCTOPRINT, temp_type, temp_type, name, + octoprint_api, temp_type, temp_type, name, SENSOR_TYPES[octo_type][3], SENSOR_TYPES[octo_type][0], SENSOR_TYPES[octo_type][1], tool) devices.append(new_sensor) else: new_sensor = OctoPrintSensor( - octoprint.OCTOPRINT, octo_type, SENSOR_TYPES[octo_type][2], + octoprint_api, octo_type, SENSOR_TYPES[octo_type][2], name, SENSOR_TYPES[octo_type][3], SENSOR_TYPES[octo_type][0], SENSOR_TYPES[octo_type][1]) devices.append(new_sensor) @@ -116,7 +115,3 @@ class OctoPrintSensor(Entity): except requests.exceptions.ConnectionError: # Error calling the api, already logged in api.update() return - - if self._state is None and self.sensor_type != "completion": - _LOGGER.warning("Unable to locate value for %s", self.sensor_type) - return