1
mirror of https://github.com/home-assistant/core synced 2024-09-06 10:29:55 +02:00
ha-core/homeassistant/components/graphite/__init__.py

158 lines
5.1 KiB
Python
Raw Normal View History

"""Support for sending data to a Graphite installation."""
import logging
import queue
import socket
import threading
import time
import voluptuous as vol
from homeassistant.const import (
2019-07-31 21:25:30 +02:00
CONF_HOST,
CONF_PORT,
CONF_PREFIX,
EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP,
EVENT_STATE_CHANGED,
)
from homeassistant.helpers import state
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
2019-07-31 21:25:30 +02:00
DEFAULT_HOST = "localhost"
DEFAULT_PORT = 2003
2019-07-31 21:25:30 +02:00
DEFAULT_PREFIX = "ha"
DOMAIN = "graphite"
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_PREFIX, default=DEFAULT_PREFIX): cv.string,
}
)
},
extra=vol.ALLOW_EXTRA,
)
def setup(hass, config):
"""Set up the Graphite feeder."""
conf = config[DOMAIN]
host = conf.get(CONF_HOST)
prefix = conf.get(CONF_PREFIX)
port = conf.get(CONF_PORT)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
sock.connect((host, port))
sock.shutdown(2)
_LOGGER.debug("Connection to Graphite possible")
except socket.error:
_LOGGER.error("Not able to connect to Graphite")
return False
GraphiteFeeder(hass, host, port, prefix)
return True
class GraphiteFeeder(threading.Thread):
2016-03-08 17:55:57 +01:00
"""Feed data to Graphite."""
def __init__(self, hass, host, port, prefix):
2016-03-08 17:55:57 +01:00
"""Initialize the feeder."""
super().__init__(daemon=True)
self._hass = hass
self._host = host
self._port = port
2016-02-26 23:52:54 +01:00
# rstrip any trailing dots in case they think they need it
2019-07-31 21:25:30 +02:00
self._prefix = prefix.rstrip(".")
self._queue = queue.Queue()
self._quit_object = object()
self._we_started = False
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, self.start_listen)
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, self.shutdown)
hass.bus.listen(EVENT_STATE_CHANGED, self.event_listener)
2019-07-31 21:25:30 +02:00
_LOGGER.debug("Graphite feeding to %s:%i initialized", self._host, self._port)
def start_listen(self, event):
2016-02-26 23:52:54 +01:00
"""Start event-processing thread."""
_LOGGER.debug("Event processing thread started")
self._we_started = True
self.start()
def shutdown(self, event):
2016-02-26 23:52:54 +01:00
"""Signal shutdown of processing event."""
_LOGGER.debug("Event processing signaled exit")
self._queue.put(self._quit_object)
def event_listener(self, event):
2016-02-26 23:52:54 +01:00
"""Queue an event for processing."""
if self.is_alive() or not self._we_started:
_LOGGER.debug("Received event")
self._queue.put(event)
else:
2019-07-31 21:25:30 +02:00
_LOGGER.error("Graphite feeder thread has died, not queuing event")
def _send_to_graphite(self, data):
2016-02-26 23:52:54 +01:00
"""Send data to Graphite."""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(10)
sock.connect((self._host, self._port))
2019-07-31 21:25:30 +02:00
sock.sendall(data.encode("ascii"))
sock.send(b"\n")
sock.close()
def _report_attributes(self, entity_id, new_state):
2016-02-26 23:52:54 +01:00
"""Report the attributes."""
now = time.time()
things = dict(new_state.attributes)
try:
2019-07-31 21:25:30 +02:00
things["state"] = state.state_as_number(new_state)
except ValueError:
pass
2019-07-31 21:25:30 +02:00
lines = [
"%s.%s.%s %f %i"
% (self._prefix, entity_id, key.replace(" ", "_"), value, now)
for key, value in things.items()
if isinstance(value, (float, int))
]
if not lines:
return
_LOGGER.debug("Sending to graphite: %s", lines)
try:
2019-07-31 21:25:30 +02:00
self._send_to_graphite("\n".join(lines))
except socket.gaierror:
_LOGGER.error("Unable to connect to host %s", self._host)
except socket.error:
_LOGGER.exception("Failed to send data to graphite")
def run(self):
2016-02-26 23:52:54 +01:00
"""Run the process to export the data."""
while True:
event = self._queue.get()
if event == self._quit_object:
_LOGGER.debug("Event processing thread stopped")
self._queue.task_done()
return
2019-07-31 21:25:30 +02:00
if event.event_type == EVENT_STATE_CHANGED and event.data.get("new_state"):
_LOGGER.debug(
"Processing STATE_CHANGED event for %s", event.data["entity_id"]
)
try:
self._report_attributes(
2019-07-31 21:25:30 +02:00
event.data["entity_id"], event.data["new_state"]
)
except Exception: # pylint: disable=broad-except
# Catch this so we can avoid the thread dying and
# make it visible.
_LOGGER.exception("Failed to process STATE_CHANGED event")
else:
2019-07-31 21:25:30 +02:00
_LOGGER.warning("Processing unexpected event type %s", event.event_type)
self._queue.task_done()