Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Update the IP addresses of your Cloudflare DNS records."""
2 
3 from __future__ import annotations
4 
5 import asyncio
6 from datetime import timedelta
7 import logging
8 import socket
9 
10 import pycfdns
11 
12 from homeassistant.config_entries import ConfigEntry
13 from homeassistant.const import CONF_API_TOKEN, CONF_ZONE
14 from homeassistant.core import HomeAssistant, ServiceCall
15 from homeassistant.exceptions import (
16  ConfigEntryAuthFailed,
17  ConfigEntryNotReady,
18  HomeAssistantError,
19 )
20 from homeassistant.helpers.aiohttp_client import async_get_clientsession
21 from homeassistant.helpers.event import async_track_time_interval
22 from homeassistant.util.location import async_detect_location_info
23 from homeassistant.util.network import is_ipv4_address
24 
25 from .const import CONF_RECORDS, DEFAULT_UPDATE_INTERVAL, DOMAIN, SERVICE_UPDATE_RECORDS
26 
27 _LOGGER = logging.getLogger(__name__)
28 
29 
30 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
31  """Set up Cloudflare from a config entry."""
32  session = async_get_clientsession(hass)
33  client = pycfdns.Client(
34  api_token=entry.data[CONF_API_TOKEN],
35  client_session=session,
36  )
37 
38  try:
39  dns_zones = await client.list_zones()
40  dns_zone = next(
41  zone for zone in dns_zones if zone["name"] == entry.data[CONF_ZONE]
42  )
43  except pycfdns.AuthenticationException as error:
44  raise ConfigEntryAuthFailed from error
45  except pycfdns.ComunicationException as error:
46  raise ConfigEntryNotReady from error
47 
48  async def update_records(now):
49  """Set up recurring update."""
50  try:
52  hass, client, dns_zone, entry.data[CONF_RECORDS]
53  )
54  except (
55  pycfdns.AuthenticationException,
56  pycfdns.ComunicationException,
57  ) as error:
58  _LOGGER.error("Error updating zone %s: %s", entry.data[CONF_ZONE], error)
59 
60  async def update_records_service(call: ServiceCall) -> None:
61  """Set up service for manual trigger."""
62  try:
64  hass, client, dns_zone, entry.data[CONF_RECORDS]
65  )
66  except (
67  pycfdns.AuthenticationException,
68  pycfdns.ComunicationException,
69  ) as error:
70  _LOGGER.error("Error updating zone %s: %s", entry.data[CONF_ZONE], error)
71 
72  update_interval = timedelta(minutes=DEFAULT_UPDATE_INTERVAL)
73  entry.async_on_unload(
74  async_track_time_interval(hass, update_records, update_interval)
75  )
76 
77  hass.data.setdefault(DOMAIN, {})
78  hass.data[DOMAIN][entry.entry_id] = {}
79 
80  hass.services.async_register(DOMAIN, SERVICE_UPDATE_RECORDS, update_records_service)
81 
82  return True
83 
84 
85 async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
86  """Unload Cloudflare config entry."""
87  hass.data[DOMAIN].pop(entry.entry_id)
88 
89  return True
90 
91 
93  hass: HomeAssistant,
94  client: pycfdns.Client,
95  dns_zone: pycfdns.ZoneModel,
96  target_records: list[str],
97 ) -> None:
98  _LOGGER.debug("Starting update for zone %s", dns_zone["name"])
99 
100  records = await client.list_dns_records(zone_id=dns_zone["id"], type="A")
101  _LOGGER.debug("Records: %s", records)
102 
103  session = async_get_clientsession(hass, family=socket.AF_INET)
104  location_info = await async_detect_location_info(session)
105 
106  if not location_info or not is_ipv4_address(location_info.ip):
107  raise HomeAssistantError("Could not get external IPv4 address")
108 
109  filtered_records = [
110  record
111  for record in records
112  if record["name"] in target_records and record["content"] != location_info.ip
113  ]
114 
115  if len(filtered_records) == 0:
116  _LOGGER.debug("All target records are up to date")
117  return
118 
119  await asyncio.gather(
120  *[
121  client.update_dns_record(
122  zone_id=dns_zone["id"],
123  record_id=record["id"],
124  record_content=location_info.ip,
125  record_name=record["name"],
126  record_type=record["type"],
127  record_proxied=record["proxied"],
128  )
129  for record in filtered_records
130  ]
131  )
132 
133  _LOGGER.debug("Update for zone %s is complete", dns_zone["name"])
None _async_update_cloudflare(HomeAssistant hass, pycfdns.Client client, pycfdns.ZoneModel dns_zone, list[str] target_records)
Definition: __init__.py:97
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:30
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:85
aiohttp.ClientSession async_get_clientsession(HomeAssistant hass, bool verify_ssl=True, socket.AddressFamily family=socket.AF_UNSPEC, ssl_util.SSLCipherList ssl_cipher=ssl_util.SSLCipherList.PYTHON_DEFAULT)
CALLBACK_TYPE async_track_time_interval(HomeAssistant hass, Callable[[datetime], Coroutine[Any, Any, None]|None] action, timedelta interval, *str|None name=None, bool|None cancel_on_shutdown=None)
Definition: event.py:1679
LocationInfo|None async_detect_location_info(aiohttp.ClientSession session)
Definition: location.py:51
bool is_ipv4_address(str address)
Definition: network.py:73