In the last few weeks I’ve brought a Tapo Bulb L530, and I’ve wanted to explore how it works so to control locally without using the Tapo official app. This blog post gather all my notes during the reverse engineering of the bulb and they can be used to build a client to control this kind of bulb locally. I’ve only this model but from what I’ve discovered during this project I suspect the same protocol can be used for other Tapo appliances.

Introduction

Since my bulb was already updated I wasn’t able to obtain a copy of the firmware and I did not want to risk breaking the bulb opening it, so I’ve decided to reverse engineer the official Android app to gain some knowledge about the setup process. The app was extremely useful to understand the handshake and login protocol described later. Also, the app contains all the commands a client can send to a bulb, probably because the app sends the commands to the cloud server and then the server redirects them to the bulb.

One important consideration is that, even if from the official app is impossible, one can use the bulb locally without using a Tapo account. However, even if one doesn’t configure a Tapo account, the bulb will continue to send data to security.iot.i.tplinknbu.com using HTTPS. I wasn’t able to decode this flow to check which data are sent, and I wasn’t able to block this data flow. I’ve decided to simply block the top domain from my local DNS: it was enough to block any data exchange with the external world except for NTP calls.

Tapo L530 documentation

Introduction

The client and the bulb communicate using HTTP POST requests. The bulb exposes a webserver and the client sends messages in JSON encoding using HTTP POST requests. The bulb, if it doesn’t have a Wi-Fi configuration, exposes a Wi-Fi network called Tapo_Bulb_8643, and it has the IP 192.168.0.1.

Except for the handshake and login_device commands, the client must send a login token in the request. This token is included in the token query parameters. If the token is missing, the webserver responses with an HTML page with OK but no actions are performed.

Except for the handshake command, all messages must be encrypted. The JSON message is encrypted using AES-128-CBC and then encoded in base64. This encrypted message is sent to the bulb using a special command, securePassthrough.

Request:
{
  "method": "securePassthrough",
  "params": { "request": "ABCDEFG" },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code": 0
  "result" {
    "response": "ABCDEFG"
  }
}

The response of the command is a base64 string encrypted using AES-128-CBC with the same key using for the request message. The key is shared between the client and the bulb using the handskake command.

The request_time_mils and terminal_uuid in the request, while mandatory, aren’t really necessary. In my examples I will always set them to 0/a random string, but one can use any values.

Handshake protocol

The first step in the communication between the client and the bulb is the exchange of a cryptographic key. To do so, the client generates an RSA 1024 keypair and then the client sends the following message at the /app endpoint:

{
  "method": "handshake",
  "params": {
    "key":"-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDo6w1iIscFT+Fd/vODWpBglYnv\nPKyb85Gt5KS8jcyCMBG9lvXxGTE0c9dvsg6YA6RAJOU7Z+t+lpA6CcQtQbd7W9qM\n03UbCHxTVWAPgcyTSOEir3aXLZXLt7a9b13F2PbnJ473R8di+tcPFzho7tuLTv/G\nJSfx+J7AVXm+86Mi2wIDAQAB\n-----END PUBLIC KEY-----\n"
    "request_time_mils": 1668462223570,
    "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
  }
}

key is the generated public key in PEM format.

The bulb sends in the HTTP response this object:

{
  "error_code": 0,
  "result": {
    "key": "b8ZvLR7GltfJxmvVeesQe892SdMvtOBPXQU1UXRhvz8TGy2nD2fcBnEf31sENQtZANyqbT5WNKwQG+ibmT9LEgrkvbPImaCOxNV4fuvnL9DeAPAH9Eeb99Z2QrD6bk77+zGhCvTQMiW75LRIqp7qwkMgbHTR/+IeiZtB7Wp9fH0="
  }
}

The key field contains a string in base64. The client will decrypt this string using the RSA private key, obtaining 32 bits. The first 16 bits are the AES key while the last 16 bits are the IV.

The HTTP response also includes a cookie, TP_SESSIONID. This cookie must be included in the following requests until we have a login token.

Login

Usually after the handshake the first command a client send to the bulb is the login_device since it’s necessary to have a login token to send any other commands.

