1
mirror of https://github.com/home-assistant/core synced 2024-09-03 08:14:07 +02:00
ha-core/homeassistant/components/no_ip/__init__.py
2022-05-17 19:36:29 +02:00

121 lines
3.4 KiB
Python

"""Integrate with NO-IP Dynamic DNS service."""
import asyncio
import base64
from datetime import datetime, timedelta
import logging
import aiohttp
from aiohttp.hdrs import AUTHORIZATION, USER_AGENT
import async_timeout
import voluptuous as vol
from homeassistant.const import CONF_DOMAIN, CONF_PASSWORD, CONF_TIMEOUT, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import (
SERVER_SOFTWARE,
async_get_clientsession,
)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.typing import ConfigType
_LOGGER = logging.getLogger(__name__)
DOMAIN = "no_ip"
# We should set a dedicated address for the user agent.
EMAIL = "hello@home-assistant.io"
INTERVAL = timedelta(minutes=5)
DEFAULT_TIMEOUT = 10
NO_IP_ERRORS = {
"nohost": "Hostname supplied does not exist under specified account",
"badauth": "Invalid username password combination",
"badagent": "Client disabled",
"!donator": "An update request was sent with a feature that is not available",
"abuse": "Username is blocked due to abuse",
"911": "A fatal error on NO-IP's side such as a database outage",
}
UPDATE_URL = "https://dynupdate.no-ip.com/nic/update"
HA_USER_AGENT = f"{SERVER_SOFTWARE} {EMAIL}"
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Required(CONF_DOMAIN): cv.string,
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
}
)
},
extra=vol.ALLOW_EXTRA,
)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Initialize the NO-IP component."""
domain = config[DOMAIN].get(CONF_DOMAIN)
user = config[DOMAIN].get(CONF_USERNAME)
password = config[DOMAIN].get(CONF_PASSWORD)
timeout = config[DOMAIN].get(CONF_TIMEOUT)
auth_str = base64.b64encode(f"{user}:{password}".encode())
session = async_get_clientsession(hass)
result = await _update_no_ip(hass, session, domain, auth_str, timeout)
if not result:
return False
async def update_domain_interval(now: datetime) -> None:
"""Update the NO-IP entry."""
await _update_no_ip(hass, session, domain, auth_str, timeout)
async_track_time_interval(hass, update_domain_interval, INTERVAL)
return True
async def _update_no_ip(
hass: HomeAssistant,
session: aiohttp.ClientSession,
domain: str,
auth_str: bytes,
timeout: int,
) -> bool:
"""Update NO-IP."""
url = UPDATE_URL
params = {"hostname": domain}
headers = {
AUTHORIZATION: f"Basic {auth_str.decode('utf-8')}",
USER_AGENT: HA_USER_AGENT,
}
try:
async with async_timeout.timeout(timeout):
resp = await session.get(url, params=params, headers=headers)
body = await resp.text()
if body.startswith("good") or body.startswith("nochg"):
return True
_LOGGER.warning(
"Updating NO-IP failed: %s => %s", domain, NO_IP_ERRORS[body.strip()]
)
except aiohttp.ClientError:
_LOGGER.warning("Can't connect to NO-IP API")
except asyncio.TimeoutError:
_LOGGER.warning("Timeout from NO-IP API for domain: %s", domain)
return False