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.

186 lines
5.0 KiB

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