diff --git a/changelog.md b/changelog.md index de046570..d5216b89 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,13 @@ # Changelog +## Version 3.4.0 + New: + - Support Image upload on camera devices + + Fixed: + - Removed calls to `containsKey` - deprecated since ArduinoJSON 7.2 + - Missing includes + ## Version 3.3.1 - Support SmartButton. diff --git a/examples/Camera/snapshot-camera/camera_pins.h b/examples/Camera/snapshot-camera/camera_pins.h new file mode 100644 index 00000000..89eca49f --- /dev/null +++ b/examples/Camera/snapshot-camera/camera_pins.h @@ -0,0 +1,272 @@ +#if defined(CAMERA_MODEL_WROVER_KIT) +#define PWDN_GPIO_NUM -1 +#define RESET_GPIO_NUM -1 +#define XCLK_GPIO_NUM 21 +#define SIOD_GPIO_NUM 26 +#define SIOC_GPIO_NUM 27 + +#define Y9_GPIO_NUM 35 +#define Y8_GPIO_NUM 34 +#define Y7_GPIO_NUM 39 +#define Y6_GPIO_NUM 36 +#define Y5_GPIO_NUM 19 +#define Y4_GPIO_NUM 18 +#define Y3_GPIO_NUM 5 +#define Y2_GPIO_NUM 4 +#define VSYNC_GPIO_NUM 25 +#define HREF_GPIO_NUM 23 +#define PCLK_GPIO_NUM 22 + +#elif defined(CAMERA_MODEL_ESP_EYE) +#define PWDN_GPIO_NUM -1 +#define RESET_GPIO_NUM -1 +#define XCLK_GPIO_NUM 4 +#define SIOD_GPIO_NUM 18 +#define SIOC_GPIO_NUM 23 + +#define Y9_GPIO_NUM 36 +#define Y8_GPIO_NUM 37 +#define Y7_GPIO_NUM 38 +#define Y6_GPIO_NUM 39 +#define Y5_GPIO_NUM 35 +#define Y4_GPIO_NUM 14 +#define Y3_GPIO_NUM 13 +#define Y2_GPIO_NUM 34 +#define VSYNC_GPIO_NUM 5 +#define HREF_GPIO_NUM 27 +#define PCLK_GPIO_NUM 25 + +#elif defined(CAMERA_MODEL_M5STACK_PSRAM) +#define PWDN_GPIO_NUM -1 +#define RESET_GPIO_NUM 15 +#define XCLK_GPIO_NUM 27 +#define SIOD_GPIO_NUM 25 +#define SIOC_GPIO_NUM 23 + +#define Y9_GPIO_NUM 19 +#define Y8_GPIO_NUM 36 +#define Y7_GPIO_NUM 18 +#define Y6_GPIO_NUM 39 +#define Y5_GPIO_NUM 5 +#define Y4_GPIO_NUM 34 +#define Y3_GPIO_NUM 35 +#define Y2_GPIO_NUM 32 +#define VSYNC_GPIO_NUM 22 +#define HREF_GPIO_NUM 26 +#define PCLK_GPIO_NUM 21 + +#elif defined(CAMERA_MODEL_M5STACK_V2_PSRAM) +#define PWDN_GPIO_NUM -1 +#define RESET_GPIO_NUM 15 +#define XCLK_GPIO_NUM 27 +#define SIOD_GPIO_NUM 22 +#define SIOC_GPIO_NUM 23 + +#define Y9_GPIO_NUM 19 +#define Y8_GPIO_NUM 36 +#define Y7_GPIO_NUM 18 +#define Y6_GPIO_NUM 39 +#define Y5_GPIO_NUM 5 +#define Y4_GPIO_NUM 34 +#define Y3_GPIO_NUM 35 +#define Y2_GPIO_NUM 32 +#define VSYNC_GPIO_NUM 25 +#define HREF_GPIO_NUM 26 +#define PCLK_GPIO_NUM 21 + +#elif defined(CAMERA_MODEL_M5STACK_WIDE) +#define PWDN_GPIO_NUM -1 +#define RESET_GPIO_NUM 15 +#define XCLK_GPIO_NUM 27 +#define SIOD_GPIO_NUM 22 +#define SIOC_GPIO_NUM 23 + +#define Y9_GPIO_NUM 19 +#define Y8_GPIO_NUM 36 +#define Y7_GPIO_NUM 18 +#define Y6_GPIO_NUM 39 +#define Y5_GPIO_NUM 5 +#define Y4_GPIO_NUM 34 +#define Y3_GPIO_NUM 35 +#define Y2_GPIO_NUM 32 +#define VSYNC_GPIO_NUM 25 +#define HREF_GPIO_NUM 26 +#define PCLK_GPIO_NUM 21 + +#elif defined(CAMERA_MODEL_M5STACK_ESP32CAM) +#define PWDN_GPIO_NUM -1 +#define RESET_GPIO_NUM 15 +#define XCLK_GPIO_NUM 27 +#define SIOD_GPIO_NUM 25 +#define SIOC_GPIO_NUM 23 + +#define Y9_GPIO_NUM 19 +#define Y8_GPIO_NUM 36 +#define Y7_GPIO_NUM 18 +#define Y6_GPIO_NUM 39 +#define Y5_GPIO_NUM 5 +#define Y4_GPIO_NUM 34 +#define Y3_GPIO_NUM 35 +#define Y2_GPIO_NUM 17 +#define VSYNC_GPIO_NUM 22 +#define HREF_GPIO_NUM 26 +#define PCLK_GPIO_NUM 21 + +#elif defined(CAMERA_MODEL_M5STACK_UNITCAM) +#define PWDN_GPIO_NUM -1 +#define RESET_GPIO_NUM 15 +#define XCLK_GPIO_NUM 27 +#define SIOD_GPIO_NUM 25 +#define SIOC_GPIO_NUM 23 + +#define Y9_GPIO_NUM 19 +#define Y8_GPIO_NUM 36 +#define Y7_GPIO_NUM 18 +#define Y6_GPIO_NUM 39 +#define Y5_GPIO_NUM 5 +#define Y4_GPIO_NUM 34 +#define Y3_GPIO_NUM 35 +#define Y2_GPIO_NUM 32 +#define VSYNC_GPIO_NUM 22 +#define HREF_GPIO_NUM 26 +#define PCLK_GPIO_NUM 21 + +#elif defined(CAMERA_MODEL_AI_THINKER) +#define PWDN_GPIO_NUM 32 +#define RESET_GPIO_NUM -1 +#define XCLK_GPIO_NUM 0 +#define SIOD_GPIO_NUM 26 +#define SIOC_GPIO_NUM 27 + +#define Y9_GPIO_NUM 35 +#define Y8_GPIO_NUM 34 +#define Y7_GPIO_NUM 39 +#define Y6_GPIO_NUM 36 +#define Y5_GPIO_NUM 21 +#define Y4_GPIO_NUM 19 +#define Y3_GPIO_NUM 18 +#define Y2_GPIO_NUM 5 +#define VSYNC_GPIO_NUM 25 +#define HREF_GPIO_NUM 23 +#define PCLK_GPIO_NUM 22 + +#elif defined(CAMERA_MODEL_TTGO_T_JOURNAL) +#define PWDN_GPIO_NUM 0 +#define RESET_GPIO_NUM 15 +#define XCLK_GPIO_NUM 27 +#define SIOD_GPIO_NUM 25 +#define SIOC_GPIO_NUM 23 + +#define Y9_GPIO_NUM 19 +#define Y8_GPIO_NUM 36 +#define Y7_GPIO_NUM 18 +#define Y6_GPIO_NUM 39 +#define Y5_GPIO_NUM 5 +#define Y4_GPIO_NUM 34 +#define Y3_GPIO_NUM 35 +#define Y2_GPIO_NUM 17 +#define VSYNC_GPIO_NUM 22 +#define HREF_GPIO_NUM 26 +#define PCLK_GPIO_NUM 21 + + +#elif defined(CAMERA_MODEL_ESP32_CAM_BOARD) +// The 18 pin header on the board has Y5 and Y3 swapped +#define USE_BOARD_HEADER 0 +#define PWDN_GPIO_NUM 32 +#define RESET_GPIO_NUM 33 +#define XCLK_GPIO_NUM 4 +#define SIOD_GPIO_NUM 18 +#define SIOC_GPIO_NUM 23 + +#define Y9_GPIO_NUM 36 +#define Y8_GPIO_NUM 19 +#define Y7_GPIO_NUM 21 +#define Y6_GPIO_NUM 39 +#if USE_BOARD_HEADER +#define Y5_GPIO_NUM 13 +#else +#define Y5_GPIO_NUM 35 +#endif +#define Y4_GPIO_NUM 14 +#if USE_BOARD_HEADER +#define Y3_GPIO_NUM 35 +#else +#define Y3_GPIO_NUM 13 +#endif +#define Y2_GPIO_NUM 34 +#define VSYNC_GPIO_NUM 5 +#define HREF_GPIO_NUM 27 +#define PCLK_GPIO_NUM 25 + +#elif defined(CAMERA_MODEL_ESP32S3_CAM_LCD) +#define PWDN_GPIO_NUM -1 +#define RESET_GPIO_NUM -1 +#define XCLK_GPIO_NUM 40 +#define SIOD_GPIO_NUM 17 +#define SIOC_GPIO_NUM 18 + +#define Y9_GPIO_NUM 39 +#define Y8_GPIO_NUM 41 +#define Y7_GPIO_NUM 42 +#define Y6_GPIO_NUM 12 +#define Y5_GPIO_NUM 3 +#define Y4_GPIO_NUM 14 +#define Y3_GPIO_NUM 47 +#define Y2_GPIO_NUM 13 +#define VSYNC_GPIO_NUM 21 +#define HREF_GPIO_NUM 38 +#define PCLK_GPIO_NUM 11 + +#elif defined(CAMERA_MODEL_ESP32S2_CAM_BOARD) +// The 18 pin header on the board has Y5 and Y3 swapped +#define USE_BOARD_HEADER 0 +#define PWDN_GPIO_NUM 1 +#define RESET_GPIO_NUM 2 +#define XCLK_GPIO_NUM 42 +#define SIOD_GPIO_NUM 41 +#define SIOC_GPIO_NUM 18 + +#define Y9_GPIO_NUM 16 +#define Y8_GPIO_NUM 39 +#define Y7_GPIO_NUM 40 +#define Y6_GPIO_NUM 15 +#if USE_BOARD_HEADER +#define Y5_GPIO_NUM 12 +#else +#define Y5_GPIO_NUM 13 +#endif +#define Y4_GPIO_NUM 5 +#if USE_BOARD_HEADER +#define Y3_GPIO_NUM 13 +#else +#define Y3_GPIO_NUM 12 +#endif +#define Y2_GPIO_NUM 14 +#define VSYNC_GPIO_NUM 38 +#define HREF_GPIO_NUM 4 +#define PCLK_GPIO_NUM 3 + +#elif defined(CAMERA_MODEL_ESP32S3_EYE) +#define PWDN_GPIO_NUM -1 +#define RESET_GPIO_NUM -1 +#define XCLK_GPIO_NUM 15 +#define SIOD_GPIO_NUM 4 +#define SIOC_GPIO_NUM 5 + +#define Y2_GPIO_NUM 11 +#define Y3_GPIO_NUM 9 +#define Y4_GPIO_NUM 8 +#define Y5_GPIO_NUM 10 +#define Y6_GPIO_NUM 12 +#define Y7_GPIO_NUM 18 +#define Y8_GPIO_NUM 17 +#define Y9_GPIO_NUM 16 + +#define VSYNC_GPIO_NUM 6 +#define HREF_GPIO_NUM 7 +#define PCLK_GPIO_NUM 13 + +#else +#error "Camera model not selected" +#endif diff --git a/examples/Camera/snapshot-camera/snapshot-camera.ino b/examples/Camera/snapshot-camera/snapshot-camera.ino new file mode 100644 index 00000000..5eb9cf86 --- /dev/null +++ b/examples/Camera/snapshot-camera/snapshot-camera.ino @@ -0,0 +1,161 @@ +/* + * Example for how to use SinricPro Camera device: + * - Create a ESP32 Camera device from portal. + * - Copy the secrets below. + * + * If you encounter any issues: + * - check the readme.md at https://github.com/sinricpro/esp8266-esp32-sdk/blob/master/README.md + * - ensure all dependent libraries are installed + * - see https://github.com/sinricpro/esp8266-esp32-sdk/blob/master/README.md#arduinoide + * - see https://github.com/sinricpro/esp8266-esp32-sdk/blob/master/README.md#dependencies + * - open serial monitor and check whats happening + * - check full user documentation at https://sinricpro.github.io/esp8266-esp32-sdk + * - visit https://github.com/sinricpro/esp8266-esp32-sdk/issues and check for existing issues or open a new one + */ + +// Uncomment the following line to enable serial debug output +//#define ENABLE_DEBUG + +#include +#include +#include +#include + +#include + +// Select camera model +//#define CAMERA_MODEL_WROVER_KIT +//#define CAMERA_MODEL_ESP_EYE +//#define CAMERA_MODEL_M5STACK_PSRAM +//#define CAMERA_MODEL_M5STACK_V2_PSRAM +//#define CAMERA_MODEL_M5STACK_WIDE +//#define CAMERA_MODEL_M5STACK_ESP32CAM +//#define CAMERA_MODEL_M5STACK_UNITCAM +//#define CAMERA_MODEL_AI_THINKER +//#define CAMERA_MODEL_TTGO_T_JOURNAL +//#define CAMERA_MODEL_ESP32_CAM_BOARD +//#define CAMERA_MODEL_ESP32S3_CAM_LCD +//#define CAMERA_MODEL_ESP32S2_CAM_BOARD +//#define CAMERA_MODEL_ESP32S3_EYE + +#include "camera_pins.h" + +#define WIFI_SSID "YOUR-WIFI-SSID" +#define WIFI_PASS "YOUR-WIFI-PASSWORD" +#define APP_KEY "YOUR-APP-KEY" // Should look like "de0bxxxx-1x3x-4x3x-ax2x-5dabxxxxxxxx" +#define APP_SECRET "YOUR-APP-SECRET" // Should look like "5f36xxxx-x3x7-4x3x-xexe-e86724a9xxxx-4c4axxxx-3x3x-x5xe-x9x3-333d65xxxxxx" +#define CAMERA_ID "YOUR-ESP32-CAMERA-ID" // Should look like "5dc1564130xxxxxxxxxxxxxx" +#define BAUD_RATE 115200 // Change baudrate to your need + +bool onSnapshot(const String& deviceId) { + camera_fb_t* fb = esp_camera_fb_get(); + + if (!fb) { + Serial.println("Failed to grab image"); + return false; + } + + SinricProCamera& myCamera = SinricPro[deviceId]; + int result = myCamera.sendSnapshot(fb->buf, fb->len); + esp_camera_fb_return(fb); + + return result == 200; +} + +bool onPowerState(const String& deviceId, bool& state) { + return true; +} + +// setup function for WiFi connection +void setupWiFi() { + Serial.printf("\r\n[Wifi]: Connecting"); + + WiFi.setSleep(false); + WiFi.setAutoReconnect(true); + + WiFi.begin(WIFI_SSID, WIFI_PASS); + + while (WiFi.status() != WL_CONNECTED) { + Serial.printf("."); + delay(250); + } + Serial.printf("connected!\r\n[WiFi]: IP-Address is %s\r\n", WiFi.localIP().toString().c_str()); +} + +void setupSinricPro() { + SinricProCamera& myCamera = SinricPro[CAMERA_ID]; + myCamera.onPowerState(onPowerState); + myCamera.onSnapshot(onSnapshot); + SinricPro.onConnected([]() { Serial.printf("Connected to SinricPro\r\n"); }); + SinricPro.onDisconnected([]() { Serial.printf("Disconnected from SinricPro\r\n"); }); + + SinricPro.begin(APP_KEY, APP_SECRET); +} + +esp_err_t setupCamera() { + camera_config_t config; + config.ledc_channel = LEDC_CHANNEL_0; + config.ledc_timer = LEDC_TIMER_0; + config.pin_d0 = Y2_GPIO_NUM; + config.pin_d1 = Y3_GPIO_NUM; + config.pin_d2 = Y4_GPIO_NUM; + config.pin_d3 = Y5_GPIO_NUM; + config.pin_d4 = Y6_GPIO_NUM; + config.pin_d5 = Y7_GPIO_NUM; + config.pin_d6 = Y8_GPIO_NUM; + config.pin_d7 = Y9_GPIO_NUM; + config.pin_xclk = XCLK_GPIO_NUM; + config.pin_pclk = PCLK_GPIO_NUM; + config.pin_vsync = VSYNC_GPIO_NUM; + config.pin_href = HREF_GPIO_NUM; + config.pin_sccb_sda = SIOD_GPIO_NUM; + config.pin_sccb_scl = SIOC_GPIO_NUM; + config.pin_pwdn = PWDN_GPIO_NUM; + config.pin_reset = RESET_GPIO_NUM; + config.xclk_freq_hz = 20000000; + config.frame_size = FRAMESIZE_XGA; + config.pixel_format = PIXFORMAT_JPEG; + config.grab_mode = CAMERA_GRAB_WHEN_EMPTY; + config.fb_location = CAMERA_FB_IN_PSRAM; + config.jpeg_quality = 12; + config.fb_count = 1; + + // if PSRAM IC present, init with UXGA resolution and higher JPEG quality + // for larger pre-allocated frame buffer. + if (psramFound()) { + config.jpeg_quality = 10; + config.fb_count = 2; + config.grab_mode = CAMERA_GRAB_LATEST; + } else { + // Limit the frame size when PSRAM is not available + config.frame_size = FRAMESIZE_SVGA; + config.fb_location = CAMERA_FB_IN_DRAM; + } + + // camera init + esp_err_t err = esp_camera_init(&config); + if (err != ESP_OK) { + return err; + } + + sensor_t* s = esp_camera_sensor_get(); + // initial sensors are flipped vertically and colors are a bit saturated + s->set_vflip(s, 1); // flip it back + s->set_brightness(s, 1); // up the brightness just a bit + s->set_saturation(s, 0); // lower the saturation + + return ESP_OK; +} + +void setup() { + Serial.begin(BAUD_RATE); Serial.printf("\r\n\r\n"); + setupWiFi(); + setupSinricPro(); + setupCamera(); +} + +void loop() { + SinricPro.handle(); +} + + diff --git a/library.json b/library.json index 8e5b45c9..d03ce870 100644 --- a/library.json +++ b/library.json @@ -13,7 +13,7 @@ "maintainer": true } ], - "version": "3.3.1", + "version": "3.4.0", "frameworks": "arduino", "platforms": [ "espressif8266", diff --git a/library.properties b/library.properties index 5bf00481..223c1eeb 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SinricPro -version=3.3.1 +version=3.4.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/CameraController.h b/src/Capabilities/CameraController.h new file mode 100644 index 00000000..1d989e63 --- /dev/null +++ b/src/Capabilities/CameraController.h @@ -0,0 +1,127 @@ +#pragma once + +// Required header includes +#include "../EventLimiter.h" +#include "../SinricProNamespace.h" +#include "../SinricProRequest.h" +#include "../SinricProStrings.h" +#include + +#if defined(ESP32) + #include +#endif + +namespace SINRICPRO_NAMESPACE { + +FSTR(CAMERA, getSnapshot); // "getSnapshot" + +using SnapshotCallback = std::function; + +/** + * @brief Camera + * @ingroup Capabilities + **/ +template +class CameraController { + public: + CameraController(); + + /** + * @brief Sets the callback function for snapshot requests + * @param cb Callback function to handle snapshot requests + */ + void onSnapshot(SnapshotCallback cb); + + /** + * @brief Sends a camera snapshot to the SinricPro server + * @param buffer Pointer to the image data buffer + * @param len Length of the image data in bytes + * @return HTTP response code + */ + int sendSnapshot(uint8_t* buffer, size_t len); + + protected: + /** + * @brief Handles incoming camera control requests + * @param request The incoming request object + * @return true if request was handled successfully, false otherwise + */ + bool handleCameraController(SinricProRequest &request); + + private: + // Callback handler for snapshot requests + SnapshotCallback getSnapshotCallback = nullptr; +}; + +template +CameraController::CameraController() { + T *device = static_cast(this); + device->registerRequestHandler(std::bind(&CameraController::handleCameraController, this, std::placeholders::_1)); +} + +template +void CameraController::onSnapshot(SnapshotCallback cb) { + getSnapshotCallback = cb; +} + +template +bool CameraController::handleCameraController(SinricProRequest &request) { + T *device = static_cast(this); + bool success = false; + + // Handle getSnapshot action + if (request.action == FSTR_CAMERA_getSnapshot) { + if (getSnapshotCallback) { + success = getSnapshotCallback(device->deviceId); + } + } + return success; +} + +template +int CameraController::sendSnapshot(uint8_t* buffer, size_t len) { + int resCode = -1; + +#if defined(ESP32) + T *device = static_cast(this); + + // Validate input buffer + if (!buffer) return resCode; + + HTTPClient http; + bool beginSuccess = false; + + #ifdef SINRICPRO_NOSSL + WiFiClient *client = new WiFiClient(); + if (!client) return resCode; + + beginSuccess = http.begin(*client, SINRICPRO_CAMERA_URL, 80, SINRICPRO_CAMERA_PATH, false); + #else + WiFiClientSecure *secureClient = new WiFiClientSecure(); + if (!secureClient) return resCode; + + secureClient->setInsecure(); // Skip certificate validation + beginSuccess = http.begin(*secureClient, SINRICPRO_CAMERA_URL, 443, SINRICPRO_CAMERA_PATH, true); + #endif + + if (!beginSuccess) { + http.end(); + return resCode; + } + + const String& deviceId = device->getDeviceId(); + String createdAt = String(device->getTimestamp()); + String signature = device->sign(deviceId+createdAt); + + http.addHeader(FSTR_SINRICPRO_deviceId, deviceId); + http.addHeader(FSTR_SINRICPRO_createdAt, createdAt); + http.addHeader(FSTR_SINRICPRO_signature, signature); + + resCode = http.POST(buffer, len); + http.end(); +#endif + + return resCode; +} + +} // namespace SINRICPRO_NAMESPACE \ No newline at end of file diff --git a/src/Capabilities/ChannelController.h b/src/Capabilities/ChannelController.h index 96bafbc2..847906c9 100644 --- a/src/Capabilities/ChannelController.h +++ b/src/Capabilities/ChannelController.h @@ -157,13 +157,13 @@ bool ChannelController::handleChannelController(SinricProRequest &request) { if (request.action == FSTR_CHANNEL_changeChannel) { - if (changeChannelCallback && request.request_value[FSTR_CHANNEL_channel].containsKey(FSTR_CHANNEL_name)) { + if (changeChannelCallback && request.request_value[FSTR_CHANNEL_channel][FSTR_CHANNEL_name].is()) { String channelName = request.request_value[FSTR_CHANNEL_channel][FSTR_CHANNEL_name] | ""; success = changeChannelCallback(device->deviceId, channelName); request.response_value[FSTR_CHANNEL_channel][FSTR_CHANNEL_name] = channelName; } - if (changeChannelNumberCallback && request.request_value[FSTR_CHANNEL_channel].containsKey(FSTR_CHANNEL_number)) { + if (changeChannelNumberCallback && request.request_value[FSTR_CHANNEL_channel][FSTR_CHANNEL_number].is()) { String channelName(""); int channelNumber = request.request_value[FSTR_CHANNEL_channel][FSTR_CHANNEL_number]; success = changeChannelNumberCallback(device->deviceId, channelNumber, channelName); diff --git a/src/Capabilities/ThermostatController.h b/src/Capabilities/ThermostatController.h index c7e8da7a..edc60bae 100644 --- a/src/Capabilities/ThermostatController.h +++ b/src/Capabilities/ThermostatController.h @@ -181,7 +181,7 @@ bool ThermostatController::handleThermostatController(SinricProRequest &reque if (request.action == FSTR_THERMOSTAT_targetTemperature && targetTemperatureCallback) { float temperature; - if (request.request_value.containsKey(FSTR_THERMOSTAT_temperature)) { + if (request.request_value[FSTR_THERMOSTAT_temperature].is()) { temperature = request.request_value[FSTR_THERMOSTAT_temperature]; } else { temperature = 1; diff --git a/src/SinricPro.h b/src/SinricPro.h index 567da203..9cb567a2 100644 --- a/src/SinricPro.h +++ b/src/SinricPro.h @@ -10,6 +10,7 @@ #include "SinricProDeviceInterface.h" #include "SinricProInterface.h" #include "SinricProMessageid.h" +#include "SinricProModuleCommandHandler.h" #include "SinricProNamespace.h" #include "SinricProQueue.h" #include "SinricProSignature.h" @@ -17,7 +18,6 @@ #include "SinricProUDP.h" #include "SinricProWebsocket.h" #include "Timestamp.h" -#include "SinricProModuleCommandHandler.h" namespace SINRICPRO_NAMESPACE { /** @@ -57,7 +57,7 @@ using OTAUpdateCallbackHandler = std::function @@ -117,7 +118,7 @@ class SinricProClass : public SinricProInterface { JsonDocument prepareResponse(JsonDocument& requestMessage); JsonDocument prepareEvent(String deviceId, const char* action, const char* cause) override; - void sendMessage(JsonDocument& jsonMessage) override; + void sendMessage(JsonDocument& jsonMessage) override; private: void handleReceiveQueue(); @@ -301,7 +302,7 @@ void SinricProClass::handle() { JsonDocument SinricProClass::prepareRequest(String deviceId, const char* action) { JsonDocument requestMessage; - JsonObject header = requestMessage[FSTR_SINRICPRO_header].to(); + JsonObject header = requestMessage[FSTR_SINRICPRO_header].to(); header[FSTR_SINRICPRO_payloadVersion] = 2; header[FSTR_SINRICPRO_signatureVersion] = 1; @@ -332,20 +333,20 @@ void SinricProClass::handleModuleRequest(JsonDocument& requestMessage, interface #endif JsonDocument responseMessage = prepareResponse(requestMessage); - - String action = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_action] | ""; - JsonObject request_value = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_value]; - JsonObject response_value = responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_value]; - SinricProRequest request{ action, "", request_value, response_value}; + + String action = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_action] | ""; + JsonObject request_value = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_value]; + JsonObject response_value = responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_value]; + SinricProRequest request{action, "", request_value, response_value}; bool success = _moduleCommandHandler.handleRequest(request); - + responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_success] = success; responseMessage[FSTR_SINRICPRO_payload].remove(FSTR_SINRICPRO_deviceId); if (!success) { if (responseMessageStr.length() > 0) { responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_message] = responseMessageStr; - responseMessageStr = ""; + responseMessageStr = ""; } else { responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_message] = "Module did not handle \"" + action + "\""; } @@ -424,13 +425,13 @@ void SinricProClass::handleReceiveQueue() { DEBUG_SINRIC("[SinricPro.handleReceiveQueue()]: Signature is valid. Processing message...\r\n"); extractTimestamp(jsonMessage); if (messageType == FSTR_SINRICPRO_response) handleResponse(jsonMessage); - if (messageType == FSTR_SINRICPRO_request) { + if (messageType == FSTR_SINRICPRO_request) { String scope = jsonMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_scope] | FSTR_SINRICPRO_device; if (strcmp(FSTR_SINRICPRO_module, scope.c_str()) == 0) { handleModuleRequest(jsonMessage, rawMessage->getInterface()); } else { handleDeviceRequest(jsonMessage, rawMessage->getInterface()); - } + } }; } else { DEBUG_SINRIC("[SinricPro.handleReceiveQueue()]: Signature is invalid! \r\n"); @@ -505,7 +506,7 @@ bool SinricProClass::isConnected() { /** * @brief Set callback function for OTA (Over-The-Air) updates. - * + * * This method registers a callback function that will be called when an OTA update is available. * The callback should handle the process of downloading and applying the update. * @@ -519,7 +520,7 @@ void SinricProClass::onOTAUpdate(OTAUpdateCallbackHandler cb) { /** * @brief Set callback function for setting a module setting. - * + * * This method registers a callback function that will be called when a request to change * a module setting is received. * @return void @@ -538,7 +539,7 @@ void SinricProClass::onSetSetting(SetSettingCallbackHandler cb) { * when the SinricPro system needs to report the device's health status. * * @param cb A function pointer of type ReportHealthCallbackHandler. - * This callback should populate a String with health information and return a boolean + * This callback should populate a String with health information and return a boolean * indicating success or failure of the health reporting process. * @return void * @see ReportHealthCallbackHandler for the definition of the callback function type. @@ -660,17 +661,21 @@ void SinricProClass::setResponseMessage(String&& message) { } /** - * @brief Get the current timestamp + * @brief * - * @return unsigned long current timestamp (unix epoch time) + * return unsigned long current timestamp(unix epoch time) * / */ unsigned long SinricProClass::getTimestamp() { return timestamp.getTimestamp(); } +String SinricProClass::sign(const String& message) { + return HMACbase64(message, appSecret); +} + JsonDocument SinricProClass::prepareResponse(JsonDocument& requestMessage) { JsonDocument responseMessage; - JsonObject header = responseMessage[FSTR_SINRICPRO_header].to(); + JsonObject header = responseMessage[FSTR_SINRICPRO_header].to(); header[FSTR_SINRICPRO_payloadVersion] = 2; header[FSTR_SINRICPRO_signatureVersion] = 1; @@ -680,7 +685,7 @@ JsonDocument SinricProClass::prepareResponse(JsonDocument& requestMessage) { payload[FSTR_SINRICPRO_scope] = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_scope]; payload[FSTR_SINRICPRO_createdAt] = 0; payload[FSTR_SINRICPRO_deviceId] = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_deviceId]; - if (requestMessage[FSTR_SINRICPRO_payload].containsKey(FSTR_SINRICPRO_instanceId)) payload[FSTR_SINRICPRO_instanceId] = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_instanceId]; + if (requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_instanceId].is()) payload[FSTR_SINRICPRO_instanceId] = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_instanceId]; payload[FSTR_SINRICPRO_message] = FSTR_SINRICPRO_OK; payload[FSTR_SINRICPRO_replyToken] = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_replyToken]; payload[FSTR_SINRICPRO_success] = false; @@ -691,7 +696,7 @@ JsonDocument SinricProClass::prepareResponse(JsonDocument& requestMessage) { JsonDocument SinricProClass::prepareEvent(String deviceId, const char* action, const char* cause) { JsonDocument eventMessage; - JsonObject header = eventMessage[FSTR_SINRICPRO_header].to(); + JsonObject header = eventMessage[FSTR_SINRICPRO_header].to(); header[FSTR_SINRICPRO_payloadVersion] = 2; header[FSTR_SINRICPRO_signatureVersion] = 1; diff --git a/src/SinricProCamera.h b/src/SinricProCamera.h index af87fd12..cc854941 100644 --- a/src/SinricProCamera.h +++ b/src/SinricProCamera.h @@ -11,6 +11,7 @@ #include "Capabilities/SettingController.h" #include "Capabilities/PushNotification.h" #include "Capabilities/PowerStateController.h" +#include "Capabilities/CameraController.h" #include "SinricProNamespace.h" namespace SINRICPRO_NAMESPACE { @@ -23,10 +24,12 @@ namespace SINRICPRO_NAMESPACE { class SinricProCamera : public SinricProDevice, public SettingController, public PushNotification, - public PowerStateController { + public PowerStateController, + public CameraController { friend class SettingController; friend class PushNotification; friend class PowerStateController; + friend class CameraController; public: SinricProCamera(const String &deviceId) : SinricProDevice(deviceId, "CAMERA") {} }; diff --git a/src/SinricProConfig.h b/src/SinricProConfig.h index 646db763..c50d0051 100644 --- a/src/SinricProConfig.h +++ b/src/SinricProConfig.h @@ -25,13 +25,34 @@ #define WEBSOCKET_SSL #endif +#ifndef SINRICPRO_SERVER_URL #define SINRICPRO_SERVER_URL "ws.sinric.pro" +#endif + +#ifndef SINRICPRO_SERVER_PORT #define SINRICPRO_SERVER_PORT 80 +#endif + +#ifndef SINRICPRO_SERVER_SSL_PORT #define SINRICPRO_SERVER_SSL_PORT 443 +#endif + +#ifndef SINRICPRO_CAMERA_URL +#define SINRICPRO_CAMERA_URL "camera.sinric.pro" +#endif + +#ifndef SINRICPRO_CAMERA_PATH +#define SINRICPRO_CAMERA_PATH "/snapshot" +#endif // UDP Configuration +#ifndef UDP_MUTLICAST_IP #define UDP_MULTICAST_IP IPAddress(224,9,9,9) +#endif + +#ifndef UDP_MULTICAST_PORT #define UDP_MULTICAST_PORT 3333 +#endif // WebSocket Configuration #ifdef DEBUG_WIFI_ISSUE @@ -43,6 +64,14 @@ #define WEBSOCKET_RETRY_COUNT 2 // EventLimiter Configuration +#ifndef EVENT_LIMIT_STATE #define EVENT_LIMIT_STATE 1000 +#endif + +#ifndef EVENT_LIMIT_SENSOR_STATE #define EVENT_LIMIT_SENSOR_STATE EVENT_LIMIT_STATE +#endif + +#ifndef EVENT_LIMIT_SENSOR_VALUE #define EVENT_LIMIT_SENSOR_VALUE 60000 +#endif diff --git a/src/SinricProDevice.h b/src/SinricProDevice.h index 04995624..6678f81f 100644 --- a/src/SinricProDevice.h +++ b/src/SinricProDevice.h @@ -33,8 +33,9 @@ class SinricProDevice : public SinricProDeviceInterface { void registerRequestHandler(const SinricProRequestHandler &requestHandler); unsigned long getTimestamp(); + String sign(const String& message); virtual bool sendEvent(JsonDocument &event); - virtual JsonDocument prepareEvent(const char *action, const char *cause); + virtual JsonDocument prepareEvent(const char *action, const char *cause); virtual String getProductType(); virtual void begin(SinricProInterface *eventSender); @@ -74,6 +75,10 @@ JsonDocument SinricProDevice::prepareEvent(const char* action, const char* cause return JsonDocument(); } +String SinricProDevice::sign(const String& message) { + if (eventSender) return eventSender->sign(message); + return ""; +} bool SinricProDevice::sendEvent(JsonDocument& event) { if (!SinricPro.isConnected()) { diff --git a/src/SinricProDeviceInterface.h b/src/SinricProDeviceInterface.h index 25740c23..0eba94f4 100644 --- a/src/SinricProDeviceInterface.h +++ b/src/SinricProDeviceInterface.h @@ -1,19 +1,19 @@ #pragma once #include "SinricProInterface.h" -#include "SinricProRequest.h" - #include "SinricProNamespace.h" +#include "SinricProRequest.h" namespace SINRICPRO_NAMESPACE { class SinricProDeviceInterface { - friend class SinricProClass; + friend class SinricProClass; + protected: - virtual bool handleRequest(SinricProRequest &request) = 0; - virtual String getDeviceId() = 0; - virtual String getProductType() = 0; - virtual void begin(SinricProInterface* eventSender) = 0; - virtual unsigned long getTimestamp(); + virtual bool handleRequest(SinricProRequest& request) = 0; + virtual String getDeviceId() = 0; + virtual String getProductType() = 0; + virtual void begin(SinricProInterface* eventSender) = 0; + virtual unsigned long getTimestamp() = 0; }; -} // SINRICPRO_NAMESPACE \ No newline at end of file +} // namespace SINRICPRO_NAMESPACE \ No newline at end of file diff --git a/src/SinricProInterface.h b/src/SinricProInterface.h index 2de9216b..38436e50 100644 --- a/src/SinricProInterface.h +++ b/src/SinricProInterface.h @@ -8,18 +8,19 @@ #pragma once #include "ArduinoJson.h" -#include "SinricProQueue.h" - #include "SinricProNamespace.h" +#include "SinricProQueue.h" namespace SINRICPRO_NAMESPACE { class SinricProInterface { - friend class SinricProDevice; + friend class SinricProDevice; + protected: - virtual void sendMessage(JsonDocument& jsonEvent); - virtual JsonDocument prepareEvent(String deviceId, const char* action, const char* cause); - virtual unsigned long getTimestamp(); - virtual bool isConnected(); + virtual void sendMessage(JsonDocument& jsonEvent) = 0; + virtual String sign(const String& message) = 0; + virtual JsonDocument prepareEvent(String deviceId, const char* action, const char* cause) = 0; + virtual unsigned long getTimestamp() = 0; + virtual bool isConnected() = 0; }; -} // SINRICPRO_NAMESPACE \ No newline at end of file +} // namespace SINRICPRO_NAMESPACE \ No newline at end of file diff --git a/src/SinricProModuleCommandHandler.h b/src/SinricProModuleCommandHandler.h index fd0d3479..fecfce33 100644 --- a/src/SinricProModuleCommandHandler.h +++ b/src/SinricProModuleCommandHandler.h @@ -8,8 +8,10 @@ #pragma once #include "SinricProRequest.h" - +#include "SinricProStrings.h" #include "SinricProNamespace.h" +#include "SinricProDebug.h" + namespace SINRICPRO_NAMESPACE { FSTR(OTA, otaUpdateAvailable); // "otaUpdateAvailable" diff --git a/src/SinricProSignature.cpp b/src/SinricProSignature.cpp index be8d06d4..3d2bc0ad 100644 --- a/src/SinricProSignature.cpp +++ b/src/SinricProSignature.cpp @@ -71,7 +71,7 @@ String calculateSignature(const char* key, String payload) { } String signMessage(String key, JsonDocument &jsonMessage) { - if (!jsonMessage.containsKey("signature")) jsonMessage["signature"].to(); + if (!jsonMessage["signature"].is()) jsonMessage["signature"].to(); jsonMessage["signature"]["HMAC"] = calculateSignature(key.c_str(), jsonMessage["payload"]); String signedMessageString; serializeJson(jsonMessage, signedMessageString); diff --git a/src/SinricProVersion.h b/src/SinricProVersion.h index 59024ce8..60f49f78 100644 --- a/src/SinricProVersion.h +++ b/src/SinricProVersion.h @@ -5,8 +5,8 @@ // Version Configuration #define SINRICPRO_VERSION_MAJOR 3 -#define SINRICPRO_VERSION_MINOR 3 -#define SINRICPRO_VERSION_REVISION 1 +#define SINRICPRO_VERSION_MINOR 4 +#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