mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Support for OctoPrint sensors (#1924)
This commit is contained in:
parent
1a59ba735f
commit
6d9254ce25
@ -32,6 +32,9 @@ omit =
|
|||||||
homeassistant/components/nest.py
|
homeassistant/components/nest.py
|
||||||
homeassistant/components/*/nest.py
|
homeassistant/components/*/nest.py
|
||||||
|
|
||||||
|
homeassistant/components/octoprint.py
|
||||||
|
homeassistant/components/*/octoprint.py
|
||||||
|
|
||||||
homeassistant/components/rpi_gpio.py
|
homeassistant/components/rpi_gpio.py
|
||||||
homeassistant/components/*/rpi_gpio.py
|
homeassistant/components/*/rpi_gpio.py
|
||||||
|
|
||||||
|
111
homeassistant/components/binary_sensor/octoprint.py
Normal file
111
homeassistant/components/binary_sensor/octoprint.py
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
"""
|
||||||
|
Support for monitoring OctoPrint binary sensors.
|
||||||
|
|
||||||
|
Uses OctoPrint REST JSON API to query for monitored variables.
|
||||||
|
For more details about this component, please refer to the documentation at
|
||||||
|
http://docs.octoprint.org/en/master/api/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from homeassistant.const import CONF_NAME, STATE_ON, STATE_OFF
|
||||||
|
from homeassistant.components.binary_sensor import BinarySensorDevice
|
||||||
|
from homeassistant.loader import get_component
|
||||||
|
|
||||||
|
DEPENDENCIES = ["octoprint"]
|
||||||
|
|
||||||
|
SENSOR_TYPES = {
|
||||||
|
# API Endpoint, Group, Key, unit
|
||||||
|
"Printing": ["printer", "state", "printing", None],
|
||||||
|
"Printing Error": ["printer", "state", "error", None]
|
||||||
|
}
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=unused-argument
|
||||||
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
|
"""Setup the available OctoPrint binary sensors."""
|
||||||
|
octoprint = get_component('octoprint')
|
||||||
|
name = config.get(CONF_NAME, "OctoPrint")
|
||||||
|
monitored_conditions = config.get("monitored_conditions",
|
||||||
|
SENSOR_TYPES.keys())
|
||||||
|
|
||||||
|
devices = []
|
||||||
|
for octo_type in monitored_conditions:
|
||||||
|
if octo_type in SENSOR_TYPES:
|
||||||
|
new_sensor = OctoPrintBinarySensor(octoprint.OCTOPRINT,
|
||||||
|
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)
|
||||||
|
else:
|
||||||
|
_LOGGER.error("Unknown OctoPrint sensor type: %s", octo_type)
|
||||||
|
add_devices(devices)
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=too-many-instance-attributes
|
||||||
|
class OctoPrintBinarySensor(BinarySensorDevice):
|
||||||
|
"""Represents an OctoPrint binary sensor."""
|
||||||
|
|
||||||
|
# pylint: disable=too-many-arguments
|
||||||
|
def __init__(self, api, condition, sensor_type, sensor_name,
|
||||||
|
unit, endpoint, group, tool=None):
|
||||||
|
"""Initialize a new OctoPrint sensor."""
|
||||||
|
self.sensor_name = sensor_name
|
||||||
|
if tool is None:
|
||||||
|
self._name = sensor_name + ' ' + condition
|
||||||
|
else:
|
||||||
|
self._name = sensor_name + ' ' + condition
|
||||||
|
self.sensor_type = sensor_type
|
||||||
|
self.api = api
|
||||||
|
self._state = False
|
||||||
|
self._unit_of_measurement = unit
|
||||||
|
self.api_endpoint = endpoint
|
||||||
|
self.api_group = group
|
||||||
|
self.api_tool = tool
|
||||||
|
# Set initial state
|
||||||
|
self.update()
|
||||||
|
_LOGGER.debug("created OctoPrint binary sensor %r", self)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""Return the name of the sensor."""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
"""Return the state of the sensor."""
|
||||||
|
return self.is_on
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self):
|
||||||
|
"""Return true if binary sensor is on."""
|
||||||
|
if self._state:
|
||||||
|
return STATE_ON
|
||||||
|
else:
|
||||||
|
return STATE_OFF
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sensor_class(self):
|
||||||
|
"""Return the class of this sensor, from SENSOR_CLASSES."""
|
||||||
|
return None
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
"""Update state of sensor."""
|
||||||
|
try:
|
||||||
|
self._state = self.api.update(self.sensor_type,
|
||||||
|
self.api_endpoint,
|
||||||
|
self.api_group,
|
||||||
|
self.api_tool)
|
||||||
|
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)
|
121
homeassistant/components/octoprint.py
Normal file
121
homeassistant/components/octoprint.py
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
"""
|
||||||
|
Support for monitoring OctoPrint 3D printers.
|
||||||
|
|
||||||
|
Uses OctoPrint REST JSON API to query for monitored variables.
|
||||||
|
http://docs.octoprint.org/en/master/api/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import time
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from homeassistant.components import discovery
|
||||||
|
from homeassistant.const import CONF_API_KEY, CONF_HOST
|
||||||
|
from homeassistant.helpers import validate_config
|
||||||
|
|
||||||
|
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 provided 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]
|
118
homeassistant/components/sensor/octoprint.py
Normal file
118
homeassistant/components/sensor/octoprint.py
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
"""
|
||||||
|
Support for monitoring OctoPrint sensors.
|
||||||
|
|
||||||
|
Uses OctoPrint REST JSON API to query for monitored variables.
|
||||||
|
For more details about this component, please refer to the documentation at
|
||||||
|
http://docs.octoprint.org/en/master/api/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from homeassistant.const import TEMP_CELSIUS, CONF_NAME
|
||||||
|
from homeassistant.helpers.entity import Entity
|
||||||
|
from homeassistant.loader import get_component
|
||||||
|
|
||||||
|
DEPENDENCIES = ["octoprint"]
|
||||||
|
|
||||||
|
SENSOR_TYPES = {
|
||||||
|
# API Endpoint, Group, Key, unit
|
||||||
|
"Temperatures": ["printer", "temperature", "*", TEMP_CELSIUS],
|
||||||
|
"Current State": ["printer", "state", "text", None],
|
||||||
|
"Job Percentage": ["job", "progress", "completion", "%"],
|
||||||
|
}
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=unused-argument
|
||||||
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
|
"""Setup the available OctoPrint sensors."""
|
||||||
|
octoprint = get_component('octoprint')
|
||||||
|
name = config.get(CONF_NAME, "OctoPrint")
|
||||||
|
monitored_conditions = config.get("monitored_conditions",
|
||||||
|
SENSOR_TYPES.keys())
|
||||||
|
|
||||||
|
devices = []
|
||||||
|
types = ["actual", "target"]
|
||||||
|
for octo_type in monitored_conditions:
|
||||||
|
if octo_type == "Temperatures":
|
||||||
|
for tool in octoprint.OCTOPRINT.get_tools():
|
||||||
|
for temp_type in types:
|
||||||
|
new_sensor = OctoPrintSensor(octoprint.OCTOPRINT,
|
||||||
|
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)
|
||||||
|
elif octo_type in SENSOR_TYPES:
|
||||||
|
new_sensor = OctoPrintSensor(octoprint.OCTOPRINT,
|
||||||
|
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)
|
||||||
|
else:
|
||||||
|
_LOGGER.error("Unknown OctoPrint sensor type: %s", octo_type)
|
||||||
|
|
||||||
|
add_devices(devices)
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=too-many-instance-attributes
|
||||||
|
class OctoPrintSensor(Entity):
|
||||||
|
"""Represents an OctoPrint sensor."""
|
||||||
|
|
||||||
|
# pylint: disable=too-many-arguments
|
||||||
|
def __init__(self, api, condition, sensor_type, sensor_name,
|
||||||
|
unit, endpoint, group, tool=None):
|
||||||
|
"""Initialize a new OctoPrint sensor."""
|
||||||
|
self.sensor_name = sensor_name
|
||||||
|
if tool is None:
|
||||||
|
self._name = sensor_name + ' ' + condition
|
||||||
|
else:
|
||||||
|
self._name = sensor_name + ' ' + condition + ' ' + tool + ' temp'
|
||||||
|
self.sensor_type = sensor_type
|
||||||
|
self.api = api
|
||||||
|
self._state = None
|
||||||
|
self._unit_of_measurement = unit
|
||||||
|
self.api_endpoint = endpoint
|
||||||
|
self.api_group = group
|
||||||
|
self.api_tool = tool
|
||||||
|
# Set initial state
|
||||||
|
self.update()
|
||||||
|
_LOGGER.debug("created OctoPrint sensor %r", self)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""Return the name of the sensor."""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
"""Return the state of the sensor."""
|
||||||
|
return self._state
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unit_of_measurement(self):
|
||||||
|
"""Unit of measurement of this entity, if any."""
|
||||||
|
return self._unit_of_measurement
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
"""Update state of sensor."""
|
||||||
|
try:
|
||||||
|
self._state = self.api.update(self.sensor_type,
|
||||||
|
self.api_endpoint,
|
||||||
|
self.api_group,
|
||||||
|
self.api_tool)
|
||||||
|
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)
|
||||||
|
return
|
Loading…
x
Reference in New Issue
Block a user