The client creates the following login command

{
  method: 'login_device',
  params: {
    username: 'ZDk4MWQwYzBlZDliMWFjMTVkMzA0MzI3Y2ZmYzEwMGY0NjA1ZmVlMw==',
    password2: 'YTk0YThmZTVjY2IxOWJhNjFjNGMwODczZDM5MWU5ODc5ODJmYmJkMw=='
  },
  request_time_mils: 1668462223570,
  terminal_uuid: 'A92CFCF117B57F9053DCFAA122E3E341'
}

The username and password are those of the Tapo account associated to the bulb. The message above reports the default values for a factory reseted bulb.

The client will encrypt the JSON string representation of this message (UTF-8) with AES-128-CBC using the key and IV obtained during the handshake exchange. The client will then send a securePassthrough with the encrypted message (in base64 encoding) in the request field to the /app endpoint.

The bulb will send the following message in the HTTP response:

{
  "error_code": 0
  "result" {
    "token": "ABCDEFG"
  }
}

The token field contains the login token to be used in the next requests. From my tests it seems that the token is valid for 24 hours.

Command documentation

This is a full documentation of the commands accepted by the bulb. Using these commands one can control every aspect of the bulb locally.

Introduction

These are common details about all these commands:

  • the fields request_time_mils and terminal_uuid are present in every request. These values can be any values.
  • Every response has a field named error_code. An error_code of 0 indicates success, otherwise an error. This is a (partial) list of error codes I was able to find:
Code Meaning
0 Success
-1 Generic failure
-1004 Json encoding invalid
-1008 Request params are invalid
-1501 Login token is invalid
-1702 Wireless info invalid
-1802 Schedule list is full

Commands list

securePassthrough

Request:
{
  "method": "securePassthrough",
  "params": {
    "request":"QfomOMug5dBpO4LFp4To8TWCkYHB0HDEtSyWEY3XBJSY+b3VbTnQj2o/ihnM6ebY9yWgwrmR5Aj6WLCL1prQq+QKcGnT04NWYO9lN3ppdhKypPhQnmMlb/yGK2NEywuCW1gvQmsQC0qcrpiwDEZ1wfwyT+PjaQRpLeLzfQK0lIr2y+4+fatWFoy9RafIoBuhotu/K20nSsRGYO3x11vx1+bFdQwmbLGmsl1pxhj8p/1SNNxWaxtXXg2+K+5ggVq/ryo7BUwdBieWeKQ0HoUkNA=="
   },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code": 0
  "result" {
    "response": "ABCDEFG"
  }
}
  • request: an encrypted message in base64 format

This command is used to send all the other messages except handshake. All commands must be sent encrypted to the bulb, and they must be sent as the payload of a securePassthrough command.

The response field contains the encrypted response to the request command. For more details about encryption and this secure protocol, check the previous paragraph.

multipleRequest

Request:
{
  "method": "multipleRequest",
  "params": { 
    "requests" : [ 
      {  
        "method": 'set_device_info', 
        "requestTimeMils": 0, 
        "params": { "device_on": true }
      },
      ...
    ]
  }
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code": 0
  "result" {
    "responses": [
      {
        "method":"set_device_info",
        "error_code":0
      },
      ...
    ]
  }
}
  • requests: a list of messages

This command is used to send multiple commands at the same time. The response message is sent after all commands are sent. The commands seem to be executed in the order of the request array.

Handshake and login

handshake

Request:
{
  "method": "handskake",
  "params": { key: "ABCDEFG" },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code": 0
  "result" {
    key: "ABCDEFG"
  }
}
  • key: an RSA public key in PEM format

This command return a symmetric encryption key in the key response value. The key is base64-encoded and encrypted using the public key passed in the request. The result decrypted key is 32 bits long, the first 16 bits are the key and the last 16 bits are an IV.

login_device

Request:
{
  "method": "login_device",
  "params": { 
    "username": "abc"
    "password": "abc"
    "password2": "abc"
  },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code": 0
  "result" {
    "token": "ABCDEFG"
  }
}
  • username: the SHA-1 of the username in base64 encoding
  • password: the password in base64 encoding
  • password2: Needed when password is missing. It is used to send the default password of the device.

