Updated ics_calendar.

This commit is contained in:
2026-03-06 01:30:59 +01:00
parent c9807a7bf0
commit e504cdf90c
18 changed files with 234 additions and 17 deletions

View File

@@ -0,0 +1,35 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Home Assistant Setup**
Please indicate your version of HA and how it is installed.
Version:
Installation Type (put an X between the square brackets for your HA):
[] Home Assistant OS
[] Home Assistant Supervised
[] Home Assistant Container
[] Home Assistant Core
Hardware platform:
[] ARM
[] x86-64
Are you running in a container environment like Docker or Kubernetes?
[] Yes
[] No
If running in a container, how is your image built?
[] Official HA container image
[] Official HA container image with customizations
[] Custom built container image
**Describe the bug**
A clear and concise description of the bug

View File

@@ -0,0 +1,14 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "pip" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
day: "monday"
reviewers:
- "franc6"

View File

@@ -0,0 +1,10 @@
Fixes #
Description of change:
## Formatting, testing, and code coverage
Please note your pull request won't be accepted if you haven't properly formatted your source code, and ensured the unit tests are appropriate. Please note if you are not running on Windows, you can either run the scripts via a bash installation (like git-bash).
- [] formatstyle.sh reports no errors
- [] All unit tests pass (test.sh)
- [] Code coverage has not decreased (test.sh)

View File

@@ -0,0 +1,21 @@
name: Validate with hassfest and run HACS action
on:
push:
pull_request:
workflow_dispatch:
schedule:
- cron: '0 0 * * *'
jobs:
validate:
runs-on: "ubuntu-latest"
steps:
- name: Checkout
uses: "actions/checkout@v4"
- name: Validate with hassfest
uses: "home-assistant/actions/hassfest@master"
- name: HACS Action
uses: "hacs/action@main"
with:
category: integration

View File

@@ -0,0 +1,46 @@
name: Lint
on:
pull_request:
branches: [releases]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
version: "0.4.20"
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version-file: "pyproject.toml"
- name: Install dependencies
run: |
uv sync --prerelease=allow --dev --extra tests
- name: Run isort --check
run: |
uv run --prerelease=allow isort --check custom_components/ics_calendar tests
- name: Run black --check
run: |
uv run --prerelease=allow black --check custom_components/ics_calendar tests
- name: Run flake8
run: |
uv run --prerelease=allow flake8
- name: Run pydocstyle
run: |
uv run --prerelease=allow pydocstyle -v custom_components/ics_calendar tests
- name: Run pylint
run: |
uv run --prerelease=allow pylint custom_components/ics_calendar

View File

@@ -0,0 +1,44 @@
name: Run Tests
on:
push:
branches: [releases]
pull_request:
branches: [releases]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Set timezone
run: |
sudo timedatectl set-timezone America/New_York
timedatectl
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
version: "0.4.20"
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version-file: "pyproject.toml"
- name: Install dependencies
run: |
uv sync --prerelease=allow --extra tests
- name: Run pytest
run: |
PYTHONDONTWRITEBYTECODE=1 uv run --prerelease=allow pytest tests/
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}

View File

@@ -88,6 +88,10 @@ TIMEOUT_OPTS_SCHEMA = vol.Schema(
{vol.Optional(CONF_CONNECTION_TIMEOUT, default=None): cv.positive_float}
)
FILTER_DOC_URL = (
"https://github.com/franc6/ics_calendar/blob/releases/README.md#filters"
)
def is_array_string(arr_str: str) -> bool:
"""Return true if arr_str starts with [ and ends with ]."""
@@ -231,6 +235,7 @@ class ICSCalendarConfigFlow(ConfigFlow, domain=DOMAIN):
data_schema=CALENDAR_OPTS_SCHEMA,
errors=errors,
last_step=False,
description_placeholders={"filterdoc": FILTER_DOC_URL},
)
async def async_step_connect_opts(

View File

@@ -1,6 +1,6 @@
"""Constants for ics_calendar platform."""
VERSION = "5.1.5"
VERSION = "5.1.7"
DOMAIN = "ics_calendar"
CONF_DEVICE_ID = "device_id"

View File

@@ -1,8 +1,6 @@
"""Provide GetParser class."""
from .icalendarparser import ICalendarParser
from .parsers.parser_ics import ParserICS
from .parsers.parser_rie import ParserRIE
class GetParser: # pylint: disable=R0903
@@ -20,8 +18,16 @@ class GetParser: # pylint: disable=R0903
# if parser_cls is not None:
# return parser_cls(*args)
if parser == "rie":
from .parsers.parser_rie import ( # pylint: disable=C0415
ParserRIE,
)
return ParserRIE(*args)
if parser == "ics":
from .parsers.parser_ics import ( # pylint: disable=C0415
ParserICS,
)
return ParserICS(*args)
return None

View File

@@ -8,6 +8,6 @@
"integration_type": "service",
"iot_class": "cloud_polling",
"issue_tracker": "https://github.com/franc6/ics_calendar/issues",
"requirements": ["icalendar~=6.1","python-dateutil>=2.9.0.post0","pytz>=2024.1","recurring_ical_events~=3.5,>=3.5.2","ics==0.7.2","arrow","httpx_auth>=0.22.0,<=0.23.1"],
"version": "5.1.5"
"requirements": ["icalendar~=6.1","python-dateutil>=2.9.0.post0","pytz>=2024.1","recurring_ical_events~=3.5,>=3.5.2","ics==0.7.2","arrow","httpx_auth>=0.22.0,<=0.23.1","tatsu>=4.2.6,<5.8.0"],
"version": "5.1.7"
}

