fbpx

Python monitors my plants using Tuya IoT

1

Introduction

This is Python tutorial relating to monitoring plants. Indeed, it seems hard to remember every week or so to water plants and sometimes a reminder could serve us a lot. We’re all busy and forgetting to water our own plants is absolutely ok, lol.

OK, so if you’re a visual person, you can just ignore this article and watch the tutorial above to follow up with how to build the plant monitoring IoT system. If not, feel free to follow this article. So python will monitor our plant thanks to Tuya API cloud.

Getting Started

So the first thing you need to do is create an account on Tuya using this link. Please check the video for details on how to do that since it seems impossible to write the steps down. This article will focus on the python code instead. Please watch the video up to the point where you get your Tuya API key and ID to define the API credentials and access the endpoints.

Installing the Tuya modules via pip

Now, we’re going to use our good old buddy, “pip” to install the following modules

!pip3 install tuya-connector-python
!pip3 install tuya-iot-py-sdk
!pip3 install pycryptodomex

Define our Access ID, Access Key and Endpoint

As mentioned, after you create your Tuya account you will have access to two important things, the Access ID and Access Key, let’s define them as such

ACCESS_ID = "7cvey7v7azt99v7mox7z"
ACCESS_KEY = "79415ad8a4d44a8db80920cb4ed8cd20"
API_ENDPOINT = "https://openapi-weaz.tuyaeu.com"

Note that, because I’m being nice with you, I’m sharing my credentials (Access ID and Access key) but you should not share them with anyone, otherwise they will be able to control your IoT devices from any part of the world. Also note that, since I’m based in Nice, France, that is in Europe, the above endpoint will look like the above (tuyaeu).

Connect to our Tuya API endpoint

Now, let’s proceed to connect to our Tuya API cloud via Tuya’s very own TuyaOpenAPI()

from tuya_connector import TuyaOpenAPI

openapi = TuyaOpenAPI(API_ENDPOINT,ACCESS_ID,ACCESS_KEY)
openapi.connect()

If all goes well, you should be able to see the following JSON response from the API cloud

{'result': {'access_token': '22b6aa36107ee30aebd30292dc4f38d7',
  'expire_time': 5865,
  'refresh_token': '9f76f71089a775bc2d7daf5854b5e740',
  'uid': 'bay1632079226744ZEW2'},
 'success': True,
 't': 1633866895317}

Define device ID

Now, just like you define variable (example: x=1), we will proceed to define our own devices that are the RGB LED strip and the humidity/temperature sensor

RGB_DEVICE_ID = "68560540807d3a1a9573"
SENSOR_DEVICE_ID = "9be9a7703302248c4esjma" 

Note that you have to create your devices using Tuya’s platform in order to proceed. Also, I encourage you to watch the video above to know how to exactly do this.

Get IoT node specifications

Tuya makes it really easy to get all the functions we have at our disposal using a simple API call. For example, the following call

response = openapi.get("/v1.0/iot-03/devices/{}/specification".format(RGB_DEVICE_ID))
print(response)

prints the following response