This command creates a login session. The response contains a token used to authenticate the next messages.

The password param is passed if the user has already an account configuration on the bulb or the bulb is connected to the Tapo servers. The password2 param is used for passing the password of the default account.

Device state

set_device_info

Request:
{
  "method": "set_device_info",
  "params": { 
    "device_on":false,
    "brightness":30,
    "hue":16,
    "saturation":68,
    "color_temp":2700
  },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code": 0
}
  • device_on: if the bulb should be on/off
  • brightness: brightness of the bulb (1-100)
  • hue and saturation: color settings
  • color_temp: light temperature of the bulb (2500-6500)

This command sets the state of bulb and can be used to turn on/off the bulb, increase/decrease brightness or change color. All the parameters are optional.

get_device_info

Request:
{
  "method": "get_device_info",
  "params": { },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code": 0
  "result" {
    "device_id":"",
    "fw_ver":"1.0.9 Build 220526 Rel.203202",
    "hw_ver":"2.0",
    "type":"SMART.TAPOBULB",
    "model":"L530",
    "mac":"6C-5A-B0-CC-86-4E",
    "hw_id":"FDE1C68674D1535B12A042682B192E4E",
    "fw_id":"7BECA9DC454565672FEC87D1104F9972",
    "oem_id":"2241AD354FF56159AF1AC4A53B011A5D",
    "color_temp_range":[2500,6500],
    "overheated":false,
    "ip":"192.168.0.1",
    "time_diff":0,
    "ssid":"",
    "rssi":-50,
    "signal_level":2,
    "latitude":0,
    "longitude":0,
    "lang":"",
    "avatar":"",
    "region":"",
    "specs":"",
    "nickname":"",
    "has_set_location_info":false,
    "device_on":false,
    "brightness":30,
    "hue":16,
    "saturation":68,
    "color_temp":2700,
    "dynamic_light_effect_enable":false,
    "default_states":{
      "type":"last_states",
      "state":{"brightness":30,"hue":16,"saturation":68,"color_temp":2700}
    }
  }
}

This command returns the current state and configuration of the bulb.

get_device_running_info

Request:
{
  "method": "get_device_running_info",
  "params": { },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code": 0
  "result" {
    "device_id":"",
    "fw_ver":"1.0.9 Build 220526 Rel.203202",
    "hw_ver":"2.0",
    "type":"SMART.TAPOBULB",
    "model":"L530",
    "mac":"6C-5A-B0-CC-86-4E",
    "hw_id":"FDE1C68674D1535B12A042682B192E4E",
    "fw_id":"7BECA9DC454565672FEC87D1104F9972",
    "oem_id":"2241AD354FF56159AF1AC4A53B011A5D",
    "color_temp_range":[2500,6500],
    "overheated":false,
    "ip":"192.168.0.1",
    "time_diff":0,
    "ssid":"",
    "rssi":-50,
    "signal_level":2,
    "latitude":0,
    "longitude":0,
    "lang":"",
    "avatar":"",
    "region":"",
    "specs":"",
    "nickname":"",
    "has_set_location_info":false,
    "device_on":false,
    "brightness":30,
    "hue":16,
    "saturation":68,
    "color_temp":2700,
    "dynamic_light_effect_enable":false,
    "default_states":{
      "type":"last_states",
      "state":{"brightness":30,"hue":16,"saturation":68,"color_temp":2700}
    }
  }
}

This command seems to be an alias for get_device_info on this bulb.

device_reboot

Request:
{
  "method": "device_reboot",
  "params": { },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code": 0
}

This command reboots the bulb.

device_reset

Request:
{
  "method": "device_reset",
  "params": { },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code": 0
}

This command resets the bulb, deleting any configurations and restoring them to factory default.

get_device_time

Request:
{
  "method": "get_device_time",
  "params": { },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code": 0
  "result": {
    "time_diff":0,
    "timestamp":1672509215,
    "region":""
  }
}

This command returns the current time on the device

set_device_time