View File

@@ -76,7 +76,12 @@ class ParserICS(ICalendarParser):
# summary = event.summary
# elif hasattr(event, "name"):
summary = event.name
rrule = None
for extra in event.extra:
if extra.name == "RRULE":
rrule = extra.value
calendar_event: ParserEvent = ParserEvent(
uid=event.uid,
summary=summary,
start=ParserICS.get_date(
event.begin, event.all_day, offset_hours
@@ -86,6 +91,14 @@ class ParserICS(ICalendarParser):
),
location=event.location,
description=event.description,
rrule=rrule,
recurrence_id=(
ParserICS.get_date(
event.begin, event.all_day, offset_hours
)
if rrule
else None
),
)
if self._filter.filter_event(calendar_event):
event_list.append(calendar_event)
@@ -145,7 +158,12 @@ class ParserICS(ICalendarParser):
# summary = temp_event.summary
# elif hasattr(event, "name"):
summary = temp_event.name
rrule = None
for extra in temp_event.extra:
if extra.name == "RRULE":
rrule = extra.value
return ParserEvent(
uid=temp_event.uid,
summary=summary,
start=ParserICS.get_date(
temp_event.begin, temp_event.all_day, offset_hours
@@ -155,6 +173,14 @@ class ParserICS(ICalendarParser):
),
location=temp_event.location,
description=temp_event.description,
rrule=rrule,
recurrence_id=(
ParserICS.get_date(
temp_event.begin, temp_event.all_day, offset_hours
)
if rrule
else None
),
)
@staticmethod

View File

@@ -75,11 +75,16 @@ class ParserRIE(ICalendarParser):
continue
calendar_event: ParserEvent = ParserEvent(
uid=event.get("UID"),
summary=event.get("SUMMARY"),
start=start,
end=end,
location=event.get("LOCATION"),
description=event.get("DESCRIPTION"),
# rrule=event.get("RRULE"),
recurrence_id=ParserRIE.get_date(
event.get("RECURRENCE-ID").dt
),
)
if self._filter.filter_event(calendar_event):
event_list.append(calendar_event)
@@ -141,11 +146,16 @@ class ParserRIE(ICalendarParser):
return None
return ParserEvent(
uid=temp_event.get("UID"),
summary=temp_event.get("SUMMARY"),
start=temp_start,
end=temp_end,
location=temp_event.get("LOCATION"),
description=temp_event.get("DESCRIPTION"),
# rrule=temp_event.get("RRULE"),
recurrence_id=ParserRIE.get_date(
temp_event.get("RECURRENCE-ID").dt
),
)
@staticmethod

View File

@@ -68,8 +68,8 @@
"empty_url": "The url must not be empty.",
"download_interval_too_small": "The download interval must be at least 15.",
"exclude_include_cannot_be_the_same": "The exclude and include strings must not be the same",
"exclude_must_be_array": "The exclude option must be an array of strings or regular expressions. See https://github.com/franc6/ics_calendar/blob/releases/README.md#filters for more information.",
"include_must_be_array": "The include option must be an array of strings or regular expressions. See https://github.com/franc6/ics_calendar/blob/releases/README.md#filters for more information."
"exclude_must_be_array": "The exclude option must be an array of strings or regular expressions. See {filterdoc} for more information.",
"include_must_be_array": "The include option must be an array of strings or regular expressions. See {filterdoc} for more information."
},
"abort": {
}

View File

