kakopappa / sinric

Amazon Alexa Smart home skill / Google Home Action for ESP8266 / ESP32 / Arduino
https://sinric.com
287 stars 166 forks source link

How To Update Sinric From NodeMCU #131

Open eddcaton opened 6 years ago

eddcaton commented 6 years ago

Hey,

Is there a way to update sinric from the node MCU to change the state of a light switch?

I have a manual override on my lighting for ease of use, and when you turn the lights off manually, sinric thinks that the light is still on. So you have to ask Alexa to turn the light off to actually turn it on.

I have managed to connect a CT Coil to the manual switch line, which i would like to report back to sinric if there is current over 1amp then the light is on and if it is under 1amp then the light is off.

I hope that makes sense...

Any assistance would be very helpful.

Edd

eddcaton commented 6 years ago

I have got this code, but i cannot control the relay... now

/*
 Version 0.3 - March 06 2018
*/ 
#include "EmonLib.h"
EnergyMonitor emon1;
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsClient.h> //  https://github.com/kakopappa/sinric/wiki/How-to-add-dependency-libraries
#include <ArduinoJson.h> // https://github.com/kakopappa/sinric/wiki/How-to-add-dependency-libraries
#include <StreamString.h>

ESP8266WiFiMulti WiFiMulti;
WebSocketsClient webSocket;
WiFiClient client;
const int ledPin = D1;
#define MyApiKey "******" // TODO: Change to your sinric API Key. Your API Key is displayed on sinric.com dashboard
#define MySSID "*****" // TODO: Change to your Wifi network SSID
#define MyWifiPassword "****" // TODO: Change to your Wifi network password

#define HEARTBEAT_INTERVAL 300000 // 5 Minutes 

uint64_t heartbeatTimestamp = 0;
bool isConnected = false;

void setPowerStateOnServer(String deviceId, String value);
//void setTargetTemperatureOnServer(String deviceId, String value, String scale);

// deviceId is the ID assgined to your smart-home-device in sinric.com dashboard. Copy it from dashboard and paste it here

void turnOn(String deviceId) {
  if (deviceId == "****") // Device ID of first device
  {  
    Serial.print("Turn on device id: ");
    Serial.println(deviceId);
     digitalWrite(ledPin, LOW);
  } 
  else 
  {
    Serial.print("Turn on for unknown device id: ");
    Serial.println(deviceId);   
  }     
}

void turnOff(String deviceId) {
   if (deviceId == "***") // Device ID of first device
   {  
     Serial.print("Turn off Device ID: ");
     Serial.println(deviceId);
     digitalWrite(ledPin, HIGH);
   }
  else 
  {
     Serial.print("Turn off for unknown device id: ");
     Serial.println(deviceId);    
  }
}

void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
  switch(type) {
    case WStype_DISCONNECTED:
      isConnected = false;    
      Serial.printf("[WSc] Webservice disconnected from sinric.com!\n");
      break;
    case WStype_CONNECTED: {
      isConnected = true;
      Serial.printf("[WSc] Service connected to sinric.com at url: %s\n", payload);
      Serial.printf("Waiting for commands from sinric.com ...\n");        
      }
      break;
    case WStype_TEXT: {
        Serial.printf("[WSc] get text: %s\n", payload);
        // Example payloads

        // For Switch or Light device types
        // {"deviceId": xxxx, "action": "setPowerState", value: "ON"} // https://developer.amazon.com/docs/device-apis/alexa-powercontroller.html

        // For Light device type
        // Look at the light example in github

        DynamicJsonBuffer jsonBuffer;
        JsonObject& json = jsonBuffer.parseObject((char*)payload); 
        String deviceId = json ["deviceId"];     
        String action = json ["action"];

        if(action == "setPowerState") { // Switch or Light
            String value = json ["value"];
            if(value == "ON") {
                turnOn(deviceId);
            } else {
                turnOff(deviceId);
            }
        }
        else if (action == "SetTargetTemperature") {
            String deviceId = json ["deviceId"];     
            String action = json ["action"];
            String value = json ["value"];
        }
        else if (action == "test") {
            Serial.println("[WSc] received test command from sinric.com");
        }
      }
      break;
    case WStype_BIN:
      Serial.printf("[WSc] get binary length: %u\n", length);
      break;
  }
}

