Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

192 lines
5.1 KiB

6 years ago
6 years ago
  1. /*
  2. ALEXA MODULE
  3. Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #include "alexa.h"
  6. #if ALEXA_SUPPORT
  7. #include <queue>
  8. #include "broker.h"
  9. #include "light.h"
  10. #include "relay.h"
  11. #include "rpc.h"
  12. #include "web.h"
  13. #include "ws.h"
  14. #include <fauxmoESP.h>
  15. #include <ArduinoJson.h>
  16. struct alexa_queue_element_t {
  17. unsigned char device_id;
  18. bool state;
  19. unsigned char value;
  20. };
  21. static std::queue<alexa_queue_element_t> _alexa_queue;
  22. fauxmoESP _alexa;
  23. // -----------------------------------------------------------------------------
  24. // ALEXA
  25. // -----------------------------------------------------------------------------
  26. bool _alexaWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
  27. return (strncmp(key, "alexa", 5) == 0);
  28. }
  29. void _alexaWebSocketOnConnected(JsonObject& root) {
  30. root["alexaEnabled"] = alexaEnabled();
  31. root["alexaName"] = getSetting("alexaName");
  32. }
  33. void _alexaConfigure() {
  34. _alexa.enable(wifiConnected() && alexaEnabled());
  35. }
  36. #if WEB_SUPPORT
  37. bool _alexaBodyCallback(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
  38. return _alexa.process(request->client(), request->method() == HTTP_GET, request->url(), String((char *)data));
  39. }
  40. bool _alexaRequestCallback(AsyncWebServerRequest *request) {
  41. String body = (request->hasParam("body", true)) ? request->getParam("body", true)->value() : String();
  42. return _alexa.process(request->client(), request->method() == HTTP_GET, request->url(), body);
  43. }
  44. #endif
  45. void _alexaBrokerCallback(const String& topic, unsigned char id, unsigned int value) {
  46. // Only process status messages for switches and channels
  47. if (!topic.equals(MQTT_TOPIC_CHANNEL)
  48. && !topic.equals(MQTT_TOPIC_RELAY)) {
  49. return;
  50. }
  51. if (topic.equals(MQTT_TOPIC_CHANNEL)) {
  52. _alexa.setState(id + 1, value > 0, value);
  53. }
  54. if (topic.equals(MQTT_TOPIC_RELAY)) {
  55. #if RELAY_PROVIDER == RELAY_PROVIDER_LIGHT
  56. if (id > 0) return;
  57. #endif
  58. _alexa.setState(id, value, value > 0 ? 255 : 0);
  59. }
  60. }
  61. // -----------------------------------------------------------------------------
  62. bool alexaEnabled() {
  63. return getSetting("alexaEnabled", 1 == ALEXA_ENABLED);
  64. }
  65. void alexaLoop() {
  66. _alexa.handle();
  67. while (!_alexa_queue.empty()) {
  68. alexa_queue_element_t element = _alexa_queue.front();
  69. DEBUG_MSG_P(PSTR("[ALEXA] Device #%u state: %s value: %d\n"), element.device_id, element.state ? "ON" : "OFF", element.value);
  70. #if RELAY_PROVIDER == RELAY_PROVIDER_LIGHT
  71. if (0 == element.device_id) {
  72. relayStatus(0, element.state);
  73. } else {
  74. lightState(element.device_id - 1, element.state);
  75. lightChannel(element.device_id - 1, element.value);
  76. lightUpdate(true, true);
  77. }
  78. #else
  79. relayStatus(element.device_id, element.state);
  80. #endif
  81. _alexa_queue.pop();
  82. }
  83. }
  84. void alexaSetup() {
  85. // Backwards compatibility
  86. moveSetting("fauxmoEnabled", "alexaEnabled");
  87. // Basic fauxmoESP configuration
  88. _alexa.createServer(!WEB_SUPPORT);
  89. _alexa.setPort(80);
  90. // Use custom alexa hostname if defined, device hostname otherwise
  91. String hostname = getSetting("alexaName", ALEXA_HOSTNAME);
  92. if (hostname.length() == 0) {
  93. hostname = getSetting("hostname");
  94. }
  95. // Lights
  96. #if RELAY_PROVIDER == RELAY_PROVIDER_LIGHT
  97. // Global switch
  98. _alexa.addDevice(hostname.c_str());
  99. // For each channel
  100. for (unsigned char i = 1; i <= lightChannels(); i++) {
  101. _alexa.addDevice((hostname + " " + i).c_str());
  102. }
  103. // Relays
  104. #else
  105. unsigned int relays = relayCount();
  106. if (relays == 1) {
  107. _alexa.addDevice(hostname.c_str());
  108. } else {
  109. for (unsigned int i=1; i<=relays; i++) {
  110. _alexa.addDevice((hostname + " " + i).c_str());
  111. }
  112. }
  113. #endif
  114. // Load & cache settings
  115. _alexaConfigure();
  116. // Websockets
  117. #if WEB_SUPPORT
  118. webBodyRegister(_alexaBodyCallback);
  119. webRequestRegister(_alexaRequestCallback);
  120. wsRegister()
  121. .onVisible([](JsonObject& root) { root["alexaVisible"] = 1; })
  122. .onConnected(_alexaWebSocketOnConnected)
  123. .onKeyCheck(_alexaWebSocketOnKeyCheck);
  124. #endif
  125. // Register wifi callback
  126. wifiRegister([](justwifi_messages_t code, char * parameter) {
  127. if ((MESSAGE_CONNECTED == code) || (MESSAGE_DISCONNECTED == code)) {
  128. _alexaConfigure();
  129. }
  130. });
  131. // Callback
  132. _alexa.onSetState([&](unsigned char device_id, const char * name, bool state, unsigned char value) {
  133. alexa_queue_element_t element;
  134. element.device_id = device_id;
  135. element.state = state;
  136. element.value = value;
  137. _alexa_queue.push(element);
  138. });
  139. // Register main callbacks
  140. StatusBroker::Register(_alexaBrokerCallback);
  141. espurnaRegisterReload(_alexaConfigure);
  142. espurnaRegisterLoop(alexaLoop);
  143. }
  144. #endif