@@ -67,8 +67,8 @@
"empty_url": "Die URL darf nicht leer sein.",
"download_interval_too_small": "Das Download-Intervall muss mindestens 15 betragen.",
"exclude_include_cannot_be_the_same": "Die Ausschluss- und Einschluss-Strings dürfen nicht identisch sein.",
"exclude_must_be_array": "Die \"auszuschließenden Ereignisse\" müssen ein Array von Zeichenfolgen oder regulären Ausdrücken sein. Weitere Informationen finden Sie unter https://github.com/franc6/ics_calendar/blob/releases/README.md#filters.",
"include_must_be_array": "Die \"einzuschließenden Ereignisse\" müssen ein Array von Zeichenfolgen oder regulären Ausdrücken sein. Weitere Informationen finden Sie unter https://github.com/franc6/ics_calendar/blob/releases/README.md#filters."
"exclude_must_be_array": "Die \"auszuschließenden Ereignisse\" müssen ein Array von Zeichenfolgen oder regulären Ausdrücken sein. Weitere Informationen finden Sie unter {filterdoc}.",
"include_must_be_array": "Die \"einzuschließenden Ereignisse\" müssen ein Array von Zeichenfolgen oder regulären Ausdrücken sein. Weitere Informationen finden Sie unter {filterdoc}."
},
"abort": {
}

View File

@@ -68,8 +68,8 @@
"empty_url": "The url must not be empty.",
"download_interval_too_small": "The download interval must be at least 15.",
"exclude_include_cannot_be_the_same": "The exclude and include strings must not be the same",
"exclude_must_be_array": "The exclude option must be an array of strings or regular expressions. See https://github.com/franc6/ics_calendar/blob/releases/README.md#filters for more information.",
"include_must_be_array": "The include option must be an array of strings or regular expressions. See https://github.com/franc6/ics_calendar/blob/releases/README.md#filters for more information."
"exclude_must_be_array": "The exclude option must be an array of strings or regular expressions. See {filterdoc} for more information.",
"include_must_be_array": "The include option must be an array of strings or regular expressions. See {filterdoc} for more information."
},
"abort": {
}

View File

@@ -68,8 +68,8 @@
"empty_url": "La url no debe estar vacía.",
"download_interval_too_small": "El intervalo de descarga debe ser de al menos 15.",
"exclude_include_cannot_be_the_same": "Las cadenas de exclusión e inclusión no deben ser las mismas",
"exclude_must_be_array": "La opción de exclusión debe ser una matriz de cadenas o expresiones regulares. Consulte https://github.com/franc6/ics_calendar/blob/releases/README.md#filters para obtener más información.",
"include_must_be_array": "La opción de inclusión debe ser un array de cadenas o expresiones regulares. Consulte https://github.com/franc6/ics_calendar/blob/releases/README.md#filters para obtener más información."
"exclude_must_be_array": "La opción de exclusión debe ser una matriz de cadenas o expresiones regulares. Consulte {filterdoc} para obtener más información.",
"include_must_be_array": "La opción de inclusión debe ser un array de cadenas o expresiones regulares. Consulte {filterdoc} para obtener más información."
},
"abort": {
}

View File

@@ -67,8 +67,8 @@
"empty_url": "L'URL du calendrier doit être renseignée.",
"download_interval_too_small": "L'intervalle de téléchargement ne peut pas être inférieur à 15 minutes.",
"exclude_include_cannot_be_the_same": "Les valeurs d'exclusion et d'inclusion ne peuvent pas être identiques.",
"exclude_must_be_array": "The exclude option must be an array of strings or regular expressions. See https://github.com/franc6/ics_calendar/blob/releases/README.md#filters for more information.",
"include_must_be_array": "The include option must be an array of strings or regular expressions. See https://github.com/franc6/ics_calendar/blob/releases/README.md#filters for more information."
"exclude_must_be_array": "L'option d'exclusion doit être un tableau de chaînes ou d'expressions régulières. Voir {filterdoc} pour plus d'informations.",
"include_must_be_array": "L'option d'inclusion doit être un tableau de chaînes ou d'expressions régulières. Voir {filterdoc} pour plus d'informations."
},
"abort": {
}

View File

@@ -68,8 +68,8 @@
"empty_url": "A URL não pode estar vazia.",
"download_interval_too_small": "O intervalo de download deve ser de pelo menos 15.",
"exclude_include_cannot_be_the_same": "As strings de exclusão e inclusão não podem ser as mesmas.",
"exclude_must_be_array": "A opção de exclusão deve ser um array de strings ou expressões regulares. Veja https://github.com/franc6/ics_calendar/blob/releases/README.md#filters para mais informações.",
"include_must_be_array": "A opção de inclusão deve ser um array de strings ou expressões regulares. Veja https://github.com/franc6/ics_calendar/blob/releases/README.md#filters para mais informações."
"exclude_must_be_array": "A opção de exclusão deve ser um array de strings ou expressões regulares. Veja {filterdoc} para mais informações.",
"include_must_be_array": "A opção de inclusão deve ser um array de strings ou expressões regulares. Veja {filterdoc} para mais informações."
},
"abort": {
}