#ifndef NOTIFICATIONLEDS_H_ #define NOTIFICATIONLEDS_H_ #include #include #include #include #define EEPROM_POS_STATUSTABLE 1 #define EEPROM_POS_HOUROFFSET 2 union _eeprom_NotificationLEDs_StatusTable { struct { unsigned hourOffset : 1; } status; uint8_t data; }; // mathematically correct modulo #define MOD(a,b) ((((a)%(b))+(b))%(b)) template< uint8_t _tpin, uint8_t _trgbwId, uint8_t _tdimmerId, uint8_t _tpowerSwitchId, uint8_t _tcommandId, uint16_t _tpixelCount, unsigned long _msgSendInterval = 60000 > class NotificationLEDs : public Sensor { public: void before() noexcept; void setup() noexcept; void presentation() noexcept; void loop(unsigned long currentUnixTime, unsigned long currentMillis) noexcept; bool process(const MyMessage& msg) noexcept; void enableDefaultScene(int id = -1) noexcept; void setStaticColor(uint32_t color) noexcept; void enableStaticColor() noexcept; void showNotification(int id = -1) noexcept; void powerOff() noexcept; void powerOn() noexcept; void returnToLastMode() noexcept; void sendRGBWStatus() noexcept; void sendDimmerStatus() noexcept; void sendPowerSwitchStatus() noexcept; void defaultScene_config(uint8_t id) noexcept; void defaultScene_init() noexcept; void defaultScene_loop(unsigned long currentUnixTime, unsigned long currentMillis) noexcept; void overrideScene_config(uint8_t id) noexcept; void overrideScene_init() noexcept; void overrideScene_loop(unsigned long currentUnixTime, unsigned long currentMillis) noexcept; void analogClock_setup() noexcept; void analogClock_config() noexcept; void analogClock_init() noexcept; void analogClock_loop(unsigned long currentUnixTime, unsigned long currentMillis) noexcept; void colorWheel1_config() noexcept; void colorWheel1_init() noexcept; void colorWheel1_loop(unsigned long currentUnixTime, unsigned long currentMillis) noexcept; void colorWheel2_config() noexcept; void colorWheel2_init() noexcept; void colorWheel2_loop(unsigned long currentUnixTime, unsigned long currentMillis) noexcept; void pulse1_config() noexcept; void pulse1_init() noexcept; void pulse1_loop(unsigned long currentUnixTime, unsigned long currentMillis) noexcept; void follow1_config() noexcept; void follow1_init() noexcept; void follow1_loop(unsigned long currentUnixTime, unsigned long currentMillis) noexcept; protected: unsigned long lastSendTime = 0; Adafruit_NeoPixel pixels = Adafruit_NeoPixel(_tpixelCount, _tpin, NEO_GRBW + NEO_KHZ800); uint8_t brightness = 25; uint8_t mode = 1; // 0: power down, 1: normal, 2: notification uint8_t lastMode = 1; uint8_t sceneMode = 0; // 0: default scene, 1: static color uint8_t defaultScene = 0; // 0: AnalogClock uint8_t overrideScene = 0; const char* _configDelims = ":"; union { uint8_t wrgb[4]; uint32_t color; } staticColor; bool staticColorOn = true; struct { struct { int8_t hourOffset = 1; bool showInnerClock = true; bool temporaryDisableInnerClock = false; unsigned long lastUnixTime = 0; } analogClock; struct { uint16_t sleepTime = 80; int8_t pixelStep = 1; int8_t colorStep = 20; int16_t currentColorIndex = 0; int16_t currentPixelIndex = 0; unsigned long lastMillis = 0; } colorWheel1; struct { uint16_t sleepTime = 80; int8_t colorStep = 20; int16_t currentColorIndex = 0; unsigned long lastMillis = 0; } colorWheel2; struct { union { uint8_t wrgb[4]; uint32_t color = 0x00FF0000; } color; uint8_t maxPulses = 5; uint8_t currentPulses = 0; int8_t pulseDir = 10; uint8_t pulseValue = 0; unsigned long lastMillis = 0; } pulse1; struct { union { uint8_t wrgb[4]; uint32_t color = 0x00FF0000; } color; uint8_t maxCycles = 5; int8_t followDir = 1; int8_t followPos = 0; uint8_t waitTime = 50; uint8_t currentCycles = 0; bool finishing = false; unsigned long lastMillis = 0; } follow1; } sceneData; }; template< uint8_t _tpin, uint8_t _trgbwId, uint8_t _tdimmerId, uint8_t _tpowerSwitchId, uint8_t _tcommandId, uint16_t _tpixelCount, unsigned long _msgSendInterval > void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> ::before() noexcept {} template< uint8_t _tpin, uint8_t _trgbwId, uint8_t _tdimmerId, uint8_t _tpowerSwitchId, uint8_t _tcommandId, uint16_t _tpixelCount, unsigned long _msgSendInterval > void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> ::setup() noexcept { pixels.begin(); analogClock_setup(); defaultScene_init(); } template< uint8_t _tpin, uint8_t _trgbwId, uint8_t _tdimmerId, uint8_t _tpowerSwitchId, uint8_t _tcommandId, uint16_t _tpixelCount, unsigned long _msgSendInterval > void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> ::presentation() noexcept { String desc(F("RGBW")); present(_trgbwId, S_RGBW_LIGHT, desc.c_str()); desc = F("Dimmer"); present(_tdimmerId, S_DIMMER, desc.c_str()); desc = F("PowerSwitch"); present(_tpowerSwitchId, S_BINARY, desc.c_str()); desc = F("Command"); present(_tcommandId, S_INFO, desc.c_str()); } template< uint8_t _tpin, uint8_t _trgbwId, uint8_t _tdimmerId, uint8_t _tpowerSwitchId, uint8_t _tcommandId, uint16_t _tpixelCount, unsigned long _msgSendInterval > void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> ::sendRGBWStatus() noexcept { if (mode > 0 && sceneMode == 1) { _sendValue(true, _trgbwId, V_STATUS); char rgbw[9] = "00000000"; // WRGB => RGBW String tmp((staticColor.color & 0xFF000000) >> 24 | ((staticColor.color & 0x00FFFFFF) << 8)); memcpy(rgbw+(8-tmp.length()), tmp.c_str(), tmp.length()); _sendValue(rgbw, _trgbwId, V_RGBW); } else { _sendValue(false, _trgbwId, V_STATUS); } } template< uint8_t _tpin, uint8_t _trgbwId, uint8_t _tdimmerId, uint8_t _tpowerSwitchId, uint8_t _tcommandId, uint16_t _tpixelCount, unsigned long _msgSendInterval > void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> ::sendDimmerStatus() noexcept { if (sceneMode == 0) { _sendValue(brightness, _tdimmerId, V_PERCENTAGE); } else { uint16_t cb = max(staticColor.wrgb[0], max(staticColor.wrgb[1], max(staticColor.wrgb[2], staticColor.wrgb[3]))) * 100 / 255; _sendValue(cb, _tdimmerId, V_PERCENTAGE); } } template< uint8_t _tpin, uint8_t _trgbwId, uint8_t _tdimmerId, uint8_t _tpowerSwitchId, uint8_t _tcommandId, uint16_t _tpixelCount, unsigned long _msgSendInterval > void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> ::sendPowerSwitchStatus() noexcept { _sendValue(mode == 0 ? 0 : 1, _tpowerSwitchId, V_STATUS); } template< uint8_t _tpin, uint8_t _trgbwId, uint8_t _tdimmerId, uint8_t _tpowerSwitchId, uint8_t _tcommandId, uint16_t _tpixelCount, unsigned long _msgSendInterval > void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> ::loop(unsigned long currentUnixTime, unsigned long currentMillis) noexcept { // default scene if (mode == 1 && sceneMode == 0) { defaultScene_loop(currentUnixTime, currentMillis); } else if (mode == 2) { overrideScene_loop(currentUnixTime, currentMillis); } if (lastSendTime == 0 || currentMillis - lastSendTime >= _msgSendInterval) { // RGBW Sensor sendRGBWStatus(); //Dimmer sendDimmerStatus(); //Power Switch sendPowerSwitchStatus(); lastSendTime = currentMillis; } } template< uint8_t _tpin, uint8_t _trgbwId, uint8_t _tdimmerId, uint8_t _tpowerSwitchId, uint8_t _tcommandId, uint16_t _tpixelCount, unsigned long _msgSendInterval > bool NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> ::process(const MyMessage& msg) noexcept { bool retval = false; // RGBW Sensor if (msg.sensor == _trgbwId) { // Set Message if (msg.getCommand() == C_SET) { if (msg.type == V_STATUS) { auto onOff = msg.getBool(); if (onOff) { enableStaticColor(); } else { enableDefaultScene(); } retval = true; } else if (msg.type == V_PERCENTAGE) { uint16_t cb = (uint16_t)max(staticColor.wrgb[0], max(staticColor.wrgb[1], max(staticColor.wrgb[2], staticColor.wrgb[3]))) * 100 / 255; uint16_t nb = msg.getByte(); for (auto i = 0; i < 4; i++) { uint16_t newcolor = ((uint16_t)staticColor.wrgb[i] * nb) / cb; if (newcolor > 255) { newcolor = 255; } staticColor.wrgb[i] = newcolor; } setStaticColor(staticColor.color); retval = true; } else if (msg.type == V_RGBW) { uint32_t color = strtoul(msg.data, NULL, 16); if (color == 0) { enableDefaultScene(); } else { // MySensors uses "RGBW" format, but the NeoPixel library uses "WRGB" format setStaticColor((color & 0xFFFFFF00) >> 8 | ((color & 0xFF) << 24)); } retval = true; } if (retval) { sendPowerSwitchStatus(); sendRGBWStatus(); sendDimmerStatus(); } // Request Message } else if (msg.getCommand() == C_REQ) { sendRGBWStatus(); retval = true; } } // Dimmer Sensor else if (msg.sensor == _tdimmerId) { // Set Message if (msg.getCommand() == C_SET) { if (msg.type == V_PERCENTAGE) { auto b = msg.getByte(); if (sceneMode == 0) { if (b == 0) { powerOff(); } else { brightness = b; sceneData.analogClock.lastUnixTime = 0; } } else { if (b == 0) { enableDefaultScene(); } else { uint16_t cb = max(staticColor.wrgb[0], max(staticColor.wrgb[1], max(staticColor.wrgb[2], staticColor.wrgb[3]))) * 100 / 255; for (auto i = 0; i < 4; i++) { uint16_t newcolor = ((uint16_t)staticColor.wrgb[i] * b) / cb; if (newcolor > 255) { newcolor = 255; } staticColor.wrgb[i] = newcolor; } setStaticColor(staticColor.color); } } retval = true; sendPowerSwitchStatus(); sendRGBWStatus(); sendDimmerStatus(); } // Request Message } else if (msg.getCommand() == C_REQ) { sendDimmerStatus(); retval = true; } } // Power Switch else if (msg.sensor == _tpowerSwitchId) { // Set Message if (msg.getCommand() == C_SET) { if (msg.type == V_STATUS) { auto v = msg.getByte(); if (v == 0) { powerOff(); } else { powerOn(); } retval = true; sendPowerSwitchStatus(); sendRGBWStatus(); sendDimmerStatus(); } // Request Message } else if (msg.getCommand() == C_REQ) { sendPowerSwitchStatus(); retval = true; } } // Command else if (msg.sensor == _tcommandId) { // Set Message if (msg.getCommand() == C_SET) { if (msg.type == V_TEXT) { auto v = msg.getString(); Serial.print("Command: "); Serial.println(v); auto cmd = strtok((char*)v, _configDelims); if (cmd[0] == 'D') { // Set default Scene auto id = atoi(cmd+1); defaultScene_config(id); if (mode == 1 && sceneMode == 0) { defaultScene_init(); } } else if (cmd[0] == 'N') { // Notification if (cmd[1] == 'C') { returnToLastMode(); } else if (cmd[1] == '\0') { showNotification(); } else { auto id = atoi(cmd+1); showNotification(id); } } retval = true; } // Request Message } else if (msg.getCommand() == C_REQ) { retval = true; } } return retval; } template< uint8_t _tpin, uint8_t _trgbwId, uint8_t _tdimmerId, uint8_t _tpowerSwitchId, uint8_t _tcommandId, uint16_t _tpixelCount, unsigned long _msgSendInterval > void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> ::enableDefaultScene(int id) { sceneMode = 0; mode = 1; if (id >= 0) { defaultScene = id; } defaultScene_init(); } template< uint8_t _tpin, uint8_t _trgbwId, uint8_t _tdimmerId, uint8_t _tpowerSwitchId, uint8_t _tcommandId, uint16_t _tpixelCount, unsigned long _msgSendInterval > void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> ::setStaticColor(uint32_t color) { sceneMode = 1; mode = 1; if (color == 0) { staticColorOn = false; for (unsigned i = 0; i < _tpixelCount; i++) { pixels.setPixelColor(i, 0); } } else { staticColorOn = true; staticColor.color = color; for (unsigned i = 0; i < _tpixelCount; i++) { pixels.setPixelColor(i, staticColor.color); } } pixels.show(); } template< uint8_t _tpin, uint8_t _trgbwId, uint8_t _tdimmerId, uint8_t _tpowerSwitchId, uint8_t _tcommandId, uint16_t _tpixelCount, unsigned long _msgSendInterval > void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> ::enableStaticColor() noexcept { sceneMode = 1; mode = 1; if (staticColorOn) { for (unsigned i = 0; i < _tpixelCount; i++) { pixels.setPixelColor(i, staticColor.color); } } else { for (unsigned i = 0; i < _tpixelCount; i++) { pixels.setPixelColor(i, 0); } } pixels.show(); } template< uint8_t _tpin, uint8_t _trgbwId, uint8_t _tdimmerId, uint8_t _tpowerSwitchId, uint8_t _tcommandId, uint16_t _tpixelCount, unsigned long _msgSendInterval > void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> ::showNotification(int id) noexcept { if (mode < 2) { lastMode = mode; } mode = 2; if (id >= 0) { overrideScene_config(id); } overrideScene_init(); } template< uint8_t _tpin, uint8_t _trgbwId, uint8_t _tdimmerId, uint8_t _tpowerSwitchId, uint8_t _tcommandId, uint16_t _tpixelCount, unsigned long _msgSendInterval > void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> ::powerOff() noexcept { mode = 0; for (unsigned i = 0; i < _tpixelCount; i++) { pixels.setPixelColor(i, 0); } pixels.show(); } template< uint8_t _tpin, uint8_t _trgbwId, uint8_t _tdimmerId, uint8_t _tpowerSwitchId, uint8_t _tcommandId, uint16_t _tpixelCount, unsigned long _msgSendInterval > void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> ::powerOn() noexcept { mode = 1; if (sceneMode == 0) { enableDefaultScene(); } else { enableStaticColor(); } pixels.show(); } template< uint8_t _tpin, uint8_t _trgbwId, uint8_t _tdimmerId, uint8_t _tpowerSwitchId, uint8_t _tcommandId, uint16_t _tpixelCount, unsigned long _msgSendInterval > void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> ::returnToLastMode() noexcept { if (lastMode == 0) { powerOff(); } else if (lastMode == 1) { powerOn(); } } template< uint8_t _tpin, uint8_t _trgbwId, uint8_t _tdimmerId, uint8_t _tpowerSwitchId, uint8_t _tcommandId, uint16_t _tpixelCount, unsigned long _msgSendInterval > void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> ::defaultScene_config(uint8_t id){ if (id == 0) { defaultScene = id; analogClock_config(); } else if (id == 1) { defaultScene = id; colorWheel1_config(); } else if (id == 2) { defaultScene = id; colorWheel2_config(); } } template< uint8_t _tpin, uint8_t _trgbwId, uint8_t _tdimmerId, uint8_t _tpowerSwitchId, uint8_t _tcommandId, uint16_t _tpixelCount, unsigned long _msgSendInterval > void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> ::defaultScene_init() noexcept { if (defaultScene == 0) { analogClock_init(); } else if (defaultScene == 1) { colorWheel1_init(); } else if (defaultScene == 2) { colorWheel2_init(); } } template< uint8_t _tpin, uint8_t _trgbwId, uint8_t _tdimmerId, uint8_t _tpowerSwitchId, uint8_t _tcommandId, uint16_t _tpixelCount, unsigned long _msgSendInterval > void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> ::defaultScene_loop(unsigned long currentUnixTime, unsigned long currentMillis) noexcept { if (defaultScene == 0) { analogClock_loop(currentUnixTime, currentMillis); } else if (defaultScene == 1) { colorWheel1_loop(currentUnixTime, currentMillis); } else if (defaultScene == 2) { colorWheel2_loop(currentUnixTime, currentMillis); } } template< uint8_t _tpin, uint8_t _trgbwId, uint8_t _tdimmerId, uint8_t _tpowerSwitchId, uint8_t _tcommandId, uint16_t _tpixelCount, unsigned long _msgSendInterval > void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> ::overrideScene_config(uint8_t id){ if (id == 0) { overrideScene = id; pulse1_config(); } else if (id == 1) { overrideScene = id; follow1_config(); } } template< uint8_t _tpin, uint8_t _trgbwId, uint8_t _tdimmerId, uint8_t _tpowerSwitchId, uint8_t _tcommandId, uint16_t _tpixelCount, unsigned long _msgSendInterval > void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> ::overrideScene_init() noexcept { if (overrideScene == 0) { pulse1_init(); } else if (overrideScene == 1) { follow1_init(); } } template< uint8_t _tpin, uint8_t _trgbwId, uint8_t _tdimmerId, uint8_t _tpowerSwitchId, uint8_t _tcommandId, uint16_t _tpixelCount, unsigned long _msgSendInterval > void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> ::overrideScene_loop(unsigned long currentUnixTime, unsigned long currentMillis) noexcept { if (overrideScene == 0) { pulse1_loop(currentUnixTime, currentMillis); } else if (overrideScene == 1) { follow1_loop(currentUnixTime, currentMillis); } } #include "SceneAnalogClock.h" #include "SceneColorWheel.h" #include "ScenePulse.h" #include "SceneFollow.h" #endif /* NOTIFICATIONLEDS_H_ */