Skip to content

Commit

Permalink
Test beta for Rpi3b
Browse files Browse the repository at this point in the history
Added option city.
  • Loading branch information
caiosweet committed Feb 11, 2022
1 parent cb97d3c commit b07b258
Show file tree
Hide file tree
Showing 12 changed files with 633 additions and 56 deletions.
36 changes: 26 additions & 10 deletions custom_components/dpc/__init__.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
For more details about this integration, please refer to
https://github.com/caiosweet/Home-Assistant-custom-components-DPC-Alert
"""
from __future__ import annotations

import asyncio
from datetime import timedelta

Expand All @@ -22,6 +24,7 @@

from .api import DpcApiClient
from .const import (
CONF_COMUNE,
DEFAULT_RADIUS,
DEFAULT_SCAN_INTERVAL,
DOMAIN,
Expand All @@ -30,6 +33,8 @@
STARTUP_MESSAGE,
)

# from .testing_api import DpcApiClient


async def async_setup(hass: HomeAssistant, config: Config):
"""Set up this integration using YAML is not supported."""
Expand All @@ -45,18 +50,17 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
location_name = entry.data.get(CONF_NAME)
latitude = entry.data.get(CONF_LATITUDE)
longitude = entry.data.get(CONF_LONGITUDE)
comune = entry.options.get(CONF_COMUNE)
update_interval = timedelta(
minutes=entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
)
radius = entry.options.get(CONF_RADIUS, DEFAULT_RADIUS)
session = async_get_clientsession(hass)
client = DpcApiClient(
location_name, latitude, longitude, radius, session, update_interval
location_name, latitude, longitude, comune, radius, session, update_interval
)

coordinator = DpcDataUpdateCoordinator(
hass, client=client, update_interval=update_interval
)
coordinator = DpcDataUpdateCoordinator(hass, client=client, update_interval=update_interval)
await coordinator.async_refresh()

if not coordinator.last_update_success:
Expand All @@ -67,9 +71,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
for platform in PLATFORMS:
if entry.options.get(platform, True):
coordinator.platforms.append(platform)
hass.async_add_job(
hass.config_entries.async_forward_entry_setup(entry, platform)
)
hass.async_add_job(hass.config_entries.async_forward_entry_setup(entry, platform))

if not entry.update_listeners:
entry.add_update_listener(async_reload_entry)
Expand All @@ -83,9 +85,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
class DpcDataUpdateCoordinator(DataUpdateCoordinator):
"""Class to manage fetching data from the API."""

def __init__(
self, hass: HomeAssistant, client: DpcApiClient, update_interval
) -> None:
def __init__(self, hass: HomeAssistant, client: DpcApiClient, update_interval) -> None:
"""Initialize."""
self.api = client
self.platforms = []
Expand All @@ -99,6 +99,12 @@ async def _async_update_data(self):
raise UpdateFailed() from exception


async def async_update_options(hass, config_entry):
"""Update options."""
LOGGER.info("async_update_options")
await hass.config_entries.async_reload(config_entry.entry_id)


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Handle removal of an entry."""
coordinator = hass.data[DOMAIN][entry.entry_id]
Expand All @@ -117,6 +123,16 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unloaded


async def async_migrate_entry(hass, entry):
LOGGER.debug("Migrating DPC entry from Version %s", entry.version)
if entry.version == 1:
entry.options = dict(entry.options)
entry.options[CONF_COMUNE] = ""
entry.version = 2

return True


async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Reload config entry."""
await async_unload_entry(hass, entry)
Expand Down
62 changes: 40 additions & 22 deletions custom_components/dpc/api.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""Dpc API Client."""
from __future__ import annotations

import asyncio
import json
import re
Expand All @@ -8,7 +10,6 @@

import aiohttp
import async_timeout
from geojson_utils import geometry_within_radius, point_distance, point_in_polygon

from .const import (
ATTR_AFTERTOMORROW,
Expand All @@ -30,8 +31,9 @@
LOGGER,
WARNING_ALERT,
)
from .geojson_utils import geometry_within_radius, point_distance, point_in_polygon

