""" Support for monitoring OctoPrint 3D printers. For more details about this component, please refer to the documentation at https://home-assistant.io/components/octoprint/ """ import logging import time import requests from homeassistant.const import CONF_API_KEY, CONF_HOST from homeassistant.helpers import validate_config, discovery DOMAIN = "octoprint" OCTOPRINT = None _LOGGER = logging.getLogger(__name__) DISCOVER_SENSORS = 'octoprint.sensors' DISCOVER_BINARY_SENSORS = 'octoprint.binary_sensor' def setup(hass, config): """Set up OctoPrint API.""" if not validate_config(config, {DOMAIN: [CONF_API_KEY], DOMAIN: [CONF_HOST]}, _LOGGER): return False base_url = config[DOMAIN][CONF_HOST] + "/api/" api_key = config[DOMAIN][CONF_API_KEY] global OCTOPRINT try: OCTOPRINT = OctoPrintAPI(base_url, api_key) OCTOPRINT.get("printer") OCTOPRINT.get("job") except requests.exceptions.RequestException as conn_err: _LOGGER.error("Error setting up OctoPrint API: %r", conn_err) return False for component, discovery_service in ( ('sensor', DISCOVER_SENSORS), ('binary_sensor', DISCOVER_BINARY_SENSORS)): discovery.discover(hass, discovery_service, component=component, hass_config=config) return True class OctoPrintAPI(object): """Simple JSON wrapper for OctoPrint's API.""" def __init__(self, api_url, key): """Initialize OctoPrint API and set headers needed later.""" self.api_url = api_url self.headers = {'content-type': 'application/json', 'X-Api-Key': key} self.printer_last_reading = [{}, None] self.job_last_reading = [{}, None] def get_tools(self): """Get the dynamic list of tools that temperature is monitored on.""" tools = self.printer_last_reading[0]['temperature'] return tools.keys() def get(self, endpoint): """Send a get request, and return the response as a dict.""" now = time.time() if endpoint == "job": last_time = self.job_last_reading[1] if last_time is not None: if now - last_time < 30.0: return self.job_last_reading[0] elif endpoint == "printer": last_time = self.printer_last_reading[1] 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) response.raise_for_status() if endpoint == "job": self.job_last_reading[0] = response.json() self.job_last_reading[1] = time.time() elif endpoint == "printer": self.printer_last_reading[0] = response.json() self.printer_last_reading[1] = time.time() return response.json() except requests.exceptions.ConnectionError as conn_exc: _LOGGER.error("Failed to update OctoPrint status. Error: %s", conn_exc) raise def update(self, sensor_type, end_point, group, tool=None): """Return the value for sensor_type from the provided endpoint.""" try: return get_value_from_json(self.get(end_point), sensor_type, group, tool) except requests.exceptions.ConnectionError: raise # pylint: disable=unused-variable def get_value_from_json(json_dict, sensor_type, group, tool): """Return the value for sensor_type from the JSON.""" if group in json_dict: if sensor_type in json_dict[group]: if sensor_type == "target" and json_dict[sensor_type] is None: return 0 else: return json_dict[group][sensor_type] elif tool is not None: if sensor_type in json_dict[group][tool]: return json_dict[group][tool][sensor_type]