We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
impossible to call fetch_data manual
Because i try to add my own ics http url i need debug it.
Calling fetch_data doesn't work:

```
takes 0 positional arguments but 1 was given
```

just an extra question: Is it possible to use {%y} (small) to get 23 not 2023?

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `custom_components/waste_collection_schedule/__init__.py`
Content:
```
1 """Waste Collection Schedule Component."""
2 import logging
3 import site
4 from pathlib import Path
5 from random import randrange
6 
7 import homeassistant.helpers.config_validation as cv
8 import homeassistant.util.dt as dt_util
9 import voluptuous as vol
10 from homeassistant.core import HomeAssistant, callback
11 from homeassistant.helpers.dispatcher import dispatcher_send
12 
13 from .const import DOMAIN, UPDATE_SENSORS_SIGNAL
14 
15 from homeassistant.helpers.event import async_call_later  # isort:skip
16 from homeassistant.helpers.event import async_track_time_change  # isort:skip
17 
18 # add module directory to path
19 package_dir = Path(__file__).resolve().parents[0]
20 site.addsitedir(str(package_dir))
21 from waste_collection_schedule import Customize, SourceShell  # type: ignore # isort:skip # noqa: E402
22 
23 _LOGGER = logging.getLogger(__name__)
24 
25 CONF_SOURCES = "sources"
26 CONF_SOURCE_NAME = "name"
27 CONF_SOURCE_ARGS = "args"  # source arguments
28 CONF_SOURCE_CALENDAR_TITLE = "calendar_title"
29 CONF_SEPARATOR = "separator"
30 CONF_FETCH_TIME = "fetch_time"
31 CONF_RANDOM_FETCH_TIME_OFFSET = "random_fetch_time_offset"
32 CONF_DAY_SWITCH_TIME = "day_switch_time"
33 
34 CONF_CUSTOMIZE = "customize"
35 CONF_TYPE = "type"
36 CONF_ALIAS = "alias"
37 CONF_SHOW = "show"
38 CONF_ICON = "icon"
39 CONF_PICTURE = "picture"
40 CONF_USE_DEDICATED_CALENDAR = "use_dedicated_calendar"
41 CONF_DEDICATED_CALENDAR_TITLE = "dedicated_calendar_title"
42 
43 CUSTOMIZE_CONFIG = vol.Schema(
44     {
45         vol.Optional(CONF_TYPE): cv.string,
46         vol.Optional(CONF_ALIAS): cv.string,
47         vol.Optional(CONF_SHOW): cv.boolean,
48         vol.Optional(CONF_ICON): cv.icon,
49         vol.Optional(CONF_PICTURE): cv.string,
50         vol.Optional(CONF_USE_DEDICATED_CALENDAR): cv.boolean,
51         vol.Optional(CONF_DEDICATED_CALENDAR_TITLE): cv.string,
52     }
53 )
54 
55 SOURCE_CONFIG = vol.Schema(
56     {
57         vol.Required(CONF_SOURCE_NAME): cv.string,
58         vol.Required(CONF_SOURCE_ARGS): dict,
59         vol.Optional(CONF_CUSTOMIZE, default=[]): vol.All(
60             cv.ensure_list, [CUSTOMIZE_CONFIG]
61         ),
62         vol.Optional(CONF_SOURCE_CALENDAR_TITLE): cv.string,
63     }
64 )
65 
66 CONFIG_SCHEMA = vol.Schema(
67     {
68         DOMAIN: vol.Schema(
69             {
70                 vol.Required(CONF_SOURCES): vol.All(cv.ensure_list, [SOURCE_CONFIG]),
71                 vol.Optional(CONF_SEPARATOR, default=", "): cv.string,
72                 vol.Optional(CONF_FETCH_TIME, default="01:00"): cv.time,
73                 vol.Optional(
74                     CONF_RANDOM_FETCH_TIME_OFFSET, default=60
75                 ): cv.positive_int,
76                 vol.Optional(CONF_DAY_SWITCH_TIME, default="10:00"): cv.time,
77             }
78         )
79     },
80     extra=vol.ALLOW_EXTRA,
81 )
82 
83 
84 async def async_setup(hass: HomeAssistant, config: dict):
85     """Set up the component. config contains data from configuration.yaml."""
86     # create empty api object as singleton
87     api = WasteCollectionApi(
88         hass,
89         separator=config[DOMAIN][CONF_SEPARATOR],
90         fetch_time=config[DOMAIN][CONF_FETCH_TIME],
91         random_fetch_time_offset=config[DOMAIN][CONF_RANDOM_FETCH_TIME_OFFSET],
92         day_switch_time=config[DOMAIN][CONF_DAY_SWITCH_TIME],
93     )
94 
95     # create shells for source(s)
96     for source in config[DOMAIN][CONF_SOURCES]:
97         # create customize object
98         customize = {}
99         for c in source.get(CONF_CUSTOMIZE, {}):
100             customize[c[CONF_TYPE]] = Customize(
101                 waste_type=c[CONF_TYPE],
102                 alias=c.get(CONF_ALIAS),
103                 show=c.get(CONF_SHOW, True),
104                 icon=c.get(CONF_ICON),
105                 picture=c.get(CONF_PICTURE),
106                 use_dedicated_calendar=c.get(CONF_USE_DEDICATED_CALENDAR, False),
107                 dedicated_calendar_title=c.get(CONF_DEDICATED_CALENDAR_TITLE, False),
108             )
109         api.add_source_shell(
110             source_name=source[CONF_SOURCE_NAME],
111             customize=customize,
112             calendar_title=source.get(CONF_SOURCE_CALENDAR_TITLE),
113             source_args=source.get(CONF_SOURCE_ARGS, {}),
114         )
115 
116     # store api object
117     hass.data.setdefault(DOMAIN, api)
118 
119     # load calendar platform
120     await hass.helpers.discovery.async_load_platform(
121         "calendar", DOMAIN, {"api": api}, config
122     )
123 
124     # initial fetch of all data
125     hass.add_job(api._fetch)
126     
127     def fetch_data():
128         hass.add_job(api._fetch)
129 
130     # Register new Service fetch_data
131     hass.services.async_register(DOMAIN, 'fetch_data', fetch_data)
132 
133     return True
134 
135 
136 class WasteCollectionApi:
137     def __init__(
138         self, hass, separator, fetch_time, random_fetch_time_offset, day_switch_time
139     ):
140         self._hass = hass
141         self._source_shells = []
142         self._separator = separator
143         self._fetch_time = fetch_time
144         self._random_fetch_time_offset = random_fetch_time_offset
145         self._day_switch_time = day_switch_time
146 
147         # start timer to fetch date once per day
148         async_track_time_change(
149             hass,
150             self._fetch_callback,
151             self._fetch_time.hour,
152             self._fetch_time.minute,
153             self._fetch_time.second,
154         )
155 
156         # start timer for day-switch time
157         if self._day_switch_time != self._fetch_time:
158             async_track_time_change(
159                 hass,
160                 self._update_sensors_callback,
161                 self._day_switch_time.hour,
162                 self._day_switch_time.minute,
163                 self._day_switch_time.second,
164             )
165 
166         # add a timer at midnight (if not already there) to update days-to
167         midnight = dt_util.parse_time("00:00")
168         if midnight != self._fetch_time and midnight != self._day_switch_time:
169             async_track_time_change(
170                 hass,
171                 self._update_sensors_callback,
172                 midnight.hour,
173                 midnight.minute,
174                 midnight.second,
175             )
176 
177     @property
178     def separator(self):
179         """Separator string, used to separator waste types."""
180         return self._separator
181 
182     @property
183     def fetch_time(self):
184         """When to fetch to data."""
185         return self._fetch_time
186 
187     @property
188     def day_switch_time(self):
189         """When to hide entries for today."""
190         return self._day_switch_time
191 
192     def add_source_shell(
193         self,
194         source_name,
195         customize,
196         source_args,
197         calendar_title,
198     ):
199         self._source_shells.append(
200             SourceShell.create(
201                 source_name=source_name,
202                 customize=customize,
203                 source_args=source_args,
204                 calendar_title=calendar_title,
205             )
206         )
207 
208     def _fetch(self, *_):
209         for shell in self._source_shells:
210             shell.fetch()
211 
212         self._update_sensors_callback()
213 
214     @property
215     def shells(self):
216         return self._source_shells
217 
218     def get_shell(self, index):
219         return self._source_shells[index] if index < len(self._source_shells) else None
220 
221     @callback
222     def _fetch_callback(self, *_):
223         async_call_later(
224             self._hass,
225             randrange(0, 60 * self._random_fetch_time_offset),
226             self._fetch_now_callback,
227         )
228 
229     @callback
230     def _fetch_now_callback(self, *_):
231         self._hass.add_job(self._fetch)
232 
233     @callback
234     def _update_sensors_callback(self, *_):
235         dispatcher_send(self._hass, UPDATE_SENSORS_SIGNAL)
236
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/custom_components/waste_collection_schedule/__init__.py b/custom_components/waste_collection_schedule/__init__.py
--- a/custom_components/waste_collection_schedule/__init__.py
+++ b/custom_components/waste_collection_schedule/__init__.py
@@ -7,7 +7,7 @@
 import homeassistant.helpers.config_validation as cv
 import homeassistant.util.dt as dt_util
 import voluptuous as vol
-from homeassistant.core import HomeAssistant, callback
+from homeassistant.core import HomeAssistant, ServiceCall, callback
 from homeassistant.helpers.dispatcher import dispatcher_send
 
 from .const import DOMAIN, UPDATE_SENSORS_SIGNAL
@@ -123,12 +123,14 @@
 
     # initial fetch of all data
     hass.add_job(api._fetch)
-    
-    def fetch_data():
+
+    async def async_fetch_data(service: ServiceCall) -> None:
         hass.add_job(api._fetch)
 
     # Register new Service fetch_data
-    hass.services.async_register(DOMAIN, 'fetch_data', fetch_data)
+    hass.services.async_register(
+        DOMAIN, "fetch_data", async_fetch_data, schema=vol.Schema({})
+    )
 
     return True

{"golden_diff": "diff --git a/custom_components/waste_collection_schedule/__init__.py b/custom_components/waste_collection_schedule/__init__.py\n--- a/custom_components/waste_collection_schedule/__init__.py\n+++ b/custom_components/waste_collection_schedule/__init__.py\n@@ -7,7 +7,7 @@\n import homeassistant.helpers.config_validation as cv\n import homeassistant.util.dt as dt_util\n import voluptuous as vol\n-from homeassistant.core import HomeAssistant, callback\n+from homeassistant.core import HomeAssistant, ServiceCall, callback\n from homeassistant.helpers.dispatcher import dispatcher_send\n \n from .const import DOMAIN, UPDATE_SENSORS_SIGNAL\n@@ -123,12 +123,14 @@\n \n     # initial fetch of all data\n     hass.add_job(api._fetch)\n-    \n-    def fetch_data():\n+\n+    async def async_fetch_data(service: ServiceCall) -> None:\n         hass.add_job(api._fetch)\n \n     # Register new Service fetch_data\n-    hass.services.async_register(DOMAIN, 'fetch_data', fetch_data)\n+    hass.services.async_register(\n+        DOMAIN, \"fetch_data\", async_fetch_data, schema=vol.Schema({})\n+    )\n \n     return True\n", "issue": "impossible to call fetch_data manual\nBecause i try to add my own ics http url i need debug it.\r\nCalling fetch_data doesn't work:\r\n\r\n```\r\ntakes 0 positional arguments but 1 was given\r\n```\r\n\r\njust an extra question: Is it possible to use {%y} (small) to get 23 not 2023?\n", "before_files": [{"content": "\"\"\"Waste Collection Schedule Component.\"\"\"\nimport logging\nimport site\nfrom pathlib import Path\nfrom random import randrange\n\nimport homeassistant.helpers.config_validation as cv\nimport homeassistant.util.dt as dt_util\nimport voluptuous as vol\nfrom homeassistant.core import HomeAssistant, callback\nfrom homeassistant.helpers.dispatcher import dispatcher_send\n\nfrom .const import DOMAIN, UPDATE_SENSORS_SIGNAL\n\nfrom homeassistant.helpers.event import async_call_later  # isort:skip\nfrom homeassistant.helpers.event import async_track_time_change  # isort:skip\n\n# add module directory to path\npackage_dir = Path(__file__).resolve().parents[0]\nsite.addsitedir(str(package_dir))\nfrom waste_collection_schedule import Customize, SourceShell  # type: ignore # isort:skip # noqa: E402\n\n_LOGGER = logging.getLogger(__name__)\n\nCONF_SOURCES = \"sources\"\nCONF_SOURCE_NAME = \"name\"\nCONF_SOURCE_ARGS = \"args\"  # source arguments\nCONF_SOURCE_CALENDAR_TITLE = \"calendar_title\"\nCONF_SEPARATOR = \"separator\"\nCONF_FETCH_TIME = \"fetch_time\"\nCONF_RANDOM_FETCH_TIME_OFFSET = \"random_fetch_time_offset\"\nCONF_DAY_SWITCH_TIME = \"day_switch_time\"\n\nCONF_CUSTOMIZE = \"customize\"\nCONF_TYPE = \"type\"\nCONF_ALIAS = \"alias\"\nCONF_SHOW = \"show\"\nCONF_ICON = \"icon\"\nCONF_PICTURE = \"picture\"\nCONF_USE_DEDICATED_CALENDAR = \"use_dedicated_calendar\"\nCONF_DEDICATED_CALENDAR_TITLE = \"dedicated_calendar_title\"\n\nCUSTOMIZE_CONFIG = vol.Schema(\n    {\n        vol.Optional(CONF_TYPE): cv.string,\n        vol.Optional(CONF_ALIAS): cv.string,\n        vol.Optional(CONF_SHOW): cv.boolean,\n        vol.Optional(CONF_ICON): cv.icon,\n        vol.Optional(CONF_PICTURE): cv.string,\n        vol.Optional(CONF_USE_DEDICATED_CALENDAR): cv.boolean,\n        vol.Optional(CONF_DEDICATED_CALENDAR_TITLE): cv.string,\n    }\n)\n\nSOURCE_CONFIG = vol.Schema(\n    {\n        vol.Required(CONF_SOURCE_NAME): cv.string,\n        vol.Required(CONF_SOURCE_ARGS): dict,\n        vol.Optional(CONF_CUSTOMIZE, default=[]): vol.All(\n            cv.ensure_list, [CUSTOMIZE_CONFIG]\n        ),\n        vol.Optional(CONF_SOURCE_CALENDAR_TITLE): cv.string,\n    }\n)\n\nCONFIG_SCHEMA = vol.Schema(\n    {\n        DOMAIN: vol.Schema(\n            {\n                vol.Required(CONF_SOURCES): vol.All(cv.ensure_list, [SOURCE_CONFIG]),\n                vol.Optional(CONF_SEPARATOR, default=\", \"): cv.string,\n                vol.Optional(CONF_FETCH_TIME, default=\"01:00\"): cv.time,\n                vol.Optional(\n                    CONF_RANDOM_FETCH_TIME_OFFSET, default=60\n                ): cv.positive_int,\n                vol.Optional(CONF_DAY_SWITCH_TIME, default=\"10:00\"): cv.time,\n            }\n        )\n    },\n    extra=vol.ALLOW_EXTRA,\n)\n\n\nasync def async_setup(hass: HomeAssistant, config: dict):\n    \"\"\"Set up the component. config contains data from configuration.yaml.\"\"\"\n    # create empty api object as singleton\n    api = WasteCollectionApi(\n        hass,\n        separator=config[DOMAIN][CONF_SEPARATOR],\n        fetch_time=config[DOMAIN][CONF_FETCH_TIME],\n        random_fetch_time_offset=config[DOMAIN][CONF_RANDOM_FETCH_TIME_OFFSET],\n        day_switch_time=config[DOMAIN][CONF_DAY_SWITCH_TIME],\n    )\n\n    # create shells for source(s)\n    for source in config[DOMAIN][CONF_SOURCES]:\n        # create customize object\n        customize = {}\n        for c in source.get(CONF_CUSTOMIZE, {}):\n            customize[c[CONF_TYPE]] = Customize(\n                waste_type=c[CONF_TYPE],\n                alias=c.get(CONF_ALIAS),\n                show=c.get(CONF_SHOW, True),\n                icon=c.get(CONF_ICON),\n                picture=c.get(CONF_PICTURE),\n                use_dedicated_calendar=c.get(CONF_USE_DEDICATED_CALENDAR, False),\n                dedicated_calendar_title=c.get(CONF_DEDICATED_CALENDAR_TITLE, False),\n            )\n        api.add_source_shell(\n            source_name=source[CONF_SOURCE_NAME],\n            customize=customize,\n            calendar_title=source.get(CONF_SOURCE_CALENDAR_TITLE),\n            source_args=source.get(CONF_SOURCE_ARGS, {}),\n        )\n\n    # store api object\n    hass.data.setdefault(DOMAIN, api)\n\n    # load calendar platform\n    await hass.helpers.discovery.async_load_platform(\n        \"calendar\", DOMAIN, {\"api\": api}, config\n    )\n\n    # initial fetch of all data\n    hass.add_job(api._fetch)\n    \n    def fetch_data():\n        hass.add_job(api._fetch)\n\n    # Register new Service fetch_data\n    hass.services.async_register(DOMAIN, 'fetch_data', fetch_data)\n\n    return True\n\n\nclass WasteCollectionApi:\n    def __init__(\n        self, hass, separator, fetch_time, random_fetch_time_offset, day_switch_time\n    ):\n        self._hass = hass\n        self._source_shells = []\n        self._separator = separator\n        self._fetch_time = fetch_time\n        self._random_fetch_time_offset = random_fetch_time_offset\n        self._day_switch_time = day_switch_time\n\n        # start timer to fetch date once per day\n        async_track_time_change(\n            hass,\n            self._fetch_callback,\n            self._fetch_time.hour,\n            self._fetch_time.minute,\n            self._fetch_time.second,\n        )\n\n        # start timer for day-switch time\n        if self._day_switch_time != self._fetch_time:\n            async_track_time_change(\n                hass,\n                self._update_sensors_callback,\n                self._day_switch_time.hour,\n                self._day_switch_time.minute,\n                self._day_switch_time.second,\n            )\n\n        # add a timer at midnight (if not already there) to update days-to\n        midnight = dt_util.parse_time(\"00:00\")\n        if midnight != self._fetch_time and midnight != self._day_switch_time:\n            async_track_time_change(\n                hass,\n                self._update_sensors_callback,\n                midnight.hour,\n                midnight.minute,\n                midnight.second,\n            )\n\n    @property\n    def separator(self):\n        \"\"\"Separator string, used to separator waste types.\"\"\"\n        return self._separator\n\n    @property\n    def fetch_time(self):\n        \"\"\"When to fetch to data.\"\"\"\n        return self._fetch_time\n\n    @property\n    def day_switch_time(self):\n        \"\"\"When to hide entries for today.\"\"\"\n        return self._day_switch_time\n\n    def add_source_shell(\n        self,\n        source_name,\n        customize,\n        source_args,\n        calendar_title,\n    ):\n        self._source_shells.append(\n            SourceShell.create(\n                source_name=source_name,\n                customize=customize,\n                source_args=source_args,\n                calendar_title=calendar_title,\n            )\n        )\n\n    def _fetch(self, *_):\n        for shell in self._source_shells:\n            shell.fetch()\n\n        self._update_sensors_callback()\n\n    @property\n    def shells(self):\n        return self._source_shells\n\n    def get_shell(self, index):\n        return self._source_shells[index] if index < len(self._source_shells) else None\n\n    @callback\n    def _fetch_callback(self, *_):\n        async_call_later(\n            self._hass,\n            randrange(0, 60 * self._random_fetch_time_offset),\n            self._fetch_now_callback,\n        )\n\n    @callback\n    def _fetch_now_callback(self, *_):\n        self._hass.add_job(self._fetch)\n\n    @callback\n    def _update_sensors_callback(self, *_):\n        dispatcher_send(self._hass, UPDATE_SENSORS_SIGNAL)\n", "path": "custom_components/waste_collection_schedule/__init__.py"}], "after_files": [{"content": "\"\"\"Waste Collection Schedule Component.\"\"\"\nimport logging\nimport site\nfrom pathlib import Path\nfrom random import randrange\n\nimport homeassistant.helpers.config_validation as cv\nimport homeassistant.util.dt as dt_util\nimport voluptuous as vol\nfrom homeassistant.core import HomeAssistant, ServiceCall, callback\nfrom homeassistant.helpers.dispatcher import dispatcher_send\n\nfrom .const import DOMAIN, UPDATE_SENSORS_SIGNAL\n\nfrom homeassistant.helpers.event import async_call_later  # isort:skip\nfrom homeassistant.helpers.event import async_track_time_change  # isort:skip\n\n# add module directory to path\npackage_dir = Path(__file__).resolve().parents[0]\nsite.addsitedir(str(package_dir))\nfrom waste_collection_schedule import Customize, SourceShell  # type: ignore # isort:skip # noqa: E402\n\n_LOGGER = logging.getLogger(__name__)\n\nCONF_SOURCES = \"sources\"\nCONF_SOURCE_NAME = \"name\"\nCONF_SOURCE_ARGS = \"args\"  # source arguments\nCONF_SOURCE_CALENDAR_TITLE = \"calendar_title\"\nCONF_SEPARATOR = \"separator\"\nCONF_FETCH_TIME = \"fetch_time\"\nCONF_RANDOM_FETCH_TIME_OFFSET = \"random_fetch_time_offset\"\nCONF_DAY_SWITCH_TIME = \"day_switch_time\"\n\nCONF_CUSTOMIZE = \"customize\"\nCONF_TYPE = \"type\"\nCONF_ALIAS = \"alias\"\nCONF_SHOW = \"show\"\nCONF_ICON = \"icon\"\nCONF_PICTURE = \"picture\"\nCONF_USE_DEDICATED_CALENDAR = \"use_dedicated_calendar\"\nCONF_DEDICATED_CALENDAR_TITLE = \"dedicated_calendar_title\"\n\nCUSTOMIZE_CONFIG = vol.Schema(\n    {\n        vol.Optional(CONF_TYPE): cv.string,\n        vol.Optional(CONF_ALIAS): cv.string,\n        vol.Optional(CONF_SHOW): cv.boolean,\n        vol.Optional(CONF_ICON): cv.icon,\n        vol.Optional(CONF_PICTURE): cv.string,\n        vol.Optional(CONF_USE_DEDICATED_CALENDAR): cv.boolean,\n        vol.Optional(CONF_DEDICATED_CALENDAR_TITLE): cv.string,\n    }\n)\n\nSOURCE_CONFIG = vol.Schema(\n    {\n        vol.Required(CONF_SOURCE_NAME): cv.string,\n        vol.Required(CONF_SOURCE_ARGS): dict,\n        vol.Optional(CONF_CUSTOMIZE, default=[]): vol.All(\n            cv.ensure_list, [CUSTOMIZE_CONFIG]\n        ),\n        vol.Optional(CONF_SOURCE_CALENDAR_TITLE): cv.string,\n    }\n)\n\nCONFIG_SCHEMA = vol.Schema(\n    {\n        DOMAIN: vol.Schema(\n            {\n                vol.Required(CONF_SOURCES): vol.All(cv.ensure_list, [SOURCE_CONFIG]),\n                vol.Optional(CONF_SEPARATOR, default=\", \"): cv.string,\n                vol.Optional(CONF_FETCH_TIME, default=\"01:00\"): cv.time,\n                vol.Optional(\n                    CONF_RANDOM_FETCH_TIME_OFFSET, default=60\n                ): cv.positive_int,\n                vol.Optional(CONF_DAY_SWITCH_TIME, default=\"10:00\"): cv.time,\n            }\n        )\n    },\n    extra=vol.ALLOW_EXTRA,\n)\n\n\nasync def async_setup(hass: HomeAssistant, config: dict):\n    \"\"\"Set up the component. config contains data from configuration.yaml.\"\"\"\n    # create empty api object as singleton\n    api = WasteCollectionApi(\n        hass,\n        separator=config[DOMAIN][CONF_SEPARATOR],\n        fetch_time=config[DOMAIN][CONF_FETCH_TIME],\n        random_fetch_time_offset=config[DOMAIN][CONF_RANDOM_FETCH_TIME_OFFSET],\n        day_switch_time=config[DOMAIN][CONF_DAY_SWITCH_TIME],\n    )\n\n    # create shells for source(s)\n    for source in config[DOMAIN][CONF_SOURCES]:\n        # create customize object\n        customize = {}\n        for c in source.get(CONF_CUSTOMIZE, {}):\n            customize[c[CONF_TYPE]] = Customize(\n                waste_type=c[CONF_TYPE],\n                alias=c.get(CONF_ALIAS),\n                show=c.get(CONF_SHOW, True),\n                icon=c.get(CONF_ICON),\n                picture=c.get(CONF_PICTURE),\n                use_dedicated_calendar=c.get(CONF_USE_DEDICATED_CALENDAR, False),\n                dedicated_calendar_title=c.get(CONF_DEDICATED_CALENDAR_TITLE, False),\n            )\n        api.add_source_shell(\n            source_name=source[CONF_SOURCE_NAME],\n            customize=customize,\n            calendar_title=source.get(CONF_SOURCE_CALENDAR_TITLE),\n            source_args=source.get(CONF_SOURCE_ARGS, {}),\n        )\n\n    # store api object\n    hass.data.setdefault(DOMAIN, api)\n\n    # load calendar platform\n    await hass.helpers.discovery.async_load_platform(\n        \"calendar\", DOMAIN, {\"api\": api}, config\n    )\n\n    # initial fetch of all data\n    hass.add_job(api._fetch)\n\n    async def async_fetch_data(service: ServiceCall) -> None:\n        hass.add_job(api._fetch)\n\n    # Register new Service fetch_data\n    hass.services.async_register(\n        DOMAIN, \"fetch_data\", async_fetch_data, schema=vol.Schema({})\n    )\n\n    return True\n\n\nclass WasteCollectionApi:\n    def __init__(\n        self, hass, separator, fetch_time, random_fetch_time_offset, day_switch_time\n    ):\n        self._hass = hass\n        self._source_shells = []\n        self._separator = separator\n        self._fetch_time = fetch_time\n        self._random_fetch_time_offset = random_fetch_time_offset\n        self._day_switch_time = day_switch_time\n\n        # start timer to fetch date once per day\n        async_track_time_change(\n            hass,\n            self._fetch_callback,\n            self._fetch_time.hour,\n            self._fetch_time.minute,\n            self._fetch_time.second,\n        )\n\n        # start timer for day-switch time\n        if self._day_switch_time != self._fetch_time:\n            async_track_time_change(\n                hass,\n                self._update_sensors_callback,\n                self._day_switch_time.hour,\n                self._day_switch_time.minute,\n                self._day_switch_time.second,\n            )\n\n        # add a timer at midnight (if not already there) to update days-to\n        midnight = dt_util.parse_time(\"00:00\")\n        if midnight != self._fetch_time and midnight != self._day_switch_time:\n            async_track_time_change(\n                hass,\n                self._update_sensors_callback,\n                midnight.hour,\n                midnight.minute,\n                midnight.second,\n            )\n\n    @property\n    def separator(self):\n        \"\"\"Separator string, used to separator waste types.\"\"\"\n        return self._separator\n\n    @property\n    def fetch_time(self):\n        \"\"\"When to fetch to data.\"\"\"\n        return self._fetch_time\n\n    @property\n    def day_switch_time(self):\n        \"\"\"When to hide entries for today.\"\"\"\n        return self._day_switch_time\n\n    def add_source_shell(\n        self,\n        source_name,\n        customize,\n        source_args,\n        calendar_title,\n    ):\n        self._source_shells.append(\n            SourceShell.create(\n                source_name=source_name,\n                customize=customize,\n                source_args=source_args,\n                calendar_title=calendar_title,\n            )\n        )\n\n    def _fetch(self, *_):\n        for shell in self._source_shells:\n            shell.fetch()\n\n        self._update_sensors_callback()\n\n    @property\n    def shells(self):\n        return self._source_shells\n\n    def get_shell(self, index):\n        return self._source_shells[index] if index < len(self._source_shells) else None\n\n    @callback\n    def _fetch_callback(self, *_):\n        async_call_later(\n            self._hass,\n            randrange(0, 60 * self._random_fetch_time_offset),\n            self._fetch_now_callback,\n        )\n\n    @callback\n    def _fetch_now_callback(self, *_):\n        self._hass.add_job(self._fetch)\n\n    @callback\n    def _update_sensors_callback(self, *_):\n        dispatcher_send(self._hass, UPDATE_SENSORS_SIGNAL)\n", "path": "custom_components/waste_collection_schedule/__init__.py"}]}