backup
This commit is contained in:
@@ -1 +1 @@
|
||||
2022.12.8
|
||||
2023.2.5
|
||||
35
airsonos.xml
Normal file
35
airsonos.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0"?>
|
||||
<main_log>info</main_log>
|
||||
<upnp_log>info</upnp_log>
|
||||
<util_log>warn</util_log>
|
||||
<raop_log>info</raop_log>
|
||||
<log_limit>-1</log_limit>
|
||||
<max_players>32</max_players>
|
||||
<binding>?</binding>
|
||||
<ports>0:0</ports>
|
||||
<enabled>1</enabled>
|
||||
<max_volume>100</max_volume>
|
||||
<http_length>-1</http_length>
|
||||
<upnp_max>1</upnp_max>
|
||||
<codec>flc</codec>
|
||||
<metadata>1</metadata>
|
||||
<flush>1</flush>
|
||||
<artwork></artwork>
|
||||
<latency>1000:2000</latency>
|
||||
<drift>0</drift>
|
||||
<pcm>http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=00;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=0d500000000000000000000000000000</pcm>
|
||||
<wav>http-get:*:audio/wav:DLNA.ORG_OP=00;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=0d500000000000000000000000000000</wav>
|
||||
<flac>http-get:*:audio/flac:DLNA.ORG_OP=00;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=0d500000000000000000000000000000</flac>
|
||||
<mp3>http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=00;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=0d500000000000000000000000000000</mp3>
|
||||
<device>
|
||||
<udn>uuid:RINCON_38420B93464001400</udn>
|
||||
<name>Keuken+</name>
|
||||
<mac>bb:bb:0b:93:46:40</mac>
|
||||
<enabled>1</enabled>
|
||||
</device>
|
||||
<device>
|
||||
<udn>uuid:BO5EBO5E-F00D-F00D-FEED-506583CEC197</udn>
|
||||
<name>Bose woonkamer+</name>
|
||||
<mac>bb:bb:7f:f3:34:e1</mac>
|
||||
<enabled>1</enabled>
|
||||
</device>
|
||||
869
automations.yaml
869
automations.yaml
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,9 @@
|
||||
script: !include scripts.yaml
|
||||
scene: !include scenes.yaml
|
||||
|
||||
frontend:
|
||||
themes: !include_dir_merge_named themes/
|
||||
|
||||
homeassistant:
|
||||
#packages: !include_dir_named packages/
|
||||
packages: !include_dir_named "integrations"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -15,7 +15,7 @@ SENSOR_TYPES = {
|
||||
|
||||
SENSOR_LOCATIONS_TO_URL = {
|
||||
"trashapi": [
|
||||
"http://trashapi.azurewebsites.net/trash?Location={0}&ZipCode={1}&HouseNumber={2}&HouseNumberSuffix={3}&DiftarCode={4}"
|
||||
"http://trashapi.azurewebsites.net/trash?Location={0}&ZipCode={1}&HouseNumber={2}&HouseNumberSuffix={3}&District={4}&DiftarCode={5}&ShowWholeYear={6}"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -66,6 +66,8 @@ CONF_LOCATION = "location"
|
||||
CONF_POSTCODE = "postcode"
|
||||
CONF_STREET_NUMBER = "streetnumber"
|
||||
CONF_STREET_NUMBER_SUFFIX = "streetnumbersuffix"
|
||||
CONF_DISTRICT = "district"
|
||||
CONF_GET_WHOLE_YEAR = "getwholeyear"
|
||||
CONF_DATE_FORMAT = "dateformat"
|
||||
CONF_TIMESPAN_IN_DAYS = "timespanindays"
|
||||
CONF_LOCALE = "locale"
|
||||
@@ -82,6 +84,7 @@ ATTR_YEAR_MONTH_DAY_DATE = "year_month_day_date"
|
||||
ATTR_FRIENDLY_NAME = "friendly_name"
|
||||
ATTR_LAST_COLLECTION_DATE = "last_collection_date"
|
||||
ATTR_TOTAL_COLLECTIONS_THIS_YEAR = "total_collections_this_year"
|
||||
ATTR_WHOLE_YEAR_DATES = "whole_year_dates"
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -16,38 +16,48 @@ class TrashApiAfval(object):
|
||||
postcode,
|
||||
street_number,
|
||||
street_number_suffix,
|
||||
district,
|
||||
diftar_code,
|
||||
get_whole_year,
|
||||
resources,
|
||||
):
|
||||
_LOGGER.debug("Updating Waste collection dates")
|
||||
|
||||
try:
|
||||
API_ENDPOINT = SENSOR_LOCATIONS_TO_URL["trashapi"][0].format(
|
||||
location, postcode, street_number, street_number_suffix, diftar_code
|
||||
location,
|
||||
postcode,
|
||||
street_number,
|
||||
street_number_suffix,
|
||||
district,
|
||||
diftar_code,
|
||||
get_whole_year,
|
||||
)
|
||||
|
||||
r = requests.get(url=API_ENDPOINT)
|
||||
dataList = r.json()
|
||||
|
||||
# Place all possible values in the dictionary even if they are not necessary
|
||||
waste_dict = {}
|
||||
waste_array = []
|
||||
|
||||
# _LOGGER.warning(dataList)
|
||||
|
||||
for data in dataList:
|
||||
|
||||
# find gft.
|
||||
if "gft" in resources and data["name"].lower() == "gft":
|
||||
waste_dict["gft"] = data["date"].split("T")[0]
|
||||
# find kerstboom.
|
||||
if "kerstboom" in resources and data["name"].lower() == "kerstboom":
|
||||
waste_dict["kerstboom"] = data["date"].split("T")[0]
|
||||
# find papier
|
||||
if "papier" in resources and data["name"].lower() == "papier":
|
||||
waste_dict["papier"] = data["date"].split("T")[0]
|
||||
# find pbd.
|
||||
if "pbd" in resources and data["name"].lower() == "pbd":
|
||||
waste_dict["pbd"] = data["date"].split("T")[0]
|
||||
# find gft, kerstboom, papier, pbd, takken or textiel
|
||||
if (
|
||||
("gft" in resources and data["name"].lower() == "gft")
|
||||
or (
|
||||
"kerstboom" in resources and data["name"].lower() == "kerstboom"
|
||||
)
|
||||
or ("papier" in resources and data["name"].lower() == "papier")
|
||||
or ("pbd" in resources and data["name"].lower() == "pbd")
|
||||
or ("takken" in resources and data["name"].lower() == "takken")
|
||||
or ("textiel" in resources and data["name"].lower() == "textiel")
|
||||
):
|
||||
waste_array.append(
|
||||
{data["name"].lower(): data["date"].split("T")[0]}
|
||||
)
|
||||
# find restafval.
|
||||
if "restafval" in resources and data["name"].lower() == "restafval":
|
||||
if (
|
||||
@@ -56,18 +66,20 @@ class TrashApiAfval(object):
|
||||
data["date"].split("T")[0], "%Y-%m-%d"
|
||||
).date()
|
||||
):
|
||||
waste_dict["restafval"] = data["date"].split("T")[0]
|
||||
waste_array.append(
|
||||
{data["name"].lower(): data["date"].split("T")[0]}
|
||||
)
|
||||
else:
|
||||
waste_dict["restafvaldiftardate"] = data["date"].split("T")[0]
|
||||
waste_dict["restafvaldiftarcollections"] = data["totalThisYear"]
|
||||
# find takken
|
||||
if "takken" in resources and data["name"].lower() == "takken":
|
||||
waste_dict["takken"] = data["date"].split("T")[0]
|
||||
# find textiel
|
||||
if "textiel" in resources and data["name"].lower() == "textiel":
|
||||
waste_dict["textiel"] = data["date"].split("T")[0]
|
||||
waste_array.append(
|
||||
{"restafvaldiftardate": data["date"].split("T")[0]}
|
||||
)
|
||||
waste_array.append(
|
||||
{"restafvaldiftarcollections": data["totalThisYear"]}
|
||||
)
|
||||
|
||||
return waste_dict
|
||||
# _LOGGER.warning(waste_array)
|
||||
|
||||
return waste_array
|
||||
except urllib.error.URLError as exc:
|
||||
_LOGGER.error("Error occurred while fetching data: %r", exc.reason)
|
||||
return False
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"domain": "afvalinfo",
|
||||
"name": "Afvalinfo",
|
||||
"version": "1.0.9",
|
||||
"version": "1.1.0",
|
||||
"documentation": "https://github.com/heyajohnny/afvalinfo",
|
||||
"issue_tracker": "https://github.com/heyajohnny/afvalinfo/issues",
|
||||
"dependencies": [],
|
||||
|
||||
@@ -16,10 +16,12 @@ from .const.const import (
|
||||
MIN_TIME_BETWEEN_UPDATES,
|
||||
_LOGGER,
|
||||
CONF_CITY,
|
||||
CONF_DISTRICT,
|
||||
CONF_LOCATION,
|
||||
CONF_POSTCODE,
|
||||
CONF_STREET_NUMBER,
|
||||
CONF_STREET_NUMBER_SUFFIX,
|
||||
CONF_GET_WHOLE_YEAR,
|
||||
CONF_DATE_FORMAT,
|
||||
CONF_TIMESPAN_IN_DAYS,
|
||||
CONF_NO_TRASH_TEXT,
|
||||
@@ -36,6 +38,7 @@ from .const.const import (
|
||||
ATTR_FRIENDLY_NAME,
|
||||
ATTR_LAST_COLLECTION_DATE,
|
||||
ATTR_TOTAL_COLLECTIONS_THIS_YEAR,
|
||||
ATTR_WHOLE_YEAR_DATES,
|
||||
SENSOR_TYPES,
|
||||
)
|
||||
|
||||
@@ -57,12 +60,14 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
vol.Required(CONF_POSTCODE, default="3361AB"): cv.string,
|
||||
vol.Required(CONF_STREET_NUMBER, default="1"): cv.string,
|
||||
vol.Optional(CONF_STREET_NUMBER_SUFFIX, default=""): cv.string,
|
||||
vol.Optional(CONF_DISTRICT, default=""): cv.string,
|
||||
vol.Optional(CONF_DATE_FORMAT, default="%d-%m-%Y"): cv.string,
|
||||
vol.Optional(CONF_TIMESPAN_IN_DAYS, default="365"): cv.string,
|
||||
vol.Optional(CONF_LOCALE, default="en"): cv.string,
|
||||
vol.Optional(CONF_ID, default=""): cv.string,
|
||||
vol.Optional(CONF_NO_TRASH_TEXT, default="none"): cv.string,
|
||||
vol.Optional(CONF_DIFTAR_CODE, default=""): cv.string,
|
||||
vol.Optional(CONF_GET_WHOLE_YEAR, default="false"): cv.string,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -76,12 +81,14 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
postcode = config.get(CONF_POSTCODE).strip()
|
||||
street_number = config.get(CONF_STREET_NUMBER)
|
||||
street_number_suffix = config.get(CONF_STREET_NUMBER_SUFFIX)
|
||||
district = config.get(CONF_DISTRICT)
|
||||
date_format = config.get(CONF_DATE_FORMAT).strip()
|
||||
timespan_in_days = config.get(CONF_TIMESPAN_IN_DAYS)
|
||||
locale = config.get(CONF_LOCALE)
|
||||
id_name = config.get(CONF_ID)
|
||||
no_trash_text = config.get(CONF_NO_TRASH_TEXT)
|
||||
diftar_code = config.get(CONF_DIFTAR_CODE)
|
||||
get_whole_year = config.get(CONF_GET_WHOLE_YEAR)
|
||||
|
||||
try:
|
||||
resources = config[CONF_RESOURCES].copy()
|
||||
@@ -102,7 +109,9 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
postcode,
|
||||
street_number,
|
||||
street_number_suffix,
|
||||
district,
|
||||
diftar_code,
|
||||
get_whole_year,
|
||||
resourcesMinusTodayAndTomorrow,
|
||||
)
|
||||
except urllib.error.HTTPError as error:
|
||||
@@ -139,6 +148,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
timespan_in_days,
|
||||
locale,
|
||||
id_name,
|
||||
get_whole_year,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -175,7 +185,9 @@ class AfvalinfoData(object):
|
||||
postcode,
|
||||
street_number,
|
||||
street_number_suffix,
|
||||
district,
|
||||
diftar_code,
|
||||
get_whole_year,
|
||||
resources,
|
||||
):
|
||||
self.data = None
|
||||
@@ -183,7 +195,9 @@ class AfvalinfoData(object):
|
||||
self.postcode = postcode
|
||||
self.street_number = street_number
|
||||
self.street_number_suffix = street_number_suffix
|
||||
self.district = district
|
||||
self.diftar_code = diftar_code
|
||||
self.get_whole_year = get_whole_year
|
||||
self.resources = resources
|
||||
|
||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||
@@ -194,7 +208,9 @@ class AfvalinfoData(object):
|
||||
self.postcode,
|
||||
self.street_number,
|
||||
self.street_number_suffix,
|
||||
self.district,
|
||||
self.diftar_code,
|
||||
self.get_whole_year,
|
||||
self.resources,
|
||||
)
|
||||
|
||||
@@ -209,6 +225,7 @@ class AfvalinfoSensor(Entity):
|
||||
timespan_in_days,
|
||||
locale,
|
||||
id_name,
|
||||
get_whole_year,
|
||||
):
|
||||
self.data = data
|
||||
self.type = sensor_type
|
||||
@@ -217,6 +234,7 @@ class AfvalinfoSensor(Entity):
|
||||
self.timespan_in_days = timespan_in_days
|
||||
self.locale = locale
|
||||
self._name = sensor_friendly_name
|
||||
self._get_whole_year = get_whole_year
|
||||
self.entity_id = "sensor." + (
|
||||
(
|
||||
SENSOR_PREFIX
|
||||
@@ -241,6 +259,7 @@ class AfvalinfoSensor(Entity):
|
||||
self._year_month_day_date = None
|
||||
self._last_collection_date = None
|
||||
self._total_collections_this_year = None
|
||||
self._whole_year_dates = None
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
@@ -266,98 +285,119 @@ class AfvalinfoSensor(Entity):
|
||||
ATTR_IS_COLLECTION_DATE_TODAY: self._is_collection_date_today,
|
||||
ATTR_LAST_COLLECTION_DATE: self._last_collection_date,
|
||||
ATTR_TOTAL_COLLECTIONS_THIS_YEAR: self._total_collections_this_year,
|
||||
ATTR_WHOLE_YEAR_DATES: self._whole_year_dates,
|
||||
}
|
||||
|
||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||
def update(self):
|
||||
self.data.update()
|
||||
waste_data = self.data.data
|
||||
waste_array = self.data.data
|
||||
self._error = False
|
||||
|
||||
try:
|
||||
if waste_data:
|
||||
# Loop through all the dates to put the dates in the whole_year_dates attribute
|
||||
if self._get_whole_year == "True":
|
||||
whole_year_dates = []
|
||||
for waste_data in waste_array:
|
||||
if self.type in waste_data:
|
||||
collection_date = datetime.strptime(
|
||||
waste_data[self.type], "%Y-%m-%d"
|
||||
).date()
|
||||
whole_year_dates.append(
|
||||
datetime.strptime(waste_data[self.type], "%Y-%m-%d").date()
|
||||
)
|
||||
|
||||
# Date in date format "%Y-%m-%d"
|
||||
self._year_month_day_date = str(collection_date)
|
||||
self._whole_year_dates = whole_year_dates
|
||||
|
||||
if collection_date:
|
||||
# Set the values of the sensor
|
||||
self._last_update = datetime.today().strftime("%d-%m-%Y %H:%M")
|
||||
try:
|
||||
if waste_array:
|
||||
for waste_data in waste_array:
|
||||
if self.type in waste_data:
|
||||
collection_date = datetime.strptime(
|
||||
waste_data[self.type], "%Y-%m-%d"
|
||||
).date()
|
||||
|
||||
# Is the collection date today?
|
||||
self._is_collection_date_today = date.today() == collection_date
|
||||
# Date in date format "%Y-%m-%d"
|
||||
self._year_month_day_date = str(collection_date)
|
||||
|
||||
if (
|
||||
self.type == "restafval"
|
||||
and "restafvaldiftardate" in waste_data
|
||||
):
|
||||
self._last_collection_date = str(
|
||||
datetime.strptime(
|
||||
waste_data["restafvaldiftardate"], "%Y-%m-%d"
|
||||
).date()
|
||||
if collection_date:
|
||||
# Set the values of the sensor
|
||||
self._last_update = datetime.today().strftime(
|
||||
"%d-%m-%Y %H:%M"
|
||||
)
|
||||
self._total_collections_this_year = waste_data[
|
||||
"restafvaldiftarcollections"
|
||||
]
|
||||
|
||||
# Days until collection date
|
||||
delta = collection_date - date.today()
|
||||
self._days_until_collection_date = delta.days
|
||||
# Is the collection date today?
|
||||
self._is_collection_date_today = (
|
||||
date.today() == collection_date
|
||||
)
|
||||
|
||||
# Only show the value if the date is lesser than or equal to (today + timespan_in_days)
|
||||
if collection_date <= date.today() + relativedelta(
|
||||
days=int(self.timespan_in_days)
|
||||
):
|
||||
# if the date does not contain a named day or month, return the date as normal
|
||||
if (
|
||||
self.date_format.find("a") == -1
|
||||
and self.date_format.find("A") == -1
|
||||
and self.date_format.find("b") == -1
|
||||
and self.date_format.find("B") == -1
|
||||
self.type == "restafval"
|
||||
and "restafvaldiftardate" in waste_data
|
||||
):
|
||||
self._state = collection_date.strftime(self.date_format)
|
||||
# else convert the named values to the locale names
|
||||
self._last_collection_date = str(
|
||||
datetime.strptime(
|
||||
waste_data["restafvaldiftardate"], "%Y-%m-%d"
|
||||
).date()
|
||||
)
|
||||
self._total_collections_this_year = waste_data[
|
||||
"restafvaldiftarcollections"
|
||||
]
|
||||
|
||||
# Days until collection date
|
||||
delta = collection_date - date.today()
|
||||
self._days_until_collection_date = delta.days
|
||||
|
||||
# Only show the value if the date is lesser than or equal to (today + timespan_in_days)
|
||||
if collection_date <= date.today() + relativedelta(
|
||||
days=int(self.timespan_in_days)
|
||||
):
|
||||
# if the date does not contain a named day or month, return the date as normal
|
||||
if (
|
||||
self.date_format.find("a") == -1
|
||||
and self.date_format.find("A") == -1
|
||||
and self.date_format.find("b") == -1
|
||||
and self.date_format.find("B") == -1
|
||||
):
|
||||
self._state = collection_date.strftime(
|
||||
self.date_format
|
||||
)
|
||||
# else convert the named values to the locale names
|
||||
else:
|
||||
edited_date_format = self.date_format.replace(
|
||||
"%a", "EEE"
|
||||
)
|
||||
edited_date_format = edited_date_format.replace(
|
||||
"%A", "EEEE"
|
||||
)
|
||||
edited_date_format = edited_date_format.replace(
|
||||
"%b", "MMM"
|
||||
)
|
||||
edited_date_format = edited_date_format.replace(
|
||||
"%B", "MMMM"
|
||||
)
|
||||
|
||||
# half babel, half date string... something like EEEE 04-MMMM-2020
|
||||
half_babel_half_date = collection_date.strftime(
|
||||
edited_date_format
|
||||
)
|
||||
|
||||
# replace the digits with qquoted digits 01 --> '01'
|
||||
half_babel_half_date = re.sub(
|
||||
r"(\d+)", r"'\1'", half_babel_half_date
|
||||
)
|
||||
# transform the EEE, EEEE etc... to a real locale date, with babel
|
||||
locale_date = format_date(
|
||||
collection_date,
|
||||
half_babel_half_date,
|
||||
locale=self.locale,
|
||||
)
|
||||
|
||||
self._state = locale_date
|
||||
break # we have a result, break the loop
|
||||
else:
|
||||
edited_date_format = self.date_format.replace(
|
||||
"%a", "EEE"
|
||||
)
|
||||
edited_date_format = edited_date_format.replace(
|
||||
"%A", "EEEE"
|
||||
)
|
||||
edited_date_format = edited_date_format.replace(
|
||||
"%b", "MMM"
|
||||
)
|
||||
edited_date_format = edited_date_format.replace(
|
||||
"%B", "MMMM"
|
||||
)
|
||||
|
||||
# half babel, half date string... something like EEEE 04-MMMM-2020
|
||||
half_babel_half_date = collection_date.strftime(
|
||||
edited_date_format
|
||||
)
|
||||
|
||||
# replace the digits with qquoted digits 01 --> '01'
|
||||
half_babel_half_date = re.sub(
|
||||
r"(\d+)", r"'\1'", half_babel_half_date
|
||||
)
|
||||
# transform the EEE, EEEE etc... to a real locale date, with babel
|
||||
locale_date = format_date(
|
||||
collection_date,
|
||||
half_babel_half_date,
|
||||
locale=self.locale,
|
||||
)
|
||||
|
||||
self._state = locale_date
|
||||
self._hidden = True
|
||||
else:
|
||||
self._hidden = True
|
||||
else:
|
||||
raise ValueError()
|
||||
else:
|
||||
raise ValueError()
|
||||
# collection_date empty
|
||||
raise ValueError()
|
||||
# else:
|
||||
# No matching result data for current waste type, no problem
|
||||
else:
|
||||
raise ValueError()
|
||||
except ValueError:
|
||||
@@ -369,4 +409,5 @@ class AfvalinfoSensor(Entity):
|
||||
# self._is_collection_date_today = False
|
||||
# self._last_collection_date = None
|
||||
# self._total_collections_this_year = None
|
||||
# self._whole_year_dates = None
|
||||
self._last_update = datetime.today().strftime("%d-%m-%Y %H:%M")
|
||||
|
||||
@@ -12,8 +12,7 @@ def get_waste_data_raw(
|
||||
postal_code,
|
||||
street_number,
|
||||
suffix,
|
||||
): # sourcery skip: avoid-builtin-shadow
|
||||
|
||||
):
|
||||
if provider not in SENSOR_COLLECTORS_ICALENDAR.keys():
|
||||
raise ValueError(f"Invalid provider: {provider}, please verify")
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ def get_waste_data_raw(
|
||||
|
||||
try:
|
||||
bag_id = None
|
||||
suffix = suffix.strip().upper()
|
||||
_verify = provider != "suez"
|
||||
url = f"{SENSOR_COLLECTORS_OPZET[provider]}/rest/adressen/{postal_code}-{street_number}"
|
||||
raw_response = requests.get(url, verify=_verify)
|
||||
|
||||
@@ -5,7 +5,7 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
API = "api"
|
||||
NAME = "afvalwijzer"
|
||||
VERSION = "2022.11.02"
|
||||
VERSION = "2023.01.01"
|
||||
ISSUE_URL = "https://github.com/xirixiz/homeassistant-afvalwijzer/issues"
|
||||
|
||||
SENSOR_COLLECTOR_TO_URL = {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"domain": "afvalwijzer",
|
||||
"name": "Afvalwijzer",
|
||||
"version": "2022.11.02",
|
||||
"version": "2023.01.01",
|
||||
"iot_class": "cloud_polling",
|
||||
"documentation": "https://github.com/xirixiz/homeassistant-afvalwijzer/blob/master/README.md",
|
||||
"issue_tracker": "https://github.com/xirixiz/homeassistant-afvalwijzer/issues",
|
||||
|
||||
@@ -40,7 +40,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
vol.Optional(CONF_SUFFIX, default=""): cv.string,
|
||||
vol.Optional(CONF_EXCLUDE_PICKUP_TODAY, default="true"): cv.string,
|
||||
vol.Optional(CONF_EXCLUDE_LIST, default=""): cv.string,
|
||||
vol.Optional(CONF_DEFAULT_LABEL, default="Geen"): cv.string,
|
||||
vol.Optional(CONF_DEFAULT_LABEL, default="geen"): cv.string,
|
||||
vol.Optional(CONF_ID.strip().lower(), default=""): cv.string,
|
||||
}
|
||||
)
|
||||
@@ -77,7 +77,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
||||
except ValueError as err:
|
||||
_LOGGER.error(f"Check afvalwijzer platform settings {err.args}")
|
||||
|
||||
fetch_data = AfvalwijzerData(config)
|
||||
fetch_data = AfvalwijzerData(hass, config)
|
||||
|
||||
waste_types_provider = collector.waste_types_provider
|
||||
_LOGGER.debug(f"Generating waste_types_provider list = {waste_types_provider}")
|
||||
@@ -98,7 +98,8 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
||||
|
||||
|
||||
class AfvalwijzerData(object):
|
||||
def __init__(self, config):
|
||||
def __init__(self, hass, config):
|
||||
self._hass = hass
|
||||
self.config = config
|
||||
|
||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||
|
||||
@@ -24,7 +24,7 @@ from ..collector.main_collector import MainCollector
|
||||
# Common
|
||||
suffix = ""
|
||||
exclude_pickup_today = "True"
|
||||
default_label = "Geen"
|
||||
default_label = "geen"
|
||||
exclude_list = ""
|
||||
|
||||
# DeAfvalapp
|
||||
@@ -42,6 +42,11 @@ exclude_list = ""
|
||||
# postal_code = "5146eg"
|
||||
# street_number = "1"
|
||||
|
||||
provider = "rmn"
|
||||
postal_code = "3701XK"
|
||||
street_number = "24"
|
||||
suffix = "b"
|
||||
|
||||
# Opzet
|
||||
# provider = "prezero"
|
||||
# postal_code = "6665CN"
|
||||
@@ -54,9 +59,9 @@ exclude_list = ""
|
||||
# suffix = "C"
|
||||
|
||||
# Ximmio
|
||||
provider = "meerlanden"
|
||||
postal_code = "2121xt"
|
||||
street_number = "38"
|
||||
# provider = "meerlanden"
|
||||
# postal_code = "2121xt"
|
||||
# street_number = "38"
|
||||
|
||||
# Ximmio
|
||||
# provider = "acv"
|
||||
|
||||
@@ -25,6 +25,7 @@ import voluptuous as vol
|
||||
|
||||
from .base import HacsBase
|
||||
from .const import DOMAIN, MINIMUM_HA_VERSION, STARTUP
|
||||
from .data_client import HacsDataClient
|
||||
from .enums import ConfigurationType, HacsDisabledReason, HacsStage, LovelaceMode
|
||||
from .frontend import async_register_frontend
|
||||
from .utils.configuration_schema import hacs_config_combined
|
||||
@@ -87,6 +88,10 @@ async def async_initialize_integration(
|
||||
hacs.hass = hass
|
||||
hacs.queue = QueueManager(hass=hass)
|
||||
hacs.data = HacsData(hacs=hacs)
|
||||
hacs.data_client = HacsDataClient(
|
||||
session=clientsession,
|
||||
client_name=f"HACS/{integration.version}",
|
||||
)
|
||||
hacs.system.running = True
|
||||
hacs.session = clientsession
|
||||
|
||||
@@ -153,8 +158,9 @@ async def async_initialize_integration(
|
||||
hacs.disable_hacs(HacsDisabledReason.RESTORE)
|
||||
return False
|
||||
|
||||
can_update = await hacs.async_can_update()
|
||||
hacs.log.debug("Can update %s repositories", can_update)
|
||||
if not hacs.configuration.experimental:
|
||||
can_update = await hacs.async_can_update()
|
||||
hacs.log.debug("Can update %s repositories", can_update)
|
||||
|
||||
hacs.set_active_categories()
|
||||
|
||||
@@ -168,7 +174,7 @@ async def async_initialize_integration(
|
||||
hacs.log.info("Update entities are only supported when using UI configuration")
|
||||
|
||||
else:
|
||||
hass.config_entries.async_setup_platforms(
|
||||
await hass.config_entries.async_forward_entry_setups(
|
||||
config_entry,
|
||||
[Platform.SENSOR, Platform.UPDATE]
|
||||
if hacs.configuration.experimental
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -28,11 +28,17 @@ from homeassistant.config_entries import ConfigEntry, ConfigEntryState
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_FINAL_WRITE, Platform
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
from homeassistant.helpers.issue_registry import async_create_issue, IssueSeverity
|
||||
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
|
||||
from homeassistant.loader import Integration
|
||||
from homeassistant.util import dt
|
||||
|
||||
from custom_components.hacs.repositories.base import (
|
||||
HACS_MANIFEST_KEYS_TO_EXPORT,
|
||||
REPOSITORY_KEYS_TO_EXPORT,
|
||||
)
|
||||
|
||||
from .const import DOMAIN, TV, URL_BASE
|
||||
from .data_client import HacsDataClient
|
||||
from .enums import (
|
||||
ConfigurationType,
|
||||
HacsCategory,
|
||||
@@ -47,6 +53,7 @@ from .exceptions import (
|
||||
HacsException,
|
||||
HacsExecutionStillInProgress,
|
||||
HacsExpectedException,
|
||||
HacsNotModifiedException,
|
||||
HacsRepositoryArchivedException,
|
||||
HacsRepositoryExistException,
|
||||
HomeAssistantCoreRepositoryException,
|
||||
@@ -166,6 +173,7 @@ class HacsStatus:
|
||||
new: bool = False
|
||||
active_frontend_endpoint_plugin: bool = False
|
||||
active_frontend_endpoint_theme: bool = False
|
||||
inital_fetch_done: bool = False
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -176,6 +184,7 @@ class HacsSystem:
|
||||
running: bool = False
|
||||
stage = HacsStage.SETUP
|
||||
action: bool = False
|
||||
generator: bool = False
|
||||
|
||||
@property
|
||||
def disabled(self) -> bool:
|
||||
@@ -265,7 +274,7 @@ class HacsRepositories:
|
||||
|
||||
self._default_repositories.add(repo_id)
|
||||
|
||||
def set_repository_id(self, repository, repo_id):
|
||||
def set_repository_id(self, repository: HacsRepository, repo_id: str):
|
||||
"""Update a repository id."""
|
||||
existing_repo_id = str(repository.data.id)
|
||||
if existing_repo_id == repo_id:
|
||||
@@ -350,6 +359,7 @@ class HacsBase:
|
||||
configuration = HacsConfiguration()
|
||||
core = HacsCore()
|
||||
data: HacsData | None = None
|
||||
data_client: HacsDataClient | None = None
|
||||
frontend_version: str | None = None
|
||||
github: GitHub | None = None
|
||||
githubapi: GitHubAPI | None = None
|
||||
@@ -546,8 +556,6 @@ class HacsBase:
|
||||
if check:
|
||||
try:
|
||||
await repository.async_registration(ref)
|
||||
if self.status.new:
|
||||
repository.data.new = False
|
||||
if repository.validate.errors:
|
||||
self.common.skip.append(repository.data.full_name)
|
||||
if not self.status.startup:
|
||||
@@ -561,7 +569,11 @@ class HacsBase:
|
||||
repository.logger.info("%s Validation completed", repository.string)
|
||||
else:
|
||||
repository.logger.info("%s Registration completed", repository.string)
|
||||
except (HacsRepositoryExistException, HacsRepositoryArchivedException):
|
||||
except (HacsRepositoryExistException, HacsRepositoryArchivedException) as exception:
|
||||
if self.system.generator:
|
||||
repository.logger.error(
|
||||
"%s Registration Failed - %s", repository.string, exception
|
||||
)
|
||||
return
|
||||
except AIOGitHubAPIException as exception:
|
||||
self.common.skip.append(repository.data.full_name)
|
||||
@@ -569,6 +581,9 @@ class HacsBase:
|
||||
f"Validation for {repository_full_name} failed with {exception}."
|
||||
) from exception
|
||||
|
||||
if self.status.new:
|
||||
repository.data.new = False
|
||||
|
||||
if repository_id is not None:
|
||||
repository.data.id = repository_id
|
||||
|
||||
@@ -588,34 +603,7 @@ class HacsBase:
|
||||
async def startup_tasks(self, _=None) -> None:
|
||||
"""Tasks that are started after setup."""
|
||||
self.set_stage(HacsStage.STARTUP)
|
||||
|
||||
try:
|
||||
repository = self.repositories.get_by_full_name(HacsGitHubRepo.INTEGRATION)
|
||||
if repository is None:
|
||||
await self.async_register_repository(
|
||||
repository_full_name=HacsGitHubRepo.INTEGRATION,
|
||||
category=HacsCategory.INTEGRATION,
|
||||
default=True,
|
||||
)
|
||||
repository = self.repositories.get_by_full_name(HacsGitHubRepo.INTEGRATION)
|
||||
if repository is None:
|
||||
raise HacsException("Unknown error")
|
||||
|
||||
repository.data.installed = True
|
||||
repository.data.installed_version = self.integration.version.string
|
||||
repository.data.new = False
|
||||
repository.data.releases = True
|
||||
|
||||
self.repository = repository.repository_object
|
||||
self.repositories.mark_default(repository)
|
||||
except HacsException as exception:
|
||||
if "403" in str(exception):
|
||||
self.log.critical(
|
||||
"GitHub API is ratelimited, or the token is wrong.",
|
||||
)
|
||||
else:
|
||||
self.log.critical("Could not load HACS! - %s", exception)
|
||||
self.disable_hacs(HacsDisabledReason.LOAD_HACS)
|
||||
await self.async_load_hacs_from_github()
|
||||
|
||||
if critical := await async_load_from_store(self.hass, "critical"):
|
||||
for repo in critical:
|
||||
@@ -626,16 +614,38 @@ class HacsBase:
|
||||
)
|
||||
break
|
||||
|
||||
if not self.configuration.experimental:
|
||||
self.recuring_tasks.append(
|
||||
self.hass.helpers.event.async_track_time_interval(
|
||||
self.async_update_downloaded_repositories, timedelta(hours=48)
|
||||
)
|
||||
)
|
||||
self.recuring_tasks.append(
|
||||
self.hass.helpers.event.async_track_time_interval(
|
||||
self.async_update_all_repositories,
|
||||
timedelta(hours=96),
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.recuring_tasks.append(
|
||||
self.hass.helpers.event.async_track_time_interval(
|
||||
self.async_load_hacs_from_github,
|
||||
timedelta(hours=48),
|
||||
)
|
||||
)
|
||||
|
||||
self.recuring_tasks.append(
|
||||
self.hass.helpers.event.async_track_time_interval(
|
||||
self.async_get_all_category_repositories, timedelta(hours=3)
|
||||
self.async_update_downloaded_custom_repositories, timedelta(hours=48)
|
||||
)
|
||||
)
|
||||
|
||||
self.recuring_tasks.append(
|
||||
self.hass.helpers.event.async_track_time_interval(
|
||||
self.async_update_all_repositories, timedelta(hours=25)
|
||||
self.async_get_all_category_repositories, timedelta(hours=6)
|
||||
)
|
||||
)
|
||||
|
||||
self.recuring_tasks.append(
|
||||
self.hass.helpers.event.async_track_time_interval(
|
||||
self.async_check_rate_limit, timedelta(minutes=5)
|
||||
@@ -646,14 +656,10 @@ class HacsBase:
|
||||
self.async_prosess_queue, timedelta(minutes=10)
|
||||
)
|
||||
)
|
||||
|
||||
self.recuring_tasks.append(
|
||||
self.hass.helpers.event.async_track_time_interval(
|
||||
self.async_update_downloaded_repositories, timedelta(hours=2)
|
||||
)
|
||||
)
|
||||
self.recuring_tasks.append(
|
||||
self.hass.helpers.event.async_track_time_interval(
|
||||
self.async_handle_critical_repositories, timedelta(hours=2)
|
||||
self.async_handle_critical_repositories, timedelta(hours=6)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -661,6 +667,8 @@ class HacsBase:
|
||||
EVENT_HOMEASSISTANT_FINAL_WRITE, self.data.async_force_write
|
||||
)
|
||||
|
||||
self.log.debug("There are %s scheduled recurring tasks", len(self.recuring_tasks))
|
||||
|
||||
self.status.startup = False
|
||||
self.async_dispatch(HacsDispatchEvent.STATUS, {})
|
||||
|
||||
@@ -758,6 +766,42 @@ class HacsBase:
|
||||
if self.configuration.netdaemon:
|
||||
self.enable_hacs_category(HacsCategory.NETDAEMON)
|
||||
|
||||
async def async_load_hacs_from_github(self, _=None) -> None:
|
||||
"""Load HACS from GitHub."""
|
||||
if self.configuration.experimental and self.status.inital_fetch_done:
|
||||
return
|
||||
|
||||
try:
|
||||
repository = self.repositories.get_by_full_name(HacsGitHubRepo.INTEGRATION)
|
||||
if repository is None:
|
||||
await self.async_register_repository(
|
||||
repository_full_name=HacsGitHubRepo.INTEGRATION,
|
||||
category=HacsCategory.INTEGRATION,
|
||||
default=True,
|
||||
)
|
||||
repository = self.repositories.get_by_full_name(HacsGitHubRepo.INTEGRATION)
|
||||
elif self.configuration.experimental and not self.status.startup:
|
||||
self.log.error("Scheduling update of hacs/integration")
|
||||
self.queue.add(repository.common_update())
|
||||
if repository is None:
|
||||
raise HacsException("Unknown error")
|
||||
|
||||
repository.data.installed = True
|
||||
repository.data.installed_version = self.integration.version.string
|
||||
repository.data.new = False
|
||||
repository.data.releases = True
|
||||
|
||||
self.repository = repository.repository_object
|
||||
self.repositories.mark_default(repository)
|
||||
except HacsException as exception:
|
||||
if "403" in str(exception):
|
||||
self.log.critical(
|
||||
"GitHub API is ratelimited, or the token is wrong.",
|
||||
)
|
||||
else:
|
||||
self.log.critical("Could not load HACS! - %s", exception)
|
||||
self.disable_hacs(HacsDisabledReason.LOAD_HACS)
|
||||
|
||||
async def async_get_all_category_repositories(self, _=None) -> None:
|
||||
"""Get all category repositories."""
|
||||
if self.system.disabled:
|
||||
@@ -765,11 +809,62 @@ class HacsBase:
|
||||
self.log.info("Loading known repositories")
|
||||
await asyncio.gather(
|
||||
*[
|
||||
self.async_get_category_repositories(HacsCategory(category))
|
||||
self.async_get_category_repositories_experimental(category)
|
||||
if self.configuration.experimental
|
||||
else self.async_get_category_repositories(HacsCategory(category))
|
||||
for category in self.common.categories or []
|
||||
]
|
||||
)
|
||||
|
||||
async def async_get_category_repositories_experimental(self, category: str) -> None:
|
||||
"""Update all category repositories."""
|
||||
self.log.debug("Fetching updated content for %s", category)
|
||||
try:
|
||||
category_data = await self.data_client.get_data(category)
|
||||
except HacsNotModifiedException:
|
||||
self.log.debug("No updates for %s", category)
|
||||
return
|
||||
except HacsException as exception:
|
||||
self.log.error("Could not update %s - %s", category, exception)
|
||||
return
|
||||
|
||||
await self.data.register_unknown_repositories(category_data, category)
|
||||
|
||||
for repo_id, repo_data in category_data.items():
|
||||
repo = repo_data["full_name"]
|
||||
if self.common.renamed_repositories.get(repo):
|
||||
repo = self.common.renamed_repositories[repo]
|
||||
if self.repositories.is_removed(repo):
|
||||
continue
|
||||
if repo in self.common.archived_repositories:
|
||||
continue
|
||||
if repository := self.repositories.get_by_full_name(repo):
|
||||
self.repositories.set_repository_id(repository, repo_id)
|
||||
self.repositories.mark_default(repository)
|
||||
if repository.data.last_fetched is None or (
|
||||
repository.data.last_fetched.timestamp() < repo_data["last_fetched"]
|
||||
):
|
||||
repository.data.update_data({**dict(REPOSITORY_KEYS_TO_EXPORT), **repo_data})
|
||||
if (manifest := repo_data.get("manifest")) is not None:
|
||||
repository.repository_manifest.update_data(
|
||||
{**dict(HACS_MANIFEST_KEYS_TO_EXPORT), **manifest}
|
||||
)
|
||||
|
||||
if category == "integration":
|
||||
self.status.inital_fetch_done = True
|
||||
|
||||
if self.stage == HacsStage.STARTUP:
|
||||
for repository in self.repositories.list_all:
|
||||
if (
|
||||
repository.data.category == category
|
||||
and not repository.data.installed
|
||||
and not self.repositories.is_default(repository.data.id)
|
||||
):
|
||||
repository.logger.debug(
|
||||
"%s Unregister stale custom repository", repository.string
|
||||
)
|
||||
self.repositories.unregister(repository)
|
||||
|
||||
async def async_get_category_repositories(self, category: HacsCategory) -> None:
|
||||
"""Get repositories from category."""
|
||||
if self.system.disabled:
|
||||
@@ -845,7 +940,7 @@ class HacsBase:
|
||||
return
|
||||
can_update = await self.async_can_update()
|
||||
self.log.debug(
|
||||
"Can update %s repositories, " "items in queue %s",
|
||||
"Can update %s repositories, items in queue %s",
|
||||
can_update,
|
||||
self.queue.pending_tasks,
|
||||
)
|
||||
@@ -867,9 +962,12 @@ class HacsBase:
|
||||
self.log.info("Loading removed repositories")
|
||||
|
||||
try:
|
||||
removed_repositories = await self.async_github_get_hacs_default_file(
|
||||
HacsCategory.REMOVED
|
||||
)
|
||||
if self.configuration.experimental:
|
||||
removed_repositories = await self.data_client.get_data("removed")
|
||||
else:
|
||||
removed_repositories = await self.async_github_get_hacs_default_file(
|
||||
HacsCategory.REMOVED
|
||||
)
|
||||
except HacsException:
|
||||
return
|
||||
|
||||
@@ -915,7 +1013,7 @@ class HacsBase:
|
||||
|
||||
async def async_update_downloaded_repositories(self, _=None) -> None:
|
||||
"""Execute the task."""
|
||||
if self.system.disabled:
|
||||
if self.system.disabled or self.configuration.experimental:
|
||||
return
|
||||
self.log.info("Starting recurring background task for downloaded repositories")
|
||||
|
||||
@@ -925,6 +1023,21 @@ class HacsBase:
|
||||
|
||||
self.log.debug("Recurring background task for downloaded repositories done")
|
||||
|
||||
async def async_update_downloaded_custom_repositories(self, _=None) -> None:
|
||||
"""Execute the task."""
|
||||
if self.system.disabled or not self.configuration.experimental:
|
||||
return
|
||||
self.log.info("Starting recurring background task for downloaded custom repositories")
|
||||
|
||||
for repository in self.repositories.list_downloaded:
|
||||
if (
|
||||
repository.data.category in self.common.categories
|
||||
and not self.repositories.is_default(repository.data.id)
|
||||
):
|
||||
self.queue.add(repository.update_repository(ignore_issues=True))
|
||||
|
||||
self.log.debug("Recurring background task for downloaded custom repositories done")
|
||||
|
||||
async def async_handle_critical_repositories(self, _=None) -> None:
|
||||
"""Handle critical repositories."""
|
||||
critical_queue = QueueManager(hass=self.hass)
|
||||
@@ -933,8 +1046,11 @@ class HacsBase:
|
||||
was_installed = False
|
||||
|
||||
try:
|
||||
critical = await self.async_github_get_hacs_default_file("critical")
|
||||
except GitHubNotModifiedException:
|
||||
if self.configuration.experimental:
|
||||
critical = await self.data_client.get_data("critical")
|
||||
else:
|
||||
critical = await self.async_github_get_hacs_default_file("critical")
|
||||
except (GitHubNotModifiedException, HacsNotModifiedException):
|
||||
return
|
||||
except HacsException:
|
||||
pass
|
||||
|
||||
@@ -17,6 +17,8 @@ PACKAGE_NAME = "custom_components.hacs"
|
||||
DEFAULT_CONCURRENT_TASKS = 15
|
||||
DEFAULT_CONCURRENT_BACKOFF_TIME = 1
|
||||
|
||||
HACS_REPOSITORY_ID = "172733314"
|
||||
|
||||
HACS_ACTION_GITHUB_API_HEADERS = {
|
||||
"User-Agent": "HACS/action",
|
||||
"Accept": ACCEPT_HEADERS["preview"],
|
||||
|
||||
@@ -4,7 +4,7 @@ import sys
|
||||
|
||||
if sys.version_info.minor >= 11:
|
||||
# Needs Python 3.11
|
||||
from enum import StrEnum ## pylint: disable=no-name-in-module
|
||||
from enum import StrEnum # # pylint: disable=no-name-in-module
|
||||
else:
|
||||
try:
|
||||
# https://github.com/home-assistant/core/blob/dev/homeassistant/backports/enum.py
|
||||
|
||||
@@ -8,13 +8,12 @@ from homeassistant.components.http import HomeAssistantView
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
|
||||
from .const import DOMAIN, URL_BASE
|
||||
from .hacs_frontend import locate_dir, VERSION as FE_VERSION
|
||||
from .hacs_frontend import VERSION as FE_VERSION, locate_dir
|
||||
from .hacs_frontend_experimental import (
|
||||
locate_dir as experimental_locate_dir,
|
||||
VERSION as EXPERIMENTAL_FE_VERSION,
|
||||
locate_dir as experimental_locate_dir,
|
||||
)
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .base import HacsBase
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,9 +1,9 @@
|
||||
|
||||
try {
|
||||
new Function("import('/hacsfiles/frontend/main-c4dd4de7.js')")();
|
||||
new Function("import('/hacsfiles/frontend/main-aeda8d41.js')")();
|
||||
} catch (err) {
|
||||
var el = document.createElement('script');
|
||||
el.src = '/hacsfiles/frontend/main-c4dd4de7.js';
|
||||
el.src = '/hacsfiles/frontend/main-aeda8d41.js';
|
||||
el.type = 'module';
|
||||
document.body.appendChild(el);
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"./src/main.ts": "main-c4dd4de7.js"
|
||||
"./src/main.ts": "main-aeda8d41.js"
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
VERSION="20221217163936"
|
||||
VERSION="20230127100107"
|
||||
@@ -19,5 +19,5 @@
|
||||
"requirements": [
|
||||
"aiogithubapi>=22.10.1"
|
||||
],
|
||||
"version": "1.29.0"
|
||||
"version": "1.30.1"
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
"""Repairs platform for HACS."""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import Any
|
||||
|
||||
import voluptuous as vol
|
||||
from typing import Any
|
||||
|
||||
from homeassistant import data_entry_flow
|
||||
from homeassistant.components.repairs import RepairsFlow
|
||||
from homeassistant.core import HomeAssistant
|
||||
import voluptuous as vol
|
||||
|
||||
from custom_components.hacs.base import HacsBase
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -50,16 +50,27 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
TOPIC_FILTER = (
|
||||
"add-on",
|
||||
"addon",
|
||||
"app",
|
||||
"appdaemon-apps",
|
||||
"appdaemon",
|
||||
"custom-card",
|
||||
"custom-cards",
|
||||
"custom-component",
|
||||
"custom-components",
|
||||
"customcomponents",
|
||||
"hacktoberfest",
|
||||
"hacs-default",
|
||||
"hacs-integration",
|
||||
"hacs-repository",
|
||||
"hacs",
|
||||
"hass",
|
||||
"hassio",
|
||||
"home-assistant-custom",
|
||||
"home-assistant-frontend",
|
||||
"home-assistant-hacs",
|
||||
"home-assistant-sensor",
|
||||
"home-assistant",
|
||||
"home-automation",
|
||||
"homeassistant-components",
|
||||
@@ -68,16 +79,45 @@ TOPIC_FILTER = (
|
||||
"homeassistant",
|
||||
"homeautomation",
|
||||
"integration",
|
||||
"lovelace-ui",
|
||||
"lovelace",
|
||||
"media-player",
|
||||
"mediaplayer",
|
||||
"netdaemon",
|
||||
"plugin",
|
||||
"python_script",
|
||||
"python-script",
|
||||
"python",
|
||||
"sensor",
|
||||
"smart-home",
|
||||
"smarthome",
|
||||
"theme",
|
||||
"themes",
|
||||
"custom-cards",
|
||||
"home-assistant-frontend",
|
||||
"home-assistant-hacs",
|
||||
"home-assistant-custom",
|
||||
"lovelace-ui",
|
||||
)
|
||||
|
||||
|
||||
REPOSITORY_KEYS_TO_EXPORT = (
|
||||
# Keys can not be removed from this list until v3
|
||||
# If keys are added, the action need to be re-run with force
|
||||
("description", ""),
|
||||
("downloads", 0),
|
||||
("domain", None),
|
||||
("etag_repository", None),
|
||||
("full_name", ""),
|
||||
("last_commit", None),
|
||||
("last_updated", 0),
|
||||
("last_version", None),
|
||||
("manifest_name", None),
|
||||
("open_issues", 0),
|
||||
("stargazers_count", 0),
|
||||
("topics", []),
|
||||
)
|
||||
|
||||
HACS_MANIFEST_KEYS_TO_EXPORT = (
|
||||
# Keys can not be removed from this list until v3
|
||||
# If keys are added, the action need to be re-run with force
|
||||
("country", []),
|
||||
("name", None),
|
||||
)
|
||||
|
||||
|
||||
@@ -120,7 +160,6 @@ class RepositoryData:
|
||||
new: bool = True
|
||||
open_issues: int = 0
|
||||
published_tags: list[str] = []
|
||||
pushed_at: str = ""
|
||||
releases: bool = False
|
||||
selected_tag: str = None
|
||||
show_beta: bool = False
|
||||
@@ -147,32 +186,24 @@ class RepositoryData:
|
||||
|
||||
def update_data(self, data: dict, action: bool = False) -> None:
|
||||
"""Update data of the repository."""
|
||||
for key in data:
|
||||
for key, value in data.items():
|
||||
if key not in self.__dict__:
|
||||
continue
|
||||
if key == "pushed_at":
|
||||
if data[key] == "":
|
||||
continue
|
||||
if "Z" in data[key]:
|
||||
setattr(
|
||||
self,
|
||||
key,
|
||||
datetime.strptime(data[key], "%Y-%m-%dT%H:%M:%SZ"),
|
||||
)
|
||||
else:
|
||||
setattr(self, key, datetime.strptime(data[key], "%Y-%m-%dT%H:%M:%S"))
|
||||
|
||||
if key == "last_fetched" and isinstance(value, float):
|
||||
setattr(self, key, datetime.fromtimestamp(value))
|
||||
elif key == "id":
|
||||
setattr(self, key, str(data[key]))
|
||||
setattr(self, key, str(value))
|
||||
elif key == "country":
|
||||
if isinstance(data[key], str):
|
||||
setattr(self, key, [data[key]])
|
||||
if isinstance(value, str):
|
||||
setattr(self, key, [value])
|
||||
else:
|
||||
setattr(self, key, data[key])
|
||||
setattr(self, key, value)
|
||||
elif key == "topics" and not action:
|
||||
setattr(self, key, [topic for topic in data[key] if topic not in TOPIC_FILTER])
|
||||
setattr(self, key, [topic for topic in value if topic not in TOPIC_FILTER])
|
||||
|
||||
else:
|
||||
setattr(self, key, data[key])
|
||||
setattr(self, key, value)
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
@@ -215,6 +246,20 @@ class HacsManifest:
|
||||
setattr(manifest_data, key, value)
|
||||
return manifest_data
|
||||
|
||||
def update_data(self, data: dict) -> None:
|
||||
"""Update the manifest data."""
|
||||
for key, value in data.items():
|
||||
if key not in self.__dict__:
|
||||
continue
|
||||
|
||||
if key == "country":
|
||||
if isinstance(value, str):
|
||||
setattr(self, key, [value])
|
||||
else:
|
||||
setattr(self, key, value)
|
||||
else:
|
||||
setattr(self, key, value)
|
||||
|
||||
|
||||
class RepositoryReleases:
|
||||
"""RepositoyReleases."""
|
||||
@@ -449,6 +494,10 @@ class HacsRepository:
|
||||
self.logger.debug("%s Did not update, content was not modified", self.string)
|
||||
return
|
||||
|
||||
if self.repository_object:
|
||||
self.data.last_updated = self.repository_object.attributes.get("pushed_at", 0)
|
||||
self.data.last_fetched = datetime.utcnow()
|
||||
|
||||
# Set topics
|
||||
self.data.topics = self.data.topics
|
||||
|
||||
@@ -497,7 +546,7 @@ class HacsRepository:
|
||||
self.additional_info = await self.async_get_info_file_contents()
|
||||
|
||||
# Set last fetch attribute
|
||||
self.data.last_fetched = datetime.now()
|
||||
self.data.last_fetched = datetime.utcnow()
|
||||
|
||||
return True
|
||||
|
||||
@@ -1011,7 +1060,11 @@ class HacsRepository:
|
||||
self.hacs.common.renamed_repositories[
|
||||
self.data.full_name
|
||||
] = repository_object.full_name
|
||||
raise HacsRepositoryExistException
|
||||
if not self.hacs.system.generator:
|
||||
raise HacsRepositoryExistException
|
||||
self.logger.error(
|
||||
"%s Repository has been renamed - %s", self.string, repository_object.full_name
|
||||
)
|
||||
self.data.update_data(
|
||||
repository_object.attributes,
|
||||
action=self.hacs.system.action,
|
||||
|
||||
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from homeassistant.helpers.issue_registry import async_create_issue, IssueSeverity
|
||||
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
|
||||
from homeassistant.loader import async_get_custom_components
|
||||
|
||||
from ..const import DOMAIN
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""Sensor platform for HACS."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from homeassistant.components.sensor import SensorEntity
|
||||
|
||||
@@ -7,6 +7,7 @@ from .base import HacsBase
|
||||
from .const import DOMAIN
|
||||
|
||||
GITHUB_STATUS = "https://www.githubstatus.com/"
|
||||
CLOUDFLARE_STATUS = "https://www.cloudflarestatus.com/"
|
||||
|
||||
|
||||
@callback
|
||||
@@ -39,4 +40,9 @@ async def system_health_info(hass):
|
||||
if hacs.system.disabled:
|
||||
data["Disabled"] = hacs.system.disabled_reason
|
||||
|
||||
if hacs.configuration.experimental:
|
||||
data["HACS Data"] = system_health.async_check_can_reach_url(
|
||||
hass, "https://data-v2.hacs.xyz/data.json", CLOUDFLARE_STATUS
|
||||
)
|
||||
|
||||
return data
|
||||
|
||||
@@ -90,6 +90,17 @@ class HacsRepositoryUpdateEntity(HacsRepositoryEntity, UpdateEntity):
|
||||
if self.repository.pending_restart or not self.repository.can_download:
|
||||
return None
|
||||
|
||||
if self.latest_version not in self.repository.data.published_tags:
|
||||
releases = await self.repository.get_releases(
|
||||
prerelease=self.repository.data.show_beta,
|
||||
returnlimit=self.hacs.configuration.release_limit,
|
||||
)
|
||||
if releases:
|
||||
self.repository.data.releases = True
|
||||
self.repository.releases.objects = releases
|
||||
self.repository.data.published_tags = [x.tag_name for x in releases]
|
||||
self.repository.data.last_version = next(iter(self.repository.data.published_tags))
|
||||
|
||||
release_notes = ""
|
||||
if len(self.repository.releases.objects) > 0:
|
||||
release = self.repository.releases.objects[0]
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,38 +1,45 @@
|
||||
"""Data handler for HACS."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.util import json as json_util
|
||||
|
||||
from ..base import HacsBase
|
||||
from ..enums import HacsDisabledReason, HacsDispatchEvent, HacsGitHubRepo
|
||||
from ..const import HACS_REPOSITORY_ID
|
||||
from ..enums import HacsDisabledReason, HacsDispatchEvent
|
||||
from ..repositories.base import TOPIC_FILTER, HacsManifest, HacsRepository
|
||||
from .logger import LOGGER
|
||||
from .path import is_safe
|
||||
from .store import async_load_from_store, async_save_to_store
|
||||
|
||||
DEFAULT_BASE_REPOSITORY_DATA = (
|
||||
EXPORTED_BASE_DATA = (
|
||||
("new", False),
|
||||
("full_name", ""),
|
||||
)
|
||||
|
||||
EXPORTED_REPOSITORY_DATA = EXPORTED_BASE_DATA + (
|
||||
("authors", []),
|
||||
("category", ""),
|
||||
("description", ""),
|
||||
("domain", None),
|
||||
("downloads", 0),
|
||||
("etag_repository", None),
|
||||
("full_name", ""),
|
||||
("last_updated", 0),
|
||||
("hide", False),
|
||||
("last_updated", 0),
|
||||
("new", False),
|
||||
("stargazers_count", 0),
|
||||
("topics", []),
|
||||
)
|
||||
|
||||
DEFAULT_EXTENDED_REPOSITORY_DATA = (
|
||||
EXPORTED_DOWNLOADED_REPOSITORY_DATA = EXPORTED_REPOSITORY_DATA + (
|
||||
("archived", False),
|
||||
("config_flow", False),
|
||||
("default_branch", None),
|
||||
("description", ""),
|
||||
("first_install", False),
|
||||
("installed_commit", None),
|
||||
("installed", False),
|
||||
@@ -41,12 +48,9 @@ DEFAULT_EXTENDED_REPOSITORY_DATA = (
|
||||
("manifest_name", None),
|
||||
("open_issues", 0),
|
||||
("published_tags", []),
|
||||
("pushed_at", ""),
|
||||
("releases", False),
|
||||
("selected_tag", None),
|
||||
("show_beta", False),
|
||||
("stargazers_count", 0),
|
||||
("topics", []),
|
||||
)
|
||||
|
||||
|
||||
@@ -80,6 +84,8 @@ class HacsData:
|
||||
"ignored_repositories": self.hacs.common.ignored_repositories,
|
||||
},
|
||||
)
|
||||
if self.hacs.configuration.experimental:
|
||||
await self._async_store_experimental_content_and_repos()
|
||||
await self._async_store_content_and_repos()
|
||||
|
||||
async def _async_store_content_and_repos(self, _=None): # bb: ignore
|
||||
@@ -94,40 +100,94 @@ class HacsData:
|
||||
for event in (HacsDispatchEvent.REPOSITORY, HacsDispatchEvent.CONFIG):
|
||||
self.hacs.async_dispatch(event, {})
|
||||
|
||||
async def _async_store_experimental_content_and_repos(self, _=None): # bb: ignore
|
||||
"""Store the main repos file and each repo that is out of date."""
|
||||
# Repositories
|
||||
self.content = {}
|
||||
for repository in self.hacs.repositories.list_all:
|
||||
if repository.data.category in self.hacs.common.categories:
|
||||
self.async_store_experimental_repository_data(repository)
|
||||
|
||||
await async_save_to_store(self.hacs.hass, "data", {"repositories": self.content})
|
||||
|
||||
@callback
|
||||
def async_store_repository_data(self, repository: HacsRepository) -> dict:
|
||||
"""Store the repository data."""
|
||||
data = {"repository_manifest": repository.repository_manifest.manifest}
|
||||
|
||||
for key, default_value in DEFAULT_BASE_REPOSITORY_DATA:
|
||||
if (value := repository.data.__getattribute__(key)) != default_value:
|
||||
for key, default in (
|
||||
EXPORTED_DOWNLOADED_REPOSITORY_DATA
|
||||
if repository.data.installed
|
||||
else EXPORTED_REPOSITORY_DATA
|
||||
):
|
||||
if (value := getattr(repository.data, key, default)) != default:
|
||||
data[key] = value
|
||||
|
||||
if repository.data.installed:
|
||||
for key, default_value in DEFAULT_EXTENDED_REPOSITORY_DATA:
|
||||
if (value := repository.data.__getattribute__(key)) != default_value:
|
||||
data[key] = value
|
||||
if repository.data.installed_version:
|
||||
data["version_installed"] = repository.data.installed_version
|
||||
|
||||
if repository.data.last_fetched:
|
||||
data["last_fetched"] = repository.data.last_fetched.timestamp()
|
||||
|
||||
self.content[str(repository.data.id)] = data
|
||||
|
||||
@callback
|
||||
def async_store_experimental_repository_data(self, repository: HacsRepository) -> None:
|
||||
"""Store the experimental repository data for non downloaded repositories."""
|
||||
data = {}
|
||||
self.content.setdefault(repository.data.category, [])
|
||||
|
||||
if repository.data.installed:
|
||||
data["repository_manifest"] = repository.repository_manifest.manifest
|
||||
for key, default in EXPORTED_DOWNLOADED_REPOSITORY_DATA:
|
||||
if (value := getattr(repository.data, key, default)) != default:
|
||||
data[key] = value
|
||||
|
||||
if repository.data.installed_version:
|
||||
data["version_installed"] = repository.data.installed_version
|
||||
if repository.data.last_fetched:
|
||||
data["last_fetched"] = repository.data.last_fetched.timestamp()
|
||||
else:
|
||||
for key, default in EXPORTED_BASE_DATA:
|
||||
if (value := getattr(repository.data, key, default)) != default:
|
||||
data[key] = value
|
||||
|
||||
self.content[repository.data.category].append({"id": str(repository.data.id), **data})
|
||||
|
||||
async def restore(self):
|
||||
"""Restore saved data."""
|
||||
self.hacs.status.new = False
|
||||
repositories = {}
|
||||
hacs = {}
|
||||
|
||||
try:
|
||||
hacs = await async_load_from_store(self.hacs.hass, "hacs") or {}
|
||||
except HomeAssistantError:
|
||||
hacs = {}
|
||||
pass
|
||||
|
||||
try:
|
||||
repositories = await async_load_from_store(self.hacs.hass, "repositories") or {}
|
||||
data = (
|
||||
await async_load_from_store(
|
||||
self.hacs.hass,
|
||||
"data" if self.hacs.configuration.experimental else "repositories",
|
||||
)
|
||||
or {}
|
||||
)
|
||||
if data and self.hacs.configuration.experimental:
|
||||
for category, entries in data.get("repositories", {}).items():
|
||||
for repository in entries:
|
||||
repositories[repository["id"]] = {"category": category, **repository}
|
||||
else:
|
||||
repositories = (
|
||||
data or await async_load_from_store(self.hacs.hass, "repositories") or {}
|
||||
)
|
||||
except HomeAssistantError as exception:
|
||||
self.hacs.log.error(
|
||||
"Could not read %s, restore the file from a backup - %s",
|
||||
self.hacs.hass.config.path(".storage/hacs.repositories"),
|
||||
self.hacs.hass.config.path(
|
||||
".storage/hacs.data"
|
||||
if self.hacs.configuration.experimental
|
||||
else ".storage/hacs.repositories"
|
||||
),
|
||||
exception,
|
||||
)
|
||||
self.hacs.disable_hacs(HacsDisabledReason.RESTORE)
|
||||
@@ -136,6 +196,8 @@ class HacsData:
|
||||
if not hacs and not repositories:
|
||||
# Assume new install
|
||||
self.hacs.status.new = True
|
||||
if self.hacs.configuration.experimental:
|
||||
return True
|
||||
self.logger.info("<HacsData restore> Loading base repository information")
|
||||
repositories = await self.hacs.hass.async_add_executor_job(
|
||||
json_util.load_json,
|
||||
@@ -186,28 +248,34 @@ class HacsData:
|
||||
return False
|
||||
return True
|
||||
|
||||
async def register_unknown_repositories(self, repositories):
|
||||
async def register_unknown_repositories(self, repositories, category: str | None = None):
|
||||
"""Registry any unknown repositories."""
|
||||
register_tasks = [
|
||||
self.hacs.async_register_repository(
|
||||
repository_full_name=repo_data["full_name"],
|
||||
category=repo_data["category"],
|
||||
category=repo_data.get("category", category),
|
||||
check=False,
|
||||
repository_id=entry,
|
||||
)
|
||||
for entry, repo_data in repositories.items()
|
||||
if entry != "0" and not self.hacs.repositories.is_registered(repository_id=entry)
|
||||
if entry != "0"
|
||||
and not self.hacs.repositories.is_registered(repository_id=entry)
|
||||
and repo_data.get("category", category) is not None
|
||||
]
|
||||
if register_tasks:
|
||||
await asyncio.gather(*register_tasks)
|
||||
|
||||
@callback
|
||||
def async_restore_repository(self, entry, repository_data):
|
||||
def async_restore_repository(self, entry: str, repository_data: dict[str, Any]):
|
||||
"""Restore repository."""
|
||||
full_name = repository_data["full_name"]
|
||||
if not (repository := self.hacs.repositories.get_by_full_name(full_name)):
|
||||
self.logger.error("<HacsData restore> Did not find %s (%s)", full_name, entry)
|
||||
repository: HacsRepository | None = None
|
||||
if full_name := repository_data.get("full_name"):
|
||||
repository = self.hacs.repositories.get_by_full_name(full_name)
|
||||
if not repository:
|
||||
repository = self.hacs.repositories.get_by_id(entry)
|
||||
if not repository:
|
||||
return
|
||||
|
||||
# Restore repository attributes
|
||||
self.hacs.repositories.set_repository_id(repository, entry)
|
||||
repository.data.authors = repository_data.get("authors", [])
|
||||
@@ -238,7 +306,7 @@ class HacsData:
|
||||
repository.data.last_fetched = datetime.fromtimestamp(last_fetched)
|
||||
|
||||
repository.repository_manifest = HacsManifest.from_dict(
|
||||
repository_data.get("repository_manifest", {})
|
||||
repository_data.get("manifest") or repository_data.get("repository_manifest") or {}
|
||||
)
|
||||
|
||||
if repository.localpath is not None and is_safe(self.hacs, repository.localpath):
|
||||
@@ -248,6 +316,6 @@ class HacsData:
|
||||
if repository.data.installed:
|
||||
repository.data.first_install = False
|
||||
|
||||
if full_name == HacsGitHubRepo.INTEGRATION:
|
||||
if entry == HACS_REPOSITORY_ID:
|
||||
repository.data.installed_version = self.hacs.version
|
||||
repository.data.installed = True
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -66,8 +66,9 @@ async def hacs_repositories_list(
|
||||
"topics": repo.data.topics,
|
||||
}
|
||||
for repo in hacs.repositories.list_all
|
||||
if repo.data.category in (msg.get("categories") or hacs.common.categories)
|
||||
if repo.data.category in msg.get("categories", hacs.common.categories)
|
||||
and not repo.ignored_by_country_configuration
|
||||
and (not hacs.configuration.experimental or repo.data.last_fetched)
|
||||
],
|
||||
)
|
||||
)
|
||||
@@ -201,8 +202,6 @@ async def hacs_repositories_remove(
|
||||
):
|
||||
"""Remove custom repositoriy."""
|
||||
hacs: HacsBase = hass.data.get(DOMAIN)
|
||||
hacs.log.warning(connection.context)
|
||||
hacs.log.warning(msg)
|
||||
repository = hacs.repositories.get_by_id(msg["repository"])
|
||||
|
||||
repository.remove()
|
||||
|
||||
@@ -10,7 +10,8 @@ from homeassistant.const import (
|
||||
CONF_SCAN_INTERVAL,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.exceptions import ConfigEntryNotReady, ConfigEntryAuthFailed
|
||||
from hyundai_kia_connect_api.exceptions import *
|
||||
import hashlib
|
||||
|
||||
from .const import (
|
||||
@@ -52,9 +53,10 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
except Exception as ex:
|
||||
raise ConfigEntryNotReady(f"Config Not Ready: {ex}")
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][config_entry.unique_id] = coordinator
|
||||
hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)
|
||||
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
|
||||
async_setup_services(hass)
|
||||
return True
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user