1 """Provide animated GIF loops of Buienradar imagery."""
3 from __future__
import annotations
6 from datetime
import datetime, timedelta
10 import voluptuous
as vol
19 from .
import BuienRadarConfigEntry
20 from .const
import CONF_DELTA, DEFAULT_COUNTRY, DEFAULT_DELTA, DEFAULT_DIMENSION
22 _LOGGER = logging.getLogger(__name__)
25 DIM_RANGE = vol.All(vol.Coerce(int), vol.Range(min=120, max=700))
28 SUPPORTED_COUNTRY_CODES = [
"NL",
"BE"]
33 entry: BuienRadarConfigEntry,
34 async_add_entities: AddEntitiesCallback,
36 """Set up buienradar radar-loop camera component."""
38 options = entry.options
40 country = options.get(
41 CONF_COUNTRY_CODE, config.get(CONF_COUNTRY_CODE, DEFAULT_COUNTRY)
44 delta = options.get(CONF_DELTA, config.get(CONF_DELTA, DEFAULT_DELTA))
46 latitude = config.get(CONF_LATITUDE, hass.config.latitude)
47 longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
53 """A camera component producing animated buienradar radar-imagery GIFs.
55 Rain radar imagery camera based on image URL taken from [0].
57 [0]: https://www.buienradar.nl/overbuienradar/gratis-weerdata
60 _attr_entity_registry_enabled_default =
False
61 _attr_name =
"Buienradar"
64 self, latitude: float, longitude: float, delta: float, country: str
66 """Initialize the component.
68 This constructor must be run in the event loop.
95 self.
_deadline_deadline: datetime |
None =
None
103 return dt_util.utcnow() > self.
_deadline_deadline
106 """Retrieve new radar image and return whether this succeeded."""
110 f
"https://api.buienradar.nl/image/1.0/RadarMap{self._country}"
111 f
"?w={self._dimension}&h={self._dimension}"
115 headers = {
"If-Modified-Since": self.
_last_modified_last_modified}
120 async
with session.get(
121 url, timeout=aiohttp.ClientTimeout(total=5), headers=headers
123 res.raise_for_status()
125 if res.status == 304:
126 _LOGGER.debug(
"HTTP 304 - success")
129 if last_modified := res.headers.get(
"Last-Modified"):
133 _LOGGER.debug(
"HTTP 200 - Last-Modified: %s", last_modified)
136 except (TimeoutError, aiohttp.ClientError)
as err:
137 _LOGGER.error(
"Failed to fetch image, %s", type(err))
141 self, width: int |
None =
None, height: int |
None =
None
143 """Return a still image response from the camera.
145 Uses asyncio conditions to make sure only one task enters the critical
146 section at the same time. Otherwise, two http requests would start
147 when two tabs with Home Assistant are open.
149 The condition is entered in two sections because otherwise the lock
150 would be held while doing the http request.
152 A boolean (_loading) is used to indicate the loading status instead of
153 _last_image since that is initialized to None.
156 * :func:`asyncio.Condition.wait` releases the lock and acquires it
157 again before continuing.
158 * :func:`asyncio.Condition.notify_all` requires the lock to be held.
167 _LOGGER.debug(
"already loading - waiting for notification")
175 now = dt_util.utcnow()
bool __needs_refresh(self)
bool __retrieve_radar_image(self)
bytes|None async_camera_image(self, int|None width=None, int|None height=None)
None __init__(self, float latitude, float longitude, float delta, str country)
None async_setup_entry(HomeAssistant hass, BuienRadarConfigEntry entry, AddEntitiesCallback async_add_entities)
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)