Initial commit
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
"""The corona_hessen component."""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import re
|
||||
from datetime import timedelta
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
from .const import DOMAIN
|
||||
from .crawler import CovidCrawler, IncidenceData
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
__version__ = "0.1.0"
|
||||
|
||||
PLATFORMS = ["sensor"]
|
||||
|
||||
HYPHEN_PATTERN = re.compile(r"- (.)")
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: dict):
|
||||
"""Set up the Coronavirus Augsburg component."""
|
||||
# Make sure coordinator is initialized.
|
||||
coordinator = await get_coordinator(hass)
|
||||
|
||||
async def handle_refresh(call):
|
||||
_LOGGER.info("Refreshing Coronavirus Augsburg data...")
|
||||
await coordinator.async_refresh()
|
||||
|
||||
hass.services.async_register(DOMAIN, "refresh", handle_refresh)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Set up Coronavirus Augsburg from a config entry."""
|
||||
|
||||
for component in PLATFORMS:
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setup(entry, component)
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Unload a config entry."""
|
||||
unload_ok = all(
|
||||
await asyncio.gather(
|
||||
*[
|
||||
hass.config_entries.async_forward_entry_unload(entry, cmp)
|
||||
for cmp in PLATFORMS
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
||||
async def get_coordinator(hass):
|
||||
"""Get the data update coordinator."""
|
||||
if DOMAIN in hass.data:
|
||||
return hass.data[DOMAIN]
|
||||
|
||||
async def async_get_data() -> IncidenceData:
|
||||
crawler = CovidCrawler()
|
||||
return crawler.crawl()
|
||||
|
||||
hass.data[DOMAIN] = DataUpdateCoordinator(
|
||||
hass,
|
||||
logging.getLogger(__name__),
|
||||
name=DOMAIN,
|
||||
update_method=async_get_data,
|
||||
update_interval=timedelta(hours=6),
|
||||
)
|
||||
await hass.data[DOMAIN].async_refresh()
|
||||
return hass.data[DOMAIN]
|
||||
@@ -0,0 +1 @@
|
||||
DOMAIN = "covid19_augsburg"
|
||||
94
custom_components/home_assistant_covid19_augsburg/crawler.py
Normal file
94
custom_components/home_assistant_covid19_augsburg/crawler.py
Normal file
@@ -0,0 +1,94 @@
|
||||
import datetime
|
||||
import locale
|
||||
import logging
|
||||
import re
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
_log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class IncidenceData:
|
||||
location: str
|
||||
date: datetime.date
|
||||
incidence: float
|
||||
total_cases: int = 0
|
||||
num_infected: int = 0
|
||||
num_recovered: int = 0
|
||||
num_dead: int = 0
|
||||
|
||||
|
||||
class CovidCrawlerBase(ABC):
|
||||
@abstractmethod
|
||||
def crawl(self) -> IncidenceData:
|
||||
pass
|
||||
|
||||
|
||||
class CovidCrawler(CovidCrawlerBase):
|
||||
def __init__(self) -> None:
|
||||
self.url = (
|
||||
"https://www.augsburg.de/umwelt-soziales/gesundheit/coronavirus/fallzahlen"
|
||||
)
|
||||
|
||||
def crawl(self) -> IncidenceData:
|
||||
"""
|
||||
Fetch COVID-19 infection data from the target website.
|
||||
"""
|
||||
|
||||
_log.info("Fetching COVID-19 data update")
|
||||
|
||||
locale.setlocale(locale.LC_ALL, "de_DE.utf8")
|
||||
|
||||
result = requests.get(self.url)
|
||||
if not result.ok:
|
||||
result.raise_for_status()
|
||||
|
||||
soup = BeautifulSoup(result.text, features="html.parser")
|
||||
|
||||
match = soup.find(class_="frame--type-textpic")
|
||||
text = match.p.text
|
||||
_log.debug(f"Infection data text: {text}")
|
||||
|
||||
matches = re.search(r"(\d+,\d+) Neuinfektion", text)
|
||||
if not matches:
|
||||
raise ValueError("Could not extract incidence from scraped web page")
|
||||
|
||||
incidence = locale.atof(matches.group(1))
|
||||
_log.debug(f"Parsed incidence: {incidence}")
|
||||
|
||||
text = match.h2.text
|
||||
matches = re.search(r"\((\d+\. \w+)\)", text)
|
||||
if not matches:
|
||||
raise ValueError("Could not extract date from scraped web page")
|
||||
|
||||
date = datetime.datetime.strptime(matches.group(1), "%d. %B")
|
||||
date = date.replace(year=datetime.datetime.now().year).date()
|
||||
_log.debug(f"Parsed date: {date}")
|
||||
|
||||
match = match.find_next_sibling(class_="frame--type-textpic")
|
||||
text = match.text
|
||||
_log.debug(f"Infection counts text: {text}")
|
||||
|
||||
regexes = [
|
||||
r"Insgesamt: (?P<total_cases>[0-9.]+)",
|
||||
r"genesen: (?P<num_recovered>[0-9.]+)",
|
||||
r"infiziert: (?P<num_infected>[0-9.]+)",
|
||||
r"verstorben: (?P<num_dead>[0-9.]+)",
|
||||
]
|
||||
cases = {}
|
||||
for r in regexes:
|
||||
matches = re.search(r, text)
|
||||
if not matches:
|
||||
continue
|
||||
cases.update(
|
||||
{k: int(v.replace(".", "")) for k, v in matches.groupdict().items()}
|
||||
)
|
||||
|
||||
result = IncidenceData("Augsburg", incidence, date, **cases)
|
||||
_log.debug(f"Result data: {result}")
|
||||
|
||||
return result
|
||||
11
custom_components/home_assistant_covid19_augsburg/main.py
Normal file
11
custom_components/home_assistant_covid19_augsburg/main.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from home_assistant_covid19_augsburg.crawler import CovidCrawler
|
||||
|
||||
|
||||
def main():
|
||||
crawler = CovidCrawler()
|
||||
result = crawler.crawl()
|
||||
print(result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"domain": "covid19_augsburg",
|
||||
"name": "COVID-19 Augsburg",
|
||||
"version": "0.1.0",
|
||||
"config_flow": true,
|
||||
"documentation": "https://github.com/AdrianoKF/home-assistant-covid19-augsburg",
|
||||
"requirements": ["beautifulsoup4==4.8.2", "requests==2.25.1"],
|
||||
"dependencies": [],
|
||||
"codeowners": ["@AdrianoKF"]
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
refresh:
|
||||
description: Refreshes data from web
|
||||
Reference in New Issue
Block a user