Skip to content

Instantly share code, notes, and snippets.

@boulama
Created January 12, 2024 07:59
Show Gist options
  • Save boulama/4fb5ccfc77a3b33c8ff01f1b3ae35156 to your computer and use it in GitHub Desktop.
Save boulama/4fb5ccfc77a3b33c8ff01f1b3ae35156 to your computer and use it in GitHub Desktop.
ESP32 code connects to Wi-Fi, uses a motion sensor to control LEDs, and interacts with the Govee API to retrieve and manipulate device based on motion detection. Motion sensor: am312.
#include <WiFi.h>
#include <ArduinoJson.h>
#include <HTTPClient.h>
HTTPClient http;
// Parse JSON response
DynamicJsonDocument doc(1024);
const int motionPin = 2; // GPIO pin connected the OUT pin of the AM312 sensor to
const int led1Pin = 13; // Replace with the pin of the first LED (green)
const int led2Pin = 15; // Replace with the pin of the second LED (red)
int time_idle = 0;
int time_in_motion = 0;
const char *ssid = "wifi-ssid";
const char *password = "wifi-password";
const char *apiKey = "govee-api-key"; // get api key from govee
const char *url = "https://developer-api.govee.com/v1/devices";
const char *control_endpoint = "https://developer-api.govee.com/v1/devices/control";
const String &state_endpoint = "https://developer-api.govee.com/v1/devices/state";
// devices that we want to turn on and off
const char *devicesToControl[] = { "[1] Smart LED Bulb", "[2] Smart LED Bulb", "[3] Smart LED Bulb", "[4] Smart LED Bulb" };
bool getDevicesSuccess = false;
struct DeviceState {
String powerState;
bool online;
};
void setup() {
Serial.begin(115200);
pinMode(motionPin, INPUT);
pinMode(led1Pin, OUTPUT);
pinMode(led2Pin, OUTPUT);
delay(10);
// Connect to Wi-Fi
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
delay(1000);
getDevices();
delay(5000);
}
// Function to turn on LED1 and turn off LED2
void turn1on2off() {
digitalWrite(led1Pin, HIGH);
digitalWrite(led2Pin, LOW);
}
// Function to turn on LED2 and turn off LED1
void turn2on1off() {
digitalWrite(led1Pin, LOW);
digitalWrite(led2Pin, HIGH);
}
void loop() {
int motionState = digitalRead(motionPin);
if (motionState == HIGH) {
Serial.print("Motion detected! - ");
Serial.print("Motion lasted ");
Serial.print(time_in_motion);
Serial.println(" s.");
time_in_motion++;
time_idle = 0;
if(time_in_motion == 4) {
printDevices("on");
}
turn1on2off();
} else {
Serial.print("No motion! - ");
Serial.print("Idleness lasted ");
Serial.print(time_idle);
Serial.println(" s.");
time_in_motion = 0;
time_idle++;
if(time_idle == 4) {
printDevices("off");
}
turn2on1off();
}
delay(1000); // 1s delay
}
void getDevices() {
HTTPClient http;
Serial.print("Sending GET request to Govee API...");
// Your Govee API URL
http.begin(url);
// Set headers
http.addHeader("Govee-API-Key", apiKey);
// Send the request
int httpCode = http.GET();
if (httpCode > 0) {
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
if (httpCode == HTTP_CODE_OK) {
String payload = http.getString();
Serial.println(payload);
deserializeJson(doc, payload);
getDevicesSuccess = true; // a successful API call
int numDevicesToControl = sizeof(devicesToControl) / sizeof(devicesToControl[0]);
// Extract and print device information
JsonArray devices = doc["data"]["devices"];
for (JsonObject device : devices) {
const char *deviceName = device["deviceName"];
const char *device_address = device["device"];
for (int i = 0; i < numDevicesToControl; i++) {
if (strcmp(deviceName, devicesToControl[i]) == 0) {
const char *model = device["model"];
bool controllable = device["controllable"];
bool retrievable = device["retrievable"];
Serial.println("Device Name: " + String(deviceName) + " " + String(device_address));
Serial.println("Model: " + String(model));
Serial.println("Controllable: " + String(controllable ? "Yes" : "No"));
Serial.println("Retrievable: " + String(retrievable ? "Yes" : "No"));
Serial.println("------");
break;
}
}
}
}
} else {
Serial.printf("[HTTP] GET request failed, error: %s\n", http.errorToString(httpCode).c_str());
}
// Free resources
http.end();
}
void printDevices(const String &action) {
// Check if the API call was successful before printing devices
if (getDevicesSuccess) {
// Extract and print device information
JsonArray devices = doc["data"]["devices"];
for (JsonObject device : devices) {
const char *deviceName = device["deviceName"];
const char *device_address = device["device"];
const char *model = device["model"];
bool controllable = device["controllable"];
bool retrievable = device["retrievable"];
DeviceState state = getDeviceState(device_address, model);
Serial.println("Device Name: " + String(deviceName));
Serial.println("Model: " + String(model));
Serial.println("Power State: " + state.powerState);
Serial.println("Online: " + String(state.online ? "Yes" : "No"));
if(state.online) {
// then we can either turn on or off
if(action != state.powerState) {
controlDevice(device_address, model, "turn", action);
Serial.println("[ACTION] Turn " + String(action));
} else {
Serial.println("do nothing...");
}
}
// Serial.println("Controllable: " + String(controllable ? "Yes" : "No"));
// Serial.println("Retrievable: " + String(retrievable ? "Yes" : "No"));
Serial.println("------");
}
} else {
Serial.println("API call was not successful. Check the previous logs for details.");
}
}
void controlDevice(const String &device, const String &model, const String &cmdName, const String &cmdValue) {
HTTPClient http;
Serial.print("[CONTROL DEVICE]");
http.begin(control_endpoint);
http.addHeader("Content-Type", "application/json");
http.addHeader("Govee-API-Key", apiKey);
// Create JSON request body
StaticJsonDocument<200> jsonBody;
jsonBody["device"] = device;
jsonBody["model"] = model;
JsonObject cmdObject = jsonBody.createNestedObject("cmd");
cmdObject["name"] = cmdName;
cmdObject["value"] = cmdValue;
// Serialize JSON to string
String jsonString;
serializeJson(jsonBody, jsonString);
// Send the request
int httpCode = http.PUT(jsonString);
if (httpCode > 0) {
// HTTP header has been sent and the response status code received
Serial.printf("[HTTP] PUT... code: %d\n", httpCode);
// File found at the server
if (httpCode == HTTP_CODE_OK) {
String payload = http.getString();
Serial.println(payload);
}
} else {
Serial.printf("[HTTP] PUT request failed, error: %s\n", http.errorToString(httpCode).c_str());
}
// Free resources
http.end();
}
DeviceState getDeviceState(const String &device, const String &model) {
HTTPClient http;
Serial.print("[GET STATE]");
String apiUrl = state_endpoint + "?device=" + device + "&model=" + model;
http.begin(apiUrl);
// Set headers
http.addHeader("Govee-API-Key", apiKey);
DeviceState result; // Create a struct to store the result
// Send the request
int httpCode = http.GET();
if (httpCode > 0) {
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
if (httpCode == HTTP_CODE_OK) {
String payload = http.getString();
Serial.println(payload);
// Parse JSON response
DynamicJsonDocument doc(1024);
deserializeJson(doc, payload);
// Extract device state information
JsonObject data = doc["data"];
result.powerState = data["properties"][1]["powerState"].as<String>();
result.online = data["properties"][0]["online"];
}
} else {
Serial.printf("[HTTP] GET request failed, error: %s\n", http.errorToString(httpCode).c_str());
}
// Free resources
http.end();
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment