Skip to content

Commit 1b5a882

Browse files
authored
Merge pull request #418 from sinricpro/feat-start-stop-controller-for-gh
feat: start/stop controller
2 parents cd4f80a + 62540f8 commit 1b5a882

File tree

6 files changed

+482
-28
lines changed

6 files changed

+482
-28
lines changed

changelog.md

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
# Changelog
22

3-
## Version 3.4.1
3+
## Version 3.5.0
44
New:
5-
- Fix: invalid signature error message.
5+
- Support Start/Stop and Open/Close capabilities.
6+
7+
## Version 3.4.1
8+
Fixed:
9+
- Fix: invalid signature error message.
610

711
## Version 3.4.0
812
New:

library.json

+13-23
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,38 @@
11
{
22
"name": "SinricPro",
3-
"keywords": "ethernet, sinric, alexa, iot",
4-
"description": "Library for https://sinric.pro - simple way to connect your device to alexa",
3+
"keywords": "sinric, alexa, google home, iot",
4+
"description": "Library for https://sinric.pro - simple way to connect your device to alexa and google home",
55
"repository": {
66
"type": "git",
77
"url": "https://github.com/sinricpro/esp8266-esp32-sdk"
88
},
99
"authors": [
10+
{
11+
"name": "SinricPro",
12+
"url": "https://sinric.pro"
13+
},
1014
{
1115
"name": "Boris Jaeger",
1216
"email": "[email protected]",
1317
"maintainer": true
1418
}
1519
],
16-
"version": "3.4.1",
20+
"homepage": "https://sinric.pro",
21+
"version": "3.5.0",
1722
"frameworks": "arduino",
18-
"platforms": [
19-
"espressif8266",
20-
"espressif32",
21-
"raspberrypi"
22-
],
23+
"platforms": ["espressif8266", "espressif32", "raspberrypi"],
2324
"dependencies": [
2425
{
2526
"name": "ArduinoJson",
2627
"version": "^7.0.3",
27-
"platforms": [
28-
"espressif32",
29-
"espressif8266",
30-
"raspberrypi"
31-
]
28+
"platforms": ["espressif32", "espressif8266", "raspberrypi"]
3229
},
3330
{
3431
"name": "WebSockets",
3532
"version": "^2.4.0",
36-
"platforms": [
37-
"espressif32",
38-
"espressif8266",
39-
"raspberrypi"
40-
]
33+
"platforms": ["espressif32", "espressif8266", "raspberrypi"]
4134
}
4235
],
43-
"examples": [
44-
"examples/*/*.ino",
45-
"examples/*/*/*.ino"
46-
],
36+
"examples": ["examples/*/*.ino", "examples/*/*/*.ino"],
4737
"license": "CC-BY-SA-4.0"
48-
}
38+
}