Request:
{
  "method": "set_device_time",
  "params": { 
    "time_diff":0,
    "timestamp":1,
    "region":""
  },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code": 0
}
  • time_diff: Differences in minutes from UTC
  • timestamp: UNIX timestamp in seconds
  • region: Timezone name

This command set the time on the bulb.

Firmware

fw_download

Request:
{
  "method": "fw_download",
  "params": { 
    "url": "https://fw.download"
  },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code": 0
}
  • *url: The firmware URL

This command instructs the bulb to download the firmware present at url.

get_fw_download_state

Request:
{
  "method": "get_fw_download_state",
  "params": {},
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code": 0,
  "result":{
    "status":0,
    "download_progress":0,
    "reboot_time":5,
    "upgrade_time":5,
    "auto_upgrade":false
  }
}

This command will return the state of a firmware update operation.

get_latest_fw

Request:
{
  "method": "get_latest_fw",
  "params": {},
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code": 0,
  "result":{
    "type":0,
    "fw_ver":"1.0.9 Build 220526 Rel.203202",
    "release_date":"",
    "release_note":"",
    "fw_size":0,
    "hw_id":"",
    "oem_id":"",
    "need_to_upgrade":false
  }
}

???

WiFi

get_wireless_scan_info

Request:
{
  "method": "get_wireless_scan_info",
  "params": { 
    "start_index": 0
  },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code": 0,
  "ap_list": [
    {
      "cipher_type":2,
      "bssid":"20B0012F5E41",
      "channel":11,
      "ssid":"TGEgVGFuYSBkZWwgUGlhY2VyZQ==",
      "key_type":"wpa2_psk",
      "signal_level":3
    },
    ...
  ],
  "sum":11,
  "start_index":0,
  "wep_supported":false
}
  • start_index: The starting index of the ap list

This command will tell the bulb to start an AP scan and to return the list of the APs found. The response message will contain at most 10 APs: to obtain the next one we must send another command with a higher start_index.

set_wireless_info

Request:
{
  "method": "set_wireless_info",
  "params": { 
    "key_type": "wpa2_psk",
    "ssid": "cGFuemJvb2s="
    "password": "YW5kcmVhMTI="
  },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code": 0
}
  • key_type: The type of the Wi-Fi network
  • ssid: The SSID of the network in base64
  • password: The network password in base64

This command sets the Wi-Fi network. After we receive the response the bulb will try to connect to the new network. The error_code 0 doesn’t mean that the bulb will be able to connect, only that the bulb has received the new configuration.

Setup

component_nego

Request:
{
  "method": "component_nego",
  "params": {},
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code":0,
  "result":{
    "component_list":[
      {"id":"device","ver_code":2},
      {"id":"firmware","ver_code":2},
      {"id":"quick_setup","ver_code":3},
      {"id":"inherit","ver_code":1},
      {"id":"time","ver_code":1},
      {"id":"wireless","ver_code":1},
      {"id":"schedule","ver_code":2},
      {"id":"countdown","ver_code":2},
      {"id":"antitheft","ver_code":1},
      {"id":"account","ver_code":1},
      {"id":"synchronize","ver_code":1},
      {"id":"sunrise_sunset","ver_code":1},
      {"id":"brightness","ver_code":1},
      {"id":"cloud_connect","ver_code":1},
      {"id":"default_states","ver_code":1},
      {"id":"preset","ver_code":1},
      {"id":"brightness","ver_code":1},
      {"id":"color","ver_code":1},
      {"id":"color_temperature","ver_code":1},
      {"id":"auto_light","ver_code":1},
      {"id":"on_off_gradually","ver_code":1},
      {"id":"device_local_time","ver_code":1},
      {"id":"light_effect","ver_code":1}
    ]
  }
}

This command returns a list of features supported by our Tapo device.

qs_component_nego

Request:
{
  "method": "qs_component_nego",
  "params": {},
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code":0,
  "result":{
    "component_list":[
      {"id":"quick_setup","ver_code":3},
      {"id":"sunrise_sunset","ver_code":1},
      {"id":"inherit","ver_code":1}
    ],
    "extra_info":{
      "device_type":"SMART.TAPOBULB",
      "device_model":"L530"
    }
  }
}