CRIT_API_URL = "https://api.github.com/repos/pcm-dpc/DPC-Bollettini-Criticita-Idrogeologica-Idraulica/contents/files" # /all
CRIT_API_URL = "https://api.github.com/repos/pcm-dpc/DPC-Bollettini-Criticita-Idrogeologica-Idraulica/contents/files"
CRIT_BULLETIN_URL = (
"https://mappe.protezionecivile.gov.it/it/mappe-rischi/bollettino-di-criticita/"
)
Expand All @@ -43,10 +45,10 @@
"https://raw.githubusercontent.com/pcm-dpc/DPC-Bollettini-Criticita-"
"Idrogeologica-Idraulica/master/files/geojson/{}_{}.json"
)
VIGI_API_URL = "https://api.github.com/repos/pcm-dpc/DPC-Bollettini-Vigilanza-Meteorologica/contents/files" # /all
VIGI_BULLETIN_URL = (
"https://mappe.protezionecivile.it/it/mappe-rischi/bollettino-di-vigilanza/"
VIGI_API_URL = (
"https://api.github.com/repos/pcm-dpc/DPC-Bollettini-Vigilanza-Meteorologica/contents/files"
)
VIGI_BULLETIN_URL = "https://mappe.protezionecivile.it/it/mappe-rischi/bollettino-di-vigilanza/"
VIGI_IMAGE_URL = (
"https://raw.githubusercontent.com/pcm-dpc/DPC-Bollettini-Vigilanza-"
"Meteorologica/master/files/preview/{}_{}.png"
Expand Down Expand Up @@ -154,6 +156,7 @@ def __init__(
location_name: str,
latitude: float,
longitude: float,
comune: str,
radius: int,
session: aiohttp.ClientSession,
update_interval: timedelta,
Expand All @@ -162,6 +165,7 @@ def __init__(
self._name = location_name
self._latitude = latitude
self._longitude = longitude
self._comune = comune
self._radius = radius
self._session = session
self._interval = update_interval
Expand Down Expand Up @@ -199,9 +203,7 @@ async def async_get_data(self):
LOGGER.debug("[%s] Get IDs from Api. %s - %s", self._name, id_ct, id_vg)
except:
ids_dpc = await self.scrape_urls([CRIT_BULLETIN_URL, VIGI_BULLETIN_URL])
id_ct, id_vg = ids_dpc.get(CRIT_BULLETIN_URL), ids_dpc.get(
VIGI_BULLETIN_URL
)
id_ct, id_vg = ids_dpc.get(CRIT_BULLETIN_URL), ids_dpc.get(VIGI_BULLETIN_URL)
LOGGER.debug("[%s] Get IDs from Site. %s - %s", self._name, id_ct, id_vg)

if id_ct:
Expand All @@ -219,9 +221,7 @@ async def async_get_data(self):
elif between_midnight_first_update:
self.swap_data_criticality()
else:
LOGGER.debug(
"[%s] Criticality No changes. %s", self._name, self._id_crit
)
LOGGER.debug("[%s] Criticality No changes. %s", self._name, self._id_crit)
self._data[CRITICALITY][ATTR_LAST_UPDATE] = now.isoformat()

if id_vg:
Expand Down Expand Up @@ -274,7 +274,8 @@ async def api_fetch(self, url: str) -> dict:
async with async_timeout.timeout(TIMEOUT):
if "json" in url:
r = await self._session.get(url, raise_for_status=True)
fetched = await r.json(content_type=None) # not import json
# fetched = await r.json(content_type=None) # not import json
fetched = json.loads(await r.text())
elif "api." in url:
r = await self._session.get(url, raise_for_status=True)
jdata = json.loads(await r.text())
Expand Down Expand Up @@ -361,13 +362,11 @@ def get_criticality(self, response: dict, url: str) -> dict:
self._cache_crit[ATTR_LINK] = CRIT_BULLETIN_URL
self._cache_crit[ATTR_PUBLICATION_DATE] = self._pub_date_crit

prop = self.get_properties(self._point, response)
prop = self.get_properties(self._comune, self._point, response)
self._cache_crit[ATTR_ZONE_NAME] = prop["Nome zona"]
image = CRIT_IMAGE_URL.format(self._id_crit, day_it)

self._cache_crit[day_en] = self.get_info_level(
prop["Rappresentata nella mappa"]
)
self._cache_crit[day_en] = self.get_info_level(prop["Rappresentata nella mappa"])
self._cache_crit[day_en].update(
{
ATTR_IMAGE_URL: image,
Expand All @@ -390,6 +389,7 @@ def get_criticality(self, response: dict, url: str) -> dict:

except Exception as exception:
LOGGER.error("Criticality Exception! - %s", exception)
self._data.pop(CRITICALITY, None)
pass

def get_vigilance(self, response: dict, url: str) -> dict:
Expand All @@ -416,7 +416,7 @@ def get_vigilance(self, response: dict, url: str) -> dict:
self._cache_vigi[day_en].update({ATTR_PHENOMENA: phenomena})

elif "Vigilanza-Meteorologica" in url:
prop = self.get_properties(self._point, response)
prop = self.get_properties(self._comune, self._point, response)
self._cache_vigi[day_en].update(
{
"icon": VIGI_ICON.get(prop["id_classificazione"]),
Expand All @@ -438,6 +438,7 @@ def get_vigilance(self, response: dict, url: str) -> dict:

except Exception as exception:
LOGGER.error("Vigilance Exception! - %s", exception)
self._data.pop(VIGILANCE, None)
pass

@staticmethod
Expand All @@ -449,11 +450,28 @@ def get_info_level(value) -> dict:
return d

@staticmethod
def get_properties(point, geojs) -> dict:
for feature in geojs["features"]:
if not point_in_polygon(point, feature["geometry"]):
continue
return feature["properties"]
def get_properties(comune_conf, point, geojs) -> dict:
if comune_conf:
# from municipality
LOGGER.debug("Get properties from city -> [%s]", comune_conf)
for feature in geojs["features"]:
comuni = feature["properties"].get("Comuni", feature["properties"].get("comuni"))
comune = [city.lower() for city in comuni if comune_conf.lower() == city.lower()]
if not comune:
continue
return feature["properties"]
LOGGER.error("City not found -> [%s]", comune_conf)
else:
# from geolocation
LOGGER.debug("Get properties from point_in_polygon -> [%s]", point)
for feature in geojs["features"]:
if not point_in_polygon(point, feature["geometry"]):
continue
return feature["properties"]
LOGGER.error(
"Unable to find the location, please try to configure the City. -> [%s]",
comune_conf,
)

def get_phenomena(self, point, geojs) -> list:
phenomena = []
Expand Down
4 changes: 1 addition & 3 deletions custom_components/dpc/binary_sensor.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,4 @@ async def async_update(self):

async def async_added_to_hass(self):
"""Subscribe to updates."""
self.async_on_remove(
self.coordinator.async_add_listener(self.async_write_ha_state)
)
self.async_on_remove(self.coordinator.async_add_listener(self.async_write_ha_state))
26 changes: 20 additions & 6 deletions custom_components/dpc/config_flow.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""Adds config flow for Dpc."""
from email.policy import default

import voluptuous as vol

from homeassistant import config_entries
Expand All @@ -13,6 +15,7 @@
import homeassistant.helpers.config_validation as cv

from .const import (
CONF_COMUNE,
CONF_WARNING_LEVEL,
DEFAULT_NAME,
DEFAULT_RADIUS,
Expand All @@ -27,7 +30,7 @@
class DpcFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Config flow for Dpc."""

VERSION = 1
VERSION = 2
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL

async def async_step_user(self, user_input=None):
Expand All @@ -46,9 +49,7 @@ async def async_step_user(self, user_input=None):
CONF_NAME,
default=(DEFAULT_NAME), # {self.hass.config.location_name}
): str,
vol.Required(
CONF_LATITUDE, default=(self.hass.config.latitude)
): cv.latitude,
vol.Required(CONF_LATITUDE, default=(self.hass.config.latitude)): cv.latitude,
vol.Required(
CONF_LONGITUDE, default=(self.hass.config.longitude)
): cv.longitude,
Expand All @@ -69,6 +70,7 @@ def __init__(self, config_entry):
"""Initialize HACS options flow."""
self.config_entry = config_entry
self.options = dict(config_entry.options)
self.data = dict(config_entry.data)

async def async_step_init(self, user_input=None): # pylint: disable=unused-argument
"""Manage the options."""
Expand All @@ -81,11 +83,23 @@ async def async_step_user(self, user_input=None):
return await self._update_options()

schema = {
vol.Required(x, default=self.options.get(x, True)): bool
for x in sorted(PLATFORMS)
vol.Required(x, default=self.options.get(x, True)): bool for x in sorted(PLATFORMS)
}
schema.update(
{
vol.Optional(
CONF_COMUNE,
default="",
description={"suggested_value": self.options.get(CONF_COMUNE, "")},
): str,
vol.Required(
CONF_LATITUDE,
description={"suggested_value": self.data[CONF_LATITUDE]},
): cv.latitude,
vol.Required(
CONF_LONGITUDE,
description={"suggested_value": self.data[CONF_LONGITUDE]},
): cv.longitude,
vol.Optional(
CONF_SCAN_INTERVAL,
default=self.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL),
Expand Down
8 changes: 3 additions & 5 deletions custom_components/dpc/const.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@
# Base component constants
ATTRIBUTION = "Data provided by Civil Protection Department"
DOMAIN = "dpc"
ISSUE_URL = (
"https://github.com/caiosweet/Home-Assistant-custom-components-DPC-Alert/issues"
)
ISSUE_URL = "https://github.com/caiosweet/Home-Assistant-custom-components-DPC-Alert/issues"
NAME = "Dipartimento Protezione Civile"
MANUFACTURER = "Italian Government"
VERSION = "2022.2.0"
VERSION = "2022.2.1"

# Config
CONF_COMUNE = "comune"
CONF_WARNING_LEVEL = "warning_level"

# Defaults
Expand All @@ -27,7 +26,6 @@
DEFAULT_SCAN_INTERVAL = 30 # min

WARNING_ALERT = {
"": 0,
"NESSUNA ALLERTA": 1,
"ALLERTA GIALLA": 2,
"ALLERTA ARANCIONE": 3,
Expand Down
Empty file modified custom_components/dpc/entity.py
100644 → 100755
Empty file.
Loading

0 comments on commit b07b258

Please sign in to comment.