library.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=SinricPro
2-
version=3.4.1
2+
version=3.5.0
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
+285
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
#pragma once
2+
3+
#include "../SinricProRequest.h"
4+
#include "../EventLimiter.h"
5+
#include "../SinricProStrings.h"
6+
7+
#include "../SinricProNamespace.h"
8+
namespace SINRICPRO_NAMESPACE {
9+
10+
FSTR(OPEN_CLOSE, openPercent); // "openPercent"
11+
FSTR(OPEN_CLOSE, openRelativePercent); // "openRelativePercent"
12+
FSTR(OPEN_CLOSE, openDirection); // "openDirection"
13+
FSTR(OPEN_CLOSE, setOpenClose); // "setOpenClose"
14+
FSTR(OPEN_CLOSE, adjustOpenClose); // "adjustOpenClose"
15+
16+
17+
/**
18+
* @brief Callback definition for onOpenClose callback
19+
*
20+
* Gets called when device receives a `setOpenClose` request
21+
* @param[in] deviceId String containing the ID of device
22+
* @param[in,out] openPercent Integer percentage (0-100) of how open the device should be set (0 = closed, 100 = fully open)
23+
* @return Success status of the request
24+
* @retval true Request handled successfully
25+
* @retval false Request handling failed
26+
*
27+
* @section OpenCloseCallback Example-Code
28+
* @snippet callbacks.cpp onSetOpenClose
29+
**/
30+
using OpenCloseCallback = std::function<bool(const String &, int &)>;
31+
32+
/**
33+
* @brief Callback definition for onAdjustOpenClose callback
34+
*
35+
* Gets called when device receives an `adjustOpenClose` request
36+
* @param[in] deviceId String containing the ID of device
37+
* @param[in,out] openRelativePercent Integer value representing the relative percentage change to apply
38+
* On output, should contain the absolute percentage value the device has been set to
39+
* @return Success status of the request
40+
* @retval true Request handled successfully
41+
* @retval false Request handling failed
42+
*
43+
* @section AdjustOpenCloseCallback Example-Code
44+
* @snippet callbacks.cpp onAdjustOpenClose
45+
**/
46+
using AdjustOpenCloseCallback = std::function<bool(const String &, int &)>;
47+
48+
/**
49+
* @brief Callback definition for onDirectionOpenClose callback
50+
*
51+
* Gets called when device receives a `setOpenClose` request with direction information
52+
* @param[in] deviceId String containing the ID of device
53+
* @param[in] openDirection String direction in which to open: `UP`, `DOWN`, `LEFT`, `RIGHT`, `IN`, `OUT`
54+
* @param[in,out] openPercent Integer percentage (0-100) of how open the device should be set
55+
* @return Success status of the request
56+
* @retval true Request handled successfully
57+
* @retval false Request handling failed
58+
*
59+
* @section DirectionOpenCloseCallback Example-Code
60+
* @snippet callbacks.cpp onDirectionOpenClose
61+
**/
62+
using DirectionOpenCloseCallback = std::function<bool(const String &, const String &, int &)>;
63+
64+
/**
65+
* @brief Callback definition for onAdjustDirectionOpenClose callback
66+
*
67+
* Gets called when device receives an `adjustOpenClose` request with direction information
68+
* @param[in] deviceId String containing the ID of device
69+
* @param[in] openDirection String direction in which to open: `UP`, `DOWN`, `LEFT`, `RIGHT`, `IN`, `OUT`
70+
* @param[in,out] openRelativePercent Integer representing relative percentage change to apply
71+
* On output, should contain the absolute percentage value the device has been set to
72+
* @return Success status of the request
73+
* @retval true Request handled successfully
74+
* @retval false Request handling failed
75+
*
76+
* @section AdjustDirectionOpenCloseCallback Example-Code
77+
* @snippet callbacks.cpp onAdjustDirectionOpenClose
78+
**/
79+
using AdjustDirectionOpenCloseCallback = std::function<bool(const String &, const String &, int &)>;
80+
81+
/**
82+
* @brief Controller class for devices with open/close functionality
83+
* @ingroup Capabilities
84+
* This controller handles open/close operations for devices that can be opened or closed by percentage and may supports multiple directions.
85+
*
86+
* @tparam T Device type that implements this controller
87+
*/
88+
template <typename T>
89+
class OpenCloseController {
90+
public:
91+
OpenCloseController();
92+
93+
/**
94+
* @brief Set callback function for simple open/close operations
95+
*
96+
* @param cb Function pointer to a `OpenCloseCallback` function
97+
* @return void
98+
* @see OpenCloseCallback
99+
**/
100+
void onOpenClose(OpenCloseCallback cb);
101+
102+
/**
103+
* @brief Set callback function for directional open/close operations
104+
*
105+
* @param cb Function pointer to a `DirectionOpenCloseCallback` function
106+
* @return void
107+
* @see DirectionOpenCloseCallback
108+
**/
109+
void onDirectionOpenClose(DirectionOpenCloseCallback cb);
110+
111+
/**
112+
* @brief Set callback function for relative adjustments to open/close status
113+
*
114+
* @param cb Function pointer to a `AdjustOpenCloseCallback` function
115+
* @return void
116+
* @see AdjustOpenCloseCallback
117+
**/
118+
void onAdjustOpenClose(AdjustOpenCloseCallback cb);
119+
120+
/**
121+
* @brief Set callback function for directional relative adjustments
122+
*
123+
* @param cb Function pointer to a `AdjustDirectionOpenCloseCallback` function
124+
* @return void
125+
* @see AdjustDirectionOpenCloseCallback
126+
**/
127+
void onAdjustDirectionOpenClose(AdjustDirectionOpenCloseCallback cb);
128+
129+
/**
130+
* @brief Send an event to update the open/close status
131+
*
132+
* @param openPercent Current open percentage (0-100)
133+
* @param cause Cause of the event (default: physical interaction)
134+
* @return bool Whether the event was sent successfully
135+
**/
136+
bool sendOpenCloseEvent(int openPercent, String cause = FSTR_SINRICPRO_PHYSICAL_INTERACTION);
137+
138+
/**
139+
* @brief Send an event to update the open/close status with direction
140+
*
141+
* @param openDirection Direction of opening (UP, DOWN, LEFT, RIGHT, IN, OUT)
142+
* @param openPercent Current open percentage (0-100)
143+
* @param cause Cause of the event (default: physical interaction)
144+
* @return bool Whether the event was sent successfully
145+
**/
146+
bool sendOpenCloseEvent(String openDirection, int openPercent, String cause = FSTR_SINRICPRO_PHYSICAL_INTERACTION);
147+
148+
protected:
149+
/**
150+
* @brief Handle incoming open/close control requests
151+
*
152+
* @param request The incoming request to process
153+
* @return bool Whether the request was handled successfully
154+
**/
155+
bool handleOpenCloseController(SinricProRequest &request);
156+
157+
private:
158+
EventLimiter event_limiter;
159+
DirectionOpenCloseCallback directionOpenCloseCallback;
160+
OpenCloseCallback openCloseCallback;
161+
AdjustOpenCloseCallback adjustOpenCloseCallback;
162+
AdjustDirectionOpenCloseCallback adjustDirectionOpenCloseCallback;
163+
};
164+
165+
template <typename T>
166+
OpenCloseController<T>::OpenCloseController()
167+
:event_limiter(EVENT_LIMIT_STATE) {
168+
T* device = static_cast<T*>(this);
169+
device->registerRequestHandler(std::bind(&OpenCloseController<T>::handleOpenCloseController, this, std::placeholders::_1));
170+
}
171+
172+
template <typename T>
173+
void OpenCloseController<T>::onOpenClose(OpenCloseCallback cb) { openCloseCallback = cb; }
174+
175+
template <typename T>
176+
void OpenCloseController<T>::onDirectionOpenClose(DirectionOpenCloseCallback cb) { directionOpenCloseCallback = cb; }
177+
178+
template <typename T>
179+
void OpenCloseController<T>::onAdjustOpenClose(AdjustOpenCloseCallback cb) { adjustOpenCloseCallback = cb; }
180+
181+
template <typename T>
182+
void OpenCloseController<T>::onAdjustDirectionOpenClose(AdjustDirectionOpenCloseCallback cb) { adjustDirectionOpenCloseCallback = cb; }
183+
184+
/**
185+
* @brief Send an event to update open/close status with open direction and precent and information
186+
*
187+
* Sends the current open/close status with direction to the Sinric Pro cloud.
188+
* The event will only be sent if the event rate limiter allows it.
189+
*
190+
* @param openDirection Direction in which the device is opening
191+
* @param openPercent Current open percentage (0-100)
192+
* @param cause Reason for the state change (default: physical interaction)
193+
* @return bool Whether the event was sent successfully
194+
*/
195+
template <typename T>
196+
bool OpenCloseController<T>::sendOpenCloseEvent(String openDirection, int openPercent, String cause) {
197+
if (event_limiter) return false;
198+
T* device = static_cast<T*>(this);
199+
200+
JsonDocument eventMessage = device->prepareEvent(FSTR_OPEN_CLOSE_setOpenClose, cause.c_str());
201+
JsonObject event_value = eventMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_value];
202+
event_value[FSTR_OPEN_CLOSE_openDirection] = openDirection;
203+
event_value[FSTR_OPEN_CLOSE_openPercent] = openPercent;
204+
205+
return device->sendEvent(eventMessage);
206+
}
207+
208+
/**
209+
* @brief Send an event to update open/close status
210+
*
211+
* Sends the current open/close percentage to the Sinric Pro cloud.
212+
* The event will only be sent if the event rate limiter allows it.
213+
*
214+
* @param openPercent Current open percentage (0-100)
215+
* @param cause Reason for the state change (default: physical interaction)
216+
* @return bool Whether the event was sent successfully
217+
*/
218+
template <typename T>
219+
bool OpenCloseController<T>::sendOpenCloseEvent(int openPercent, String cause) {
220+
if (event_limiter) return false;
221+
T* device = static_cast<T*>(this);
222+
223+
JsonDocument eventMessage = device->prepareEvent(FSTR_OPEN_CLOSE_setOpenClose, cause.c_str());
224+
JsonObject event_value = eventMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_value];
225+
event_value[FSTR_OPEN_CLOSE_openPercent] = openPercent;
226+
227+
return device->sendEvent(eventMessage);
228+
}
229+
230+
/**
231+
* @brief Handle incoming open/close requests
232+
*
233+
* Processes both setOpenClose and adjustOpenClose requests with or without direction information.
234+
* Delegates to the appropriate callback function based on the request type and parameters.
235+
*
236+
* @param request The incoming request containing action and parameters
237+
* @return bool Whether the request was handled successfully
238+
*/
239+
template <typename T>
240+
bool OpenCloseController<T>::handleOpenCloseController(SinricProRequest &request) {
241+
T* device = static_cast<T*>(this);
242+
243+
bool success = false;
244+
245+
if (request.action == FSTR_OPEN_CLOSE_setOpenClose) {
246+
bool hasOpenDirection = !request.request_value[FSTR_OPEN_CLOSE_openDirection].isNull();
247+
int openPercent = request.request_value[FSTR_OPEN_CLOSE_openPercent];
248+
249+
if (hasOpenDirection && directionOpenCloseCallback) {
250+
String openDirection = request.request_value[FSTR_OPEN_CLOSE_openDirection];
251+
success = directionOpenCloseCallback(device->deviceId, openDirection, openPercent);
252+
request.response_value[FSTR_OPEN_CLOSE_openDirection] = openDirection;
253+
request.response_value[FSTR_OPEN_CLOSE_openPercent] = openPercent;
254+
} else if (!hasOpenDirection && openCloseCallback) {
255+
success = openCloseCallback(device->deviceId, openPercent);
256+
request.response_value[FSTR_OPEN_CLOSE_openPercent] = openPercent;
257+
}
258+
259+
return success;
260+
}
261+
262+
if (request.action == FSTR_OPEN_CLOSE_adjustOpenClose) {
263+
bool hasOpenDirection = !request.request_value[FSTR_OPEN_CLOSE_openDirection].isNull();
264+
int openRelativePercent = request.request_value[FSTR_OPEN_CLOSE_openRelativePercent];
265+
266+
if (hasOpenDirection && adjustDirectionOpenCloseCallback) {
267+
String openDirection = request.request_value[FSTR_OPEN_CLOSE_openDirection];
268+
success = adjustDirectionOpenCloseCallback(device->deviceId, openDirection, openRelativePercent);
269+
request.response_value[FSTR_OPEN_CLOSE_openDirection] = openDirection;
270+
request.response_value[FSTR_OPEN_CLOSE_openPercent] = openRelativePercent;
271+
} else if (!hasOpenDirection && adjustOpenCloseCallback) {
272+
success = adjustOpenCloseCallback(device->deviceId, openRelativePercent);
273+
request.response_value[FSTR_OPEN_CLOSE_openPercent] = openRelativePercent;
274+
}
275+
276+
return success;
277+
}
278+
279+
return false;
280+
}
281+
282+
} // SINRICPRO_NAMESPACE
283+
284+
template <typename T>
285+
using OpenCloseController = SINRICPRO_NAMESPACE::OpenCloseController<T>;

0 commit comments

Comments
 (0)