void setup() {
  Serial.begin(115200);
  emon1.current(A0, 111.1);             // Current: input pin, calibration.
  WiFiMulti.addAP(MySSID, MyWifiPassword);
  Serial.println();
  Serial.print("Connecting to Wifi: ");
  Serial.println(MySSID);  

  // Waiting for Wifi connect
  while(WiFiMulti.run() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  if(WiFiMulti.run() == WL_CONNECTED) {
    Serial.println("");
    Serial.print("WiFi connected. ");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
  }

  pinMode(ledPin, OUTPUT); // initialize digital ledPin as an output.
  delay(10);
  digitalWrite(ledPin, HIGH); // Wemos BUILTIN_LED is active Low, so high is off

  // server address, port and URL
  webSocket.begin("iot.sinric.com", 80, "/");

  // event handler
  webSocket.onEvent(webSocketEvent);
  webSocket.setAuthorization("apikey", MyApiKey);

  // try again every 5000ms if connection has failed
  webSocket.setReconnectInterval(5000);   // If you see 'class WebSocketsClient' has no member named 'setReconnectInterval' error update arduinoWebSockets
}
void loop() {
  webSocket.loop();
  int volts = 1;
  int Irms = emon1.calcIrms(1480);  // Calculate Irms only
  Serial.println(Irms);  
  if (Irms > volts)
  {
    setPowerStateOnServer("***", "ON");
  }
  else if (Irms < volts)
  {
    setPowerStateOnServer("***", "OFF");

  }
  if(isConnected) {
      uint64_t now = millis();

      // Send heartbeat in order to avoid disconnections during ISP resetting IPs over night. Thanks @MacSass
      if((now - heartbeatTimestamp) > HEARTBEAT_INTERVAL) {
          heartbeatTimestamp = now;
          webSocket.sendTXT("H");          
      }
  }   
}

// If you are going to use a push button to on/off the switch manually, use this function to update the status on the server
// so it will reflect on Alexa app.
// eg: setPowerStateOnServer("deviceid", "ON")
void setPowerStateOnServer(String deviceId, String value) {
  DynamicJsonBuffer jsonBuffer;
  JsonObject& root = jsonBuffer.createObject();
  root["deviceId"] = deviceId;
  root["action"] = "setPowerState";
  root["value"] = value;
  StreamString databuf;
  root.printTo(databuf);

  webSocket.sendTXT(databuf);
}
kakopappa commented 6 years ago

use the setPowerStateOnServer function in the above sketch to update the on/off status in the server.

eddcaton commented 6 years ago

I think i am having issues with this piece of code as sinric is not getting updated `int volts = 1; int Irms = emon1.calcIrms(1480); // Calculate Irms only Serial.println(Irms);
if (Irms > volts) { setPowerStateOnServer("", "ON"); } else if (Irms < volts) { setPowerStateOnServer("", "OFF");

}`

eddcaton commented 6 years ago

Hey i have changed some of the coding: `void loop() { webSocket.loop(); float volts = 1.0; float Irms = emon1.calcIrms(1480); // Calculate Irms only Serial.println(Irms, 2); // print 2 decimal places if (Irms >= volts) { setPowerStateOnServer("", "ON"); } else { setPowerStateOnServer("", "OFF");

}

`

But for some reason Sinric does not seem to update.

I have checked alexa app and it has not changed the status of the light.

kakopappa commented 6 years ago

as you know, loop() gets executed in a loop very fast. You are sending either on or off status to server too often. This is bad. very bad. This will slow down the server a lot. Please fix this

eddcaton commented 6 years ago

Okay, this is fixed. Here is my current code, it seems to update the server on power up only. can you advise? `/ Version 0.3 - March 06 2018 /

include "EmonLib.h"

EnergyMonitor emon1;

include

include

include

include // https://github.com/kakopappa/sinric/wiki/How-to-add-dependency-libraries

include // https://github.com/kakopappa/sinric/wiki/How-to-add-dependency-libraries

include

ESP8266WiFiMulti WiFiMulti; WebSocketsClient webSocket; WiFiClient client; const int ledPin = D1;

define MyApiKey "" // TODO: Change to your sinric API Key. Your API Key is displayed on sinric.com dashboard

define MySSID "" // TODO: Change to your Wifi network SSID

define MyWifiPassword "" // TODO: Change to your Wifi network password

define HEARTBEAT_INTERVAL 300000 // 5 Minutes

uint64_t heartbeatTimestamp = 0; bool isConnected = false;

void setPowerStateOnServer(String deviceId, String value); //void setTargetTemperatureOnServer(String deviceId, String value, String scale);

// deviceId is the ID assgined to your smart-home-device in sinric.com dashboard. Copy it from dashboard and paste it here

void turnOn(String deviceId) {

if (deviceId == "") // Device ID of first device {
Serial.print("Turn on device id: "); Serial.println(deviceId); digitalWrite(ledPin, LOW); } else { Serial.print("Turn on for unknown device id: "); Serial.println(deviceId);
}
}

void turnOff(String deviceId) { if (deviceId == "") // Device ID of first device {
Serial.print("Turn off Device ID: "); Serial.println(deviceId); digitalWrite(ledPin, HIGH); } else { Serial.print("Turn off for unknown device id: "); Serial.println(deviceId);
} }

void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { switch(type) { case WStype_DISCONNECTED: isConnected = false;
Serial.printf("[WSc] Webservice disconnected from sinric.com!\n"); break; case WStype_CONNECTED: { isConnected = true; Serial.printf("[WSc] Service connected to sinric.com at url: %s\n", payload); Serial.printf("Waiting for commands from sinric.com ...\n");
} break; case WStype_TEXT: {

    Serial.printf("[WSc] get text: %s\n", payload);
    // Example payloads

    // For Switch or Light device types
    // {"deviceId": xxxx, "action": "setPowerState", value: "ON"} // https://developer.amazon.com/docs/device-apis/alexa-powercontroller.html

    // For Light device type
    // Look at the light example in github

    DynamicJsonBuffer jsonBuffer;
    JsonObject& json = jsonBuffer.parseObject((char*)payload); 
    String deviceId = json ["deviceId"];     
    String action = json ["action"];

    if(action == "setPowerState") { // Switch or Light
        String value = json ["value"];

        if(value == "ON") {
            turnOn(deviceId);
        } else {
            turnOff(deviceId);
        }
    }
    else if (action == "test") {
        Serial.println("[WSc] received test command from sinric.com");
    }

       //setPowerStateOnServer("", "ON");

float volts = 1.0; float Irms = emon1.calcIrms(1480); // Calculate Irms only // //Serial.println(Irms, 2); // print 2 decimal places if (Irms >= volts) {

setPowerStateOnServer("", "ON");
Serial.print("Light Is On");

} else { setPowerStateOnServer("", "OFF"); Serial.print("Light Is Off");

}

  }
  break;
case WStype_BIN:
  Serial.printf("[WSc] get binary length: %u\n", length);
  break;

} }

void setup() { Serial.begin(115200); emon1.current(A0, 111.1); WiFiMulti.addAP(MySSID, MyWifiPassword); Serial.println(); Serial.print("Connecting to Wifi: "); Serial.println(MySSID);

// Waiting for Wifi connect while(WiFiMulti.run() != WL_CONNECTED) { delay(500); Serial.print("."); } if(WiFiMulti.run() == WL_CONNECTED) { Serial.println(""); Serial.print("WiFi connected. "); Serial.print("IP address: "); Serial.println(WiFi.localIP()); }

pinMode(ledPin, OUTPUT); // initialize digital ledPin as an output. delay(10); digitalWrite(ledPin, HIGH); // Wemos BUILTIN_LED is active Low, so high is off

// server address, port and URL webSocket.begin("iot.sinric.com", 80, "/");

// event handler webSocket.onEvent(webSocketEvent); webSocket.setAuthorization("apikey", MyApiKey);

// try again every 5000ms if connection has failed webSocket.setReconnectInterval(5000); // If you see 'class WebSocketsClient' has no member named 'setReconnectInterval' error update arduinoWebSockets

} void loop() { webSocket.loop();

if(isConnected) { uint64_t now = millis();

  // Send heartbeat in order to avoid disconnections during ISP resetting IPs over night. Thanks @MacSass
  if((now - heartbeatTimestamp) > HEARTBEAT_INTERVAL) {
      heartbeatTimestamp = now;
      webSocket.sendTXT("H");          
  }

}

}

// If you are going to use a push button to on/off the switch manually, use this function to update the status on the server // so it will reflect on Alexa app.

void setPowerStateOnServer(String deviceId, String value) { DynamicJsonBuffer jsonBuffer; JsonObject& root = jsonBuffer.createObject(); root["deviceId"] = deviceId; root["action"] = "setPowerState"; root["value"] = value; StreamString databuf; root.printTo(databuf);

webSocket.sendTXT(databuf); } `

amitabh-srivastav commented 5 years ago

Dear @eddcaton, good work done. However, the use of variable name volts is confusing (e.g. float volts = 1.0;). You are measuring/ comparing current and not voltage. By the way, What is the CT coil specification?

philb1192 commented 5 years ago

Hi, @eddcaton I would like to know if you had it working. I'm trying to build an Alexa switch with an esp8266 with a physical switch to update Sinric by sending 5V to an input of the esp8266. I've been looking everywhere and your code is the closest of what I'm trying to build. Where did you connect your coil?

eddcaton commented 5 years ago

Hey @philb1192, I couldn't get the CT Coil to work, it constantly gave false reading back. I have however got the system working with a 240v Relay which is switched on when the light is on. Then i have connected the NO and Common on the relay to the node MCU which updates Sinric if the light is on or off ever 5 minutes.

philb1192 commented 5 years ago

Thx @eddcaton I have my project working! Im using a GPIO as an input to receive the signal from a physical switch and compare it to the device. After I did a small truth table to turn on a relay that will activate my light when one or another is on.

void loop() { if (digitalRead(device_1) == LOW && digitalRead(threeway_IN) == LOW) { digitalWrite(relay_OUT, LOW); setPowerStateOnServer("", "OFF"); Serial.print("Light Is Off"); }

if (digitalRead(device_1) == HIGH && digitalRead(threeway_IN) == LOW) { digitalWrite(relay_OUT, HIGH); setPowerStateOnServer("", "ON"); Serial.print("Light Is On"); }

if (digitalRead(device_1) == LOW && digitalRead(threeway_IN) == HIGH) { digitalWrite(relay_OUT, HIGH); setPowerStateOnServer("", "ON"); Serial.print("Light Is On"); }

if (digitalRead(device_1) == HIGH && digitalRead(threeway_IN) == HIGH) { digitalWrite(relay_OUT, LOW); setPowerStateOnServer("", "OFF"); Serial.print("Light Is Off"); }

kakopappa commented 5 years ago

Hi,

I am not sure about you setup but please make sure you don’t send the status update to server on every loop. This will slow the server and relay responding to other 900 devices connected

On Sat, 13 Apr 2019 at 8:05 PM philb1192 notifications@github.com wrote:

Thx @eddcaton https://github.com/eddcaton I have my project working! Im using a GPIO as an input to receive the signal from a physical switch and compare it to the device. After I did a small truth table to turn on a relay that will activate my light when one or another is on.

void loop() { if (digitalRead(device_1) == LOW && digitalRead(threeway_IN) == LOW) { digitalWrite(relay_OUT, LOW); setPowerStateOnServer("", "OFF"); Serial.print("Light Is Off"); }

if (digitalRead(device_1) == HIGH && digitalRead(threeway_IN) == LOW) { digitalWrite(relay_OUT, HIGH); setPowerStateOnServer("", "ON"); Serial.print("Light Is On"); }

if (digitalRead(device_1) == LOW && digitalRead(threeway_IN) == HIGH) { digitalWrite(relay_OUT, HIGH); setPowerStateOnServer("", "ON"); Serial.print("Light Is On"); }

if (digitalRead(device_1) == HIGH && digitalRead(threeway_IN) == HIGH) { digitalWrite(relay_OUT, LOW); setPowerStateOnServer("", "OFF"); Serial.print("Light Is Off"); }

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/kakopappa/sinric/issues/131#issuecomment-482807537, or mute the thread https://github.com/notifications/unsubscribe-auth/AHIM5g_1gR2QHBKTmXUDQyNIaPMnCM-bks5vgdYIgaJpZM4XLgBh .

philb1192 commented 5 years ago

Hi kakopappa, in a nutshell, what I'm trying to do is to have an external switch connected to the esp8266. When I press it, the light turns on, but the status in sinric doesn't change. Consequently, I have to tell Alexa to turn on the light to turn it off.

I'm fairly new in coding and I don't get what you mean by ''don’t send the status update to server on every loop''. Could you look my code and point me in the right direction? Thx.

void loop() {

if (digitalRead(device_1) == LOW && digitalRead(threeway_IN) == LOW) { digitalWrite(relay_OUT, LOW); Serial.print("Light Is Off / D0,T0"); Serial.print('\n'); }

if (digitalRead(device_1) == HIGH && digitalRead(threeway_IN) == LOW) { digitalWrite(relay_OUT, HIGH); Serial.print("Light Is On / D1,T0"); Serial.print('\n'); }

if (digitalRead(device_1) == LOW && digitalRead(threeway_IN) == HIGH) { digitalWrite(relay_OUT, HIGH); Serial.print("Light Is On / D0,T1"); Serial.print('\n'); }

if (digitalRead(device_1) == HIGH && digitalRead(threeway_IN) == HIGH) { digitalWrite(relay_OUT, LOW); Serial.print("Light Is Off / D1,T1"); Serial.print('\n'); }

if (digitalRead(relay_OUT) == HIGH) { setPowerStateOnServer("", "ON"); Serial.print("Server On"); Serial.print('\n'); }

else { setPowerStateOnServer("", "OFF"); Serial.print("Server Off"); Serial.print('\n'); }

kakopappa commented 5 years ago

if (digitalRead(relay_OUT) == HIGH) { setPowerStateOnServer("", "ON"); Serial.print("Server On"); Serial.print('\n'); }

else

{ setPowerStateOnServer("", "OFF"); Serial.print("Server Off"); Serial.print('\n'); }

If you look at this logic you are sending OFF to the server regardless whether user pushed the button or not. Arduino loop sends about 1000 messages to the server per second and the server cpu goes high updating your status on the database. That’s what I mean by don’t send it in the loop

Take a look at the push button example code in the repo

On Tue, 25 Jun 2019 at 9:04 AM philb1192 notifications@github.com wrote:

Hi kakopappa, in a nutshell, what I'm trying to do is to have an external switch connected to the esp8266. When I press it, the light turns on, but the status in sinric doesn't change. Consequently, I have to tell Alexa to turn on the light to turn it off.

I'm fairly new in coding and I don't get what you mean by ''don’t send the status update to server on every loop''. Could you look my code and point me in the right direction? Thx.

void loop() {

if (digitalRead(device_1) == LOW && digitalRead(threeway_IN) == LOW) { digitalWrite(relay_OUT, LOW); Serial.print("Light Is Off / D0,T0"); Serial.print('\n'); }

if (digitalRead(device_1) == HIGH && digitalRead(threeway_IN) == LOW) { digitalWrite(relay_OUT, HIGH); Serial.print("Light Is On / D1,T0"); Serial.print('\n'); }

if (digitalRead(device_1) == LOW && digitalRead(threeway_IN) == HIGH) { digitalWrite(relay_OUT, HIGH); Serial.print("Light Is On / D0,T1"); Serial.print('\n'); }

if (digitalRead(device_1) == HIGH && digitalRead(threeway_IN) == HIGH) { digitalWrite(relay_OUT, LOW); Serial.print("Light Is Off / D1,T1"); Serial.print('\n'); }

if (digitalRead(relay_OUT) == HIGH) { setPowerStateOnServer("", "ON"); Serial.print("Server On"); Serial.print('\n'); }

else { setPowerStateOnServer("", "OFF"); Serial.print("Server Off"); Serial.print('\n'); }

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/kakopappa/sinric/issues/131?email_source=notifications&email_token=ABZAZZTFOUHYGPKMXGMSVIDP4F4MNA5CNFSM4FZOABQ2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODYOXRVI#issuecomment-505247957, or mute the thread https://github.com/notifications/unsubscribe-auth/ABZAZZQBDSL5YBGH7VYFH3LP4F4MNANCNFSM4FZOABQQ .