This command returns a subset of the features supported by the Tapo device, used during the first setup with the official app.

set_qs_info

Request:
{
  "method": "set_qs_info",
  "params": {
    "account":{
      "password":"ABCDEF",
      "username":"ABCDEF"
    },
    "extra_info":{
      "specs":"US"
    },
    "time":{
      "region":"Europe/Madrid",
      "time_diff":60,
      "timestamp":1668462242
    },
    "wireless":{
      "key_type":"wpa2_psk",
      "password":"ABCDEF",
      "ssid":"ABCDEF"
    }
  },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code":0,
  "result":{
    "device_id":"802398CD8"
  }
}
  • account.username: Tapo Cloud account username (base64)
  • account.password: Tapo Cloud account password (base64)

  • extra_info.specs: The region where we are setting up the bulb, for Wi-Fi frequencies.

  • time.time_diff: Differences in minutes from UTC
  • time.timestamp: UNIX timestamp in seconds
  • time.region: Timezone name

  • wireless.key_type: The type of the Wi-Fi network
  • wireless.ssid: The SSID of the network in base64
  • wireless.password: The network password in base64

This command is used by the app to do a “quick setup” of the bulb configuring network, Tapo cloud account and timezone in a single request.

The device_id returned is used by the app to identify this device while sending commands to the Tapo cloud server.

Schedule

get_schedule_rules

Request:
{
  "method": "get_schedule_rules",
  "params": {
    "start_index": 0
  },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "result":{
    "enable":true,
    "schedule_rule_max_count":32,
    "start_index":0,
    "sum":1,
    "rule_list":[
      {
        "enable":true,
        "id":"S1",
        "s_type":"normal",
        "s_min":1180,
        "e_type":"normal",
        "e_min":0,
        "e_action":"none",
        "week_day":71,
        "mode":"repeat",
        "year":2022,
        "month":12,
        "day":31,
        "time_offset":0,
        "desired_states":{
          "on":true,
          "brightness":100,
          "auto":false,"hue":0,
          "saturation":0,
          "color_temp":2700
        }
      }
    ]
  },
  "error_code":0
}
  • start_index: The starting index of the schedule list

This command returns the list of light schedules saved on the bulb. The command will return only the first 10 schedules from the start_index index: the client can retrieve the next ones using the field sum to know the number of schedules saved.

add_schedule_rule

Request:
{
  "method": "add_schedule_rule",
  "params": {
    "enable":true,
    "s_type":"normal",
    "s_min":1180,
    "e_type":"normal",
    "e_min":0,
    "week_day":71,
    "mode":"repeat",
    "year":2022,
    "month":12,
    "day":31,
    "time_offset":0,
    "desired_states":{
      "on":true,
      "brightness":100,
      "auto":false,
      "hue":0,
      "saturation":0,
      "color_temp":2700
    }
  },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code":0,
  "result":{
    "id":"A1"
  }
}
  • enable: Enable/disable this schedule rule
  • s_type: Starting offset type. One value between normal, sunrise, sunset, none
  • s_min: Starting minute of schedule from midnight + time_offset. Considered only if s_type is normal
  • e_type: Ending offset type. One value between normal, sunrise, sunset, none
  • e_min: Ending minute of schedule from midnight + time_offset. Considered only if e_type is normal
  • week_day: A bitmask for indicating the day of the week this schedule is considered. Order from LSB is Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
  • mode: Type of the schedule. One value between once and repeat
  • year, month, day: The first date to apply this schedule rule
  • time_offset: Time offset from midnight
  • desired_states: The states to apply during the scheduled time

This command create a new scheduling rule. The rule will apply the state described in desired_states during the timeframe indicated by s_min and e_min, considering the mode, week day and time offset. At the end of the period the bulb returns to the previous state.

edit_schedule_rule

