shark24
shark24

Reputation: 31

Why is this POST request from ESP32 to control KASA smart plug not working?

I am trying to communicate to a KASA HS103 smart plug using an HTTPS POST request sent via ESP32 (LoRa V2). For the actual POST content, I'm quite new to HTTP and have been following the instructions here: https://itnerd.space/2017/01/22/how-to-control-your-tp-link-hs100-smartplug-from-internet/

This is the POST request I am trying to send (with token & IDs modified):

URL: https://use1-wap.tplinkcloud.com/?token=fb2f7209-ATebDhHDOxB2wWc6wslPewO&appName=Kasa_Android&termID=1263f577-4387-4d3e-be79-705445d33bb08&appVer=1.4.4.607&ospf=Android+6.0.1&netType=wifi&locale=en_US

{
 "method":"passthrough",
 "params":{
 "deviceId":"80068FEB5A735A5BB187B4EC309EF1BE1D6D8997",
 "requestData":"{\"system\":{\"set_relay_state\":{\"state\":1}}}"
 }
}

This will turn on the smart plug. I have verified that the POST request itself works, with both an online API tester (https://reqbin.com/) and through cURL on my MacBook.

I retrieved the URL token and device ID by authenticating with TP-Link server using my credentials through the API tester (also in the instructions linked above).

However, I am unable to control the smart plug when sending with ESP32. I am writing and compiling through the Arduino IDE, using the Heltec framework / libraries. Here is my code (started with the code from Rui Santos here and modified for my application):

/*
  Rui Santos
  Complete project details at Complete project details at https://RandomNerdTutorials.com/esp32-http-get-post-arduino/

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.

  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include <WiFi.h>
#include <HTTPClient.h>

#include <WiFiClientSecure.h>


const char* ssid = "XXXXX";
const char* password = "XXXXX";

//Your Domain name with URL path or IP address with path
const char* serverName = "https://use1-wap.tplinkcloud.com/?token=fb2f7209-ATebDhHDOxB2wWc6wslPewO&appName=Kasa_Android&termID=1263f577-4387-4d3e-be79-705445d33bb08&appVer=1.4.4.607&ospf=Android+6.0.1&netType=wifi&locale=en_US HTTP/1.1";
const int port = 443;

// the following variables are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastTime = 0;
// Timer set to 10 minutes (600000)
//unsigned long timerDelay = 600000;
// Set timer to 5 seconds (5000)
unsigned long timerDelay = 5000;

void setup() {
  Serial.begin(115200);

  WiFi.begin(ssid, password);
  Serial.println("Connecting");
  while(WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to WiFi network with IP Address: ");
  Serial.println(WiFi.localIP());
 
  Serial.println("Timer set to 5 seconds (timerDelay variable), it will take 5 seconds before publishing the first reading.");
}

void loop() {
  //Send an HTTP POST request every 10 minutes
  if ((millis() - lastTime) > timerDelay) {
    //Check WiFi connection status
    if(WiFi.status()== WL_CONNECTED){
      WiFiClientSecure *client = new WiFiClientSecure;

      if (client) {
        Serial.println("Client Created!");
        //client -> setCACert(rootCACertificate);
        
        {
          HTTPClient http;
        
          // Your Domain name with URL path or IP address with path
          http.begin(*client, serverName);
          
          // If you need an HTTP request with a content type: application/json, use the following:
          http.addHeader("Content-Type", "application/json");
          //int httpResponseCode = http.POST("{\"api_key\":\"tPmAT5Ab3j7F9\",\"sensor\":\"BME280\",\"value1\":\"24.25\",\"value2\":\"49.54\",\"value3\":\"1005.14\"}");
          int httpResponseCode = http.POST("{\"method\":\"passthrough\", \"params\": {\"deviceId\": \"80068FEB5A735A5BB187B4EC309EF1BE1D6D8997\", \"requestData\": \"{\"system\":{\"set_relay_state\":{\"state\":0}}}\" }}");
         
          Serial.print("HTTP Response code: ");
          Serial.println(httpResponseCode);

          String payload = http.getString();
          Serial.print("HTTP String: ");
          Serial.println(payload);
            
          // Free resources
          http.end();
        }
        
        delete client;
      } else {
        Serial.println("Unable to create client");
      }
      
    } else {
      Serial.println("WiFi Disconnected");
    }
    lastTime = millis();
  }
}

Here is the output from the serial terminal:

Connected to WiFi network with IP Address: 192.168.X.XXX
Timer set to 5 seconds (timerDelay variable), it will take 5 seconds before publishing the first reading.
Client Created!
HTTP Response code: 200
HTTP String: {"error_code":-10100,"msg":"JSON format error"}

The smart plug does not turn off when uploading and running on ESP32.

Doing a quick search online for POST response codes, receiving 200 seems to mean the request was processed and OK, yet the error code is negative and message is "JSON format error".

Any ideas why this POST request is not working? Or anything I should try to get more info?

Thanks in advance!

Upvotes: 2

Views: 877

Answers (1)

shark24
shark24

Reputation: 31

Digging into "JSON format error" - turns out I had an extra "" around the 'requestData' value which parsed fine in cURL but could not be understood when sending raw through ESP32.

Removing those quotes fixed the problem and now I'm able to send POST requests successfully.

Here is the working code now:

#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>

#include <Arduino_JSON.h>


const char* ssid = "XXXXX";
const char* password = "XXXXX";

//Your Domain name with URL path or IP address with path
const char* serverName = "https://use1-wap.tplinkcloud.com/?token=fb2f7209-ATebDhHDOxB2wWc6wslPewO&appName=Kasa_Android&termID=1163f577-4288-4d3d-be69-705445d33ba08&appVer=1.4.4.607&ospf=Android+6.0.1&netType=wifi&locale=en_US HTTP/1.1";

// the following variables are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastTime = 0;
// Set timer to 5 seconds (5000)
unsigned long timerDelay = 5000;

void setup() {
  Serial.begin(115200);

  WiFi.begin(ssid, password);
  Serial.println("Connecting");
  while(WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to WiFi network with IP Address: ");
  Serial.println(WiFi.localIP());
 
  Serial.println("Timer set to 5 seconds (timerDelay variable), it will take 5 seconds before publishing the first reading.");
}

void loop() {
  //Send an HTTP POST request every 10 minutes
  if ((millis() - lastTime) > timerDelay) {
    //Check WiFi connection status
    if(WiFi.status()== WL_CONNECTED){
      WiFiClientSecure *client = new WiFiClientSecure;

      if (client) {
        
        {
          HTTPClient http;
        
          // Your Domain name with URL path or IP address with path
          http.begin(*client, serverName);
          
          // If you need an HTTP request with a content type: application/json, use the following:
          http.addHeader("Content-Type", "application/json");
          int httpResponseCode = http.POST("{\"method\":\"passthrough\",\"params\":{\"deviceId\":\"80068FEB3A733B5BB287B4EC309FE1BE1D7D8997\",\"requestData\":{\"system\":{\"set_relay_state\":{\"state\":1}}}}}");
         
          Serial.print("HTTP Response code: ");
          Serial.println(httpResponseCode);

          String payload = http.getString();
          Serial.print("HTTP String: ");
          Serial.println(payload);
            
          // Free resources
          http.end();
        }
        
        delete client;
      } else {
        Serial.println("Unable to create client");
      }
      
    } else {
      Serial.println("WiFi Disconnected");
    }
    
    lastTime = millis();
  }
}

Upvotes: 1

Related Questions