core/homeassistant/components/no_ip.py

117 lines
3.3 KiB
Python

"""
Integrate with NO-IP Dynamic DNS service.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/no_ip/
"""
import asyncio
import base64
from datetime import timedelta
import logging
import aiohttp
from aiohttp.hdrs import USER_AGENT, AUTHORIZATION
import async_timeout
import voluptuous as vol
from homeassistant.const import (
CONF_DOMAIN, CONF_TIMEOUT, CONF_PASSWORD, CONF_USERNAME)
from homeassistant.helpers.aiohttp_client import SERVER_SOFTWARE
import homeassistant.helpers.config_validation as cv
_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.noip.com/nic/update'
HA_USER_AGENT = "{} {}".format(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)
@asyncio.coroutine
def async_setup(hass, config):
"""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('{}:{}'.format(user, password).encode('utf-8'))
session = hass.helpers.aiohttp_client.async_get_clientsession()
result = yield from _update_no_ip(
hass, session, domain, auth_str, timeout)
if not result:
return False
@asyncio.coroutine
def update_domain_interval(now):
"""Update the NO-IP entry."""
yield from _update_no_ip(hass, session, domain, auth_str, timeout)
hass.helpers.event.async_track_time_interval(
update_domain_interval, INTERVAL)
return True
@asyncio.coroutine
def _update_no_ip(hass, session, domain, auth_str, timeout):
"""Update NO-IP."""
url = UPDATE_URL
params = {
'hostname': domain,
}
headers = {
AUTHORIZATION: "Basic {}".format(auth_str.decode('utf-8')),
USER_AGENT: HA_USER_AGENT,
}
try:
with async_timeout.timeout(timeout, loop=hass.loop):
resp = yield from session.get(url, params=params, headers=headers)
body = yield from 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