Await returns coroutine instead of result

I am trying to create an integration that is a little like Sun but wraps a simple and synchronous python library which does some IO. Here is my pseudocode:

async def async_setup(hass: core.HomeAssistant, config: dict) -> bool:
    """Set up the component."""
    # @TODO: Add setup code.
    Lupt(hass)
    return True


class Lupt(Entity):

    entity_id = ENTITY_ID

    async def async_init_timetable(self):
        config = lupt_config.load_config(None)
        tt = await self.hass.async_add_executor_job(lupt_cache.init_timetable(config))
        return tt

    def __init__(self, hass):
        """Initialise lupt."""
        self.hass = hass

        try:
            self.timetable = lupt_cache.load_timetable()
        except Exception:
            self.timetable = self.async_init_timetable()

        self.async_write_ha_state()

I’ve taken the await line from https://developers.home-assistant.io/docs/asyncio_working_with_async#calling-sync-functions-from-async

I initially had the await line in the exception handler but kept getting a “await called outside of async function” error. So I made it an async function. However, async_add_executor_job returns a coroutine instead of the timetable I’m expecting.

I suspect I’m falling in and out of an async context somewhere but I can’t figure out why, as I presume async_setup is the entry point.

The reason why I want to use async is because eventually I will be scheduling callbacks to update state using event.async_track_point_in_utc_time (a little like Sun, from where I’m getting most of my inspiration).

Second minimal try:

async def async_setup(hass: core.HomeAssistant, config: dict) -> bool:
    # @TODO: Add setup code.
    lupt = Lupt(hass)
    await lupt.async_init()
    return True


class Lupt(Entity):
    def __init__(self, hass):
        """Initialise lupt."""
        self.hass = hass

    async def async_init(self):
        config = lupt_config.load_config(None)
        self.timetable = await self.hass.async_add_executor_job(lupt_cache.init_timetable(config))

        await self.async_write_ha_state()

    @property
     def state(self):
         return "Hello world!"

Amusingly I am getting:

RuntimeError: I/O must be done in the executor; Use `await hass.async_add_executor_job()` at custom_components/lupt/__init__.py, line 47: self.timetable = await self.hass.async_add_executor_job(lupt_cache.init_timetable(config))

Which is patently not true so I’m not quite sure where else to go from here.

A friendly dev on Discord spotted my mistake straight away. The issue is that hass.async_add_executor_job needs a function and my code was actually executing the function I was trying to pass. hass.async_add_executor_job allows the caller to pass a parameter list:

self.timetable = await self.hass.async_add_executor_job(lupt_cache.init_timetable, config)

or you can use my preference (can define non anoymously if you prefer):

self.timetable = await self.hass.async_add_executor_job(lambda: lupt_cache.init_timetable(config))