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.

183 lines
5.0 KiB

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