This commit is contained in:
mampfes
2021-04-01 12:13:54 +02:00
parent 136e1c6017
commit dc1fe38f95
22 changed files with 106 additions and 111 deletions

View File

@@ -518,15 +518,14 @@ Example for `abc_com.py`:
```py
import datetime
from waste_collection_schedule import CollectionAppointment
from collections import OrderedDict
from waste_collection_schedule import Collection
DESCRIPTION = "Example source for abc.com" # Describe your source
URL = "abc.com" # Insert url to service homepage
TEST_CASES = OrderedDict( # Insert arguments for test cases using test_sources.py script
[("TestName", {"arg1": 100, "arg2": "street"})]
)
TEST_CASES = { # Insert arguments for test cases using test_sources.py script
"TestName": {"arg1": 100, "arg2": "street"}
}
class Source:
@@ -538,7 +537,7 @@ class Source:
entries = []
entries.append(
CollectionAppointment(
Collection(
datetime.datetime(2020, 4, 11),
"Waste Type",
)

View File

@@ -39,26 +39,26 @@ class WasteCollectionCalendar(CalendarEventDevice):
@property
def event(self):
"""Return next collection event."""
appointments = self._scraper.get_upcoming(count=1, include_today=True)
if len(appointments) == 0:
collections = self._scraper.get_upcoming(count=1, include_today=True)
if len(collections) == 0:
return None
else:
return self._convert(appointments[0])
return self._convert(collections[0])
async def async_get_events(self, hass, start_date, end_date):
"""Return all events within specified time span."""
appointments = []
collections = []
for a in self._scraper.get_upcoming(include_today=True):
if a.date >= start_date.date() and a.date <= end_date.date():
appointments.append(self._convert(a))
return appointments
collections.append(self._convert(a))
return collections
def _convert(self, appointment):
"""Convert an collection appointment into a Home Assistant calendar event."""
def _convert(self, collection):
"""Convert an collection into a Home Assistant calendar event."""
return {
"uid": f"self._scraper.calendar_title-{appointment.date.isoformat()}-{appointment.type}",
"summary": appointment.type,
"start": {"date": appointment.date.isoformat()},
"end": {"date": (appointment.date + timedelta(days=1)).isoformat()},
"uid": f"self._scraper.calendar_title-{collection.date.isoformat()}-{collection.type}",
"summary": collection.type,
"start": {"date": collection.date.isoformat()},
"end": {"date": (collection.date + timedelta(days=1)).isoformat()},
"allDay": True,
}

View File

@@ -1,6 +1,5 @@
"""Sensor platform support for Waste Collection Schedule."""
import collections
import datetime
import logging
from enum import Enum
@@ -22,14 +21,14 @@ CONF_DETAILS_FORMAT = "details_format"
CONF_COUNT = "count"
CONF_LEADTIME = "leadtime"
CONF_DATE_TEMPLATE = "date_template"
CONF_APPOINTMENT_TYPES = "types"
CONF_COLLECTION_TYPES = "types"
class DetailsFormat(Enum):
"""Values for CONF_DETAILS_FORMAT."""
upcoming = "upcoming" # list of "<date> <type1, type2, ...>"
appointment_types = "appointment_types" # list of "<type> <date>"
collection_types = "appointment_types" # list of "<type> <date>"
generic = "generic" # all values in separate attributes
@@ -40,7 +39,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
vol.Optional(CONF_DETAILS_FORMAT, default="upcoming"): cv.enum(DetailsFormat),
vol.Optional(CONF_COUNT): cv.positive_int,
vol.Optional(CONF_LEADTIME): cv.positive_int,
vol.Optional(CONF_APPOINTMENT_TYPES): cv.ensure_list,
vol.Optional(CONF_COLLECTION_TYPES): cv.ensure_list,
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
vol.Optional(CONF_DATE_TEMPLATE): cv.template,
}
@@ -67,7 +66,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
details_format=config[CONF_DETAILS_FORMAT],
count=config.get(CONF_COUNT),
leadtime=config.get(CONF_LEADTIME),
appointment_types=config.get(CONF_APPOINTMENT_TYPES),
collection_types=config.get(CONF_COLLECTION_TYPES),
value_template=value_template,
date_template=date_template,
)
@@ -88,7 +87,7 @@ class ScheduleSensor(Entity):
details_format,
count,
leadtime,
appointment_types,
collection_types,
value_template,
date_template,
):
@@ -99,7 +98,7 @@ class ScheduleSensor(Entity):
self._details_format = details_format
self._count = count
self._leadtime = leadtime
self._appointment_types = appointment_types
self._collection_types = collection_types
self._value_template = value_template
self._date_template = date_template
@@ -155,7 +154,7 @@ class ScheduleSensor(Entity):
@property
def _include_today(self):
"""Return true if appointments for today shall be included in the results."""
"""Return true if collections for today shall be included in the results."""
return datetime.datetime.now().time() < self._api._day_switch_time
def _add_refreshtime(self):
@@ -173,26 +172,28 @@ class ScheduleSensor(Entity):
self._picture = None
return
appointment = upcoming[0]
# appointment::=CollectionAppointmentGroup{date=2020-04-01, types=['Type1', 'Type2']}
collection = upcoming[0]
# collection::=CollectionGroup{date=2020-04-01, types=['Type1', 'Type2']}
if self._value_template is not None:
self._state = self._value_template.async_render_with_possible_json_value(
appointment, None
collection, None
)
else:
self._state = f"{self._separator.join(appointment.types)} in {appointment.daysTo} days"
self._state = (
f"{self._separator.join(collection.types)} in {collection.daysTo} days"
)
self._icon = appointment.icon
self._picture = appointment.picture
self._icon = collection.icon
self._picture = collection.picture
def _render_date(self, appointment):
def _render_date(self, collection):
if self._date_template is not None:
return self._date_template.async_render_with_possible_json_value(
appointment, None
collection, None
)
else:
return appointment.date.isoformat()
return collection.date.isoformat()
@callback
def _update_sensor(self):
@@ -207,17 +208,17 @@ class ScheduleSensor(Entity):
self._set_state(
self._scraper.get_upcoming_group_by_day(
count=1,
types=self._appointment_types,
types=self._collection_types,
include_today=self._include_today,
)
)
attributes = collections.OrderedDict()
attributes = {}
appointment_types = (
collection_types = (
sorted(self._scraper.get_types())
if self._appointment_types is None
else self._appointment_types
if self._collection_types is None
else self._collection_types
)
if self._details_format == DetailsFormat.upcoming:
@@ -225,30 +226,30 @@ class ScheduleSensor(Entity):
upcoming = self._scraper.get_upcoming_group_by_day(
count=self._count,
leadtime=self._leadtime,
types=self._appointment_types,
types=self._collection_types,
include_today=self._include_today,
)
for appointment in upcoming:
attributes[self._render_date(appointment)] = self._separator.join(
appointment.types
for collection in upcoming:
attributes[self._render_date(collection)] = self._separator.join(
collection.types
)
elif self._details_format == DetailsFormat.appointment_types:
# show list of appointments in details
for t in appointment_types:
appointments = self._scraper.get_upcoming(
elif self._details_format == DetailsFormat.collection_types:
# show list of collections in details
for t in collection_types:
collections = self._scraper.get_upcoming(
count=1, types=[t], include_today=self._include_today
)
date = (
"" if len(appointments) == 0 else self._render_date(appointments[0])
"" if len(collections) == 0 else self._render_date(collections[0])
)
attributes[t] = date
elif self._details_format == DetailsFormat.generic:
# insert generic attributes into details
attributes["types"] = appointment_types
attributes["types"] = collection_types
attributes["upcoming"] = self._scraper.get_upcoming(
count=self._count,
leadtime=self._leadtime,
types=self._appointment_types,
types=self._collection_types,
include_today=self._include_today,
)
refreshtime = ""

View File

@@ -1,3 +1,2 @@
from .helpers import (CollectionAppointment, CollectionAppointmentBase,
CollectionAppointmentGroup)
from .scraper import Customize, Scraper
from .collection import Collection, CollectionBase, CollectionGroup # type: ignore # isort:skip # noqa: F401
from .scraper import Customize, Scraper # noqa: F401

View File

@@ -1,7 +1,7 @@
import datetime
class CollectionAppointmentBase(dict): # inherit from dict to enable JSON serialization
class CollectionBase(dict): # inherit from dict to enable JSON serialization
def __init__(self, date: datetime.date, icon: str = None, picture: str = None):
dict.__init__(self, date=date.isoformat(), icon=icon, picture=picture)
self._date = date # store date also as python date object
@@ -29,11 +29,11 @@ class CollectionAppointmentBase(dict): # inherit from dict to enable JSON seria
self["picture"] = picture
class CollectionAppointment(CollectionAppointmentBase):
class Collection(CollectionBase):
def __init__(
self, date: datetime.date, t: str, icon: str = None, picture: str = None
):
CollectionAppointmentBase.__init__(self, date=date, icon=icon, picture=picture)
CollectionBase.__init__(self, date=date, icon=icon, picture=picture)
self["type"] = t
@property
@@ -44,17 +44,17 @@ class CollectionAppointment(CollectionAppointmentBase):
self["type"] = t
def __repr__(self):
return f"CollectionAppointment{{date={self.date}, type={self.type}}}"
return f"Collection{{date={self.date}, type={self.type}}}"
class CollectionAppointmentGroup(CollectionAppointmentBase):
class CollectionGroup(CollectionBase):
def __init__(self, date: datetime.date):
CollectionAppointmentBase.__init__(self, date=date)
CollectionBase.__init__(self, date=date)
@staticmethod
def create(group):
"""Create from list of CollectionAppointment's."""
x = CollectionAppointmentGroup(group[0].date)
"""Create from list of Collection's."""
x = CollectionGroup(group[0].date)
if len(group) == 1:
x.set_icon(group[0].icon)
x.set_picture(group[0].picture)
@@ -68,4 +68,4 @@ class CollectionAppointmentGroup(CollectionAppointmentBase):
return self["types"]
def __repr__(self):
return f"CollectionAppointmentGroup{{date={self.date}, types={self.types}}}"
return f"CollectionGroup{{date={self.date}, types={self.types}}}"

View File

@@ -7,7 +7,7 @@ import logging
import os
from typing import Dict, List, Optional
from .helpers import CollectionAppointment, CollectionAppointmentGroup
from .collection import Collection, CollectionGroup
_LOGGER = logging.getLogger(__name__)
@@ -46,7 +46,7 @@ class Customize:
return f"Customize{{waste_type={self._waste_type}, alias={self._alias}, show={self._show}, icon={self._icon}, picture={self._picture}}}"
def filter_function(entry: CollectionAppointment, customize: Dict[str, Customize]):
def filter_function(entry: Collection, customize: Dict[str, Customize]):
c = customize.get(entry.type)
if c is None:
return True
@@ -54,7 +54,7 @@ def filter_function(entry: CollectionAppointment, customize: Dict[str, Customize
return c.show
def customize_function(entry: CollectionAppointment, customize: Dict[str, Customize]):
def customize_function(entry: Collection, customize: Dict[str, Customize]):
c = customize.get(entry.type)
if c is not None:
if c.alias is not None:
@@ -83,7 +83,7 @@ class Scraper:
self._url = url
self._calendar_title = calendar_title
self._refreshtime = None
self._entries: List[CollectionAppointment] = []
self._entries: List[Collection] = []
@property
def source(self):
@@ -112,7 +112,7 @@ class Scraper:
def fetch(self):
"""Fetch data from source."""
try:
# fetch returns a list of CollectionAppointment's
# fetch returns a list of Collection's
entries = self._source.fetch()
self._refreshtime = datetime.datetime.now()
@@ -131,7 +131,7 @@ class Scraper:
_LOGGER.error(f"fetch failed for source {self._source}: {error}")
def get_types(self):
"""Return set() of all appointment types."""
"""Return set() of all collection types."""
types = set()
for e in self._entries:
types.add(e.type)
@@ -169,7 +169,7 @@ class Scraper:
)
for key, group in iterator:
entries.append(CollectionAppointmentGroup.create(list(group)))
entries.append(CollectionGroup.create(list(group)))
if count is not None:
entries = entries[:count]

View File

@@ -88,7 +88,7 @@ class AbfallnaviDe:
return result
def _get_dates(self, target, id, waste_types=None):
# retrieve appointments
# retrieve collections
args = []
if waste_types is None:

View File

@@ -2,7 +2,7 @@ import datetime
from html.parser import HTMLParser
import requests
from waste_collection_schedule import CollectionAppointment
from waste_collection_schedule import Collection # type: ignore[attr-defined]
from waste_collection_schedule.service.ICS import ICS
TITLE = "AbfallPlus"
@@ -119,5 +119,5 @@ class Source:
entries = []
for d in dates:
entries.append(CollectionAppointment(d[0], d[1]))
entries.append(Collection(d[0], d[1]))
return entries

View File

@@ -1,7 +1,7 @@
import datetime
import requests
from waste_collection_schedule import CollectionAppointment
from waste_collection_schedule import Collection # type: ignore[attr-defined]
from waste_collection_schedule.service.ICS import ICS
TITLE = "Abfall Kreis Tübingen"
@@ -65,5 +65,5 @@ class Source:
entries = []
for d in dates:
entries.append(CollectionAppointment(d[0], d[1]))
entries.append(Collection(d[0], d[1]))
return entries

View File

@@ -1,7 +1,7 @@
from datetime import datetime
import requests
from waste_collection_schedule import CollectionAppointment
from waste_collection_schedule import Collection # type: ignore[attr-defined]
from waste_collection_schedule.service.ICS import ICS
TITLE = "Abfall Zollernalbkreis"
@@ -83,5 +83,5 @@ class Source:
entries = []
for d in dates:
entries.append(CollectionAppointment(d[0], d[1]))
entries.append(Collection(d[0], d[1]))
return entries

View File

@@ -1,4 +1,4 @@
from waste_collection_schedule import CollectionAppointment
from waste_collection_schedule import Collection # type: ignore[attr-defined]
from waste_collection_schedule.service.AbfallnaviDe import AbfallnaviDe
TITLE = "AbfallNavi"
@@ -39,5 +39,5 @@ class Source:
entries = []
for d in dates:
entries.append(CollectionAppointment(d[0], d[1]))
entries.append(Collection(d[0], d[1]))
return entries

View File

@@ -2,7 +2,7 @@ import datetime
import json
import requests
from waste_collection_schedule import CollectionAppointment
from waste_collection_schedule import Collection # type: ignore[attr-defined]
TITLE = "AWB Köln"
DESCRIPTION = "Source for Abfallwirtschaftsbetriebe Köln waste collection."
@@ -35,6 +35,6 @@ class Source:
entries = []
for d in data["data"]:
date = datetime.date(year=d["year"], month=d["month"], day=d["day"])
entries.append(CollectionAppointment(date, d["type"]))
entries.append(Collection(date, d["type"]))
return entries

View File

@@ -2,7 +2,7 @@ import json
from datetime import date, timedelta
import requests
from waste_collection_schedule import CollectionAppointment
from waste_collection_schedule import Collection # type: ignore[attr-defined]
TITLE = "Brisbane City Council"
DESCRIPTION = "Source for Brisbane City Council rubbish collection."
@@ -98,19 +98,19 @@ class Source:
if (collection_date - today).days >= 0:
# Every collection day includes rubbish
entries.append(
CollectionAppointment(
Collection(
date=collection_date, t="Rubbish", icon="mdi:trash-can"
)
)
if item["event_type"] == "recycle":
entries.append(
CollectionAppointment(
Collection(
date=collection_date, t="Recycling", icon="mdi:recycle"
)
)
if item["event_type"] == "organic":
entries.append(
CollectionAppointment(
Collection(
date=collection_date, t="Garden", icon="mdi:leaf"
)
)

View File

@@ -1,7 +1,7 @@
import urllib.parse
import requests
from waste_collection_schedule import CollectionAppointment
from waste_collection_schedule import Collection # type: ignore[attr-defined]
from waste_collection_schedule.service.ICS import ICS
TITLE = "BSR"
@@ -93,5 +93,5 @@ class Source:
entries = []
for d in dates:
entries.append(CollectionAppointment(d[0], d[1]))
entries.append(Collection(d[0], d[1]))
return entries

View File

@@ -1,7 +1,7 @@
import datetime
from typing import Dict
from waste_collection_schedule import CollectionAppointment
from waste_collection_schedule import Collection # type: ignore[attr-defined]
TITLE = "Example Source"
DESCRIPTION = "Source for example waste collection."
@@ -24,7 +24,7 @@ class Source:
for day in range(self._days):
for idx in range(self._per_day):
entries.append(
CollectionAppointment(
Collection(
now + datetime.timedelta(days=day + 7),
f"Type{(ap_type % self._types) + 1}",
)

View File

@@ -2,7 +2,7 @@ import datetime
from pathlib import Path
import requests
from waste_collection_schedule import CollectionAppointment
from waste_collection_schedule import Collection # type: ignore[attr-defined]
from waste_collection_schedule.service.ICS import ICS
TITLE = "ICS"
@@ -129,5 +129,5 @@ class Source:
entries = []
for d in dates:
entries.append(CollectionAppointment(d[0], d[1]))
entries.append(Collection(d[0], d[1]))
return entries

View File

@@ -2,7 +2,7 @@ import datetime
import json
import requests
from waste_collection_schedule import CollectionAppointment
from waste_collection_schedule import Collection # type: ignore[attr-defined]
TITLE = "Jumomind"
DESCRIPTION = "Source for Jumomind.de waste collection."
@@ -32,7 +32,7 @@ class Source:
data = json.loads(r.text)
for d in data:
entries.append(
CollectionAppointment(datetime.date.fromisoformat(d["day"]), d["title"])
Collection(datetime.date.fromisoformat(d["day"]), d["title"])
)
return entries

View File

@@ -1,7 +1,7 @@
from html.parser import HTMLParser
import requests
from waste_collection_schedule import CollectionAppointment
from waste_collection_schedule import Collection # type: ignore[attr-defined]
from waste_collection_schedule.service.ICS import ICS
TITLE = "Müllmax"
@@ -147,5 +147,5 @@ class Source:
entries = []
for d in dates:
entries.append(CollectionAppointment(d[0], d[1]))
entries.append(Collection(d[0], d[1]))
return entries

View File

@@ -3,7 +3,7 @@ import json
from urllib.parse import quote
import requests
from waste_collection_schedule import CollectionAppointment
from waste_collection_schedule import Collection # type: ignore[attr-defined]
TITLE = "PGH.ST"
DESCRIPTION = "Source for PGH.ST services for the city of Pittsburgh, PA, USA."
@@ -35,21 +35,21 @@ class Source:
# create entries for trash, recycling, and yard waste
entries = [
CollectionAppointment(
Collection(
date=datetime.datetime.strptime(
data[0]["next_pickup_date"], "%m-%d-%Y"
).date(),
t="Trash",
icon="mdi:trash-can",
),
CollectionAppointment(
Collection(
date=datetime.datetime.strptime(
data[0]["next_recycling_date"], "%m-%d-%Y"
).date(),
t="Recycling",
icon="mdi:recycle",
),
CollectionAppointment(
Collection(
date=datetime.datetime.strptime(
data[0]["next_yard_date"], "%m-%d-%Y"
).date(),

View File

@@ -4,7 +4,7 @@ import time
from urllib.parse import quote
import requests
from waste_collection_schedule import CollectionAppointment
from waste_collection_schedule import Collection # type: ignore[attr-defined]
TITLE = "Seattle Public Utilities"
DESCRIPTION = "Source for Seattle Public Utilities waste collection."
@@ -44,21 +44,17 @@ class Source:
if next_pickup["Garbage"]:
entries.append(
CollectionAppointment(
date=next_pickup_date, t="Trash", icon="mdi:trash-can"
)
Collection(date=next_pickup_date, t="Trash", icon="mdi:trash-can")
)
if next_pickup["FoodAndYardWaste"]:
entries.append(
CollectionAppointment(
Collection(
date=next_pickup_date, t="Food and Yard Waste", icon="mdi:leaf"
)
)
if next_pickup["Recycling"]:
entries.append(
CollectionAppointment(
date=next_pickup_date, t="Recycling", icon="mdi:recycle"
)
Collection(date=next_pickup_date, t="Recycling", icon="mdi:recycle")
)
return entries

View File

@@ -1,5 +1,5 @@
import requests
from waste_collection_schedule import CollectionAppointment
from waste_collection_schedule import Collection # type: ignore[attr-defined]
from waste_collection_schedule.service.ICS import ICS
TITLE = "Stadtreinigung Hamburg"
@@ -29,5 +29,5 @@ class Source:
entries = []
for d in dates:
entries.append(CollectionAppointment(d[0], d[1]))
entries.append(Collection(d[0], d[1]))
return entries

View File

@@ -2,7 +2,7 @@ import datetime
from html.parser import HTMLParser
import requests
from waste_collection_schedule import CollectionAppointment
from waste_collection_schedule import Collection # type: ignore[attr-defined]
TITLE = "Abfall Stuttgart"
DESCRIPTION = "Source for waste collections for the city of Stuttgart, Germany."
@@ -72,7 +72,7 @@ class TableParser(HTMLParser):
elif tag == "tr":
if self._within_tr and len(self._date) > 0:
date = datetime.datetime.strptime(self._date, "%d.%m.%Y").date()
self._entries.append(CollectionAppointment(date, self._type))
self._entries.append(Collection(date, self._type))
self._date = ""
self._within_tr = False
self._within_th = False