{'result': {'category': 'dj',
  'functions': [{'code': 'switch_led',
    'desc': '{}',
    'name': '开关',
    'type': 'Boolean',
    'values': '{}'},
   {'code': 'bright_value',
    'desc': '{"min":25,"scale":0,"unit":"","max":255,"step":1}',
    'name': '亮度',
    'type': 'Integer',
    'values': '{"min":25,"scale":0,"unit":"","max":255,"step":1}'},
   {'code': 'flash_scene_1',
    'desc': '{"h":{"min":1,"scale":0,"unit":"","max":360,"step":1},"s":{"min":1,"scale":0,"unit":"","max":255,"step":1},"v":{"min":1,"scale":0,"unit":"","max":255,"step":1}}',
    'name': '柔光模式',
    'type': 'Json',
    'values': '{"h":{"min":1,"scale":0,"unit":"","max":360,"step":1},"s":{"min":1,"scale":0,"unit":"","max":255,"step":1},"v":{"min":1,"scale":0,"unit":"","max":255,"step":1}}'},
   {'code': 'flash_scene_2',
    'desc': '{"h":{"min":1,"scale":0,"unit":"","max":360,"step":1},"s":{"min":1,"scale":0,"unit":"","max":255,"step":1},"v":{"min":1,"scale":0,"unit":"","max":255,"step":1}}',
    'name': '缤纷模式',
    'type': 'Json',
    'values': '{"h":{"min":1,"scale":0,"unit":"","max":360,"step":1},"s":{"min":1,"scale":0,"unit":"","max":255,"step":1},"v":{"min":1,"scale":0,"unit":"","max":255,"step":1}}'},
   {'code': 'flash_scene_3',
    'desc': '{"h":{"min":1,"scale":0,"unit":"","max":360,"step":1},"s":{"min":1,"scale":0,"unit":"","max":255,"step":1},"v":{"min":1,"scale":0,"unit":"","max":255,"step":1}}',
    'name': '炫彩模式',
    'type': 'Json',
    'values': '{"h":{"min":1,"scale":0,"unit":"","max":360,"step":1},"s":{"min":1,"scale":0,"unit":"","max":255,"step":1},"v":{"min":1,"scale":0,"unit":"","max":255,"step":1}}'},
   {'code': 'work_mode',
    'desc': '{"range":["white","colour"]}',
    'name': '工作模式',
    'type': 'Enum',
    'values': '{"range":["white","colour"]}'},
   {'code': 'temp_value',
    'desc': '{"min":0,"scale":0,"unit":"","max":255,"step":1}\t',
    'name': '色温',
    'type': 'Integer',
    'values': '{"min":0,"scale":0,"unit":"","max":255,"step":1}\t'},
   {'code': 'colour_data',
    'desc': '{"h":{"min":1,"scale":0,"unit":"","max":360,"step":1},"s":{"min":1,"scale":0,"unit":"","max":255,"step":1},"v":{"min":1,"scale":0,"unit":"","max":255,"step":1}}',
    'name': '彩光模式数',
    'type': 'Json',
    'values': '{"h":{"min":1,"scale":0,"unit":"","max":360,"step":1},"s":{"min":1,"scale":0,"unit":"","max":255,"step":1},"v":{"min":1,"scale":0,"unit":"","max":255,"step":1}}'},
   {'code': 'scene_data',
    'desc': '{"h":{"min":1,"scale":0,"unit":"","max":360,"step":1},"s":{"min":1,"scale":0,"unit":"","max":255,"step":1},"v":{"min":1,"scale":0,"unit":"","max":255,"step":1}}',
    'name': '情景模式数',
    'type': 'Json',
    'values': '{"h":{"min":1,"scale":0,"unit":"","max":360,"step":1},"s":{"min":1,"scale":0,"unit":"","max":255,"step":1},"v":{"min":1,"scale":0,"unit":"","max":255,"step":1}}'},
   {'code': 'flash_scene_4',
    'desc': '{"h":{"min":1,"scale":0,"unit":"","max":360,"step":1},"s":{"min":1,"scale":0,"unit":"","max":255,"step":1},"v":{"min":1,"scale":0,"unit":"","max":255,"step":1}}',
    'name': '斑斓模式',
    'type': 'Json',
    'values': '{"h":{"min":1,"scale":0,"unit":"","max":360,"step":1},"s":{"min":1,"scale":0,"unit":"","max":255,"step":1},"v":{"min":1,"scale":0,"unit":"","max":255,"step":1}}'}],
  'status': [{'code': 'bright_value',
    'name': '亮度值',
    'type': 'Integer',
    'values': '{"min":25,"scale":0,"unit":"","max":255,"step":1}'},
   {'code': 'colour_data',
    'name': '彩光模式数',
    'type': 'Json',
    'values': '{"h":{"min":1,"scale":0,"unit":"","max":360,"step":1},"s":{"min":1,"scale":0,"unit":"","max":255,"step":1},"v":{"min":1,"scale":0,"unit":"","max":255,"step":1}}'},
   {'code': 'scene_data',
    'name': '情景模式数',
    'type': 'Json',
    'values': '{"h":{"min":1,"scale":0,"unit":"","max":360,"step":1},"s":{"min":1,"scale":0,"unit":"","max":255,"step":1},"v":{"min":1,"scale":0,"unit":"","max":255,"step":1}}'},
   {'code': 'flash_scene_2',
    'name': '缤纷模式',
    'type': 'Json',
    'values': '{"h":{"min":1,"scale":0,"unit":"","max":360,"step":1},"s":{"min":1,"scale":0,"unit":"","max":255,"step":1},"v":{"min":1,"scale":0,"unit":"","max":255,"step":1}}'},
   {'code': 'switch_led', 'name': '开关', 'type': 'Boolean', 'values': '{}'},
   {'code': 'work_mode',
    'name': '工作模式',
    'type': 'Enum',
    'values': '{"range":["white","colour"]}'},
   {'code': 'temp_value',
    'name': '冷暖值',
    'type': 'Integer',
    'values': '{"min":0,"scale":0,"unit":"","max":255,"step":1}\t'},
   {'code': 'flash_scene_1',
    'name': '柔光模式',
    'type': 'Json',
    'values': '{"h":{"min":1,"scale":0,"unit":"","max":360,"step":1},"s":{"min":1,"scale":0,"unit":"","max":255,"step":1},"v":{"min":1,"scale":0,"unit":"","max":255,"step":1}}'},
   {'code': 'flash_scene_3',
    'name': '炫彩模式',
    'type': 'Json',
    'values': '{"h":{"min":1,"scale":0,"unit":"","max":360,"step":1},"s":{"min":1,"scale":0,"unit":"","max":255,"step":1},"v":{"min":1,"scale":0,"unit":"","max":255,"step":1}}'},
   {'code': 'flash_scene_4',
    'name': '斑斓模式',
    'type': 'Json',
    'values': '{"h":{"min":1,"scale":0,"unit":"","max":360,"step":1},"s":{"min":1,"scale":0,"unit":"","max":255,"step":1},"v":{"min":1,"scale":0,"unit":"","max":255,"step":1}}'}]},
 'success': True,
 't': 1633866895551}

