From e504cdf90cc3c3e79c3e7f36eb86545f1ed2a3c2 Mon Sep 17 00:00:00 2001 From: Commander1024 Date: Fri, 6 Mar 2026 01:30:59 +0100 Subject: [PATCH] Updated ics_calendar. --- .../.github/ISSUE_TEMPLATE/bug_report.md | 35 ++++++++++++++ .../ics_calendar/.github/dependabot.yml | 14 ++++++ .../.github/pull_request_template.md | 10 ++++ .../ics_calendar/.github/workflows/hacs.yaml | 21 +++++++++ .../ics_calendar/.github/workflows/lint.yaml | 46 +++++++++++++++++++ .../.github/workflows/runtests.yaml | 44 ++++++++++++++++++ custom_components/ics_calendar/config_flow.py | 5 ++ custom_components/ics_calendar/const.py | 2 +- custom_components/ics_calendar/getparser.py | 10 +++- custom_components/ics_calendar/manifest.json | 4 +- .../ics_calendar/parsers/parser_ics.py | 26 +++++++++++ .../ics_calendar/parsers/parser_rie.py | 10 ++++ custom_components/ics_calendar/strings.json | 4 +- .../ics_calendar/translations/de.json | 4 +- .../ics_calendar/translations/en.json | 4 +- .../ics_calendar/translations/es.json | 4 +- .../ics_calendar/translations/fr.json | 4 +- .../ics_calendar/translations/pt-br.json | 4 +- 18 files changed, 234 insertions(+), 17 deletions(-) create mode 100644 custom_components/ics_calendar/.github/ISSUE_TEMPLATE/bug_report.md create mode 100644 custom_components/ics_calendar/.github/dependabot.yml create mode 100644 custom_components/ics_calendar/.github/pull_request_template.md create mode 100644 custom_components/ics_calendar/.github/workflows/hacs.yaml create mode 100644 custom_components/ics_calendar/.github/workflows/lint.yaml create mode 100644 custom_components/ics_calendar/.github/workflows/runtests.yaml diff --git a/custom_components/ics_calendar/.github/ISSUE_TEMPLATE/bug_report.md b/custom_components/ics_calendar/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..7d383df --- /dev/null +++ b/custom_components/ics_calendar/.github/ISSUE_TEMPLATE/bug_report.md @@ -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 diff --git a/custom_components/ics_calendar/.github/dependabot.yml b/custom_components/ics_calendar/.github/dependabot.yml new file mode 100644 index 0000000..d167225 --- /dev/null +++ b/custom_components/ics_calendar/.github/dependabot.yml @@ -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" diff --git a/custom_components/ics_calendar/.github/pull_request_template.md b/custom_components/ics_calendar/.github/pull_request_template.md new file mode 100644 index 0000000..cf886ca --- /dev/null +++ b/custom_components/ics_calendar/.github/pull_request_template.md @@ -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) diff --git a/custom_components/ics_calendar/.github/workflows/hacs.yaml b/custom_components/ics_calendar/.github/workflows/hacs.yaml new file mode 100644 index 0000000..cd3b38b --- /dev/null +++ b/custom_components/ics_calendar/.github/workflows/hacs.yaml @@ -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 diff --git a/custom_components/ics_calendar/.github/workflows/lint.yaml b/custom_components/ics_calendar/.github/workflows/lint.yaml new file mode 100644 index 0000000..f4d903e --- /dev/null +++ b/custom_components/ics_calendar/.github/workflows/lint.yaml @@ -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 diff --git a/custom_components/ics_calendar/.github/workflows/runtests.yaml b/custom_components/ics_calendar/.github/workflows/runtests.yaml new file mode 100644 index 0000000..6435d47 --- /dev/null +++ b/custom_components/ics_calendar/.github/workflows/runtests.yaml @@ -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 }} + diff --git a/custom_components/ics_calendar/config_flow.py b/custom_components/ics_calendar/config_flow.py index 59a16e6..69cb54c 100644 --- a/custom_components/ics_calendar/config_flow.py +++ b/custom_components/ics_calendar/config_flow.py @@ -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( diff --git a/custom_components/ics_calendar/const.py b/custom_components/ics_calendar/const.py index c8d3455..7704bce 100644 --- a/custom_components/ics_calendar/const.py +++ b/custom_components/ics_calendar/const.py @@ -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" diff --git a/custom_components/ics_calendar/getparser.py b/custom_components/ics_calendar/getparser.py index 594c2e2..3f2c7c8 100644 --- a/custom_components/ics_calendar/getparser.py +++ b/custom_components/ics_calendar/getparser.py @@ -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 diff --git a/custom_components/ics_calendar/manifest.json b/custom_components/ics_calendar/manifest.json index c3f9cce..53a0972 100644 --- a/custom_components/ics_calendar/manifest.json +++ b/custom_components/ics_calendar/manifest.json @@ -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" } diff --git a/custom_components/ics_calendar/parsers/parser_ics.py b/custom_components/ics_calendar/parsers/parser_ics.py index 5e10cd1..ce550a9 100644 --- a/custom_components/ics_calendar/parsers/parser_ics.py +++ b/custom_components/ics_calendar/parsers/parser_ics.py @@ -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 diff --git a/custom_components/ics_calendar/parsers/parser_rie.py b/custom_components/ics_calendar/parsers/parser_rie.py index 8902142..dc0962e 100644 --- a/custom_components/ics_calendar/parsers/parser_rie.py +++ b/custom_components/ics_calendar/parsers/parser_rie.py @@ -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 diff --git a/custom_components/ics_calendar/strings.json b/custom_components/ics_calendar/strings.json index dee0db3..6db6fbe 100644 --- a/custom_components/ics_calendar/strings.json +++ b/custom_components/ics_calendar/strings.json @@ -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": { } diff --git a/custom_components/ics_calendar/translations/de.json b/custom_components/ics_calendar/translations/de.json index 130a0e0..9b9edaf 100644 --- a/custom_components/ics_calendar/translations/de.json +++ b/custom_components/ics_calendar/translations/de.json @@ -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": { } diff --git a/custom_components/ics_calendar/translations/en.json b/custom_components/ics_calendar/translations/en.json index dee0db3..6db6fbe 100644 --- a/custom_components/ics_calendar/translations/en.json +++ b/custom_components/ics_calendar/translations/en.json @@ -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": { } diff --git a/custom_components/ics_calendar/translations/es.json b/custom_components/ics_calendar/translations/es.json index ca74ff7..49c383b 100644 --- a/custom_components/ics_calendar/translations/es.json +++ b/custom_components/ics_calendar/translations/es.json @@ -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": { } diff --git a/custom_components/ics_calendar/translations/fr.json b/custom_components/ics_calendar/translations/fr.json index 4212e4b..cc6fe20 100644 --- a/custom_components/ics_calendar/translations/fr.json +++ b/custom_components/ics_calendar/translations/fr.json @@ -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": { } diff --git a/custom_components/ics_calendar/translations/pt-br.json b/custom_components/ics_calendar/translations/pt-br.json index 40dc547..3769561 100644 --- a/custom_components/ics_calendar/translations/pt-br.json +++ b/custom_components/ics_calendar/translations/pt-br.json @@ -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": { }