Request:
{
  "method": "edit_schedule_rule",
  "params": {
    "id": "S1",
    "enable":true,
    "s_type":"normal",
    "s_min":1180,
    "e_type":"normal",
    "e_min":0,
    "week_day":71,
    "mode":"repeat",
    "year":2022,
    "month":12,
    "day":31,
    "time_offset":0,
    "desired_states":{
      "on":true,
      "brightness":100,
      "auto":false,
      "hue":0,
      "saturation":0,
      "color_temp":2700
    }
  },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code":0
}
  • id: The ID of the schedule rule to edit
  • enable: Enable/disable this schedule rule
  • s_type: Starting offset type. One value between normal, sunrise, sunset, none
  • s_min: Starting minute of schedule from midnight + time_offset. Considered only if s_type is normal
  • e_type: Ending offset type. One value between normal, sunrise, sunset, none
  • e_min: Ending minute of schedule from midnight + time_offset. Considered only if e_type is normal
  • week_day: A bitmask for indicating the day of the week this schedule is considered. Order from LSB is Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
  • mode: Type of the schedule. One value between once and repeat
  • year, month, day: The first date to apply this schedule rule
  • time_offset: Time offset from midnight
  • desired_states: The states to apply during the scheduled time

This command edits a scheduling rule. The id field will identify which rule should be updated. It is possible to send only a subset of these fields to update only some specific values.

remove_schedule_rules

Request:
{
  "method": "remove_schedule_rules",
  "params": {
    "remove_all": false
    "rule_list": [
      { "id": "S1" },
      ...
    ]
  },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code":0
}
  • remove_all: If the command should delete all scheduled rules
  • rule_list: A list of schedule rule IDs

This command will delete the rules indicated in the rule_list field. If the remove_all flag is active all rules will be deleted and rule_list is ignored. If a rule is deleted while active, the bulb will not revert to the previous state when the period should have ended.

Countdown

get_countdown_rules

Request:
{
  "method": "get_countdown_rules",
  "params": {
    "start_index": 0
  },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "result":{
    "enable":true,
    "schedule_rule_max_count":32,
    "start_index":0,
    "sum":1,
    "rule_list":[
      {
        "enable":true,
        "id":"S1",
        "s_type":"normal",
        "s_min":1180,
        "e_type":"normal",
        "e_min":0,
        "e_action":"none",
        "week_day":71,
        "mode":"repeat",
        "year":2022,
        "month":12,
        "day":31,
        "time_offset":0,
        "desired_states":{
          "on":true,
          "brightness":100,
          "auto":false,"hue":0,
          "saturation":0,
          "color_temp":2700
        }
      }
    ]
  },
  "error_code":0
}
  • start_index: The starting index of the schedule list

This command returns the list of light schedules saved on the bulb. The command will return only the first 10 schedules from the start_index index: the client can retrieve the next ones using the field sum to know the number of schedules saved.

add_countdown_rule

Request:
{
  "method": "add_countdown_rule",
  "params": {
    "enable": true, 
    "delay": 25, 
    "desired_states": { 
      "on": true, 
      "brightness": 50
    }
  },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code":0
  "result":{
    "id":"A1"
  }
}
  • enable: Enable/disable this countdown
  • delay: Number of seconds of the countdown
  • desired_states: The states to apply during the scheduled time

This command create a new “countdown”: after delay seconds the bulb will change its state to match desired_states.

edit_countdown_rule

Request:
{
  "method": "edit_countdown_rule",
  "params": {
    "id": "S1",
    "enable": true, 
    "delay": 25, 
    "desired_states": { 
      "on": true, 
      "brightness": 50 
    }
  },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code":0
}
  • id: The ID of the schedule rule to edit
  • enable: Enable/disable this countdown
  • delay: Number of seconds of the countdown
  • desired_states: The states to apply during the scheduled time

This command edits a countdown. The id field will identify which rule should be updated.

remove_countdown_rules

Request:
{
  "method": "remove_countdown_rules",
  "params": {
    "remove_all": false
    "rule_list": [
      { "id": "S1" },
      ...
    ]
  },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code":0
}
  • remove_all: If the command should delete all scheduled rules
  • rule_list: A list of schedule rule IDs

This command will delete the rules indicated in the rule_list field. If the remove_all flag is active all rules will be deleted and rule_list is ignored. If one or more IDs are not present, the command will return error_code = 0.

Away mode

Away mode is how Tapo call a time period where the bulb turn on and off randomly during the interval indicated to simulate the presence of someone at home.

