diff --git a/changelog.md b/changelog.md index d9592afa..fb09ad30 100644 --- a/changelog.md +++ b/changelog.md @@ -1,8 +1,12 @@ # Changelog -## Version 3.4.1 +## Version 3.5.0 New: - - Fix: invalid signature error message. + - Support Start/Stop and Open/Close capabilities. + +## Version 3.4.1 + Fixed: + - Fix: invalid signature error message. ## Version 3.4.0 New: diff --git a/library.json b/library.json index d018e23c..e09adc5b 100644 --- a/library.json +++ b/library.json @@ -1,48 +1,38 @@ { "name": "SinricPro", - "keywords": "ethernet, sinric, alexa, iot", - "description": "Library for https://sinric.pro - simple way to connect your device to alexa", + "keywords": "sinric, alexa, google home, iot", + "description": "Library for https://sinric.pro - simple way to connect your device to alexa and google home", "repository": { "type": "git", "url": "https://github.com/sinricpro/esp8266-esp32-sdk" }, "authors": [ + { + "name": "SinricPro", + "url": "https://sinric.pro" + }, { "name": "Boris Jaeger", "email": "sivar2311@gmail.com", "maintainer": true } ], - "version": "3.4.1", + "homepage": "https://sinric.pro", + "version": "3.5.0", "frameworks": "arduino", - "platforms": [ - "espressif8266", - "espressif32", - "raspberrypi" - ], + "platforms": ["espressif8266", "espressif32", "raspberrypi"], "dependencies": [ { "name": "ArduinoJson", "version": "^7.0.3", - "platforms": [ - "espressif32", - "espressif8266", - "raspberrypi" - ] + "platforms": ["espressif32", "espressif8266", "raspberrypi"] }, { "name": "WebSockets", "version": "^2.4.0", - "platforms": [ - "espressif32", - "espressif8266", - "raspberrypi" - ] + "platforms": ["espressif32", "espressif8266", "raspberrypi"] } ], - "examples": [ - "examples/*/*.ino", - "examples/*/*/*.ino" - ], + "examples": ["examples/*/*.ino", "examples/*/*/*.ino"], "license": "CC-BY-SA-4.0" -} \ No newline at end of file +} diff --git a/library.properties b/library.properties index d610a1ad..6e91728a 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SinricPro -version=3.4.1 +version=3.5.0 author=Boris Jaeger maintainer=Boris Jaeger sentence=Library for https://sinric.pro - simple way to connect your device to alexa diff --git a/src/Capabilities/OpenCloseController.h b/src/Capabilities/OpenCloseController.h new file mode 100644 index 00000000..d04927f6 --- /dev/null +++ b/src/Capabilities/OpenCloseController.h @@ -0,0 +1,285 @@ +#pragma once + +#include "../SinricProRequest.h" +#include "../EventLimiter.h" +#include "../SinricProStrings.h" + +#include "../SinricProNamespace.h" +namespace SINRICPRO_NAMESPACE { + +FSTR(OPEN_CLOSE, openPercent); // "openPercent" +FSTR(OPEN_CLOSE, openRelativePercent); // "openRelativePercent" +FSTR(OPEN_CLOSE, openDirection); // "openDirection" +FSTR(OPEN_CLOSE, setOpenClose); // "setOpenClose" +FSTR(OPEN_CLOSE, adjustOpenClose); // "adjustOpenClose" + + +/** + * @brief Callback definition for onOpenClose callback + * + * Gets called when device receives a `setOpenClose` request + * @param[in] deviceId String containing the ID of device + * @param[in,out] openPercent Integer percentage (0-100) of how open the device should be set (0 = closed, 100 = fully open) + * @return Success status of the request + * @retval true Request handled successfully + * @retval false Request handling failed + * + * @section OpenCloseCallback Example-Code + * @snippet callbacks.cpp onSetOpenClose + **/ +using OpenCloseCallback = std::function; + +/** + * @brief Callback definition for onAdjustOpenClose callback + * + * Gets called when device receives an `adjustOpenClose` request + * @param[in] deviceId String containing the ID of device + * @param[in,out] openRelativePercent Integer value representing the relative percentage change to apply + * On output, should contain the absolute percentage value the device has been set to + * @return Success status of the request + * @retval true Request handled successfully + * @retval false Request handling failed + * + * @section AdjustOpenCloseCallback Example-Code + * @snippet callbacks.cpp onAdjustOpenClose + **/ +using AdjustOpenCloseCallback = std::function; + +/** + * @brief Callback definition for onDirectionOpenClose callback + * + * Gets called when device receives a `setOpenClose` request with direction information + * @param[in] deviceId String containing the ID of device + * @param[in] openDirection String direction in which to open: `UP`, `DOWN`, `LEFT`, `RIGHT`, `IN`, `OUT` + * @param[in,out] openPercent Integer percentage (0-100) of how open the device should be set + * @return Success status of the request + * @retval true Request handled successfully + * @retval false Request handling failed + * + * @section DirectionOpenCloseCallback Example-Code + * @snippet callbacks.cpp onDirectionOpenClose + **/ +using DirectionOpenCloseCallback = std::function; + +/** + * @brief Callback definition for onAdjustDirectionOpenClose callback + * + * Gets called when device receives an `adjustOpenClose` request with direction information + * @param[in] deviceId String containing the ID of device + * @param[in] openDirection String direction in which to open: `UP`, `DOWN`, `LEFT`, `RIGHT`, `IN`, `OUT` + * @param[in,out] openRelativePercent Integer representing relative percentage change to apply + * On output, should contain the absolute percentage value the device has been set to + * @return Success status of the request + * @retval true Request handled successfully + * @retval false Request handling failed + * + * @section AdjustDirectionOpenCloseCallback Example-Code + * @snippet callbacks.cpp onAdjustDirectionOpenClose + **/ +using AdjustDirectionOpenCloseCallback = std::function; + +/** + * @brief Controller class for devices with open/close functionality + * @ingroup Capabilities + * This controller handles open/close operations for devices that can be opened or closed by percentage and may supports multiple directions. + * + * @tparam T Device type that implements this controller + */ +template +class OpenCloseController { + public: + OpenCloseController(); + + /** + * @brief Set callback function for simple open/close operations + * + * @param cb Function pointer to a `OpenCloseCallback` function + * @return void + * @see OpenCloseCallback + **/ + void onOpenClose(OpenCloseCallback cb); + + /** + * @brief Set callback function for directional open/close operations + * + * @param cb Function pointer to a `DirectionOpenCloseCallback` function + * @return void + * @see DirectionOpenCloseCallback + **/ + void onDirectionOpenClose(DirectionOpenCloseCallback cb); + + /** + * @brief Set callback function for relative adjustments to open/close status + * + * @param cb Function pointer to a `AdjustOpenCloseCallback` function + * @return void + * @see AdjustOpenCloseCallback + **/ + void onAdjustOpenClose(AdjustOpenCloseCallback cb); + + /** + * @brief Set callback function for directional relative adjustments + * + * @param cb Function pointer to a `AdjustDirectionOpenCloseCallback` function + * @return void + * @see AdjustDirectionOpenCloseCallback + **/ + void onAdjustDirectionOpenClose(AdjustDirectionOpenCloseCallback cb); + + /** + * @brief Send an event to update the open/close status + * + * @param openPercent Current open percentage (0-100) + * @param cause Cause of the event (default: physical interaction) + * @return bool Whether the event was sent successfully + **/ + bool sendOpenCloseEvent(int openPercent, String cause = FSTR_SINRICPRO_PHYSICAL_INTERACTION); + + /** + * @brief Send an event to update the open/close status with direction + * + * @param openDirection Direction of opening (UP, DOWN, LEFT, RIGHT, IN, OUT) + * @param openPercent Current open percentage (0-100) + * @param cause Cause of the event (default: physical interaction) + * @return bool Whether the event was sent successfully + **/ + bool sendOpenCloseEvent(String openDirection, int openPercent, String cause = FSTR_SINRICPRO_PHYSICAL_INTERACTION); + + protected: + /** + * @brief Handle incoming open/close control requests + * + * @param request The incoming request to process + * @return bool Whether the request was handled successfully + **/ + bool handleOpenCloseController(SinricProRequest &request); + + private: + EventLimiter event_limiter; + DirectionOpenCloseCallback directionOpenCloseCallback; + OpenCloseCallback openCloseCallback; + AdjustOpenCloseCallback adjustOpenCloseCallback; + AdjustDirectionOpenCloseCallback adjustDirectionOpenCloseCallback; +}; + +template +OpenCloseController::OpenCloseController() +:event_limiter(EVENT_LIMIT_STATE) { + T* device = static_cast(this); + device->registerRequestHandler(std::bind(&OpenCloseController::handleOpenCloseController, this, std::placeholders::_1)); +} + +template +void OpenCloseController::onOpenClose(OpenCloseCallback cb) { openCloseCallback = cb; } + +template +void OpenCloseController::onDirectionOpenClose(DirectionOpenCloseCallback cb) { directionOpenCloseCallback = cb; } + +template +void OpenCloseController::onAdjustOpenClose(AdjustOpenCloseCallback cb) { adjustOpenCloseCallback = cb; } + +template +void OpenCloseController::onAdjustDirectionOpenClose(AdjustDirectionOpenCloseCallback cb) { adjustDirectionOpenCloseCallback = cb; } + +/** + * @brief Send an event to update open/close status with open direction and precent and information + * + * Sends the current open/close status with direction to the Sinric Pro cloud. + * The event will only be sent if the event rate limiter allows it. + * + * @param openDirection Direction in which the device is opening + * @param openPercent Current open percentage (0-100) + * @param cause Reason for the state change (default: physical interaction) + * @return bool Whether the event was sent successfully + */ +template +bool OpenCloseController::sendOpenCloseEvent(String openDirection, int openPercent, String cause) { + if (event_limiter) return false; + T* device = static_cast(this); + + JsonDocument eventMessage = device->prepareEvent(FSTR_OPEN_CLOSE_setOpenClose, cause.c_str()); + JsonObject event_value = eventMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_value]; + event_value[FSTR_OPEN_CLOSE_openDirection] = openDirection; + event_value[FSTR_OPEN_CLOSE_openPercent] = openPercent; + + return device->sendEvent(eventMessage); +} + +/** + * @brief Send an event to update open/close status + * + * Sends the current open/close percentage to the Sinric Pro cloud. + * The event will only be sent if the event rate limiter allows it. + * + * @param openPercent Current open percentage (0-100) + * @param cause Reason for the state change (default: physical interaction) + * @return bool Whether the event was sent successfully + */ +template +bool OpenCloseController::sendOpenCloseEvent(int openPercent, String cause) { + if (event_limiter) return false; + T* device = static_cast(this); + + JsonDocument eventMessage = device->prepareEvent(FSTR_OPEN_CLOSE_setOpenClose, cause.c_str()); + JsonObject event_value = eventMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_value]; + event_value[FSTR_OPEN_CLOSE_openPercent] = openPercent; + + return device->sendEvent(eventMessage); +} + +/** + * @brief Handle incoming open/close requests + * + * Processes both setOpenClose and adjustOpenClose requests with or without direction information. + * Delegates to the appropriate callback function based on the request type and parameters. + * + * @param request The incoming request containing action and parameters + * @return bool Whether the request was handled successfully + */ +template +bool OpenCloseController::handleOpenCloseController(SinricProRequest &request) { + T* device = static_cast(this); + + bool success = false; + + if (request.action == FSTR_OPEN_CLOSE_setOpenClose) { + bool hasOpenDirection = !request.request_value[FSTR_OPEN_CLOSE_openDirection].isNull(); + int openPercent = request.request_value[FSTR_OPEN_CLOSE_openPercent]; + + if (hasOpenDirection && directionOpenCloseCallback) { + String openDirection = request.request_value[FSTR_OPEN_CLOSE_openDirection]; + success = directionOpenCloseCallback(device->deviceId, openDirection, openPercent); + request.response_value[FSTR_OPEN_CLOSE_openDirection] = openDirection; + request.response_value[FSTR_OPEN_CLOSE_openPercent] = openPercent; + } else if (!hasOpenDirection && openCloseCallback) { + success = openCloseCallback(device->deviceId, openPercent); + request.response_value[FSTR_OPEN_CLOSE_openPercent] = openPercent; + } + + return success; + } + + if (request.action == FSTR_OPEN_CLOSE_adjustOpenClose) { + bool hasOpenDirection = !request.request_value[FSTR_OPEN_CLOSE_openDirection].isNull(); + int openRelativePercent = request.request_value[FSTR_OPEN_CLOSE_openRelativePercent]; + + if (hasOpenDirection && adjustDirectionOpenCloseCallback) { + String openDirection = request.request_value[FSTR_OPEN_CLOSE_openDirection]; + success = adjustDirectionOpenCloseCallback(device->deviceId, openDirection, openRelativePercent); + request.response_value[FSTR_OPEN_CLOSE_openDirection] = openDirection; + request.response_value[FSTR_OPEN_CLOSE_openPercent] = openRelativePercent; + } else if (!hasOpenDirection && adjustOpenCloseCallback) { + success = adjustOpenCloseCallback(device->deviceId, openRelativePercent); + request.response_value[FSTR_OPEN_CLOSE_openPercent] = openRelativePercent; + } + + return success; + } + + return false; +} + +} // SINRICPRO_NAMESPACE + +template +using OpenCloseController = SINRICPRO_NAMESPACE::OpenCloseController; \ No newline at end of file diff --git a/src/Capabilities/StartStopController.h b/src/Capabilities/StartStopController.h new file mode 100644 index 00000000..f4994fdd --- /dev/null +++ b/src/Capabilities/StartStopController.h @@ -0,0 +1,175 @@ +#pragma once + +#include "../SinricProRequest.h" +#include "../EventLimiter.h" +#include "../SinricProStrings.h" + +#include "../SinricProNamespace.h" +namespace SINRICPRO_NAMESPACE { + +FSTR(START_STOP, start); // "start" +FSTR(START_STOP, pause); // "pause" +FSTR(START_STOP, setStartStop); // "setStartStop" +FSTR(START_STOP, setPauseUnpause); // "setPauseUnpause" + +/** + * @brief Definition for onStartStop callback + * + * Gets called when device receive a `setStartStop` reuqest \n + * @param[in] deviceId String which contains the ID of device + * @param[in] start true for start, false for stop + * @return the success of the request + * @retval true request handled properly + * @retval false request was not handled properly because of some error + * + * @section StartStopCallback Example-Code + * @snippet callbacks.cpp onStartStop + **/ +using StartStopCallback = std::function; + +/** + * @brief Definition for onPauseUnpause callback + * + * Gets called when device receive a `setPauseUnpause` reuqest \n + * @param[in] deviceId String which contains the ID of device + * @param[in] pause true for pause, false for unpause + * @return the success of the request + * @retval true request handled properly + * @retval false request was not handled properly because of some error + * + * @section StartStopCallback Example-Code + * @snippet callbacks.cpp onPauseUnpause + **/ + +using PauseUnpauseCallback = std::function; + +/** + * @brief StartStopController class to handle start/stop and pause/unpause functionality + * + * This template class provides functionality to control devices that can be started, + * stopped, paused, and unpaused (like a vacuum cleaner, washing machine, etc.) + * + * @tparam T The device type that this controller is attached to + */ +template +class StartStopController { + public: + StartStopController(); + + /** + * @brief Set the callback function for start/stop events + * + * @param cb Callback function that will be called when a start/stop request is received + */ + void onStartStop(StartStopCallback cb); + + /** + * @brief Set the callback function for pause/unpause events + * + * @param cb Callback function that will be called when a pause/unpause request is received + */ + void onPauseUnpause(StartStopCallback cb); + + /** + * @brief Send a start/stop event to the SinricPro server + * + * @param start true for start, false for stop + * @param cause The cause of the event (default: "PHYSICAL_INTERACTION") + * @return true if event was sent successfully, false otherwise + */ + bool sendStartStopEvent(bool start, String cause = FSTR_SINRICPRO_PHYSICAL_INTERACTION); + + /** + * @brief Send a pause/unpause event to the SinricPro server + * + * @param pause true for pause, false for unpause + * @param cause The cause of the event (default: "PHYSICAL_INTERACTION") + * @return true if event was sent successfully, false otherwise + */ + bool sendPauseUnpauseEvent(bool pause, String cause = FSTR_SINRICPRO_PHYSICAL_INTERACTION); + + protected: + bool handleStartStopController(SinricProRequest &request); + + private: + EventLimiter event_limiter; + StartStopCallback startStopCallbackCallback; + PauseUnpauseCallback pauseUnpauseCallback; +}; + +template +StartStopController::StartStopController() +:event_limiter(EVENT_LIMIT_STATE) { + T* device = static_cast(this); + device->registerRequestHandler(std::bind(&StartStopController::handleStartStopController, this, std::placeholders::_1)); +} + +template +void StartStopController::onStartStop(StartStopCallback cb) { startStopCallbackCallback = cb; } + +template +void StartStopController::onPauseUnpause(PauseUnpauseCallback cb) { pauseUnpauseCallback = cb; } + +/** + * @brief Send a start/stop event to the SinricPro server + * + * Creates a JSON message with the start status and sends it to the server + * @param start `bool` true for start, false for stop + * @param cause (optional) Reason why event is sent (default = `"PHYSICAL_INTERACTION"`) + */ +template +bool StartStopController::sendStartStopEvent(bool start, String cause) { + if (event_limiter) return false; + T* device = static_cast(this); + + JsonDocument eventMessage = device->prepareEvent(FSTR_START_STOP_setStartStop, cause.c_str()); + JsonObject event_value = eventMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_value]; + event_value[FSTR_START_STOP_start] = start; + return device->sendEvent(eventMessage); +} + +/** + * @brief Send a pause/unpause event to the SinricPro server + * + * Creates a JSON message with the pause status and sends it to the server + * @param pause `bool` true for pause, false for unpause + * @param cause (optional) Reason why event is sent (default = `"PHYSICAL_INTERACTION"`) + */ +template +bool StartStopController::sendPauseUnpauseEvent(bool pause, String cause) { + if (event_limiter) return false; + T* device = static_cast(this); + + JsonDocument eventMessage = device->prepareEvent(FSTR_START_STOP_setPauseUnpause, cause.c_str()); + JsonObject event_value = eventMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_value]; + event_value[FSTR_START_STOP_pause] = pause; + return device->sendEvent(eventMessage); +} + +template +bool StartStopController::handleStartStopController(SinricProRequest &request) { + T* device = static_cast(this); + + bool success = false; + + if (startStopCallbackCallback && request.action == FSTR_START_STOP_setStartStop) { + bool start = request.request_value[FSTR_START_STOP_start]; + success = startStopCallbackCallback(device->deviceId, start); + request.response_value[FSTR_START_STOP_start] = start; + return success; + } + + if (pauseUnpauseCallback && request.action == FSTR_START_STOP_setPauseUnpause) { + bool pause = request.request_value[FSTR_START_STOP_pause]; + success = pauseUnpauseCallback(device->deviceId, pause); + request.response_value[FSTR_START_STOP_pause] = pause; + return success; + } + + return false; +} + +} // SINRICPRO_NAMESPACE + +template +using StartStopController = SINRICPRO_NAMESPACE::StartStopController; \ No newline at end of file diff --git a/src/SinricProVersion.h b/src/SinricProVersion.h index 749ec458..6659c3e8 100644 --- a/src/SinricProVersion.h +++ b/src/SinricProVersion.h @@ -5,8 +5,8 @@ // Version Configuration #define SINRICPRO_VERSION_MAJOR 3 -#define SINRICPRO_VERSION_MINOR 4 -#define SINRICPRO_VERSION_REVISION 1 +#define SINRICPRO_VERSION_MINOR 5 +#define SINRICPRO_VERSION_REVISION 0 #define SINRICPRO_VERSION STR(SINRICPRO_VERSION_MAJOR) "." STR(SINRICPRO_VERSION_MINOR) "." STR(SINRICPRO_VERSION_REVISION) #define SINRICPRO_VERSION_STR "SinricPro (v" SINRICPRO_VERSION ")" #define SINRICPRO_VERISON_INT SINRICPRO_VERSION_MAJOR * 1000000 + SINRICPRO_VERSION_MINOR * 1000 + SINRICPRO_VERSION_REVISION \ No newline at end of file