Interactive python input() for IoTEdge Module

Question:

I am new to python and I inherited some code that requires keyboard input by the user. When turning this into a docker container, I can easily run it locally with "-it" flag to take in my input and avoid EOF errors.

while True:
  key = input("Please enter:n")
        if key == "q":
            break
        if key == "t":
           #do something

However, the code needs to run as an IoT Edge Module on an IoT Edge Device on Azure IoT Hub, and there doesn’t seem to be an equivalent option to the "-it" flag for that. When I run the code without changes as IoT Edge Module, I get EOF errors and it breaks on the "input" line.

How can I change my code to achieve the following requirements when running as IoT Edge Module?

  1. code can receive interactive input from the user
  2. code WAITS for this input (no matter how long it takes) without breaking

I am considering looking into messaging and async/await, however this is only for testing and not production code, so I am wondering if that is overkill and there is maybe some simpler workaround to achieve what I want?

As a basic workaround, I tried to have the code check for a file, that I then manually created in the docker container, however for some reason when I did that, it never went into the if-condition, but instead left the loop

   while True:
        if os.path.isfile(break_file_path):
            break
        if os.path.isfile(trigger_file_path):
           #do something
   #do something else

When creating the file at trigger_file_path, it never does "something", but immediately goes to "something else".

I also learned from this, that I can only see the logs in the IoTHub after the while-loop is done, which is bad for some code that should potentially be able to wait arbitrary long for input.

I would appreciate input on any of these points. Thank you.

Asked By: JS.

||

Answers:

There are a few approaches you can take to handle user input when running an IoT Edge module in a container. One approach would be to use a message-based communication mechanism, such as Azure IoT Hub’s device-to-cloud messaging, to pass commands to the module. You could have the module listen for messages on the IoT Hub, and take action based on the message contents. Here is an example of how you could modify your code to do this:

import asyncio
from azure.iot.device.aio import IoTHubDeviceClient

async def main():
    device_connection_string = "<your device connection string>"
    device_client = IoTHubDeviceClient.create_from_connection_string(device_connection_string)
    await device_client.connect()

    while True:
        message = await device_client.receive_message()
        if message:
            if message.data == b'q':
                break
            if message.data == b't':
                #do something

    await device_client.disconnect()

asyncio.run(main())

Another approach you could take would be to use environment variables to pass commands to the module. For example, you could have the module check for the presence of a specific environment variable and take action based on its value. Here is an example of how you could modify your code to do this:

import os

while True:
    key = os.environ.get('INPUT_KEY', None)
    if key == "q":
        break
    if key == "t":
        #do something

In this approach, you would set the value of the INPUT_KEY environment variable to t or q as appropriate, and the module would take action based on the value of the environment variable. Both of these approaches allow you to communicate with the module without having to directly interact with the container, which is useful when running in an IoT Edge scenario where you may not have direct access to the container.

Answered By: fanthore

Thanks for the input. I tried the env variables, but did not get that to work. The code didn’t find the env variables I created inside the docker container and restarting the docker container with new env variables was not what I wanted.

I realized however that my original work-around with the files worked just fine. I just had mixed up the variables for triggerfile and breakfile in the IoT Edge Module setup… so creating the triggerfile lead to breaking. Once that was fixed, it worked like a charm. 🙂

Answered By: JS.