Skip to content

Commit 4f74318

Browse files
authored
Merge pull request #6 from sinricpro/dev-2.2.3
2.2.3
2 parents 4b9b2a0 + a79fa1b commit 4f74318

19 files changed

+370
-96
lines changed

README.md

+57-56
Large diffs are not rendered by default.

examples/GarageDoor/GarageDoor.ino

+179
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
/*
2+
* This example needs SinricPro 2.2.2
3+
*
4+
* If you encounter any issues:
5+
* - enable serial debug (see section below)
6+
* - open serial monitor and check whats happening
7+
* - visit https://github.com/sinricpro/esp8266-esp32-sdk/issues and check for existing issues or open a new one
8+
*/
9+
10+
// Uncomment the following line to enable serial debug output
11+
//#define ENABLE_DEBUG
12+
13+
#ifdef ENABLE_DEBUG
14+
#define DEBUG_ESP_PORT Serial
15+
#define NODEBUG_WEBSOCKETS
16+
#define NDEBUG
17+
#endif
18+
19+
#include <Arduino.h>
20+
#ifdef ESP8266
21+
#include <ESP8266WiFi.h>
22+
#endif
23+
#ifdef ESP32
24+
#include <WiFi.h>
25+
#endif
26+
27+
#include "SinricPro.h"
28+
#include "SinricProLock.h"
29+
30+
#define WIFI_SSID "YOUR_WIFI_SSID"
31+
#define WIFI_PASS "YOUR_WIFI_PASSWORD"
32+
#define APP_KEY "YOUR_APP_KEY_HERE" // Should look like "de0bxxxx-1x3x-4x3x-ax2x-5dabxxxxxxxx"
33+
#define APP_SECRET "YOUR_APP_SECRET_HERE" // Should look like "5f36xxxx-x3x7-4x3x-xexe-e86724a9xxxx-4c4axxxx-3x3x-x5xe-x9x3-333d65xxxxxx"
34+
#define GARAGEDOOR_ID "YOUR_DEVICE_ID_HERE" // Should look like "5dc1564130xxxxxxxxxxxxxx"
35+
#define BAUD_RATE 9600 // Change baudrate to your need
36+
37+
#define RELAY D5 // PIN where relay is attachted to
38+
#define ENDSTOP_OPEN D6 // PIN where open position reedswitch is connected to (high = door is open | low = door is NOT open)
39+
#define ENDSTOP_CLOSED D7 // PIN where close position reedswitch is connected to (high = door is closed | low = door is NOT closed)
40+
#define MOVE_TIMEOUT 20000 // Timout (20 seconds) for door movement -> please use a value which fits to your open/close time but not more than 20 seconds
41+
#define RELAY_TIME 4000 // Relay high time (4 seconds) - usually between 2500 and 5000
42+
43+
44+
// Door state definitions
45+
#define DOOR_MOVING 0 // Door is not in close and not in open position...somewere between, might be moving
46+
#define DOOR_OPEN 1 // Door is in open position
47+
#define DOOR_CLOSED 2 // Door is in closed position
48+
#define DOOR_MALFUNCTION 3 // Malfunction! Door can't be open and closed at the same time
49+
50+
/* function getDoorState()
51+
* returs door state (see section above)
52+
*/
53+
int getDoorState() {
54+
if (digitalRead(ENDSTOP_CLOSED)==false && digitalRead(ENDSTOP_OPEN)==false) return DOOR_MOVING;
55+
if (digitalRead(ENDSTOP_CLOSED)==false && digitalRead(ENDSTOP_OPEN)==true) return DOOR_OPEN;
56+
if (digitalRead(ENDSTOP_CLOSED)==true && digitalRead(ENDSTOP_OPEN)==false) return DOOR_CLOSED;
57+
return DOOR_MALFUNCTION;
58+
}
59+
60+
int lastDoorState; // last known doorstate
61+
62+
/* turnOnRelayAndWait
63+
*
64+
* function: turn on relay for a given time and waits until endstop gets triggered or timeout happened
65+
*
66+
* input: relay_pin = pin which is RELAY connected to
67+
* relay_time = time in milliseconds to keep RELAY high
68+
* endstop_pin = pin which is MAGNET connected to
69+
* endstop_timeout = timeout time in milliseconds
70+
*
71+
* return: true if enstop is triggered within timeout time
72+
* false if endstop was not triggered within timeout time
73+
*/
74+
bool turnOnRelayAndWait(int relay_pin, unsigned long relay_time, int endstop_pin, unsigned long endstop_timeout) {
75+
unsigned long startMillis = millis();
76+
77+
bool timeout = false; // timeout flag
78+
bool success = false; // success flag
79+
Serial.printf("RELAY: HIGH\r\n");
80+
81+
digitalWrite(relay_pin, HIGH); // turn on relay
82+
bool relay_High = true; // relay flag
83+
84+
while (success==false && timeout==false) { // while operation is not completed and within timeout time
85+
unsigned long actualMillis = millis();
86+
87+
success = digitalRead(endstop_pin); // check endstop
88+
timeout = (actualMillis - startMillis >= endstop_timeout); // check for timeout
89+
90+
// if relay_time is reached, turn off relay
91+
if (relay_High == true && actualMillis - startMillis >= relay_time){
92+
Serial.printf("RELAY: LOW\r\n");
93+
digitalWrite(relay_pin, LOW);
94+
relay_High = false;
95+
}
96+
yield(); // make sure to keep esp stay alive ;)
97+
}
98+
digitalWrite(relay_pin, LOW); // turn off relay, just to be safe!
99+
lastDoorState = getDoorState(); // update lastDoorState to prevent event sending
100+
return success;
101+
}
102+
103+
bool onLockState(String deviceId, bool &lockState) {
104+
int doorState = getDoorState();
105+
if (doorState == DOOR_MALFUNCTION){
106+
Serial.printf("Malfunction! Door is reporting to be open and closed at the same time!\r\n");
107+
SinricPro.setResponseMessage("Error: malfunction!");
108+
return false;
109+
}
110+
bool success = false;
111+
if (lockState) { // close door
112+
if (doorState == DOOR_CLOSED) return true; // door is allready closed return true
113+
Serial.printf("Closing door...\r\n");
114+
success = turnOnRelayAndWait(RELAY, RELAY_TIME, ENDSTOP_CLOSED, MOVE_TIMEOUT); // close the door
115+
Serial.printf("%s\r\n", success?"Door is closed now.":"Error! Door did not close properly (timeout)!");
116+
} else { // open door
117+
if (doorState == DOOR_OPEN) return true; // door is allready open...return true
118+
Serial.printf("Opening door...\r\n");
119+
success = turnOnRelayAndWait(RELAY, RELAY_TIME, ENDSTOP_OPEN, MOVE_TIMEOUT); // open the door
120+
Serial.printf("%s\r\n", success?"Door is open now.":"Error! Door did not open properly (timeout)!");
121+
}
122+
if (!success) SinricPro.setResponseMessage("Error: timeout!");
123+
return success;
124+
}
125+
126+
void handleManualDoorState() {
127+
int actualDoorState = getDoorState();
128+
if (actualDoorState != lastDoorState) {
129+
SinricProLock& garageDoor = SinricPro[GARAGEDOOR_ID];
130+
switch (actualDoorState) {
131+
case DOOR_OPEN : Serial.printf("Door has been opened manually.\r\n");
132+
garageDoor.sendLockStateEvent(false);
133+
break;
134+
case DOOR_CLOSED : garageDoor.sendLockStateEvent(true);
135+
Serial.printf("Door has been closed manually.\r\n");
136+
break;
137+
case DOOR_MOVING : Serial.printf("Door is moving.\r\n");
138+
break;
139+
default : Serial.printf("WARNING! DOOR HAS A MALFUNCTION!\r\n");
140+
break;
141+
}
142+
lastDoorState = actualDoorState;
143+
}
144+
}
145+
146+
147+
void setupWiFi() {
148+
Serial.printf("\r\n[Wifi]: Connecting");
149+
WiFi.begin(WIFI_SSID, WIFI_PASS);
150+
151+
while (WiFi.status() != WL_CONNECTED) {
152+
Serial.printf(".");
153+
delay(250);
154+
}
155+
IPAddress localIP = WiFi.localIP();
156+
Serial.printf("connected!\r\n[WiFi]: IP-Address is %d.%d.%d.%d\r\n", localIP[0], localIP[1], localIP[2], localIP[3]);
157+
}
158+
159+
void setupSinricPro() {
160+
SinricProLock &garageDoor = SinricPro[GARAGEDOOR_ID];
161+
garageDoor.onLockState(onLockState);
162+
163+
SinricPro.begin(APP_KEY, APP_SECRET);
164+
}
165+
166+
void setup() {
167+
Serial.begin(BAUD_RATE);
168+
pinMode(RELAY, OUTPUT);
169+
pinMode(ENDSTOP_OPEN, INPUT);
170+
pinMode(ENDSTOP_CLOSED, INPUT);
171+
lastDoorState = getDoorState();
172+
setupWiFi();
173+
setupSinricPro();
174+
}
175+
176+
void loop() {
177+
SinricPro.handle();
178+
handleManualDoorState();
179+
}
Loading