get_antitheft_rules

Request:
{
  "method": "get_antitheft_rules",
  "params": {
    "start_index": 0
  },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "result":{
    "enable":true,
    "antitheft_rule_max_count":1,
    "rule_list":[
      {
        "enable":true,
        "id":"A1",
        "start_time_type":"normal",
        "start_min":1170,
        "end_time_type":"normal",
        "end_min":360,
        "frequency":5,
        "week_day":6,
        "mode":"repeat",
        "year":0,
        "month":0,
        "day":0,
        "start_time_offset":0,
        "end_time_offset":0
      }
    ]
  },
  "error_code":0
}
  • start_index: The starting index of the schedule list

This command returns the configuration of the anti-theft rules saved on the bulb. The command will return only the first 10 schedules from the start_index index: the client can retrieve the next ones using the field sum to know the number of schedules saved.

add_antitheft_rule

Request:
{
  "method": "add_antitheft_rule",
  "params": {
    "enable":true,
    "start_time_type":"normal",
    "start_min":1170,
    "end_time_type":"normal",
    "end_min":360,
    "frequency":5,
    "week_day":6,
    "mode":"repeat",
    "year":0,
    "month":0,
    "day":0,
    "start_time_offset":0,
    "end_time_offset":0
  },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code":0,
  "result":{
    "id":"A1"
  }
}
  • enable: Enable/disable this schedule rule
  • start_time_type: Starting offset type. One value between normal, sunrise, sunset, none
  • start_min: Starting minute of schedule from midnight + time_offset. Considered only if start_time_type is normal
  • end_time_type: Ending offset type. One value between normal, sunrise, sunset, none
  • end_min: Ending minute of schedule from midnight + time_offset. Considered only if end_time_type is normal
  • frequency: The minute frequency the bulb should change state.
  • week_day: A bitmask for indicating the day of the week this schedule is considered. Order from LSB is Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
  • mode: Type of the schedule. One value between once and repeat
  • year, month, day: The first date to apply this schedule rule
  • start_time_offset: Time offset from midnight for start time
  • end_time_offset: Time offset from midnight for end time

This command create a new anti-theft schedule. For each week_day indicated after the day set, during the timeframe indicated by start_min and end_min the bulb is randomly turned on/off in frequency intervals.

edit_antitheft_rule

Request:
{
  "method": "edit_antitheft_rule",
  "params": {
    "id": "A1",
    "enable":true,
    "start_time_type":"normal",
    "start_min":1170,
    "end_time_type":"normal",
    "end_min":360,
    "frequency":5,
    "week_day":6,
    "mode":"repeat",
    "year":0,
    "month":0,
    "day":0,
    "start_time_offset":0,
    "end_time_offset":0
  },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code":0,
  "result": {}
}
  • id: The id of the rule to update
  • enable: Enable/disable this schedule rule
  • start_time_type: Starting offset type. One value between normal, sunrise, sunset, none
  • start_min: Starting minute of schedule from midnight + time_offset. Considered only if start_time_type is normal
  • end_time_type: Ending offset type. One value between normal, sunrise, sunset, none
  • end_min: Ending minute of schedule from midnight + time_offset. Considered only if end_time_type is normal
  • frequency: The minute frequency the bulb should change state.
  • week_day: A bitmask for indicating the day of the week this schedule is considered. Order from LSB is Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
  • mode: Type of the schedule. One value between once and repeat
  • year, month, day: The first date to apply this schedule rule
  • start_time_offset: Time offset from midnight for start time
  • end_time_offset: Time offset from midnight for end time

This command edits an anti-theft schedule. The id field will identify which schedule should be updated.

remove_antitheft_rules

Request:
{
  "method": "remove_antitheft_rules",
  "params": {
    "remove_all": false
    "rule_list": [
      { "id": "A1" },
      ...
    ]
  },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code":0
}
  • remove_all: If the command should delete all scheduled rules
  • rule_list: A list of schedule rule IDs

This command will delete the anti-theft rules indicated in the rule_list field. If the remove_all flag is active all rules will be deleted and rule_list is ignored. If one or more IDs are not present, the command will return error_code = 0.

