mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
Refactor nzbget to support future platform changes (#26462)
* Re-factor nzbget platform to enable future features. * Re-factor nzbget platform to enable future features. * Re-factor nzbget platform to enable future features. * Re-factor nzbget platform to enable future features. * Use pynzbgetapi instead of raw HTTP requests * Using pynzbgetapi * Pinning pynzbgetapi version. * Requiring pynzbgetapi 0.2.0 * Addressing review comments * Refreshing requirements (adding pynzbgetapi) * Remove period from logging message * Updating requirements file * Add nzbget init to .coveragerc * Adding nzbget codeowner * Updating codeowners file
This commit is contained in:
parent
9c2053a251
commit
57833f5b1e
@ -436,6 +436,7 @@ omit =
|
|||||||
homeassistant/components/nuki/lock.py
|
homeassistant/components/nuki/lock.py
|
||||||
homeassistant/components/nut/sensor.py
|
homeassistant/components/nut/sensor.py
|
||||||
homeassistant/components/nx584/alarm_control_panel.py
|
homeassistant/components/nx584/alarm_control_panel.py
|
||||||
|
homeassistant/components/nzbget/__init__.py
|
||||||
homeassistant/components/nzbget/sensor.py
|
homeassistant/components/nzbget/sensor.py
|
||||||
homeassistant/components/obihai/*
|
homeassistant/components/obihai/*
|
||||||
homeassistant/components/octoprint/*
|
homeassistant/components/octoprint/*
|
||||||
|
@ -197,6 +197,7 @@ homeassistant/components/nsw_fuel_station/* @nickw444
|
|||||||
homeassistant/components/nsw_rural_fire_service_feed/* @exxamalte
|
homeassistant/components/nsw_rural_fire_service_feed/* @exxamalte
|
||||||
homeassistant/components/nuki/* @pschmitt
|
homeassistant/components/nuki/* @pschmitt
|
||||||
homeassistant/components/nws/* @MatthewFlamm
|
homeassistant/components/nws/* @MatthewFlamm
|
||||||
|
homeassistant/components/nzbget/* @chriscla
|
||||||
homeassistant/components/obihai/* @dshokouhi
|
homeassistant/components/obihai/* @dshokouhi
|
||||||
homeassistant/components/ohmconnect/* @robbiet480
|
homeassistant/components/ohmconnect/* @robbiet480
|
||||||
homeassistant/components/onboarding/* @home-assistant/core
|
homeassistant/components/onboarding/* @home-assistant/core
|
||||||
|
@ -1 +1,106 @@
|
|||||||
"""The nzbget component."""
|
"""The nzbget component."""
|
||||||
|
from datetime import timedelta
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import pynzbgetapi
|
||||||
|
import requests
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.const import (
|
||||||
|
CONF_HOST,
|
||||||
|
CONF_NAME,
|
||||||
|
CONF_PASSWORD,
|
||||||
|
CONF_PORT,
|
||||||
|
CONF_SCAN_INTERVAL,
|
||||||
|
CONF_SSL,
|
||||||
|
CONF_USERNAME,
|
||||||
|
)
|
||||||
|
from homeassistant.helpers import config_validation as cv
|
||||||
|
from homeassistant.helpers.dispatcher import dispatcher_send
|
||||||
|
from homeassistant.helpers.event import track_time_interval
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
DOMAIN = "nzbget"
|
||||||
|
DATA_NZBGET = "data_nzbget"
|
||||||
|
DATA_UPDATED = "nzbget_data_updated"
|
||||||
|
|
||||||
|
DEFAULT_NAME = "NZBGet"
|
||||||
|
DEFAULT_PORT = 6789
|
||||||
|
|
||||||
|
DEFAULT_SCAN_INTERVAL = timedelta(seconds=5)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = vol.Schema(
|
||||||
|
{
|
||||||
|
DOMAIN: vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(CONF_HOST): cv.string,
|
||||||
|
vol.Optional(CONF_PASSWORD): cv.string,
|
||||||
|
vol.Optional(CONF_USERNAME): cv.string,
|
||||||
|
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
|
||||||
|
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||||
|
vol.Optional(
|
||||||
|
CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL
|
||||||
|
): cv.time_period,
|
||||||
|
vol.Optional(CONF_SSL, default=False): cv.boolean,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
extra=vol.ALLOW_EXTRA,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def setup(hass, config):
|
||||||
|
"""Set up the NZBGet sensors."""
|
||||||
|
host = config[DOMAIN][CONF_HOST]
|
||||||
|
port = config[DOMAIN][CONF_PORT]
|
||||||
|
ssl = "s" if config[DOMAIN][CONF_SSL] else ""
|
||||||
|
name = config[DOMAIN][CONF_NAME]
|
||||||
|
username = config[DOMAIN].get(CONF_USERNAME)
|
||||||
|
password = config[DOMAIN].get(CONF_PASSWORD)
|
||||||
|
scan_interval = config[DOMAIN][CONF_SCAN_INTERVAL]
|
||||||
|
|
||||||
|
try:
|
||||||
|
nzbget_api = pynzbgetapi.NZBGetAPI(host, username, password, ssl, ssl, port)
|
||||||
|
nzbget_api.version()
|
||||||
|
except pynzbgetapi.NZBGetAPIException as conn_err:
|
||||||
|
_LOGGER.error("Error setting up NZBGet API: %s", conn_err)
|
||||||
|
return False
|
||||||
|
|
||||||
|
_LOGGER.debug("Successfully validated NZBGet API connection")
|
||||||
|
|
||||||
|
nzbget_data = hass.data[DATA_NZBGET] = NZBGetData(hass, nzbget_api)
|
||||||
|
nzbget_data.update()
|
||||||
|
|
||||||
|
def refresh(event_time):
|
||||||
|
"""Get the latest data from NZBGet."""
|
||||||
|
nzbget_data.update()
|
||||||
|
|
||||||
|
track_time_interval(hass, refresh, scan_interval)
|
||||||
|
|
||||||
|
sensorconfig = {"client_name": name}
|
||||||
|
|
||||||
|
hass.helpers.discovery.load_platform("sensor", DOMAIN, sensorconfig, config)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class NZBGetData:
|
||||||
|
"""Get the latest data and update the states."""
|
||||||
|
|
||||||
|
def __init__(self, hass, api):
|
||||||
|
"""Initialize the NZBGet RPC API."""
|
||||||
|
self.hass = hass
|
||||||
|
self.status = None
|
||||||
|
self.available = True
|
||||||
|
self._api = api
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
"""Get the latest data from NZBGet instance."""
|
||||||
|
try:
|
||||||
|
self.status = self._api.status()
|
||||||
|
self.available = True
|
||||||
|
dispatcher_send(self.hass, DATA_UPDATED)
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
self.available = False
|
||||||
|
_LOGGER.error("Unable to refresh NZBGet data")
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"domain": "nzbget",
|
"domain": "nzbget",
|
||||||
"name": "Nzbget",
|
"name": "Nzbget",
|
||||||
"documentation": "https://www.home-assistant.io/components/nzbget",
|
"documentation": "https://www.home-assistant.io/components/nzbget",
|
||||||
"requirements": [],
|
"requirements": ["pynzbgetapi==0.2.0"],
|
||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
"codeowners": []
|
"codeowners": ["@chriscla"]
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,15 @@
|
|||||||
"""Support for monitoring NZBGet NZB client."""
|
"""Monitor the NZBGet API."""
|
||||||
from datetime import timedelta
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from aiohttp.hdrs import CONTENT_TYPE
|
from homeassistant.core import callback
|
||||||
import requests
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
import voluptuous as vol
|
|
||||||
|
|
||||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
|
||||||
from homeassistant.const import (
|
|
||||||
CONF_SSL,
|
|
||||||
CONF_HOST,
|
|
||||||
CONF_NAME,
|
|
||||||
CONF_PORT,
|
|
||||||
CONF_PASSWORD,
|
|
||||||
CONF_USERNAME,
|
|
||||||
CONTENT_TYPE_JSON,
|
|
||||||
CONF_MONITORED_VARIABLES,
|
|
||||||
)
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.util import Throttle
|
|
||||||
|
from . import DATA_NZBGET, DATA_UPDATED
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
DEFAULT_NAME = "NZBGet"
|
DEFAULT_NAME = "NZBGet"
|
||||||
DEFAULT_PORT = 6789
|
|
||||||
|
|
||||||
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=5)
|
|
||||||
|
|
||||||
SENSOR_TYPES = {
|
SENSOR_TYPES = {
|
||||||
"article_cache": ["ArticleCacheMB", "Article Cache", "MB"],
|
"article_cache": ["ArticleCacheMB", "Article Cache", "MB"],
|
||||||
@ -40,66 +23,39 @@ SENSOR_TYPES = {
|
|||||||
"uptime": ["UpTimeSec", "Uptime", "min"],
|
"uptime": ["UpTimeSec", "Uptime", "min"],
|
||||||
}
|
}
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
|
||||||
{
|
|
||||||
vol.Required(CONF_HOST): cv.string,
|
|
||||||
vol.Optional(CONF_MONITORED_VARIABLES, default=["download_rate"]): vol.All(
|
|
||||||
cv.ensure_list, [vol.In(SENSOR_TYPES)]
|
|
||||||
),
|
|
||||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
|
||||||
vol.Optional(CONF_PASSWORD): cv.string,
|
|
||||||
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
|
|
||||||
vol.Optional(CONF_SSL, default=False): cv.boolean,
|
|
||||||
vol.Optional(CONF_USERNAME): cv.string,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||||
"""Set up the NZBGet sensors."""
|
"""Create NZBGet sensors."""
|
||||||
host = config.get(CONF_HOST)
|
|
||||||
port = config.get(CONF_PORT)
|
|
||||||
ssl = "s" if config.get(CONF_SSL) else ""
|
|
||||||
name = config.get(CONF_NAME)
|
|
||||||
username = config.get(CONF_USERNAME)
|
|
||||||
password = config.get(CONF_PASSWORD)
|
|
||||||
monitored_types = config.get(CONF_MONITORED_VARIABLES)
|
|
||||||
|
|
||||||
url = f"http{ssl}://{host}:{port}/jsonrpc"
|
if discovery_info is None:
|
||||||
|
return
|
||||||
|
|
||||||
try:
|
nzbget_data = hass.data[DATA_NZBGET]
|
||||||
nzbgetapi = NZBGetAPI(api_url=url, username=username, password=password)
|
name = discovery_info["client_name"]
|
||||||
nzbgetapi.update()
|
|
||||||
except (
|
|
||||||
requests.exceptions.ConnectionError,
|
|
||||||
requests.exceptions.HTTPError,
|
|
||||||
) as conn_err:
|
|
||||||
_LOGGER.error("Error setting up NZBGet API: %s", conn_err)
|
|
||||||
return False
|
|
||||||
|
|
||||||
devices = []
|
devices = []
|
||||||
for ng_type in monitored_types:
|
for sensor_type, sensor_config in SENSOR_TYPES.items():
|
||||||
new_sensor = NZBGetSensor(
|
new_sensor = NZBGetSensor(
|
||||||
api=nzbgetapi, sensor_type=SENSOR_TYPES.get(ng_type), client_name=name
|
nzbget_data, sensor_type, name, sensor_config[0], sensor_config[1]
|
||||||
)
|
)
|
||||||
devices.append(new_sensor)
|
devices.append(new_sensor)
|
||||||
|
|
||||||
add_entities(devices)
|
add_entities(devices, True)
|
||||||
|
|
||||||
|
|
||||||
class NZBGetSensor(Entity):
|
class NZBGetSensor(Entity):
|
||||||
"""Representation of a NZBGet sensor."""
|
"""Representation of a NZBGet sensor."""
|
||||||
|
|
||||||
def __init__(self, api, sensor_type, client_name):
|
def __init__(
|
||||||
|
self, nzbget_data, sensor_type, client_name, sensor_name, unit_of_measurement
|
||||||
|
):
|
||||||
"""Initialize a new NZBGet sensor."""
|
"""Initialize a new NZBGet sensor."""
|
||||||
self._name = "{} {}".format(client_name, sensor_type[1])
|
self._name = f"{client_name} {sensor_type}"
|
||||||
self.type = sensor_type[0]
|
self.type = sensor_name
|
||||||
self.client_name = client_name
|
self.client_name = client_name
|
||||||
self.api = api
|
self.nzbget_data = nzbget_data
|
||||||
self._state = None
|
self._state = None
|
||||||
self._unit_of_measurement = sensor_type[2]
|
self._unit_of_measurement = unit_of_measurement
|
||||||
self.update()
|
|
||||||
_LOGGER.debug("Created NZBGet sensor: %s", self.type)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
@ -116,21 +72,31 @@ class NZBGetSensor(Entity):
|
|||||||
"""Return the unit of measurement of this entity, if any."""
|
"""Return the unit of measurement of this entity, if any."""
|
||||||
return self._unit_of_measurement
|
return self._unit_of_measurement
|
||||||
|
|
||||||
|
@property
|
||||||
|
def available(self):
|
||||||
|
"""Return whether the sensor is available."""
|
||||||
|
return self.nzbget_data.available
|
||||||
|
|
||||||
|
async def async_added_to_hass(self):
|
||||||
|
"""Handle entity which will be added."""
|
||||||
|
async_dispatcher_connect(
|
||||||
|
self.hass, DATA_UPDATED, self._schedule_immediate_update
|
||||||
|
)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _schedule_immediate_update(self):
|
||||||
|
self.async_schedule_update_ha_state(True)
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Update state of sensor."""
|
"""Update state of sensor."""
|
||||||
try:
|
|
||||||
self.api.update()
|
|
||||||
except requests.exceptions.ConnectionError:
|
|
||||||
# Error calling the API, already logged in api.update()
|
|
||||||
return
|
|
||||||
|
|
||||||
if self.api.status is None:
|
if self.nzbget_data.status is None:
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"Update of %s requested, but no status is available", self._name
|
"Update of %s requested, but no status is available", self._name
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
value = self.api.status.get(self.type)
|
value = self.nzbget_data.status.get(self.type)
|
||||||
if value is None:
|
if value is None:
|
||||||
_LOGGER.warning("Unable to locate value for %s", self.type)
|
_LOGGER.warning("Unable to locate value for %s", self.type)
|
||||||
return
|
return
|
||||||
@ -143,48 +109,3 @@ class NZBGetSensor(Entity):
|
|||||||
self._state = round(value / 60, 2)
|
self._state = round(value / 60, 2)
|
||||||
else:
|
else:
|
||||||
self._state = value
|
self._state = value
|
||||||
|
|
||||||
|
|
||||||
class NZBGetAPI:
|
|
||||||
"""Simple JSON-RPC wrapper for NZBGet's API."""
|
|
||||||
|
|
||||||
def __init__(self, api_url, username=None, password=None):
|
|
||||||
"""Initialize NZBGet API and set headers needed later."""
|
|
||||||
self.api_url = api_url
|
|
||||||
self.status = None
|
|
||||||
self.headers = {CONTENT_TYPE: CONTENT_TYPE_JSON}
|
|
||||||
|
|
||||||
if username is not None and password is not None:
|
|
||||||
self.auth = (username, password)
|
|
||||||
else:
|
|
||||||
self.auth = None
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
def post(self, method, params=None):
|
|
||||||
"""Send a POST request and return the response as a dict."""
|
|
||||||
payload = {"method": method}
|
|
||||||
|
|
||||||
if params:
|
|
||||||
payload["params"] = params
|
|
||||||
try:
|
|
||||||
response = requests.post(
|
|
||||||
self.api_url,
|
|
||||||
json=payload,
|
|
||||||
auth=self.auth,
|
|
||||||
headers=self.headers,
|
|
||||||
timeout=5,
|
|
||||||
)
|
|
||||||
response.raise_for_status()
|
|
||||||
return response.json()
|
|
||||||
except requests.exceptions.ConnectionError as conn_exc:
|
|
||||||
_LOGGER.error(
|
|
||||||
"Failed to update NZBGet status from %s. Error: %s",
|
|
||||||
self.api_url,
|
|
||||||
conn_exc,
|
|
||||||
)
|
|
||||||
raise
|
|
||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
|
||||||
def update(self):
|
|
||||||
"""Update cached response."""
|
|
||||||
self.status = self.post("status")["result"]
|
|
||||||
|
@ -1347,6 +1347,9 @@ pynws==0.7.4
|
|||||||
# homeassistant.components.nx584
|
# homeassistant.components.nx584
|
||||||
pynx584==0.4
|
pynx584==0.4
|
||||||
|
|
||||||
|
# homeassistant.components.nzbget
|
||||||
|
pynzbgetapi==0.2.0
|
||||||
|
|
||||||
# homeassistant.components.obihai
|
# homeassistant.components.obihai
|
||||||
pyobihai==1.0.2
|
pyobihai==1.0.2
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user