{"id":16171512,"url":"https://github.com/koljawindeler/ics","last_synced_at":"2025-08-08T17:13:11.164Z","repository":{"id":44300217,"uuid":"246410785","full_name":"KoljaWindeler/ics","owner":"KoljaWindeler","description":"Integration that displays the next event of an ics link (support reoccuring events)","archived":false,"fork":false,"pushed_at":"2025-01-14T13:41:15.000Z","size":462,"stargazers_count":54,"open_issues_count":30,"forks_count":14,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-09T16:13:14.217Z","etag":null,"topics":["appointments","filtering","homeassistant","ics","reoccuring-events"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/KoljaWindeler.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-03-10T21:20:32.000Z","updated_at":"2025-04-02T14:00:39.000Z","dependencies_parsed_at":"2024-04-23T09:15:53.007Z","dependency_job_id":"e63face6-2617-4093-a61c-5e72d9e27801","html_url":"https://github.com/KoljaWindeler/ics","commit_stats":{"total_commits":64,"total_committers":10,"mean_commits":6.4,"dds":0.34375,"last_synced_commit":"da35ed548c5d6318f9ec00348fd228e0e33b37e8"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KoljaWindeler%2Fics","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KoljaWindeler%2Fics/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KoljaWindeler%2Fics/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KoljaWindeler%2Fics/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/KoljaWindeler","download_url":"https://codeload.github.com/KoljaWindeler/ics/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248065282,"owners_count":21041872,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["appointments","filtering","homeassistant","ics","reoccuring-events"],"created_at":"2024-10-10T03:24:10.943Z","updated_at":"2025-04-09T16:13:22.151Z","avatar_url":"https://github.com/KoljaWindeler.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ICS\n\nAdds a sensor to Home Assistant that displays the date and number of days to the next event. \nE.g. 5 days until the trash will be picked up. The information will be read from a user definded \nics file.\n\n**This component will set up the following platforms.**\n\nPlatform | Description\n-- | --\n`sensor` | Show date and remaining days to event\n\n![Example](img_example.png)\n\n\n## Features\n\n- Supports ICS file (local and online) with reoccuring events\n- Events can be filtered, so you can tell it to look only for certain events\n- Has attributes that calculated the number of days, so you can easily run a automation trigger, show start / end of the events\n- Low CPU and network usage, as it only updates once per day (whenever the date changes) or when the event is over (assuming force reload is disabled)\n- If multiple events occure on the same time, all title will be shown, connected with \"/\"\n- Can be configured to look for 'the event after the event'\n\n# Installation\n\n## HACS\n\nThe easiest way to add this to your Homeassistant installation is using [HACS]. \n\nIt's recommended to restart Homeassistent directly after the installation without any change to the Configuration. \nHomeassistent will install the dependencies during the next reboot. After that you can add and check the configuration without error messages. \nThis is nothing special to this Integration but the same for all custom components.\n\n\n## Manual\n\n1. Using the tool of choice open the directory (folder) for your HA configuration (where you find `configuration.yaml`).\n2. If you do not have a `custom_components` directory (folder) there, you need to create it.\n3. In the `custom_components` directory (folder) create a new folder called `ics`.\n4. Download _all_ the files from the `custom_components/ics/` directory (folder) in this repository.\n5. Place the files you downloaded in the new directory (folder) you created.\n6. Follow the instructions under [Configuration](#Configuration) below.\n\nUsing your HA configuration directory (folder) as a starting point you should now also have this:\n\n```text\ncustom_components/ics/.translations/en.json\ncustom_components/ics/__init__.py\ncustom_components/ics/manifest.json\ncustom_components/ics/sensor.py\ncustom_components/ics/config_flow.py\ncustom_components/ics/const.py\n\n```\n\n# Setup\n\nAll you need to have is a link to a ICS file, e.g. https://www.rmg-gmbh.de/download/Hamb%C3%BChren.ics\n\n## Configuration options\n\nKey | Type | Required | Default | Description\n-- | -- | -- | -- | --\n`name` | `string` | `true` | `None` |  The friendly name of the sensor\n`url` | `string` | `true` | `None` | The url to the ics file usually some weblink, but can also be local file e.g. https://www.rmg-gmbh.de/download/Hamb\\%C3%BChren.ics or file:///tmp/test.ics\n`id` | `int` | `true` | `None` | A number to identify your sensor later on. e.g. for id=1 the entity will be sensor.ics_1 using id 1 a second will result in sensor.ics_1_2\n`timeformat` | `string` | `false` | `\"%A, %d.%m.%Y\"` | The format that is used to display the date see http://strftime.org/ for more infomation\n`lookahead` | `int` | `false` | `365` | The number of days that limits the forecast. E.g. 1 will only show the events of today\n`startswith` | `string` | `false` | `\"\"` | A filter that will limit the display of events. E.g. if your file contains multiple entries and you only want to know one type at per sensor, simply create multiple sensors and filter. Have a look at sensor 3 and 4 above. startswith: Bio will ohne show events that start with Bio.\n`contains` | `string` | `false` | `\"\"` | A filter like startswith, but this will search within the string instead of the start.\n`show_blank` | `string` | `false` | `\"\"` | Indicates whether to show empty events (events without title), and what should be used as title instead. e.g. \"Meeting123\" would show events with empty title with the string \"Meeting123\". An empty string (default) or \" \" will avoid showing blank events.\n`force_update` | `int` | `false` | `0` | Force to update the data with given intervall (seconds). This can be useful if the calendar is very dynamic, but pointless for almost static calendars. The calendar will reload at midnight and once the (start/end) of the event is over regardless of this setting. 0 = Disabled\n`show_remaining` | `bool` | `false` | `true` | Show the remaining days in the sensor state, close to the date.\n`show_ongoing` | `bool` | `false` | `false` | Show events that have already started but not finished.\n`group_events` | `bool` | `false` | `true` | Show events with same start date as one event\n`n_skip` | `int` | `false` | `0` | Skip the given amount of events, useful to show the appointment AFTER the next appointment\n`description_in_state` | `bool` | `false` | `false` | Show the title of the events in the state\n`icon` | `string` | `false` | `mdi:calendar` | MDI Icon string, check https://materialdesignicons.com/\n\n## GUI configuration \n\nAs of 2020/04/20 config flow is supported and is the prefered way to setup the integration. (No need to restart Home-Assistant)\n\n## Manual configuration \n\nTo enable the sensor, add the following lines to your `configuration.yaml` file and replace the link accordingly:\n\n```yaml\n# Example entry for configuration.yaml\nsensor:\n\n  - platform: ics\n    name: Packaging\n    url: https://www.rmg-gmbh.de/download/Hamb%C3%BChren.ics\n    id: 1\n    icon: \"mdi:recycle\"\n\n  - platform: ics\n    name: Trash\n    url: http://www.zacelle.de/privatkunden/muellabfuhr/abfuhrtermine/?tx_ckcellextermine_pi1%5Bot%5D=148\u0026tx_ckcellextermine_pi1%5Bics%5D=0\u0026tx_ckcellextermine_pi1%5Bstartingpoint%5D=234\u0026type=3333\n    id: 2\n    icon: \"mdi:trash-can-outline\"\n\n  - platform: ics\n    name: Trash 2\n    url: https://www.ab-peine.de/mcalendar/export_termine.php?menuid=185\u0026area=141\u0026year=2020\n    startswith: Rest\n    id: 3\n    icon: \"mdi:trash-can-outline\"\n\n  - platform: ics\n    name: Bio\n    url: https://www.ab-peine.de/mcalendar/export_termine.php?menuid=185\u0026area=141\u0026year=2020\n    startswith: Bio\n    show_ongoing: true\n    id: 4\n\n  - platform: ics\n    name: Work\n    url: https://calendar.google.com/calendar/ical/xxxxxxxxxxxxxgroup.calendar.google.com/private-xxxxxxxxxxxxxxx/basic.ics\n    timeformat: \"%d.%m. %H:%M\"\n    force_update: 600\n    show_remaining: false\n    id: 5\n\n  - platform: ics\n    name: Work today\n    url: https://calendar.google.com/calendar/ical/xxxxxxxxxxxxxgroup.calendar.google.com/private-xxxxxxxxxxxxxxx/basic.ics\n    timeformat: \"%H:%M\"\n    lookahead: 1\n    force_update: 600\n    show_remaining: false\n    id: 6\n\n  - platform: template\n    sensors:\n      ics_5_txt:\n        value_template: '{{ states.sensor.ics_5.attributes.description}} @ {{states.sensor.ics_5.state}}'\n        friendly_name: \"Work Next\"\n      ics_6_txt:\n        value_template: '{{ states.sensor.ics_6.attributes.description}} @ {{states.sensor.ics_6.state}}'\n        friendly_name: \"Work today\"\n\n\n```\n\n# Automation\n\nExample that executes on the day before one of the 'events'\n\n```yaml\nautomation:\n  - alias: 'trash pickup msg'\n    initial_state: 'on'\n    trigger:\n      - platform: time_pattern\n        hours: 19\n        minutes: 00\n        seconds: 00\n    condition:\n        condition: or\n        conditions:\n        - condition: template\n          value_template: \"{{ state_attr('sensor.ics_1', 'remaining') == 1 }}\"\n        - condition: template\n          value_template: \"{{ state_attr('sensor.ics_2', 'remaining') == 1 }}\"\n        - condition: template\n          value_template: \"{{ state_attr('sensor.ics_3', 'remaining') == 1 }}\"\n```\n\nand create / send some beautiful messages like this:\n\n```yaml\nscript:\n   seq_trash:\n      sequence:\n         - service: notify.pb\n           data_template:\n           title: \"Trash pickup tomorrow\"\n           message: \u003e\n             {% if is_state_attr(\"sensor.ics_1\", \"remaining\",1) %} {{states.sensor.ics_1.attributes.friendly_name}} pickup tomorrow.{% endif %}\n             {% if is_state_attr(\"sensor.ics_2\", \"remaining\",1) %} {{states.sensor.ics_2.attributes.friendly_name}} pickup tomorrow.{% endif %}\n             {% if is_state_attr(\"sensor.ics_3\", \"remaining\",1) %} {{states.sensor.ics_3.attributes.friendly_name}} pickup tomorrow.{% endif %}\n```\n\n# Use-cases for skip property\nShow the next n-Events. Simply by creating three sensors with `n_skip:0 / n_skip:1 / n_skip:2` \u003cbr\u003e\nSetting 'description_in_state: True` will also show the title.\n\n\n![Example](img_feiertage.png)\n\nOr list the next sport events\n\n![Example](img_fussi.png)\n\n# Advance feature\nReoccuring events, events at the same time, skippig events (EXDATE) ... all that can have quite some complexity to it. \n\n![Example](img_complex_cal.png)\n\nThe image above shows my test calendar (year = 2030! thus all lookahead must be \u003e\u003e 365)\n\n## Regular (with grouping)\n```yaml\nsensor:\n- platform: ics\n    name: \"regular\"\n    url: https://calendar.google.com/calendar/...\n    id: 3\n    timeformat: \"%A, %d.%m.%Y\"\n    lookahead: 36500\n    startswith: \"\"\n    show_blank: \"\"\n    force_update: 0\n    show_remaining: True\n    show_ongoing: False\n    group_events: True\n    n_skip: 0\n    description_in_state: False\n```\nWithout advance options (all defaults) we'll get a sensor, showing both event1 and event3 because grouping is enabled (`group_events: True`). \n\n![Example](img_1_reg.png)\n\n`Setting group_event: False` would either show event1 or event3 (depends on their order in the ics file)\n\n## Skip 3 next events\n\n```yaml\n  - platform: ics\n    name: \"skip 3, no blank\"\n    url: https://calendar.google.com/calendar/...\n    id: 5\n    timeformat: \"%d.%m. \"\n    lookahead: 36500\n    startswith: \"\"\n    show_blank: \"\"\n    force_update: 0\n    show_remaining: False\n    show_ongoing: False\n    group_events: True\n    n_skip: 3\n    description_in_state: True\n```\nThis configuration will show 'allday'. `group_events` will combine the \"event1/event3\". `show_blank` is not set, so the event without title will be ignored. \n\n## Skip 3 next events but show blank events\n```yaml\n- platform: ics\n    name: \"skip 3, incl blank\"\n    url: https://calendar.google.com/calendar/...\n    id: 6\n    timeformat: \"%A, %d.%m.%Y\"\n    lookahead: 36500\n    startswith: \"\"\n    show_blank: \"Hallo\"\n    force_update: 0\n    show_remaining: False\n    show_ongoing: False\n    group_events: True\n    n_skip: 3\n    description_in_state: False\n```\nSetting `show_blank: 'Hallo'` will add the previous empty event to the list and thus show 'Hallo' instead of 'allday'\n\n## Filter and Skip\n```yaml\n- platform: ics\n    name: \"second reoccur\"\n    url: https://calendar.google.com/calendar/...\n    id: 8\n    timeformat: \"%A, %d.%m.%Y\"\n    lookahead: 36500\n    startswith: \"reocc\"\n    show_blank: \"\"\n    force_update: 0\n    show_remaining: True\n    show_ongoing: False\n    group_events: True\n    n_skip: 1\n    description_in_state: False\n```\nThis will only look at events that `startswith: 'reocc'`. The first occurance on the 7.1.2030 will be skipped (`n_skip:1`), [the exdate will drop the 8.1.2030] and thus 9.1.2030 will be shown.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkoljawindeler%2Fics","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkoljawindeler%2Fics","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkoljawindeler%2Fics/lists"}