Miscellaneous

get_inherit_info

Request:
{
  "method": "get_inherit_info",
  "params": {},
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code":0,
  "result":{
    "inherit_status":false
  }
}

This command returns if the bulb is set up as an inherit device from another Tapo cloud account

set_inherit_info

Request:
{
  "method": "set_inherit_info",
  "params": {
    "is_inherit": false
  },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code":0
}
  • is_inherit: Inherit flag

This command set the inheritance flag on the device.

get_on_off_gradually_info

Request:
{
  "method": "get_on_off_gradually_info",
  "params": {},
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code":0,
  "result":{
    "enable": false
  }
}

This command returns if the bulb is set up to turn off/on the light gradually

set_on_off_gradually_info

Request:
{
  "method": "set_on_off_gradually_info",
  "params": {
    "enable": true
  },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code":0
}
  • enable: If the off/on should be gradually

This command set if the off/on of the device should be gradually or not.

get_auto_light_info

Request:
{
  "method": "get_auto_light_info",
  "params": {},
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code":0,
  "result":{
    "enable": false,
    "mode": "light_track"
  }
}

This command returns if the bulb is set up to regulate the light brightness and color based on the location and time.

set_auto_light_info

Request:
{
  "method": "set_auto_light_info",
  "params": {
    "enable": true,
    "mode": "light_compensate"
  },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code":0
}
  • enable: If the auto light should be enabled
  • mode: auto light modality. One between “light_compensate” or “light_track”

This command set if the off/on of the device should be gradually or not.

get_dynamic_light_effect_rules

Request:
{
  "method": "get_dynamic_light_effect_rules",
  "params": {},
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "result":{
    "enable":false,
    "sum":2,
    "rule_list":[
      {
        "id":"L1",
        "change_time":1000,
        "scene_name":"",
        "change_mode":"direct",
        "color_status_list":[
          [100,0,0,2700],[100,321,99,0],[100,196,99,0],[100,6,97,0],[100,160,100,0],[100,274,95,0],[100,48,100,0],[100,242,99,0]
        ]
      },
      {
        "id":"L2",
        "change_time":2000,
        "scene_name":"",
        "change_mode":"bln",
        "color_status_list":[
          [100,54,6,0],[100,19,39,0],[100,194,52,0],[100,324,24,0],[100,170,34,0],[100,276,27,0],[100,56,46,0],[100,221,36,0]
        ]
      }
    ],
    "max_count":2,
    "start_index":0
  },
"error_code":0
}

This command returns the list of the light effects saved on the bulb.

set_dynamic_light_effect_rules

Request:
{
  "method": "set_dynamic_light_effect_rules",
  "params": {
    "enable": true, 
    "id": "L1"
  },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code":0
}
  • enable: If the light effect should be enabled
  • id: The id of the light effect to activate

This command activate/deactivate a specific light effect. The ID of the light effect is one of the IDs presents in the rules_list returned by get_dynamic_light_effect_rules

edit_dynamic_light_effect_rules

Request:
{
  "method": "edit_dynamic_light_effect_rules",
  "params": {
    "id":"L1",
    "change_time":1000,
    "scene_name":"",
    "change_mode":"direct",
    "color_status_list":[
      [100,0,0,2700],[100,321,99,0],[100,196,99,0],[100,6,97,0],[100,160,100,0],[100,274,95,0],[100,48,100,0],[100,242,99,0]
    ]
  },
  "request_time_mils": 0,
  "terminal_uuid": "A92CFCF117B57F9053DCFAA122E3E341"
}
Response:
{
  "error_code":0
}
  • id: Light effect id to edit
  • change_time: How often the color should change, in milliseconds
  • scene_name: Unused on Tapo L530
  • change_mode: How the bulb should transition between the color state. Cane be direct or bln (a gradual change)
  • color_status_list: List of color statuses. Each value is an array of 4 values and represent a bulb state. The 4 values, in order, are:
    • Brightness
    • Saturation
    • Hue
    • Color temperature

This command edits an ID effect present in the bulb. A light effect is composed by a list of bulb states. The bulb will change the current state each change_time milliseconds to tne next one in the color_status_list list.