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.

265 lines
7.5 KiB

  1. /*
  2. LIGHT MODULE
  3. Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
  6. #include <Ticker.h>
  7. Ticker colorTicker;
  8. bool _lightState = false;
  9. unsigned int _lightColor[3] = {0};
  10. #if LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192
  11. #include <my9291.h>
  12. my9291 * _my9291;
  13. #endif
  14. // -----------------------------------------------------------------------------
  15. // UTILS
  16. // -----------------------------------------------------------------------------
  17. void color_string2array(const char * rgb, unsigned int * array) {
  18. char * p = (char *) rgb;
  19. if (strlen(p) == 0) return;
  20. // if color begins with a # then assume HEX RGB
  21. if (p[0] == '#') {
  22. ++p;
  23. unsigned long value = strtol(p, NULL, 16);
  24. array[0] = (value >> 16) & 0xFF;
  25. array[1] = (value >> 8) & 0xFF;
  26. array[2] = (value) & 0xFF;
  27. // it's a temperature
  28. } else if (p[strlen(p)-1] == 'K') {
  29. p[strlen(p)-1] = 0;
  30. unsigned int temperature = atoi(p);
  31. color_temperature2array(temperature, array);
  32. // otherwise assume decimal values separated by commas
  33. } else {
  34. char * tok;
  35. tok = strtok(p, ",");
  36. array[0] = atoi(tok);
  37. tok = strtok(NULL, ",");
  38. // if there are more than one value assume R,G,B
  39. if (tok != NULL) {
  40. array[1] = atoi(tok);
  41. tok = strtok(NULL, ",");
  42. if (tok != NULL) {
  43. array[2] = atoi(tok);
  44. } else {
  45. array[2] = 0;
  46. }
  47. // only one value set red, green and blue to the same value
  48. } else {
  49. array[2] = array[1] = array[0];
  50. }
  51. }
  52. }
  53. void color_array2rgb(unsigned int * array, char * rgb) {
  54. unsigned long value = array[0];
  55. value = (value << 8) + array[1];
  56. value = (value << 8) + array[2];
  57. sprintf(rgb, "#%06X", value);
  58. }
  59. // Thanks to Sacha Telgenhof for sharing this code in his AiLight library
  60. // https://github.com/stelgenhof/AiLight
  61. void color_temperature2array(unsigned int temperature, unsigned int * array) {
  62. // Force boundaries and conversion
  63. temperature = constrain(temperature, 1000, 40000) / 100;
  64. // Calculate colors
  65. unsigned int red = (temperature <= 66)
  66. ? LIGHT_MAX_VALUE
  67. : 329.698727446 * pow((temperature - 60), -0.1332047592);
  68. unsigned int green = (temperature <= 66)
  69. ? 99.4708025861 * log(temperature) - 161.1195681661
  70. : 288.1221695283 * pow(temperature, -0.0755148492);
  71. unsigned int blue = (temperature >= 66)
  72. ? LIGHT_MAX_VALUE
  73. : ((temperature <= 19)
  74. ? 0
  75. : 138.5177312231 * log(temperature - 10) - 305.0447927307);
  76. // Save values
  77. array[0] = constrain(red, 0, LIGHT_MAX_VALUE);
  78. array[1] = constrain(green, 0, LIGHT_MAX_VALUE);
  79. array[2] = constrain(blue, 0, LIGHT_MAX_VALUE);
  80. }
  81. // -----------------------------------------------------------------------------
  82. // PROVIDER
  83. // -----------------------------------------------------------------------------
  84. void _lightProviderSet(bool state, unsigned int red, unsigned int green, unsigned int blue) {
  85. unsigned int white = 0;
  86. #if (LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192) || (LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW)
  87. // If all set to the same value use white instead
  88. if ((red == green) && (green == blue)) {
  89. white = red;
  90. red = green = blue = 0;
  91. }
  92. #endif
  93. #if LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192
  94. _my9291->setState(state);
  95. _my9291->setColor((my9291_color_t) { red, green, blue, white });
  96. #endif
  97. #if (LIGHT_PROVIDER == LIGHT_PROVIDER_RGB) || (LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW)
  98. // Check state
  99. if (!state) red = green = blue = white = 0;
  100. if (RGBW_INVERSE_LOGIC) {
  101. analogWrite(RGBW_RED_PIN, red);
  102. analogWrite(RGBW_GREEN_PIN, green);
  103. analogWrite(RGBW_BLUE_PIN, blue);
  104. #if (LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW)
  105. analogWrite(RGBW_WHITE_PIN, white);
  106. #endif
  107. } else {
  108. analogWrite(RGBW_RED_PIN, LIGHT_MAX_VALUE - red);
  109. analogWrite(RGBW_GREEN_PIN, LIGHT_MAX_VALUE - green);
  110. analogWrite(RGBW_BLUE_PIN, LIGHT_MAX_VALUE - blue);
  111. #if (LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW)
  112. analogWrite(RGBW_WHITE_PIN, LIGHT_MAX_VALUE - white);
  113. #endif
  114. }
  115. #endif
  116. }
  117. // -----------------------------------------------------------------------------
  118. // LIGHT MANAGEMENT
  119. // -----------------------------------------------------------------------------
  120. void lightState(bool state) {
  121. _lightState = state;
  122. _lightProviderSet(_lightState, _lightColor[0], _lightColor[1], _lightColor[2]);
  123. }
  124. bool lightState() {
  125. return _lightState;
  126. }
  127. void lightColor(const char * color, bool save, bool forward) {
  128. color_string2array(color, _lightColor);
  129. _lightProviderSet(_lightState, _lightColor[0], _lightColor[1], _lightColor[2]);
  130. char rgb[8];
  131. color_array2rgb(_lightColor, rgb);
  132. // Delay saving to EEPROM 5 seconds to avoid wearing it out unnecessarily
  133. if (save) colorTicker.once(LIGHT_SAVE_DELAY, _lightColorSave);
  134. // Report color to MQTT broker
  135. if (forward) mqttSend(MQTT_TOPIC_COLOR, rgb);
  136. // Report color to WS clients
  137. char message[20];
  138. sprintf(message, "{\"color\": \"%s\"}", rgb);
  139. wsSend(message);
  140. }
  141. String lightColor() {
  142. char rgb[8];
  143. color_array2rgb(_lightColor, rgb);
  144. return String(rgb);
  145. }
  146. // -----------------------------------------------------------------------------
  147. // PERSISTANCE
  148. // -----------------------------------------------------------------------------
  149. void _lightColorSave() {
  150. setSetting("color", lightColor());
  151. saveSettings();
  152. }
  153. void _lightColorRestore() {
  154. String color = getSetting("color", LIGHT_DEFAULT_COLOR);
  155. color_string2array(color.c_str(), _lightColor);
  156. }
  157. // -----------------------------------------------------------------------------
  158. // MQTT
  159. // -----------------------------------------------------------------------------
  160. void lightMQTTCallback(unsigned int type, const char * topic, const char * payload) {
  161. if (type == MQTT_CONNECT_EVENT) {
  162. mqttSubscribe(MQTT_TOPIC_COLOR);
  163. }
  164. if (type == MQTT_MESSAGE_EVENT) {
  165. // Match topic
  166. String t = mqttSubtopic((char *) topic);
  167. if (!t.equals(MQTT_TOPIC_COLOR)) return;
  168. lightColor(payload, true, mqttForward());
  169. }
  170. }
  171. // -----------------------------------------------------------------------------
  172. // SETUP
  173. // -----------------------------------------------------------------------------
  174. void lightSetup() {
  175. #if LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192
  176. _my9291 = new my9291(MY9291_DI_PIN, MY9291_DCKI_PIN, MY9291_COMMAND);
  177. #endif
  178. #if (LIGHT_PROVIDER == LIGHT_PROVIDER_RGB) || (LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW)
  179. analogWriteRange(LIGHT_MAX_VALUE);
  180. analogWriteFreq(1000);
  181. pinMode(RGBW_RED_PIN, OUTPUT);
  182. pinMode(RGBW_GREEN_PIN, OUTPUT);
  183. pinMode(RGBW_BLUE_PIN, OUTPUT);
  184. #if LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW
  185. pinMode(RGBW_WHITE_PIN, OUTPUT);
  186. #endif
  187. #endif
  188. _lightColorRestore();
  189. // API entry points (protected with apikey)
  190. apiRegister(MQTT_TOPIC_COLOR, MQTT_TOPIC_COLOR,
  191. [](char * buffer, size_t len) {
  192. snprintf(buffer, len, "%s", lightColor().c_str());
  193. },
  194. [](const char * payload) {
  195. lightColor(payload, true, mqttForward());
  196. }
  197. );
  198. mqttRegister(lightMQTTCallback);
  199. }
  200. #endif // LIGHT_PROVIDER != LIGHT_PROVIDER_NONE