examples/GarageDoor/README.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# GarageDoorExample
2+
![Wiring](https://github.com/sivar2311/GarageDoorExample/raw/master/GarageDoor3_Steckplatine.png)
3+
4+
D5: connected to relay
5+
D6: connected to reed_switch (OPEN ENDSTOP)
6+
D7: connected to reed_switch (CLOSED ENDSTOP)

examples/Switch/Switch.ino

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ unsigned long lastBtnPress = 0;
5555
* return
5656
* true if request should be marked as handled correctly / false if not
5757
*/
58-
bool onPowerState(String deviceId, bool &state) {
58+
bool onPowerState(const String &deviceId, bool &state) {
5959
Serial.printf("Device %s turned %s (via SinricPro) \r\n", deviceId.c_str(), state?"on":"off");
6060
myPowerState = state;
6161
digitalWrite(LED_BUILTIN, myPowerState?LOW:HIGH);

library.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"maintainer": true
1414
}
1515
],
16-
"version": "2.2.2",
16+
"version": "2.2.3",
1717
"frameworks": "arduino",
1818
"platforms": [
1919
"espressif8266",

library.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=SinricPro
2-
version=2.2.2
2+
version=2.2.3
33
author=Boris Jaeger <[email protected]>
44
maintainer=Boris Jaeger <[email protected]>
55
sentence=Library for https://sinric.pro - simple way to connect your device to alexa

pio-examples/switch/src/switch.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ unsigned long lastBtnPress = 0;
4444
* return
4545
* true if request should be marked as handled correctly / false if not
4646
*/
47-
bool onPowerState(String deviceId, bool &state) {
47+
bool onPowerState(const String &deviceId, bool &state) {
4848
Serial.printf("Device %s turned %s (via SinricPro) \r\n", deviceId.c_str(), state?"on":"off");
4949
myPowerState = state;
5050
digitalWrite(LED_BUILTIN, myPowerState?LOW:HIGH);

src/LeakyBucket.h

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright (c) 2019 Sinric. All rights reserved.
3+
* Licensed under Creative Commons Attribution-Share Alike (CC BY-SA)
4+
*
5+
* This file is part of the Sinric Pro (https://github.com/sinricpro/)
6+
*/
7+
8+
9+
#ifndef _LEAKY_BUCKET_H_
10+
#define _LEAKY_BUCKET_H_
11+
12+
class LeakyBucket_t {
13+
public:
14+
LeakyBucket_t() : dropsInBucket(0), lastDrop(-DROP_IN_TIME), once(false) {}
15+
bool addDrop();
16+
private:
17+
void leak();
18+
int dropsInBucket;
19+
unsigned long lastDrop;
20+
bool once;
21+
unsigned long lastWarning;
22+
};
23+
24+
bool LeakyBucket_t::addDrop() {
25+
leak();
26+
unsigned long actualMillis = millis();
27+
28+
if (dropsInBucket < BUCKET_SIZE && actualMillis-lastDrop > dropsInBucket + DROP_IN_TIME) { // new drop can be placed into bucket?
29+
dropsInBucket++; // place drop in bucket
30+
lastDrop = actualMillis; // store last drop time
31+
if (dropsInBucket == BUCKET_SIZE && once==false) {
32+
Serial.printf("[SinricPro]: WARNING: YOU SENT TOO MUCH EVENTS IN A SHORT PERIOD OF TIME!\r\n - PLEASE CHECK YOUR CODE AND SEND EVENTS ONLY IF DEVICE STATE HAS CHANGED!\r\n"); // Print a warning when bucket is full
33+
once = true;
34+
}
35+
return true;
36+
}
37+
38+
if (dropsInBucket >= BUCKET_SIZE) {
39+
if (actualMillis-lastWarning > 1000) {
40+
Serial.printf("[SinricPro]: EVENTS ARE BLOCKED FOR %lu SECONDS!\r\n",(DROP_OUT_TIME-(actualMillis-lastDrop))/1000);
41+
lastWarning = actualMillis;
42+
}
43+
}
44+
return false;
45+
}
46+
47+
void LeakyBucket_t::leak() {
48+
// leack bucket...
49+
unsigned long actualMillis = millis();
50+
int drops_to_leak = (actualMillis - lastDrop) / DROP_OUT_TIME;
51+
if (drops_to_leak > 0) {
52+
if (dropsInBucket <= drops_to_leak) {
53+
dropsInBucket = 0;
54+
} else {
55+
dropsInBucket -= drops_to_leak;
56+
}
57+
}
58+
}
59+
60+
61+
#endif

src/SinricProConfig.h

+21-4
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,37 @@
77

88
#ifndef __SINRICPRO_CONFIG_H__
99
#define __SINRICPRO_CONFIG_H__
10+
/*
11+
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
12+
* !! !!
13+
* !! WARNING: DON'T TOUCH ! !!
14+
* !! ====================== !!
15+
* !! PLEASE DO NOT MODIFY ANY OF THESE SETTINGS HERE !!
16+
* !! THIS IS FOR INTERNAL CONFIGURATION ONLY !!
17+
* !! SINRIC PRO MIGHT NOT WORK IF YOU MODIFY THIS !!
18+
* !! !!
19+
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
20+
*/
1021

11-
#define SDK_VERSION "2.2.2"
22+
// Version Configuration
23+
#define SDK_VERSION "2.2.3"
1224

25+
// Server Configuration
1326
#define SINRICPRO_SERVER_URL "ws.sinric.pro"
1427
#define SINRICPRO_SERVER_PORT 80
1528

29+
// UDP Configuration
1630
#define UDP_MULTICAST_IP IPAddress(224,9,9,9)
1731
#define UDP_MULTICAST_PORT 3333
1832

19-
// websocket sends every WEBSOCKET_PING_INTERVAL milliseconds a ping to Server
20-
// if there is no pong received after WEBSOCKET_PING_TIMEOUT milliseconds, retry count is incremented by one
21-
// if retry count reaches WEBSOCKET_RETRY_COUNT websocket connection is closed and try to reconnect server
33+
// WebSocket Configuration
2234
#define WEBSOCKET_PING_INTERVAL 300000
2335
#define WEBSOCKET_PING_TIMEOUT 10000
2436
#define WEBSOCKET_RETRY_COUNT 2
2537

38+
// LeakyBucket Configuration
39+
#define BUCKET_SIZE 10
40+
#define DROP_OUT_TIME 60000
41+
#define DROP_IN_TIME 1000u
42+
2643
#endif

src/SinricProDevice.h

+20-10
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#define _SINRICDEVICE_H_
1010

1111
#include "SinricProDeviceInterface.h"
12+
#include "LeakyBucket.h"
13+
1214
#include <map>
1315

1416
class SinricProDevice : public SinricProDeviceInterface {
@@ -23,7 +25,7 @@ class SinricProDevice : public SinricProDeviceInterface {
2325
virtual bool handleRequest(const char* deviceId, const char* action, JsonObject &request_value, JsonObject &response_value);
2426

2527
// standard callbacks
26-
typedef std::function<bool(const String, bool&)> PowerStateCallback;
28+
typedef std::function<bool(const String&, bool&)> PowerStateCallback;
2729
virtual void onPowerState(PowerStateCallback cb) { powerStateCallback = cb; }
2830

2931
// standard events
@@ -37,7 +39,7 @@ class SinricProDevice : public SinricProDeviceInterface {
3739
private:
3840
SinricProInterface* eventSender;
3941
unsigned long eventWaitTime;
40-
std::map<String, unsigned long> eventFilter;
42+
std::map<String, LeakyBucket_t> eventFilter;
4143
};
4244

4345
SinricProDevice::SinricProDevice(const char* newDeviceId, unsigned long eventWaitTime) :
@@ -81,22 +83,30 @@ DynamicJsonDocument SinricProDevice::prepareEvent(const char* deviceId, const ch
8183
return DynamicJsonDocument(1024);
8284
}
8385

86+
8487
bool SinricProDevice::sendEvent(JsonDocument& event) {
85-
unsigned long actualMillis = millis();
8688
String eventName = event["payload"]["action"] | ""; // get event name
8789

88-
if (eventFilter.find(eventName) == eventFilter.end()) { // if eventFilter is not initialized
89-
eventFilter[eventName] = -eventWaitTime; // initialize eventFilter
90+
LeakyBucket_t bucket; // leaky bucket algorithm is used to prevent flooding the server
91+
92+
// get leaky bucket for event from eventFilter
93+
if (eventFilter.find(eventName) == eventFilter.end()) { // if there is no bucket ...
94+
eventFilter[eventName] = bucket; // ...add a new bucket
95+
} else {
96+
bucket = eventFilter[eventName]; // else get bucket
9097
}
9198

92-
unsigned long lastEventMillis = eventFilter[eventName] | 0; // get the last timestamp for event
93-
if (actualMillis - lastEventMillis < eventWaitTime) return false; // if last event was before waitTime return...
99+
if (bucket.addDrop()) { // if we can add a new drop
100+
if (eventSender) eventSender->sendMessage(event); // send event
101+
eventFilter[eventName] = bucket; // update bucket on eventFilter
102+
return true;
103+
}
94104

95-
if (eventSender) eventSender->sendMessage(event); // send event
96-
eventFilter[eventName] = actualMillis; // update lastEventTime
97-
return true;
105+
eventFilter[eventName] = bucket; // update bucket on eventFilter
106+
return false;
98107
}
99108

109+
100110
bool SinricProDevice::sendPowerStateEvent(bool state, String cause) {
101111
DynamicJsonDocument eventMessage = prepareEvent(deviceId, "setPowerState", cause.c_str());
102112
JsonObject event_value = eventMessage["payload"]["value"];

0 commit comments

Comments
 (0)