Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to run Python Microdot web API module, app.run() that is already an asyncio task, at the same time with other asyncio functions?

I'm trying to run a Microdot app.run() that is asyncio with other functions that are also asyncio and I keep getting that the Microdot.start_server() was never awaited

In this case the other function is run_thermostat() that is an asyncio function that is running fine if I do not start at all the Microdot app.run()

I need some help on how to use Microdot app.run() to run along with other functions in a forever loop

from microdot_asyncio import Microdot
import asyncio
from asyncio import gather

class Thermostat:
    def __init__(self):
        self.current_temp = 0
        self.target_temp = 0
        ###
        
        self.loop = asyncio.get_event_loop()
    
    def call__async_main(self):
        self.loop.run_until_complete(self.__async_main())
        print(self.loop)
    
    async def __async_main(self):
    ####does not work | sys:1: RuntimeWarning: coroutine 'Microdot.start_server' was never awaited
        await gather(
            self.start_web_server(),
            self.run_thermostat(),
            )

    ##### does not work | sys:1: RuntimeWarning: coroutine 'Microdot.start_server' was never awaited
        # task1 = asyncio.create_task(self.run_thermostat())
        # task2 = asyncio.create_task(self.start_web_server())
        # # Iterate over the tasks and wait for them to complete one by one
        # for task in asyncio.as_completed([task1, task2]):
        #     await task
        
    ####### does not work | sys:1: RuntimeWarning: coroutine 'Microdot.start_server' was never awaited
        # task1 = asyncio.create_task(self.run_thermostat())
        # task2 = asyncio.create_task(self.start_web_server())
        # await task1
        # await task2
        # self.loop.create_task(task1)
        # self.loop.create_task(task2)

    #####other method | does not work | sys:1: RuntimeWarning: coroutine 'Microdot.start_server' was never awaited
        # await self.start_web_server()
        # await self.run_thermostat()

    #Function for starting Microdot web-server API
    def start_web_server(self):
        app.run()

    # Function to read the current temperature from a temperature sensor
    async def read_temperature(self):
        # read temperature from sensor 
        self.current_temp = 15  # ex: set the current temperature to 15 //simulate

    # Function to set the target temperature
    def set_target_temp(self, temp):
        self.target_temp = temp

    # Function to start or stop the heating system based on the current and target temperatures
    def control_heating(self):
        if self.current_temp < self.target_temp:
            
            # Turn on the heating system 
            print("Starting heating system")
        else:
            # Stop the heating system
            
            print("Stopping heating system")

# Function to run the thermostat control loop
    async def run_thermostat(self):
        while True:
            # Read the current temperature from the sensor
            await thermostat.read_temperature()

            # Control the heating system based on the current and target temperatures
            thermostat.control_heating()

            # Wait for a short time before checking the temperature again
            await asyncio.sleep(1)

#Instantiating Microdot app
app = Microdot()
# Set up a route to allow users to set the target temperature
@app.route('/set_target_temp')
def set_target_temp(request):
    # Get the target temperature from the request
    target_temp = int(request.args['temp'])

    # Set the target temperature on the thermostat
    thermostat.set_target_temp(target_temp)

    # Return a response to confirm the target temperature has been set
    return "Target temperature set to {}".format(target_temp)

# Create an instance of the Thermostat class
thermostat = Thermostat()

#===###
# Start the system
thermostat.call__async_main()

#===###

# Start the Microdot app
# asyncio.create_task(app.run())
# app.run()

# Start the thermostat control loop
# asyncio.create_task(run_thermostat())
################################################
like image 252
yaml01H2S04 Avatar asked Oct 18 '25 13:10

yaml01H2S04


1 Answers

Never mind, I have found the solution, here it is:

    async def __async_main(self):
    ##### does work OK
        task1 = self.loop.create_task(self.run_thermostat())
        task2 = self.loop.create_task(self.start_web_server())
        # Iterate over the tasks and wait for them to complete one by one
        for task in asyncio.as_completed([task1, task2]):
            await task

and the Microdot should be started with app.server_start() and not by app.run() like this:

    #Function for starting Microdot web-server API
    async def start_web_server(self):
      await app.start_server(port=5000, debug=True)

Now, the Microdot web api runs along with other async tasks

In conclusion the main problem was starting the Microdot with app.run() , and also I had to add the task in the loop event like this:

task2 = self.loop.create_task(self.start_web_server())

before was:

task2 = asyncio.create_task(self.start_web_server())

and in this way it does not append into the loop, this loop:

self.loop = asyncio.get_event_loop()

Here is the complete working code:

from microdot_asyncio import Microdot
import asyncio

class Thermostat:
    def __init__(self):
        self.current_temp = 0
        self.target_temp = 0        
        self.loop = asyncio.get_event_loop()
    
    def call__async_main(self):
        self.loop.run_until_complete(self.__async_main())
        # print(self.loop)
    
    async def __async_main(self):
    ##### does work OK
        task1 = self.loop.create_task(self.run_thermostat())
        task2 = self.loop.create_task(self.start_web_server())
        # Iterate over the tasks and wait for them to complete one by one
        for task in asyncio.as_completed([task1, task2]):
            await task
        
    ####### does work OK
        # task1 = self.loop.create_task(self.run_thermostat())
        # task2 = self.loop.create_task(self.start_web_server())
        # await task1
        # await task2

    #Function for starting Microdot web-server API
    async def start_web_server(self):
      await app.start_server(port=5000, debug=True)

    # Function to read the current temperature from a temperature sensor
    async def read_temperature(self):
        # read temperature from sensor 
        self.current_temp = 15  # ex: set the current temperature to 15 //simulate

    # Function to set the target temperature
    def set_target_temp(self, temp):
        self.target_temp = temp

    # Function to start or stop the heating system based on the current and target temperatures
    def control_heating(self):
        if self.current_temp < self.target_temp:
            
            # Turn on the heating system 
            print("Starting heating system")
        else:
            # Stop the heating system
            
            print("Stopping heating system")

# Function to run the thermostat control loop
    async def run_thermostat(self):
        while True:
            # Read the current temperature from the sensor
            await thermostat.read_temperature()

            # Control the heating system based on the current and target temperatures
            thermostat.control_heating()

            # Wait for a short time before checking the temperature again
            await asyncio.sleep(1)

#Instantiating Microdot app
app = Microdot()
# Set up a route to allow users to set the target temperature
@app.route('/set_target_temp')
def set_target_temp(request):
    # Get the target temperature from the request
    target_temp = int(request.args['temp'])

    # Set the target temperature on the thermostat
    thermostat.set_target_temp(target_temp)

    # Return a response to confirm the target temperature has been set
    return "Target temperature set to {}".format(target_temp)

# Create an instance of the Thermostat class
thermostat = Thermostat()

#===###
# Start the system
thermostat.call__async_main()

#===###

you can navigate to your ESP32 IP or localhost and enter this link to change the target_temp variabile:

http://localhost:5000/set_target_temp?temp=21

... you should then see in the VScode console : Starting heating system .... if you enter another int query parameter for the temp like this:

http://localhost:5000/set_target_temp?temp=12

...you shoud see in the VScode console : Stopping heating system

CONCLUSION:

  • the code works
  • the run_thermostat() function runs along (asyncio) with app = Microdot() and does what is supposed to do
  • the Microdot decorator @app.route('/set_target_temp') works OK
like image 133
yaml01H2S04 Avatar answered Oct 20 '25 03:10

yaml01H2S04