The way we interpret the above is as follows: For example, the first function is switch_led and it accepts a boolean (either ON/OFF). To discover more functionalities, please refer to the video above.

Change the LED Strip color

I will show you a bonus here on how to switch colors of the RGB strip as follows:

command = {"commands":[{"code":"colour_data","value":{'h':0,'s':100,'v':40}}]}
openapi.post("/v1.0/iot-03/devices/{}/commands".format(RGB_DEVICE_ID),command)

As you can see, all we did is “pack-up” a JSON in a variable called command, where we define the color defined in an hsv basis instead of an rgb one. Feel free to experiment with different colors.

Read temperature/humidity information from sensor

Now that we have the RGB all set up, we will configure it to blink when the plants need watering. But FIRST. How do we know that the plant needs to be watered ? That’s where the humidity sensor comes into play. The humidity sensor is placed on the surface of the soil of the plant. All we need to do now is configure it properly so that we can read its humidity. This is attained as follows:

response = openapi.get("/v1.0/iot-03/devices/{}/status".format(SENSOR_DEVICE_ID))
response

If done properly, you should receive the following API response

{'result': [{'code': 'humidity_current', 'value': 6050},
  {'code': 'va_battery', 'value': 100},
  {'code': 'temp_current', 'value': 2340}],
 'success': True,
 't': 1633868016000}

which basically reads as follows: Humidity is 60.5%, Battery life is 100% and temperature is 23.4 degrees Celsius. For better parsing, we can do this

humidity = response['result'][0]['value'] / 100
battery  = response['result'][1]['value']
temperature = response['result'][2]['value'] / 100
print("Humidity = " + str(humidity))
print("Battery = " + str(battery))
print("Temperature = " + str(temperature))

which outputs this in our case

Humidity = 60.5
Battery = 100
Temperature = 23.4

Monitor my Plant

Now, we are ready to define the main loop to monitor our plants and save our planet ! This is where a Python loop could save nature

humidityThreshold = 70
LedEn = True
sensorReadingTime = 5 # seconds
while True:
    response = openapi.get('/v1.0/iot-03/devices/{}/status'.format(SENSOR_DEVICE_ID))
    humidity = response['result'][0]['value'] / 100
    time.sleep(sensorReadingTime)
    if humidity < humidityThreshold:
        LedEn = False
    else:
        LendEn = True
    commandLEDStrip = {"commands":[{"code":"switch_led","value":LedEn}]}
    openapi.post('/v1.0/iot-03/devices/{}/commands'.format(RGB_DEVICE_ID),commandLEDStrip)

So all we did above was set the humidity threshold to 70%, i.e. if the humidity sensed by the sensor is less than 70, we turn the RGB strip off as a reminder to water our plants, else it stays on. I really urge you to check the last part of the video as a demo of the above loop.

Subscribe to my channel to support us ! We are very close to 100K subscribers <3 w/ love. The algorithmic chef – Ahmad Bazzi. Click here to subscribe. Check my other articles here