├── LICENSE.md ├── README.md ├── arduino ├── Credentials.h ├── README.md ├── Settings.h ├── data │ ├── countdown.html.gz │ ├── css │ │ ├── bootstrap.min.css.gz │ │ ├── jquery.minicolors.css.gz │ │ └── jquery.minicolors.png.gz │ ├── datetime.html.gz │ ├── index.html.gz │ ├── js │ │ ├── bootstrap.min.js.gz │ │ ├── index.js.gz │ │ ├── jquery-3.5.1.min.js.gz │ │ ├── jquery.minicolors.min.js.gz │ │ └── moment-with-locales.js.gz │ ├── scoreboard.html.gz │ └── temperature.html.gz └── matrix_clock_main.ino ├── homeassistant ├── README.md ├── automation.yaml ├── input_boolean.yaml ├── input_number.yaml ├── input_select.yaml ├── input_text.yaml ├── lovelace.yaml ├── script.yaml ├── sensor.yaml └── switch.yaml ├── mqtt ├── MQTT_Topics.pdf └── README.md └── stl ├── 3-Button Case.stl ├── 3_Button_1_Toggle Case.stl ├── Clock_Control_Enclosure V3.stl ├── README.md ├── RTC_Mount.STL ├── Toggle_Switch_Enclosure.stl └── WLED_Small_ Enclosure V2.stl /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Resinchem 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Matrix Clock (and more) using WS2812b LEDs 2 | 3 | ![all_modes](https://user-images.githubusercontent.com/55962781/113493423-2a029f00-94ad-11eb-8f39-43af33dc5daa.jpg) 4 | 5 | ### **NOTE**: A newer ESP32-based version with more features that does not require Home Assistant can be found here: [MatrixClock ESP-32](https://github.com/Resinchem/Matrix-Clock-ESP32) 6 | 7 | Updated version of the original 3D LED Clock, countdown timer, scoreboard and new text display mode, using 400 WS2811b pixels. 8 | 9 | ## Key Features: 10 | * Multiple large number 'fonts': original 7-segment, modern or hybrid 11 | * Temperature can display indoor or outdoor temperature (via MQTT/External service) in F or C 12 | * Scoreboard supports 3 character "team names" (e.g. DET, BOS, IND, - or any 3 characters you define). 13 | * User controllable colors for all display modes 14 | * Text display supports almost all ASCII characters from 32-122 (except for a couple reserved characters) 15 | * Text display effects, including flash, flash alternate, fade in/out, appear/appear flash and rainbow 16 | * Control via 30+ MQTT commands, local buttons or built-in web browser 17 | 18 | (While basic functionality can be controlled via local buttons or the built-in web server, most of the advanced features including the text display mode requires a properly configured MQTT broker). 19 | 20 | ### Full build details can be viewed at [Resinchem Tech Blog](https://resinchemtech.blogspot.com/2021/04/ws2812b-led-matrix-clock-scoreboard-and.html) 21 | 22 | ### Details on installation, configuration, settings and other options can be found on the [wiki](https://github.com/Resinchem/LED-Matrix-Clock_WS2812b/wiki). 23 | 24 | >*If you found this project helpful, would like to say thanks or help support future development:*
25 | >[![buy_me_a_coffee_sm](https://user-images.githubusercontent.com/55962781/159586675-7476e996-a990-4918-8825-aa6812f3ea28.jpg)](https://www.buymeacoffee.com/resinchemtech) 26 | -------------------------------------------------------------------------------- /arduino/Credentials.h: -------------------------------------------------------------------------------- 1 | #define SID "YOUR_WIFI_SSID_HERE" 2 | #define PW "YOUR_WIFI_PASSWORD" 3 | #define MQTTUSERNAME "YOUR_MQTT_USER" 4 | #define MQTTPWD "YOUR_MQTT_PASSWORD" 5 | -------------------------------------------------------------------------------- /arduino/README.md: -------------------------------------------------------------------------------- 1 | # Arduino Files 2 | 3 | There are some additional steps you must complete before uploading the .ino file to your board. See the [Wiki Installation Page](https://github.com/Resinchem/LED-Matrix-Clock_WS2812b/wiki/Installation) 4 | 5 | ### Additional Information: 6 | Full details on the build, including parts list, wiring schematics and more can be found at [Resinchem Tech Blog](https://resinchemtech.blogspot.com/2021/04/ws2812b-led-matrix-clock-scoreboard-and.html) 7 | -------------------------------------------------------------------------------- /arduino/Settings.h: -------------------------------------------------------------------------------- 1 | // Setting.h version 0.53 2 | // =============================================================================== 3 | // Update these values to match your build and board type if not using D1 Mini 4 | #define NUM_LEDS 400 // Total of 400 LED's if matrix built as shown 5 | #define MILLI_AMPS 30000 // Update to match max milliamp output of your power suppy (or about 500 milliamp lower than max to be safe) 6 | #define COUNTDOWN_OUTPUT D5 // Output pin to drive buzzer or other device upon countdown expiration 7 | #define WIFIMODE 2 // 0 = Only Soft Access Point, 1 = Only connect to local WiFi network with UN/PW, 2 = Both 8 | #define MQTTMODE 1 // 0 = Disable MQTT, 1 = Enable (will only be enabled if WiFi mode = 1 or 2 - broker must be on same network) 9 | #define MQTTSERVER "192.168.1.108" // IP Address (or url) of MQTT Broker. Use '0.0.0.0' if not enabling MQTT 10 | #define MQTTPORT 1883 // Port of MQTT Broker. This is usually 1883 11 | // Optional pushbuttons (colors listed match ones used in build instructions) 12 | // All pins need to be LOW when button open or code modifications will be required in main sketch 13 | #define DATA_PIN D6 // Change this if you are using another type of ESP board than a WeMos D1 Mini 14 | #define MODE_PIN D3 // Push button: - not used - 15 | #define V1_PIN D4 // Push button: - not used - 16 | #define V0_PIN 3 // Push button (blue): Mode button (RX pin) 17 | #define H1_PIN D7 // Push button (green): Visitor+/Countdown start/stop button 18 | #define H0_PIN 1 // Push button (red): Home+/Countdown reset button (TX pin) 19 | // Default state topic prefix: stat/matrix 20 | // Default command topci prefix: cmnd/matrix 21 | // --------------------------------------------------------------------------------------------------------------- 22 | // Options - Defaults upon boot-up. Most values can be updated via web app or via Home Assistant/MQTT after boot 23 | // --------------------------------------------------------------------------------------------------------------- 24 | 25 | byte clockMode = 0; // Default mode at boot: 0=Clock, 1=Countdown, 2=Temperature, 3=Scoreboard 26 | byte brightness = 30; // Default starting brightness at boot. 255=max brightness based on milliamp rating of power supply 27 | byte numFont = 1; // Default Large number font: 0-Original 7-segment (21 pixels), 1-modern (31 pixels), 2-hybrid (28 pixels) 28 | byte temperatureSymbol = 13; // Default temp display: 12=Celcius, 13=Fahrenheit 29 | byte temperatureSource = 1; // 0 Interior temp (from RTC module), 1 Exterior temp (provided via MQTT - MQTT use required/no conversion or correction applied) 30 | float temperatureCorrection = -3.0; // Temp from RTC module. Generally runs "hot" due to heat from chip. Adjust as needed. Does not apply if external source selected. 31 | unsigned long temperatureUpdatePeriod = 60000; // How often, in milliseconds to update MQTT time. Recommend minimum of 60000 (one minute) or greater. Set to 0 to disable updates. 32 | byte hourFormat = 12; // Change this to 24 if you want default 24 hours format instead of 12 33 | byte scoreboardLeft = 0; // Starting "Visitor" (left) score on scoreboard 34 | byte scoreboardRight = 0; // Starting "Home" (right) score on scoreboard 35 | String scoreboardTeamLeft = "&V&"; // Default visitor (left) team name on scoreboard 36 | String scoreboardTeamRight = "&H&"; // Default home (right) team name on scoreboard 37 | byte textEffect = 0; // Text effect to supply (0 = none, see documentation for others) 38 | byte textEffectSpeed = 5; // 1 = slow (1 sec) to max of 10 (.1 second) 39 | bool autoSetTimeOnBoot = true; // v0.53: Make MQTT call to request current time on boot - call must be handled via automation system (e.g. Home Assistant, NodeRed, etc.) 40 | 41 | // Default starting colors for modes 42 | // Named colors can be used. Valid values found at: http://fastled.io/docs/3.1/struct_c_r_g_b.html 43 | // Alternatively, you can specify each of the following as rgb values. Example: CRGB clockColor = CRBG(0,0,255); 44 | 45 | CRGB clockColor = CRGB:: Blue; 46 | CRGB countdownColor = CRGB::Green; 47 | CRGB countdownColorPaused = CRGB::Orange; // If different from countdownColor, countdown color will change to this when paused/stopped. 48 | CRGB countdownColorFinalMin = CRGB::Red; // If different from countdownColor, countdown color will change to this for final 60 seconds. 49 | CRGB temperatureColor = CRGB::Green; 50 | CRGB scoreboardColorLeft = CRGB::Red; 51 | CRGB scoreboardColorRight = CRGB::Green; 52 | CRGB textColorTop = CRGB::Red; 53 | CRGB textColorBottom = CRGB::Green; 54 | CRGB alternateColor = CRGB::Black; // Recommend to leave as Black. Otherwise unused pixels will be lit in digits 55 | -------------------------------------------------------------------------------- /arduino/data/countdown.html.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Resinchem/LED-Matrix-Clock_WS2812b/efc0655020edddbbd5b87c8a93def0bfdb43e4cc/arduino/data/countdown.html.gz -------------------------------------------------------------------------------- /arduino/data/css/bootstrap.min.css.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Resinchem/LED-Matrix-Clock_WS2812b/efc0655020edddbbd5b87c8a93def0bfdb43e4cc/arduino/data/css/bootstrap.min.css.gz -------------------------------------------------------------------------------- /arduino/data/css/jquery.minicolors.css.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Resinchem/LED-Matrix-Clock_WS2812b/efc0655020edddbbd5b87c8a93def0bfdb43e4cc/arduino/data/css/jquery.minicolors.css.gz -------------------------------------------------------------------------------- /arduino/data/css/jquery.minicolors.png.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Resinchem/LED-Matrix-Clock_WS2812b/efc0655020edddbbd5b87c8a93def0bfdb43e4cc/arduino/data/css/jquery.minicolors.png.gz -------------------------------------------------------------------------------- /arduino/data/datetime.html.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Resinchem/LED-Matrix-Clock_WS2812b/efc0655020edddbbd5b87c8a93def0bfdb43e4cc/arduino/data/datetime.html.gz -------------------------------------------------------------------------------- /arduino/data/index.html.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Resinchem/LED-Matrix-Clock_WS2812b/efc0655020edddbbd5b87c8a93def0bfdb43e4cc/arduino/data/index.html.gz -------------------------------------------------------------------------------- /arduino/data/js/bootstrap.min.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Resinchem/LED-Matrix-Clock_WS2812b/efc0655020edddbbd5b87c8a93def0bfdb43e4cc/arduino/data/js/bootstrap.min.js.gz -------------------------------------------------------------------------------- /arduino/data/js/index.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Resinchem/LED-Matrix-Clock_WS2812b/efc0655020edddbbd5b87c8a93def0bfdb43e4cc/arduino/data/js/index.js.gz -------------------------------------------------------------------------------- /arduino/data/js/jquery-3.5.1.min.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Resinchem/LED-Matrix-Clock_WS2812b/efc0655020edddbbd5b87c8a93def0bfdb43e4cc/arduino/data/js/jquery-3.5.1.min.js.gz -------------------------------------------------------------------------------- /arduino/data/js/jquery.minicolors.min.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Resinchem/LED-Matrix-Clock_WS2812b/efc0655020edddbbd5b87c8a93def0bfdb43e4cc/arduino/data/js/jquery.minicolors.min.js.gz -------------------------------------------------------------------------------- /arduino/data/js/moment-with-locales.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Resinchem/LED-Matrix-Clock_WS2812b/efc0655020edddbbd5b87c8a93def0bfdb43e4cc/arduino/data/js/moment-with-locales.js.gz -------------------------------------------------------------------------------- /arduino/data/scoreboard.html.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Resinchem/LED-Matrix-Clock_WS2812b/efc0655020edddbbd5b87c8a93def0bfdb43e4cc/arduino/data/scoreboard.html.gz -------------------------------------------------------------------------------- /arduino/data/temperature.html.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Resinchem/LED-Matrix-Clock_WS2812b/efc0655020edddbbd5b87c8a93def0bfdb43e4cc/arduino/data/temperature.html.gz -------------------------------------------------------------------------------- /arduino/matrix_clock_main.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * VERSION 0.53 3 | * August 7, 2022 4 | * Adds time sync upon initial boot 5 | * Adds new button press options: Next text effect (green), clear notifications (red) 6 | * Fix missing mqttConnected = true in main loop MQTT reconnect 7 | * By ResinChem Tech - licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License 8 | */ 9 | #include 10 | #include // Include RTC library by Makuna: https://github.com/Makuna/Rtc 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include // Please read the instructions on http://arduino.esp8266.com/Arduino/versions/2.3.0/doc/filesystem.html#uploading-files-to-file-system 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "Settings.h" 21 | #define countof(a) (sizeof(a) / sizeof(a[0])) 22 | // ============================================================================================== 23 | // *** REVIEW THE Settings.h and Credentials.h FILES AND SET TO MATCH YOUR ENVIRONMENT/BUILD *** 24 | // You may also set various default boot options in the Settings.h file 25 | // ============================================================================================== 26 | // Do not change any values below unless you are sure you know what you are doing! 27 | 28 | byte oldMode = 0; 29 | byte oldMQTTMode = 0; 30 | int oldTemp = 0; 31 | bool ota_flag = true; //v1.00 32 | uint16_t ota_time_elapsed = 0; //v1.00 33 | uint16_t ota_time_window = 2500; //v1.00 minimum time on boot for IP address to show in IDE ports 34 | unsigned long tempUpdatePeriod = 60; //Seconds to elapse before updating temp display in clock mode. Default 1 minute if update period not specified in Settings 35 | unsigned long tempUpdateCount = 0; 36 | int externalTemperature = 0; // Will only be used if external temp selected in Settings.h (MQTT is required to provide external temp) 37 | String textTop = ""; 38 | String textBottom = ""; 39 | String textFull = ""; 40 | long lastReconnectAttempt = 0; 41 | unsigned long prevTime = 0; 42 | unsigned long prevTempTime = 0; // To limit MQTT Temp updates so as not to flood broker 43 | byte r_val = 255; 44 | byte g_val = 0; 45 | byte b_val = 0; 46 | bool dotsOn = true; 47 | unsigned long countdownMilliSeconds; 48 | unsigned long endCountDownMillis; 49 | bool mqttConnected = false; // Will be set to true upon valid connection 50 | bool timerRunning = false; 51 | unsigned long remCountdownMillis = 0; // Stores value on pause/resume 52 | unsigned long initCountdownMillis = 0; // Stores initial last countdown value for reset 53 | //Variables for processing text effects 54 | unsigned int textEffectPeriod = 1000; // Update period for text effects in millis (min 250 max 2500) 55 | bool effectTextHide = false; // For text Flash and FlashAlternate effects 56 | int effectBrightness = 0; // For text Fade In and Fade Out effects 57 | byte oldTextEffect = 0; // For determining effect switch and to setup new effect 58 | int appearCount = 99; // Counter for Appear and Appear Flash effects 59 | 60 | #if defined(WIFIMODE) && (WIFIMODE == 0 || WIFIMODE == 2) 61 | const char* APssid = "MATRIX_AP"; 62 | const char* APpassword = "1234567890"; 63 | #endif 64 | 65 | #if defined(WIFIMODE) && (WIFIMODE == 1 || WIFIMODE == 2) 66 | #include "Credentials.h" // Edit this file in the same directory as the .ino file and add your own credentials 67 | const char *ssid = SID; 68 | const char *password = PW; 69 | #if (WIFIMODE == 1 || WIFIMODE == 2) && (MQTTMODE == 1) // MQTT only available when on local wifi 70 | const char *mqttUser = MQTTUSERNAME; 71 | const char *mqttPW = MQTTPWD; 72 | #endif 73 | #endif 74 | WiFiClient espClient; 75 | PubSubClient client(espClient); 76 | 77 | RtcDS3231 Rtc(Wire); 78 | ESP8266WebServer server(80); 79 | ESP8266HTTPUpdateServer httpUpdateServer; 80 | CRGB LEDs[NUM_LEDS]; 81 | //Large Numbers 82 | unsigned long numbers[3][18] = { 83 | // Original 7-Segment Font 84 | { 85 | 0b00000000001110111011101111110111, // [0,0] 0 86 | 0b00000000001110000000000000000111, // [1] 1 87 | 0b00000011101110111000001111110000, // [2] 2 88 | 0b00000011101110111000000001110111, // [3] 3 89 | 0b00000011101110000011100000000111, // [4] 4 90 | 0b00000011100000111011100001110111, // [5] 5 91 | 0b00000011100000111011101111110111, // [6] 6 92 | 0b00000000001110111000000000000111, // [7] 7 93 | 0b00000011101110111011101111110111, // [8] 8 94 | 0b00000011101110111011100001110111, // [9] 9 95 | 0b00000000000000000000000000000000, // [10] off 96 | 0b00000011101110111011100000000000, // [11] degrees symbol 97 | 0b00000000000000111011101111110000, // [12] C(elsius) 98 | 0b00000011100000111011101110000000, // [13] F(ahrenheit) 99 | 0b00000000001110000011101111110111, // [14] U 100 | 0b00000011101110111011101110000000, // [15] P 101 | 0b00000000000000000011101111110000, // [16] L 102 | 0b00000011101110000000001111110111, // {17] d 103 | }, 104 | // Modern Font 105 | { 106 | 0b00000000011110111011111111110111, // [1,0] 0 107 | 0b11111101000000000000000000000000, // [1] 1 108 | 0b00000011101110111010001111111000, // [2] 2 109 | 0b00000011101110111010000011110111, // [3] 3 110 | 0b00000011111111000111100000001111, // [4] 4 111 | 0b00000011100000111111100011110111, // [5] 5 112 | 0b00000011100000111011111111110111, // [6] 6 113 | 0b11100000101111111000000000100000, // [7] 7 114 | 0b00000011101110111011101111110111, // [8] 8 115 | 0b00000011101110111011100011110111, // [9] 9 116 | 0b00000000000000000000000000000000, // [10] off 117 | 0b00000011101110111011100000000000, // [11] degrees symbol 118 | 0b00000000000000111011101111110000, // [12] C(elsius) 119 | 0b00000011100000111011101110000000, // [13] F(ahrenheit) 120 | 0b00000000001110000011101111110111, // [14] U 121 | 0b00000011101110111011101110000000, // [15] P 122 | 0b00000000000000000011101111110000, // [16] L 123 | 0b00000011101110000000001111110111, // {17] d 124 | }, 125 | // Hybrid Font 126 | { 127 | 0b00000000011110111011111111110111, // [0] 0 128 | 0b11111101000000000000000000000000, // [1] 1 129 | 0b00000011101110111000001111110000, // [2] 2 130 | 0b00000011101110111000000001110111, // [3] 3 131 | 0b00000011111110000011100000000111, // [4] 4 132 | 0b00000011100000111011100001110111, // [5] 5 133 | 0b00000011100000111011111111110111, // [6] 6 134 | 0b00000000011110111000000000000111, // [7] 7 135 | 0b00000011101110111011101111110111, // [8] 8 136 | 0b00000011101110111011100001110111, // [9] 9 137 | 0b00000000000000000000000000000000, // [10] off 138 | 0b00000011101110111011100000000000, // [11] degrees symbol 139 | 0b00000000000000111011101111110000, // [12] C(elsius) 140 | 0b00000011100000111011101110000000, // [13] F(ahrenheit) 141 | 0b00000000001110000011101111110111, // [14] U 142 | 0b00000011101110111011101110000000, // [15] P 143 | 0b00000000000000000011101111110000, // [16] L 144 | 0b00000011101110000000001111110111, // {17] d 145 | } 146 | }; 147 | //#endif 148 | 149 | //Define small numbers (3 x 5) - 12 pixels total - For temp display (right-to-left) 150 | long smallNums[] = { 151 | 0b0111111111111, // [0] 0 152 | 0b0111000000011, // [1] 1 153 | 0b1111110111110, // [2] 2 154 | 0b1111110101111, // [3] 3 155 | 0b1111011100011, // [4] 4 156 | 0b1101111101111, // [5] 5 157 | 0b1101111111111, // [6] 6 158 | 0b0111110000011, // [7] 7 159 | 0b1111111111111, // [8] 8 160 | 0b1111111100011, // [9] 9 161 | 0b1110101111011, // [10] A 162 | 0b1010111111000, // [11] P 163 | 0b0001111111110, // [12] C 164 | 0b1001111111000, // [13] F 165 | 0b0111011110101, // [14] V 166 | 0b1111011111011, // [15] H 167 | 0b0000000000000, // [16] Off 168 | }; 169 | 170 | //Define small letters (3 x 5) - 14 pixels total - for text display mode (right-to-left) 171 | //Valid ascii values: 32-122 (both upper and lower case letters render the same) 172 | //Must use & or $ for leading blanks - these will always render as blank 173 | long letters[] = { 174 | 0b000000000000000, // [0] ' ' ascii 32 (space) 175 | 0b000000011101000, // [1] '!' ascii 33 (exclamation) 176 | 0b000011011000000, // [2] '"' ascii 34 (quote) 177 | 0b111010101010101, // [3] '#' ascii 35 (hashtag) 178 | 0b000000000000000, // [4] '$' ascii 36 (dollar - render as blank) 179 | 0b001010010010010, // [5] '%' ascii 37 (percent) 180 | 0b000000000000000, // [6] '&' ascii 38 (ampersand - render as blank) 181 | 0b000000101000000, // [7] ''' ascii 39 (apostrophe) 182 | 0b111001000000010, // [8] '(' ascii 40 (left paren) 183 | 0b111000010001000, // [9] ')' ascii 41 (right paren) 184 | 0b010101010100000, // [10] '*' ascii 42 (asterisk) 185 | 0b111100000100000, // [11] '+' ascii 43 (plus) 186 | 0b100000000001000, // [12] ',' ascii 44 (comma) 187 | 0b001100000100000, // [13] '-' ascii 45 (minus/dash) 188 | 0b000000000001000, // [14] '.' ascii 46 (period) 189 | 0b001010000010000, // [15] '/' ascii 47 (slash) 190 | 0b000110101110101, // [16] '0' ascii 48 //numbers 191 | 0b111000100000100, // [17] '1' ascii 49 192 | 0b001111110111110, // [18] '2' ascii 50 193 | 0b001111110001111, // [19] '3' ascii 51 194 | 0b001111011100011, // [20] '4' ascii 52 195 | 0b001101111101111, // [21] '5' ascii 53 196 | 0b001101111111111, // [22] '6' ascii 54 197 | 0b000111110000011, // [23] '7' ascii 55 198 | 0b001111111111111, // [24] '8' ascii 56 199 | 0b001111111100011, // [25] '9' ascii 57 200 | 0b110000000000000, // [26] ':' ascii 58 (colon) 201 | 0b110000000001000, // [27] ';' ascii 59 (semicolon) 202 | 0b110001000100010, // [28] '<' ascii 60 (less than) 203 | 0b110010001010001, // [29] '=' ascii 61 (equal) 204 | 0b110100010001000, // [30] '>' ascii 62 (greater than) 205 | 0b101111110000100, // [31] '?' ascii 63 (question) 206 | 0b001111111110110, // [32] '@' ascii 64 (at) 207 | 0b001110101111011, // [33] 'A' ascii 65/96 //alphas 208 | 0b001100011111111, // [34] 'B' ascii 66/98 209 | 0b000001111111110, // [35] 'C' ascii 67/99 210 | 0b000110111111101, // [36] 'D' ascii 68/100 211 | 0b001001111111110, // [37] 'E' ascii 69/101 212 | 0b001001111111000, // [38] 'F' ascii 70/102 213 | 0b001101101111101, // [39] 'G' ascii 71/103 214 | 0b001111011111011, // [40] 'H' ascii 72/104 215 | 0b111000100000100, // [41] 'I' ascii 73/105 216 | 0b000111000010101, // [42] 'J' ascii 74/106 217 | 0b001011011111011, // [43] 'K' ascii 75/107 218 | 0b000000011111110, // [44] 'L' ascii 76/108 219 | 0b010111011111011, // [45] 'M' ascii 77/109 220 | 0b010100011111011, // [46] 'N' ascii 78/110 221 | 0b000111111111111, // [47] 'O' ascii 79/111 222 | 0b001111111111000, // [48] 'P' ascii 80/112 223 | 0b100110101110111, // [49] 'Q' ascii 81/113 224 | 0b001010111111011, // [50] 'R' ascii 82/114 225 | 0b001101111101111, // [51] 'S' ascii 83/115 226 | 0b111001110000100, // [52] 'T' ascii 84/116 227 | 0b000111011111111, // [53] 'U' ascii 85/117 228 | 0b000111011110101, // [54] 'V' ascii 86/118 229 | 0b100111011111011, // [55] 'W' ascii 87/119 230 | 0b001011011011011, // [56] 'X' ascii 88/120 231 | 0b101111011100100, // [57] 'Y' ascii 89/121 232 | 0b001011110011110, // [58] 'Z' ascii 90/122 233 | 0b111001100000110, // [59] '[' ascii 91 (left bracket) 234 | 0b001000001000001, // [60] '\' ascii 92 (back slash) 235 | 0b111000110001100, // [61] ']' ascii 93 (right bracket) 236 | 0b000010101000000, // [62] '^' ascii 94 (carat) 237 | 0b000000000001110, // [63] '_' ascii 95 (underscore) 238 | 0b000010100000000, // [64] '`' ascii 96 (tick) 239 | }; 240 | 241 | //Pixel Locations for full digits. First index is segment position on matrix. (0-3 Clock, 4-7 countdown, 8-11 score) 242 | // Second index is the pixel position within the segment 243 | unsigned int fullnumPixelPos[12][33] = { 244 | {226,225,176,175,174,173,172,180,221,230,271,280,321,330,371,372,373,374,375,326,325,276,275,274,273,272,328,323,278,228,223,178}, //[0] Segment Pos 0 245 | {232,219,182,169,168,167,166,186,215,236,265,286,315,336,365,366,367,368,369,332,319,282,269,268,267,266,334,317,284,234,217,184}, //[1] Segment Pos 1 246 | {240,211,190,161,160,159,158,194,207,244,257,294,307,344,357,358,359,360,361,340,311,290,261,260,259,258,342,309,292,242,209,192}, //[2] Segment Pos 2 247 | {246,205,196,155,154,153,152,200,201,250,251,300,301,350,351,352,353,354,355,346,305,296,255,254,253,252,348,303,298,248,203,198}, //[3] Segment Pos 3 248 | {176,175,126,125,124,123,122,130,171,180,221,230,271,280,321,322,323,324,325,276,275,226,225,224,223,222,278,273,228,178,173,128}, //[4] Segment Pos 4 249 | {182,169,132,119,118,117,116,136,165,186,215,236,265,286,315,316,317,318,319,282,269,232,219,218,217,216,284,267,234,184,167,134}, //[5] Segment Pos 5 250 | {190,161,140,111,110,109,108,144,157,194,207,244,257,294,307,308,309,310,311,290,261,240,211,210,209,208,292,259,242,192,159,142}, //[6] Segment Pos 6 251 | {196,155,146,105,104,103,102,150,151,200,201,250,251,300,301,302,303,304,305,296,255,246,205,204,203,202,298,253,248,198,153,148}, //[7] Segment Pos 7 252 | {125,76,75,26,27,28,29,71,80,121,130,171,180,221,230,229,228,227,226,225,176,175,126,127,128,129,223,178,173,123,78,73}, //[8] Segment Pos 8 253 | {119,82,69,32,33,34,35,65,86,115,136,165,186,215,236,235,234,233,232,219,182,169,132,133,134,135,217,184,167,117,84,67}, //[9] Segment Pos 9 254 | {111,90,61,40,41,42,43,57,94,107,144,157,194,207,244,243,242,241,240,211,190,161,140,141,142,143,209,192,159,109,92,59}, //[10] Segment Pos 10 255 | {105,96,55,46,47,48,49,51,100,101,150,151,200,201,250,249,248,247,246,205,196,155,146,147,148,149,203,198,153,103,98,53}, //[11] Segment Pos 11 256 | 257 | }; 258 | 259 | //Pixel locations for temp digits. First index is segment position on matrix (0=ones, 1=tens) 260 | // Second index is the pixel position within the segment 261 | unsigned int smallPixelPos[6][13] = { 262 | {42,9,8,7,44,57,94,107,108,109,92,59,58}, //[0] Segment Pos Temp ones 263 | {46,5,4,3,48,53,98,103,104,105,96,55,54}, //[1] Segment Pos Temp tens 264 | {36,15,14,13,38,63,88,113,114,115,86,65,64}, //[2] Segment for Temp C/F symbol 265 | {27,24,23,22,29,72,79,122,123,124,77,74,73}, //[3] Segment for Clock AM/PM symbol 266 | {307,294,295,296,305,346,355,396,395,394,357,344,345}, //[4] Segment for Scoreboard V 267 | {321,280,281,282,319,332,369,382,381,380,371,330,331}, //[5] Segment for Scoreboard H 268 | }; 269 | 270 | // Pixel locations for small letters. First index is segment position on matrix (0-5 top row, right to left, 6-11 bottom row, right to left) 271 | // Second index is the pixel position within the segment (0-14) 272 | unsigned int letterPixelPos[18][15] = { 273 | {274,227,228,229,272,279,322,329,328,327,324,277,278,323,273}, //[0] Top row, rightmost 274 | {270,231,232,233,268,283,318,333,332,331,320,281,282,319,269}, //[1] Top row, second from right 275 | {266,235,236,237,264,287,314,337,336,335,316,285,286,315,265}, //[2] Top row, third from right 276 | {262,239,240,241,260,291,310,341,340,339,312,289,290,311,261}, //[3] Top row, fourth from right 277 | {258,243,244,245,256,295,306,345,344,343,308,293,294,307,257}, //[4] Top row, fifth from right 278 | {254,247,248,249,252,299,302,349,348,347,304,297,298,303,253}, //[5] Top row, sixth from right 279 | {77,74,73,72,79,122,129,172,173,174,127,124,123,128,78}, //[6] Bottom row, rightmost 280 | {81,70,69,68,83,118,133,168,169,170,131,120,119,132,82}, //[7] Bottom row, second from right 281 | {85,66,65,64,87,114,137,164,165,166,135,116,115,136,86}, //[8] Botton row, third from right 282 | {89,62,61,60,91,110,141,160,161,162,139,112,111,140,90}, //[9] Bottom row, fourth from right 283 | {93,58,57,56,95,106,145,156,157,158,143,108,107,144,94}, //[10] Bottom row, fifth from right 284 | {97,54,53,52,99,102,149,152,153,154,147,104,103,148,98}, //[11] Bottom row, sixth from right 285 | {325,276,277,278,323,328,373,378,377,376,375,326,327,374,324}, //[12] Scoreboard team right, rightmost 286 | {321,280,281,282,319,332,369,382,381,380,371,330,331,370,320}, //[13] Scoreboard team right, middle 287 | {317,284,285,286,315,336,365,386,385,384,367,334,335,366,316}, //[14] Scoreboard team right, leftmost 288 | {311,290,291,292,309,342,359,392,391,390,361,340,341,360,310}, //[15] Socreboard team left, rightmost 289 | {307,294,295,296,305,346,355,396,395,394,357,344,345,356,306}, //[16] Scoreboard team left, middle 290 | {303,298,299,300,301,350,351,400,399,398,353,348,349,352,302}, //[17] Scoreboard team left, leftmost 291 | }; 292 | 293 | boolean reconnect() { 294 | if (client.connect("MatrixClient", mqttUser, mqttPW)) { 295 | // Once connected, publish an announcement... 296 | client.publish("stat/matrix/status", "connected"); 297 | // ... and resubscribe 298 | client.subscribe("cmnd/matrix/#"); 299 | } 300 | return client.connected(); 301 | } 302 | // ============================================ 303 | // SETUP 304 | // ============================================ 305 | void setup() { 306 | pinMode(3, FUNCTION_3); 307 | pinMode(1, FUNCTION_3); 308 | pinMode(COUNTDOWN_OUTPUT, OUTPUT); 309 | pinMode(MODE_PIN, INPUT_PULLUP); //not used 310 | pinMode(V1_PIN, INPUT_PULLUP); //not used 311 | pinMode(V0_PIN, INPUT_PULLUP); //Blue 312 | pinMode(H1_PIN, INPUT_PULLUP); //Green 313 | pinMode(H0_PIN, INPUT_PULLUP); //Red 314 | delay(200); 315 | 316 | // RTC DS3231 Setup 317 | Rtc.Begin(); 318 | RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__); 319 | 320 | if (!Rtc.IsDateTimeValid()) { 321 | if (Rtc.LastError() != 0) { 322 | // we have a communications error see https://www.arduino.cc/en/Reference/WireEndTransmission for what the number means 323 | // Serial.print("RTC communications error = "); 324 | // Serial.println(Rtc.LastError()); 325 | } else { 326 | // Common Causes: 327 | // 1) first time you ran and the device wasn't running yet 328 | // 2) the battery on the device is low or even missing 329 | // Serial.println("RTC lost confidence in the DateTime!"); 330 | // following line sets the RTC to the date & time this sketch was compiled 331 | // it will also reset the valid flag internally unless the Rtc device is 332 | // having an issue 333 | Rtc.SetDateTime(compiled); 334 | } 335 | } 336 | 337 | WiFi.setSleepMode(WIFI_NONE_SLEEP); 338 | 339 | delay(200); 340 | 341 | FastLED.addLeds(LEDs, NUM_LEDS); 342 | FastLED.setDither(false); 343 | FastLED.setCorrection(TypicalLEDStrip); 344 | FastLED.setMaxPowerInVoltsAndMilliamps(5, MILLI_AMPS); 345 | fill_solid(LEDs, NUM_LEDS, CRGB::Black); 346 | FastLED.show(); 347 | 348 | // WiFi - AP Mode or both 349 | #if defined(WIFIMODE) && (WIFIMODE == 0 || WIFIMODE == 2) 350 | WiFi.mode(WIFI_AP_STA); 351 | WiFi.softAP(APssid, APpassword); // IP is usually 192.168.4.1 352 | #endif 353 | 354 | // WiFi - Local network Mode or both 355 | #if defined(WIFIMODE) && (WIFIMODE == 1 || WIFIMODE == 2) 356 | byte count = 0; 357 | WiFi.begin(ssid, password); 358 | while (WiFi.status() != WL_CONNECTED) { 359 | // Stop if cannot connect 360 | if (count >= 60) { 361 | // Could not connect to local WiFi 362 | return; 363 | } 364 | delay(500); 365 | LEDs[count] = CRGB::Green; 366 | FastLED.show(); 367 | count++; 368 | } 369 | IPAddress ip = WiFi.localIP(); 370 | #endif 371 | // MQTT - Only if on local wifi and MQTT enabled 372 | #if defined(MQTTMODE) && (MQTTMODE == 1 && (WIFIMODE == 1 || WIFIMODE == 2)) 373 | byte mcount = 0; 374 | client.setServer(MQTTSERVER, MQTTPORT); 375 | client.setCallback(callback); 376 | while (!client.connected( )) { 377 | client.connect("MatrixClient", mqttUser, mqttPW); 378 | if (mcount >= 60) { 379 | // Could not connect to MQTT broker 380 | return; 381 | } 382 | delay(500); 383 | LEDs[mcount] = CRGB::Blue; 384 | FastLED.show(); 385 | mcount++; 386 | } 387 | mqttConnected = true; 388 | client.subscribe("cmnd/matrix/#"); 389 | client.publish("stat/matrix/status", "connected"); 390 | oldMQTTMode = clockMode; 391 | updateMqttMode(); 392 | #endif 393 | httpUpdateServer.setup(&server); 394 | 395 | //OTA Updates 396 | ArduinoOTA.setHostname("matrix"); 397 | ArduinoOTA.onStart([]() { 398 | String type; 399 | if (ArduinoOTA.getCommand() == U_FLASH) { 400 | type = "sketch"; 401 | } else { // U_FS 402 | type = "filesystem"; 403 | } 404 | // NOTE: if updating FS this would be the place to unmount FS using FS.end() 405 | }); 406 | ArduinoOTA.begin(); 407 | // Set tempDisplayUpdate 408 | if (temperatureUpdatePeriod > 0) { 409 | // set display refresh rates to seconds 410 | tempUpdatePeriod = (temperatureUpdatePeriod / 1000); 411 | } 412 | // Set Text Effect Speed 413 | if (textEffectSpeed <= 10) { //10 is max speed 414 | textEffectPeriod = int(1000 / textEffectSpeed); 415 | } else { 416 | textEffectPeriod = 100; 417 | } 418 | 419 | // ************ Update MQTT stat vals with boot values ***************** 420 | if (mqttConnected) { 421 | //Force temperature update on MQTT broker via HA if external temp selected and temp = 0 422 | if ((temperatureSource == 1) && (externalTemperature == 0)) { 423 | client.publish("cmnd/matrix/temperature/refresh", "1"); 424 | delay(500); 425 | } 426 | updateMqttBrightness(brightness); 427 | updateMqttFont(); 428 | updateMqttClockDisplay(hourFormat); 429 | updateMqttTemperature(); 430 | updateMqttTempSymbol(temperatureSymbol); 431 | updateMqttTempCorrection(); 432 | updateMqttCountdownStartTime(initCountdownMillis); 433 | updateMqttCountdownStatus(timerRunning); 434 | updateMqttScoreboardScores(); 435 | updateMqttTextMessage(); 436 | // Default boot colors 437 | updateMqttColor(0, clockColor.r, clockColor.g, clockColor.b); 438 | updateMqttColor(1, countdownColor.r, countdownColor.g, countdownColor.b); 439 | updateMqttColor(2, countdownColorPaused.r, countdownColorPaused.g, countdownColorPaused.b); 440 | updateMqttColor(3, countdownColorFinalMin.r, countdownColorFinalMin.g, countdownColorFinalMin.b); 441 | updateMqttColor(4, temperatureColor.r, temperatureColor.g, temperatureColor.b); 442 | updateMqttColor(5, scoreboardColorLeft.r, scoreboardColorLeft.g, scoreboardColorLeft.b); 443 | updateMqttColor(6, scoreboardColorRight.r, scoreboardColorRight.g, scoreboardColorRight.b); 444 | updateMqttColor(7, textColorTop.r, textColorTop.g, textColorTop.b); 445 | updateMqttColor(8, textColorBottom.r, textColorBottom.g, textColorBottom.b); 446 | //Reset clock mode in case any MQTT updates changed it 447 | clockMode = 0; 448 | //v 0.53 Request current time in case this is a cold boot. Automation system (e.g Home Assistant/NodeRed) should handle call and send/set current date/time via MQTT call 449 | if (autoSetTimeOnBoot) { 450 | updateMQTTTime(); 451 | } 452 | } 453 | // Handlers 454 | server.on("/color", HTTP_POST, []() { 455 | r_val = server.arg("r").toInt(); 456 | g_val = server.arg("g").toInt(); 457 | b_val = server.arg("b").toInt(); 458 | clockColor = CRGB(r_val, g_val, b_val); 459 | server.send(200, "text/json", "{\"result\":\"ok\"}"); 460 | if (mqttConnected) { 461 | updateMqttColor(0, r_val, g_val, b_val); 462 | } 463 | }); 464 | 465 | server.on("/setdate", HTTP_POST, []() { 466 | // Sample input: date = "Dec 06 2009", time = "12:34:56" 467 | // Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec 468 | String datearg = server.arg("date"); 469 | String timearg = server.arg("time"); 470 | char d[12]; 471 | char t[9]; 472 | datearg.toCharArray(d, 12); 473 | timearg.toCharArray(t, 9); 474 | RtcDateTime compiled = RtcDateTime(d, t); 475 | Rtc.SetDateTime(compiled); 476 | clockMode = 0; 477 | tempUpdateCount = 0; 478 | server.send(200, "text/json", "{\"result\":\"ok\"}"); 479 | }); 480 | 481 | server.on("/brightness", HTTP_POST, []() { 482 | brightness = server.arg("brightness").toInt(); 483 | server.send(200, "text/json", "{\"result\":\"ok\"}"); 484 | if (mqttConnected) { 485 | updateMqttBrightness(brightness); 486 | } 487 | }); 488 | 489 | server.on("/countdown", HTTP_POST, []() { 490 | countdownMilliSeconds = server.arg("ms").toInt(); 491 | byte cd_r_val = server.arg("r").toInt(); 492 | byte cd_g_val = server.arg("g").toInt(); 493 | byte cd_b_val = server.arg("b").toInt(); 494 | digitalWrite(COUNTDOWN_OUTPUT, LOW); 495 | countdownColor = CRGB(cd_r_val, cd_g_val, cd_b_val); 496 | endCountDownMillis = millis() + countdownMilliSeconds; 497 | initCountdownMillis = countdownMilliSeconds; // store for reset to init value 498 | allBlank(); 499 | clockMode = 1; 500 | timerRunning = true; 501 | server.send(200, "text/json", "{\"result\":\"ok\"}"); 502 | if (mqttConnected) { 503 | updateMqttColor(1, server.arg("r").toInt(), server.arg("g").toInt(), server.arg("b").toInt()); 504 | updateMqttCountdownStartTime(initCountdownMillis); 505 | updateMqttCountdownStatus(timerRunning); 506 | } 507 | }); 508 | 509 | server.on("/temperature", HTTP_POST, []() { 510 | temperatureCorrection = server.arg("correction").toInt(); 511 | temperatureSymbol = server.arg("symbol").toInt(); 512 | clockMode = 0; //temp shares same mode with Clock 513 | tempUpdateCount = 0; 514 | server.send(200, "text/json", "{\"result\":\"ok\"}"); 515 | if (mqttConnected) { 516 | updateMqttTempCorrection(); 517 | updateMqttTempSymbol(temperatureSymbol); 518 | updateMqttTemperature(); 519 | } 520 | }); 521 | 522 | server.on("/scoreboard", HTTP_POST, []() { 523 | scoreboardLeft = server.arg("left").toInt(); 524 | scoreboardRight = server.arg("right").toInt(); 525 | scoreboardColorLeft = CRGB(server.arg("rl").toInt(),server.arg("gl").toInt(),server.arg("bl").toInt()); 526 | scoreboardColorRight = CRGB(server.arg("rr").toInt(),server.arg("gr").toInt(),server.arg("br").toInt()); 527 | clockMode = 2; 528 | server.send(200, "text/json", "{\"result\":\"ok\"}"); 529 | if (mqttConnected) { 530 | updateMqttColor(5, server.arg("rl").toInt(),server.arg("gl").toInt(),server.arg("bl").toInt()); 531 | updateMqttColor(6, server.arg("rr").toInt(),server.arg("gr").toInt(),server.arg("br").toInt()); 532 | updateMqttScoreboardScores(); 533 | } 534 | }); 535 | 536 | server.on("/hourformat", HTTP_POST, []() { 537 | hourFormat = server.arg("hourformat").toInt(); 538 | clockMode = 0; 539 | tempUpdateCount = 0; 540 | server.send(200, "text/json", "{\"result\":\"ok\"}"); 541 | if (mqttConnected) { 542 | updateMqttClockDisplay(hourFormat); 543 | } 544 | }); 545 | 546 | server.on("/clock", HTTP_POST, []() { 547 | clockMode = 0; 548 | tempUpdateCount = 0; 549 | server.send(200, "text/json", "{\"result\":\"ok\"}"); 550 | }); 551 | 552 | // New web commands for OTA updates - v1.00 553 | server.on("/restart",[](){ 554 | server.send(200, "text/html", "

Restarting...

"); 555 | delay(1000); 556 | ESP.restart(); 557 | }); 558 | server.on("/otaupdate",[]() { 559 | server.send(200, "text/html", "

Ready for upload...

Start upload from IDE now

"); 560 | ota_flag = true; 561 | ota_time_window = 20000; 562 | ota_time_elapsed = 0; 563 | }); 564 | 565 | // Before uploading the files with the "ESP8266 Sketch Data Upload" tool, zip the files (if not already zipped) with the command "gzip -r ./data/" (on Windows you can do this with a Git Bash) 566 | // *.gz files are automatically unpacked and served from your ESP (so you don't need to create a handler for each file). 567 | server.serveStatic("/", SPIFFS, "/", "max-age=86400"); 568 | server.begin(); 569 | 570 | SPIFFS.begin(); 571 | Dir dir = SPIFFS.openDir("/"); 572 | while (dir.next()) { 573 | String fileName = dir.fileName(); 574 | size_t fileSize = dir.fileSize(); 575 | } 576 | 577 | digitalWrite(COUNTDOWN_OUTPUT, LOW); 578 | //Force MQTT Temperature update from MQTT on boot and if external temp = 0 579 | }; 580 | 581 | // ============================================================= 582 | // *************** MQTT Message Processing ********************* 583 | // ============================================================= 584 | void callback(char* topic, byte* payload, unsigned int length) { 585 | payload[length] = '\0'; 586 | String message = (char*)payload; 587 | 588 | // Mode 589 | if (strcmp(topic,"cmnd/matrix/mode")==0){ 590 | clockMode = message.toInt(); 591 | if (clockMode == 0) 592 | tempUpdateCount = 0; 593 | updateMqttMode(); 594 | return; 595 | 596 | // Brightness 597 | } else if (strcmp(topic,"cmnd/matrix/brightness") == 0) { 598 | int testVal = message.toInt(); 599 | if ((testVal >= 0) && (testVal <= 255)) { 600 | brightness = message.toInt(); 601 | updateMqttBrightness(brightness); 602 | } 603 | return; 604 | // Large Number fort 605 | } else if (strcmp(topic, "cmnd/matrix/font") == 0) { 606 | int font = message.toInt(); 607 | if (font > 2) { 608 | font = 0; 609 | } 610 | numFont = font; 611 | updateMqttFont(); 612 | return; 613 | // Buzzer 614 | } else if (strcmp(topic, "cmnd/matrix/buzzer") == 0) { 615 | int buzzTime = message.toInt(); 616 | if (buzzTime > 9999) //max time 9.99 sec to avoid blocking issues 617 | buzzTime = 9999; 618 | soundBuzzer(buzzTime); 619 | 620 | // Clock 621 | // Color 622 | } else if (strcmp(topic, "cmnd/matrix/clock/color") == 0) { 623 | int firstDelim = message.indexOf(","); 624 | int secondDelim = message.indexOf(",", firstDelim+1); 625 | int mqqt_r_val = message.substring(0, firstDelim).toInt(); 626 | int mqqt_g_val = message.substring(firstDelim+1, secondDelim).toInt(); 627 | int mqqt_b_val = message.substring(secondDelim+1).toInt(); 628 | clockColor = CRGB(mqqt_r_val, mqqt_g_val, mqqt_b_val); 629 | updateMqttColor(0, mqqt_r_val, mqqt_g_val, mqqt_b_val); 630 | // Display 12/24 631 | } else if (strcmp(topic, "cmnd/matrix/clock/display") == 0) { 632 | int testVal = message.toInt(); 633 | if ((testVal == 12) || (testVal == 24)) { 634 | hourFormat = message.toInt(); 635 | clockMode = 0; 636 | tempUpdateCount = 0; 637 | updateMqttClockDisplay(hourFormat); 638 | } 639 | // Time Set 640 | } else if (strcmp(topic, "cmnd/matrix/clock/settime") == 0) { 641 | int timeDelim = message.indexOf(";"); 642 | String datearg = message.substring(0, timeDelim); 643 | String timearg = message.substring(timeDelim+1); 644 | char d[12]; 645 | char t[9]; 646 | datearg.toCharArray(d, 12); 647 | timearg.toCharArray(t, 9); 648 | RtcDateTime compiled = RtcDateTime(d, t); 649 | Rtc.SetDateTime(compiled); 650 | clockMode = 0; 651 | tempUpdateCount = 0; 652 | 653 | // Countdown 654 | // Color 655 | } else if (strcmp(topic, "cmnd/matrix/countdown/color") == 0) { 656 | int firstDelim = message.indexOf(","); 657 | int secondDelim = message.indexOf(",", firstDelim+1); 658 | int mqqt_r_val = message.substring(0, firstDelim).toInt(); 659 | int mqqt_g_val = message.substring(firstDelim+1, secondDelim).toInt(); 660 | int mqqt_b_val = message.substring(secondDelim+1).toInt(); 661 | countdownColor = CRGB(mqqt_r_val, mqqt_g_val, mqqt_b_val); 662 | updateMqttColor(1, mqqt_r_val, mqqt_g_val, mqqt_b_val); 663 | // Color Paused 664 | } else if (strcmp(topic, "cmnd/matrix/countdown/colorpaused") == 0) { 665 | int firstDelim = message.indexOf(","); 666 | int secondDelim = message.indexOf(",", firstDelim+1); 667 | int mqqt_r_val = message.substring(0, firstDelim).toInt(); 668 | int mqqt_g_val = message.substring(firstDelim+1, secondDelim).toInt(); 669 | int mqqt_b_val = message.substring(secondDelim+1).toInt(); 670 | countdownColorPaused = CRGB(mqqt_r_val, mqqt_g_val, mqqt_b_val); 671 | updateMqttColor(2, mqqt_r_val, mqqt_g_val, mqqt_b_val); 672 | // Color Final Min 673 | } else if (strcmp(topic, "cmnd/matrix/countdown/colorfinalmin") == 0) { 674 | int firstDelim = message.indexOf(","); 675 | int secondDelim = message.indexOf(",", firstDelim+1); 676 | int mqqt_r_val = message.substring(0, firstDelim).toInt(); 677 | int mqqt_g_val = message.substring(firstDelim+1, secondDelim).toInt(); 678 | int mqqt_b_val = message.substring(secondDelim+1).toInt(); 679 | countdownColorFinalMin = CRGB(mqqt_r_val, mqqt_g_val, mqqt_b_val); 680 | updateMqttColor(3, mqqt_r_val, mqqt_g_val, mqqt_b_val); 681 | } else if (strcmp(topic, "cmnd/matrix/countdown/starttime") == 0) { 682 | int firstDelim = message.indexOf(":"); 683 | int secondDelim = message.indexOf(":", firstDelim+1); 684 | int hh_val = message.substring(0, firstDelim).toInt(); 685 | int mm_val = message.substring(firstDelim+1, secondDelim).toInt(); 686 | int ss_val = message.substring(secondDelim+1).toInt(); 687 | if (hh_val > 23) 688 | hh_val = 23; 689 | if (mm_val > 59) 690 | mm_val = 59; 691 | if (ss_val > 59) 692 | ss_val = 59; 693 | timerRunning = false; //Stop timer if running to set time 694 | initCountdownMillis = (hh_val * 3600000) + (mm_val * 60000) + (ss_val * 1000); //convert to ms 695 | countdownMilliSeconds = initCountdownMillis; 696 | remCountdownMillis = initCountdownMillis; 697 | endCountDownMillis = countdownMilliSeconds + millis(); 698 | updateMqttCountdownStartTime(initCountdownMillis); 699 | updateMqttCountdownStatus(timerRunning); 700 | } else if (strcmp(topic, "cmnd/matrix/countdown/action") == 0) { 701 | int whichAction = message.toInt(); 702 | switch(whichAction) { 703 | case 0: // Start/resume countdown 704 | if (!timerRunning && remCountdownMillis > 0) { 705 | endCountDownMillis = millis() + remCountdownMillis; 706 | timerRunning = true; 707 | updateMqttCountdownAction(0); 708 | break; 709 | } 710 | case 1: // Pause/stop countdown 711 | timerRunning = false; 712 | updateMqttCountdownAction(1); 713 | break; 714 | case 2: // Stop (if running) and reset to last start time 715 | timerRunning = false; 716 | countdownMilliSeconds = initCountdownMillis; 717 | remCountdownMillis = initCountdownMillis; 718 | endCountDownMillis = countdownMilliSeconds + millis(); 719 | updateMqttCountdownAction(2); 720 | break; 721 | case 3: // Stop (if running), set to 00:00 and clear init start time 722 | timerRunning = false; 723 | countdownMilliSeconds = 0; 724 | endCountDownMillis = 0; 725 | remCountdownMillis = 0; 726 | initCountdownMillis = 0; 727 | updateMqttCountdownStartTime(initCountdownMillis); 728 | updateMqttCountdownAction(3); 729 | break; 730 | } 731 | updateMqttCountdownStatus(timerRunning); 732 | 733 | // Temperature 734 | // Color 735 | } else if (strcmp(topic, "cmnd/matrix/temperature/color") == 0) { 736 | int firstDelim = message.indexOf(","); 737 | int secondDelim = message.indexOf(",", firstDelim+1); 738 | int mqqt_r_val = message.substring(0, firstDelim).toInt(); 739 | int mqqt_g_val = message.substring(firstDelim+1, secondDelim).toInt(); 740 | int mqqt_b_val = message.substring(secondDelim+1).toInt(); 741 | temperatureColor = CRGB(mqqt_r_val, mqqt_g_val, mqqt_b_val); 742 | updateMqttColor(4, mqqt_r_val, mqqt_g_val, mqqt_b_val); 743 | if (clockMode == 0) { 744 | tempUpdateCount = 0; 745 | } 746 | //Source - internal/external 747 | } else if (strcmp(topic, "cmnd/matrix/temperature/external") == 0) { 748 | externalTemperature = message.toInt(); 749 | if (clockMode == 0) { 750 | tempUpdateCount = 0; 751 | } 752 | updateMqttTemperature(); 753 | // Symbol (12 = C, 13 = F) 754 | } else if (strcmp(topic, "cmnd/matrix/temperature/symbol") == 0) { 755 | int testVal = message.toInt(); 756 | if ((testVal == 12) || (testVal == 13)) { 757 | temperatureSymbol = message.toInt(); 758 | clockMode = 0; 759 | tempUpdateCount = 0; 760 | updateMqttTempSymbol(temperatureSymbol); 761 | updateMqttTemperature(); 762 | } 763 | // Correction 764 | } else if (strcmp(topic, "cmnd/matrix/temperature/correction") == 0) { 765 | temperatureCorrection = message.toInt(); 766 | clockMode = 0; 767 | tempUpdateCount = 0; 768 | updateMqttTempCorrection(); 769 | updateMqttTemperature(); 770 | 771 | // Scoreboard 772 | // Left Color 773 | } else if (strcmp(topic, "cmnd/matrix/scoreboard/colorleft") == 0) { 774 | int firstDelim = message.indexOf(","); 775 | int secondDelim = message.indexOf(",", firstDelim+1); 776 | int mqqt_r_val = message.substring(0, firstDelim).toInt(); 777 | int mqqt_g_val = message.substring(firstDelim+1, secondDelim).toInt(); 778 | int mqqt_b_val = message.substring(secondDelim+1).toInt(); 779 | scoreboardColorLeft = CRGB(mqqt_r_val, mqqt_g_val, mqqt_b_val); 780 | updateMqttColor(5, mqqt_r_val, mqqt_g_val, mqqt_b_val); 781 | // Right Color 782 | } else if (strcmp(topic, "cmnd/matrix/scoreboard/colorright") == 0) { 783 | int firstDelim = message.indexOf(","); 784 | int secondDelim = message.indexOf(",", firstDelim+1); 785 | int mqqt_r_val = message.substring(0, firstDelim).toInt(); 786 | int mqqt_g_val = message.substring(firstDelim+1, secondDelim).toInt(); 787 | int mqqt_b_val = message.substring(secondDelim+1).toInt(); 788 | scoreboardColorRight = CRGB(mqqt_r_val, mqqt_g_val, mqqt_b_val); 789 | updateMqttColor(6, mqqt_r_val, mqqt_g_val, mqqt_b_val); 790 | } else if (strcmp(topic, "cmnd/matrix/scoreboard/teamleft") == 0) { 791 | scoreboardTeamLeft = message.substring(0,4); 792 | clockMode = 2; 793 | updateMqttScoreboardScores(); 794 | } else if (strcmp(topic, "cmnd/matrix/scoreboard/teamright") == 0) { 795 | scoreboardTeamRight = message.substring(0,4); 796 | updateMqttScoreboardScores(); 797 | } else if (strcmp(topic, "cmnd/matrix/scoreboard/scoreleft") == 0) { 798 | scoreboardLeft = message.toInt(); 799 | if ((scoreboardLeft > 99) || (scoreboardLeft < 0)) 800 | scoreboardLeft = 0; 801 | updateMqttScoreboardScores(); 802 | } else if (strcmp(topic, "cmnd/matrix/scoreboard/scoreright") == 0) { 803 | scoreboardRight = message.toInt(); 804 | if ((scoreboardRight > 99) || (scoreboardRight < 0)) 805 | scoreboardRight = 0; 806 | updateMqttScoreboardScores(); 807 | } else if (strcmp(topic, "cmnd/matrix/scoreboard/scoreup") == 0) { 808 | unsigned int whichScore = message.toInt(); // 0=left score, 1=right score, 2=both 809 | switch(whichScore) { 810 | case 0: 811 | scoreboardLeft = scoreboardLeft + 1; 812 | if (scoreboardLeft > 99) 813 | scoreboardLeft = 0; 814 | break; 815 | case 1: 816 | scoreboardRight = scoreboardRight + 1; 817 | if (scoreboardRight > 99) 818 | scoreboardRight = 0; 819 | break; 820 | case 2: 821 | scoreboardLeft = scoreboardLeft + 1; 822 | if (scoreboardLeft > 99) 823 | scoreboardLeft = 0; 824 | scoreboardRight = scoreboardRight + 1; 825 | if (scoreboardRight > 99) 826 | scoreboardRight = 0; 827 | break; 828 | } 829 | updateMqttScoreboardScores(); 830 | } else if (strcmp(topic, "cmnd/matrix/scoreboard/scoredown") == 0) { 831 | unsigned int whichScore = message.toInt(); // 0=left score, 1=right score, 2=both 832 | switch(whichScore) { 833 | case 0: 834 | if (scoreboardLeft > 0) 835 | scoreboardLeft = scoreboardLeft - 1; 836 | break; 837 | case 1: 838 | if (scoreboardRight > 0) 839 | scoreboardRight = scoreboardRight - 1; 840 | break; 841 | case 2: 842 | if (scoreboardLeft > 0) 843 | scoreboardLeft = scoreboardLeft - 1; 844 | if (scoreboardRight > 0) 845 | scoreboardRight = scoreboardRight - 1; 846 | break; 847 | } 848 | updateMqttScoreboardScores(); 849 | } else if (strcmp(topic, "cmnd/matrix/scoreboard/reset") == 0) { 850 | unsigned int whichScore = message.toInt(); // 0=left score, 1=right score, 2=both 851 | switch(whichScore) { 852 | case 0: 853 | scoreboardLeft = 0; 854 | break; 855 | case 1: 856 | scoreboardRight = 0; 857 | break; 858 | case 2: 859 | scoreboardLeft = 0; 860 | scoreboardRight = 0; 861 | break; 862 | } 863 | updateMqttScoreboardScores(); 864 | 865 | // Text display 866 | // Top Color 867 | } else if (strcmp(topic, "cmnd/matrix/text/colortop") == 0) { 868 | int firstDelim = message.indexOf(","); 869 | int secondDelim = message.indexOf(",", firstDelim+1); 870 | int mqqt_r_val = message.substring(0, firstDelim).toInt(); 871 | int mqqt_g_val = message.substring(firstDelim+1, secondDelim).toInt(); 872 | int mqqt_b_val = message.substring(secondDelim+1).toInt(); 873 | textColorTop = CRGB(mqqt_r_val, mqqt_g_val, mqqt_b_val); 874 | updateMqttColor(7, mqqt_r_val, mqqt_g_val, mqqt_b_val); 875 | // Bottom Color 876 | } else if (strcmp(topic, "cmnd/matrix/text/colorbottom") == 0) { 877 | int firstDelim = message.indexOf(","); 878 | int secondDelim = message.indexOf(",", firstDelim+1); 879 | int mqqt_r_val = message.substring(0, firstDelim).toInt(); 880 | int mqqt_g_val = message.substring(firstDelim+1, secondDelim).toInt(); 881 | int mqqt_b_val = message.substring(secondDelim+1).toInt(); 882 | textColorBottom = CRGB(mqqt_r_val, mqqt_g_val, mqqt_b_val); 883 | updateMqttColor(8, mqqt_r_val, mqqt_g_val, mqqt_b_val); 884 | // Text Message 885 | } else if (strcmp(topic, "cmnd/matrix/text/message") == 0){ 886 | int textDelim = message.indexOf("|"); 887 | textFull = message; 888 | textTop = message.substring(0, textDelim); 889 | if (textDelim > -1) { 890 | textBottom = message.substring(textDelim+1); 891 | } else { 892 | textBottom = ""; 893 | } 894 | updateMqttTextMessage(); 895 | } else if (strcmp(topic, "cmnd/matrix/text/effect") == 0) { 896 | textEffect = message.toInt(); 897 | updateMqttTextMessage(); 898 | } else if (strcmp(topic, "cmnd/matrix/text/speed") == 0) { 899 | textEffectSpeed = message.toInt(); 900 | if ((textEffectSpeed > 0) && (textEffectSpeed <= 10)) { 901 | textEffectPeriod = int((11-textEffectSpeed) * 200); 902 | } else { 903 | textEffectPeriod = 1000; //default to 1 sec 904 | } 905 | updateMqttTextMessage(); 906 | } 907 | }; 908 | 909 | // =============================================================== 910 | // MAIN LOOP 911 | // =============================================================== 912 | void loop() { 913 | // When OTA flag set via HTML call, time to upload set at 20 sec. via server callback above. Alter there if more time desired. 914 | if (ota_flag) { 915 | displayOTA(); 916 | uint16_t ota_time_start = millis(); 917 | while (ota_time_elapsed < ota_time_window) { 918 | ArduinoOTA.handle(); 919 | ota_time_elapsed = millis()-ota_time_start; 920 | delay(10); 921 | } 922 | ota_flag = false; 923 | tempUpdateCount = 0; 924 | allBlank(); 925 | } 926 | 927 | server.handleClient(); 928 | 929 | if (MQTTMODE == 1) { 930 | if (!client.connected()) { 931 | long now = millis(); 932 | if (now - lastReconnectAttempt > 60000) { //attempt reconnect once per minute. Drop usually a result of Home Assistant/broker server restart 933 | lastReconnectAttempt = now; 934 | // Attempt to reconnect 935 | if (reconnect()) { 936 | lastReconnectAttempt = 0; 937 | mqttConnected = true; 938 | } 939 | } 940 | } else { 941 | // Client connected 942 | client.loop(); 943 | } 944 | } 945 | 946 | int modeReading = digitalRead(V0_PIN); //Blue 947 | int v1Reading = digitalRead(H1_PIN); //Green 948 | int h1_Reading = digitalRead(H0_PIN); //Red 949 | 950 | // Mode Button (Blue) 951 | if (modeReading == LOW) { 952 | clockMode = clockMode + 1; 953 | delay(500); 954 | } 955 | if (clockMode > 3) { 956 | clockMode = 0; 957 | tempUpdateCount = 0; 958 | } 959 | 960 | //V+ Button (Green): Increase visitor score / Toggle timer (run/stop) / Next text effect 961 | if (v1Reading == LOW && h1_Reading == !LOW) { 962 | if (clockMode == 2) { 963 | scoreboardLeft = scoreboardLeft + 1; 964 | if (scoreboardLeft > 99) 965 | scoreboardLeft = 0; 966 | if (mqttConnected) 967 | updateMqttScoreboardScores(); 968 | } else if (clockMode == 1) { 969 | if (!timerRunning && remCountdownMillis > 0) { 970 | endCountDownMillis = millis() + remCountdownMillis; 971 | timerRunning = true; 972 | if (mqttConnected) 973 | updateMqttCountdownAction(0); 974 | } else if (timerRunning) { 975 | timerRunning = false; 976 | if (mqttConnected) 977 | updateMqttCountdownAction(1); 978 | } 979 | if (mqttConnected) 980 | updateMqttCountdownStatus(timerRunning); 981 | } else if (clockMode == 3) { 982 | textEffect = textEffect + 1; 983 | if (textEffect > 7) { 984 | textEffect = 0; 985 | oldTextEffect = textEffect; 986 | } 987 | } 988 | delay(500); 989 | } 990 | //H+ Button (Red): Increase home score / Reset timer to starting value / Clear notification (e.g. YouTube) 991 | if (h1_Reading == LOW && v1Reading == !LOW) { 992 | if (clockMode == 2) { 993 | scoreboardRight = scoreboardRight + 1; 994 | if (scoreboardRight > 99) 995 | scoreboardRight = 0; 996 | if (mqttConnected) 997 | updateMqttScoreboardScores(); 998 | } else if (clockMode == 1) { 999 | if (!timerRunning) { 1000 | // Reset timer to last start value 1001 | countdownMilliSeconds = initCountdownMillis; 1002 | remCountdownMillis = initCountdownMillis; 1003 | endCountDownMillis = countdownMilliSeconds + millis(); 1004 | if (mqttConnected) 1005 | updateMqttCountdownAction(2); 1006 | } else { 1007 | timerRunning = false; 1008 | if (mqttConnected) { 1009 | updateMqttCountdownStatus(timerRunning); 1010 | updateMqttCountdownAction(1); 1011 | } 1012 | } 1013 | } else if (clockMode == 3) { 1014 | textEffect = 0; 1015 | oldTextEffect = 0; 1016 | allBlank(); 1017 | tempUpdateCount = 0; 1018 | clockMode = 0; 1019 | } 1020 | delay(500); 1021 | } 1022 | 1023 | //V+ and H+ buttons (green + red): Reset scoreboard scores / clear starting time 1024 | if (v1Reading == LOW && h1_Reading == LOW) { 1025 | if (clockMode == 2) { 1026 | scoreboardLeft = 0; 1027 | scoreboardRight = 0; 1028 | if (mqttConnected) 1029 | updateMqttScoreboardScores(); 1030 | } else if (clockMode == 1 && !timerRunning) { 1031 | countdownMilliSeconds = 0; 1032 | endCountDownMillis = 0; 1033 | remCountdownMillis = 0; 1034 | initCountdownMillis = 0; 1035 | if (mqttConnected) 1036 | updateMqttCountdownStartTime(initCountdownMillis); 1037 | updateMqttCountdownAction(3); 1038 | } 1039 | delay(500); 1040 | } 1041 | 1042 | unsigned long currentMillis = millis(); 1043 | // Text Effect processing 1044 | //Flash, Flash Alternate, Fade In and Fade Out 1045 | if ((clockMode == 3) && (textEffect > 0)) { 1046 | yield(); 1047 | if ((currentMillis - prevTime) >= textEffectPeriod) { 1048 | prevTime = currentMillis; 1049 | switch (textEffect) { 1050 | case 1: //Flash 1051 | if (textEffect != oldTextEffect){ 1052 | oldTextEffect = textEffect; 1053 | } 1054 | updateTextFlash(); 1055 | FastLED.setBrightness(brightness); 1056 | break; 1057 | case 2: //FlashAlternate 1058 | if (textEffect != oldTextEffect){ 1059 | oldTextEffect = textEffect; 1060 | } 1061 | updateTextFlashAlternate(); 1062 | FastLED.setBrightness(brightness); 1063 | break; 1064 | case 3: //Fade In 1065 | if (textEffect != oldTextEffect) { 1066 | effectBrightness = 0; 1067 | oldTextEffect = textEffect; 1068 | } 1069 | updateTextFadeIn(); 1070 | FastLED.setBrightness(effectBrightness); 1071 | break; 1072 | case 4: //Fade Out 1073 | if (textEffect != oldTextEffect) { 1074 | effectBrightness = brightness; 1075 | oldTextEffect = textEffect; 1076 | } 1077 | updateTextFadeOut(); 1078 | FastLED.setBrightness(effectBrightness); 1079 | break; 1080 | case 5: //Appear 1081 | if (textEffect != oldTextEffect) { 1082 | appearCount = 99; 1083 | oldTextEffect = textEffect; 1084 | } 1085 | updateTextAppear(false); 1086 | FastLED.setBrightness(brightness); 1087 | break; 1088 | case 6: //Appear Flash 1089 | if (textEffect != oldTextEffect) { 1090 | appearCount = 99; 1091 | oldTextEffect = textEffect; 1092 | } 1093 | updateTextAppear(true); 1094 | FastLED.setBrightness(brightness); 1095 | break; 1096 | case 7: //Rainbow (random letter colors) 1097 | if (textEffect != oldTextEffect){ 1098 | oldTextEffect = textEffect; 1099 | } 1100 | updateTextRainbow(); 1101 | FastLED.setBrightness(brightness); 1102 | break; 1103 | default: 1104 | textEffect = 0; 1105 | break; 1106 | } 1107 | FastLED.show(); 1108 | } 1109 | } else if (currentMillis - prevTime >= 1000) { 1110 | prevTime = currentMillis; 1111 | 1112 | if (oldMode != clockMode) { //If mode has changed, clear the display first 1113 | allBlank(); 1114 | oldMode = clockMode; 1115 | } 1116 | 1117 | if (clockMode == 0) { 1118 | updateClock(); 1119 | if (tempUpdateCount <= 0) { 1120 | updateTemperature(); 1121 | tempUpdateCount = tempUpdatePeriod; 1122 | } 1123 | tempUpdateCount -= 1; 1124 | } else if (clockMode == 1) { 1125 | updateCountdown(); 1126 | } else if (clockMode == 2) { 1127 | updateScoreboard(); 1128 | } else if (clockMode == 3) { 1129 | updateText(); 1130 | } 1131 | 1132 | 1133 | FastLED.setBrightness(brightness); 1134 | FastLED.show(); 1135 | // Update MQTT here if enabled and connected 1136 | if (mqttConnected) { 1137 | unsigned long currTempMillis = millis(); 1138 | // Mode 1139 | if (clockMode != oldMQTTMode) { 1140 | updateMqttMode(); 1141 | oldMQTTMode = clockMode; 1142 | } 1143 | if ((temperatureUpdatePeriod > 0) && (currTempMillis - prevTempTime >= temperatureUpdatePeriod)) { 1144 | prevTempTime = currTempMillis; 1145 | updateMqttTemperature(); 1146 | } 1147 | } 1148 | } 1149 | } 1150 | 1151 | // =============================================================== 1152 | // UPDATE FUNCTIONS 1153 | // =============================================================== 1154 | void updateClock() { 1155 | RtcDateTime now = Rtc.GetDateTime(); 1156 | // printDateTime(now); 1157 | 1158 | int hour = now.Hour(); 1159 | int mins = now.Minute(); 1160 | int secs = now.Second(); 1161 | byte AmPm = 16; // Default to no symbol 1162 | 1163 | //Get Am/Pm symbol if 12 hour mode 1164 | if (hourFormat == 12 && hour < 12) { 1165 | AmPm = 10; 1166 | } else if (hourFormat == 12 && hour >= 12) { 1167 | AmPm = 11; 1168 | } 1169 | 1170 | if (hourFormat == 12 && hour > 12) { 1171 | hour = hour - 12; 1172 | } else if (hourFormat == 12 && hour == 0) { //fix for midnight - 1 am, where previously showed "0" for hour 1173 | hour = 12; 1174 | } 1175 | 1176 | byte h1 = hour / 10; 1177 | byte h2 = hour % 10; 1178 | byte m1 = mins / 10; 1179 | byte m2 = mins % 10; 1180 | byte s1 = secs / 10; 1181 | byte s2 = secs % 10; 1182 | 1183 | CRGB color = clockColor; //CRGB(r_val, g_val, b_val); 1184 | 1185 | if (h1 > 0 || (hourFormat == 24 && hour == 0)) //display leading zero for midnight when 24-hour display 1186 | displayNumber(h1,3,color); 1187 | else 1188 | displayNumber(10,3,color); // Blank 1189 | 1190 | displayNumber(h2,2,color); 1191 | displayNumber(m1,1,color); 1192 | displayNumber(m2,0,color); 1193 | //Display A/P symbol if in 12-hour mode 1194 | displaySmallNum(AmPm, 3, color); 1195 | displayDots(color); 1196 | } 1197 | 1198 | void updateCountdown() { 1199 | if (countdownMilliSeconds == 0 && endCountDownMillis == 0) { 1200 | displayNumber(0,7,countdownColorPaused); 1201 | displayNumber(0,6,countdownColorPaused); 1202 | displayNumber(0,5,countdownColorPaused); 1203 | displayNumber(0,4,countdownColorPaused); 1204 | LEDs[237] = countdownColorPaused; 1205 | LEDs[187] = countdownColorPaused; 1206 | return; 1207 | } 1208 | 1209 | if (!timerRunning) { 1210 | unsigned long restMillis = remCountdownMillis; 1211 | unsigned long hours = ((restMillis / 1000) / 60) / 60; 1212 | unsigned long minutes = (restMillis / 1000) / 60; 1213 | unsigned long seconds = restMillis / 1000; 1214 | int remSeconds = seconds - (minutes * 60); 1215 | int remMinutes = minutes - (hours * 60); 1216 | byte h1 = hours / 10; 1217 | byte h2 = hours % 10; 1218 | byte m1 = remMinutes / 10; 1219 | byte m2 = remMinutes % 10; 1220 | byte s1 = remSeconds / 10; 1221 | byte s2 = remSeconds % 10; 1222 | if (hours > 0) { 1223 | // hh:mm 1224 | displayNumber(h1,7,countdownColorPaused); 1225 | displayNumber(h2,6,countdownColorPaused); 1226 | displayNumber(m1,5,countdownColorPaused); 1227 | displayNumber(m2,4,countdownColorPaused); 1228 | } else { 1229 | // mm:ss 1230 | displayNumber(m1,7,countdownColorPaused); 1231 | displayNumber(m2,6,countdownColorPaused); 1232 | displayNumber(s1,5,countdownColorPaused); 1233 | displayNumber(s2,4,countdownColorPaused); 1234 | } 1235 | LEDs[237] = countdownColorPaused; 1236 | LEDs[187] = countdownColorPaused; 1237 | return; 1238 | } 1239 | 1240 | unsigned long restMillis = endCountDownMillis - millis(); 1241 | unsigned long hours = ((restMillis / 1000) / 60) / 60; 1242 | unsigned long minutes = (restMillis / 1000) / 60; 1243 | unsigned long seconds = restMillis / 1000; 1244 | int remSeconds = seconds - (minutes * 60); 1245 | int remMinutes = minutes - (hours * 60); 1246 | 1247 | byte h1 = hours / 10; 1248 | byte h2 = hours % 10; 1249 | byte m1 = remMinutes / 10; 1250 | byte m2 = remMinutes % 10; 1251 | byte s1 = remSeconds / 10; 1252 | byte s2 = remSeconds % 10; 1253 | 1254 | remCountdownMillis = restMillis; //Store current remaining in event of pause 1255 | 1256 | CRGB color = countdownColor; 1257 | if (restMillis <= 60000) { 1258 | color = countdownColorFinalMin; 1259 | } 1260 | 1261 | if (hours > 0) { 1262 | // hh:mm 1263 | displayNumber(h1,7,color); 1264 | displayNumber(h2,6,color); 1265 | displayNumber(m1,5,color); 1266 | displayNumber(m2,4,color); 1267 | } else { 1268 | // mm:ss 1269 | displayNumber(m1,7,color); 1270 | displayNumber(m2,6,color); 1271 | displayNumber(s1,5,color); 1272 | displayNumber(s2,4,color); 1273 | } 1274 | 1275 | displayDots(color); 1276 | 1277 | if (hours <= 0 && remMinutes <= 0 && remSeconds <= 0) { 1278 | //endCountdown(); 1279 | countdownMilliSeconds = 0; 1280 | endCountDownMillis = 0; 1281 | remCountdownMillis = 0; 1282 | timerRunning = false; 1283 | FastLED.setBrightness(brightness); 1284 | FastLED.show(); 1285 | digitalWrite(COUNTDOWN_OUTPUT, HIGH); 1286 | delay(2000); //sound for 2 seconds 1287 | digitalWrite(COUNTDOWN_OUTPUT, LOW); 1288 | updateMqttCountdownStatus(timerRunning); 1289 | return; 1290 | } 1291 | } 1292 | 1293 | void updateTemperature() { 1294 | float ctemp = 0; 1295 | bool isNegative = false; 1296 | if (temperatureSource == 0) { 1297 | RtcTemperature temp = Rtc.GetTemperature(); 1298 | float ftemp = temp.AsFloatDegC(); 1299 | 1300 | if (temperatureSymbol == 13) 1301 | ftemp = (ftemp * 1.8000) + 32; 1302 | 1303 | float ctemp = ftemp + temperatureCorrection; 1304 | 1305 | } else if (temperatureSource == 1) { 1306 | ctemp = (externalTemperature * 1.0); 1307 | } 1308 | 1309 | if (ctemp < 0) { //Flip sign and set isNegative to true since byte cannot contain negative num 1310 | ctemp = ctemp * -1; 1311 | isNegative = true; 1312 | } 1313 | byte t1 = int(ctemp) / 10; 1314 | byte t2 = int(ctemp) % 10; 1315 | 1316 | //Hide leading zero 1317 | if (t1 == 0) 1318 | t1 = 16; //blank 1319 | displaySmallNum(t1,1,temperatureColor); 1320 | displaySmallNum(t2,0,temperatureColor); 1321 | displaySmallNum(temperatureSymbol,2,temperatureColor); 1322 | LEDs[110] = temperatureColor; //degree dot 1323 | if (isNegative) { 1324 | //show negative 1325 | LEDs[50] = temperatureColor; 1326 | LEDs[51] = temperatureColor; 1327 | } else { 1328 | LEDs[50] = CRGB::Black; 1329 | LEDs[51] = CRGB::Black; 1330 | } 1331 | } 1332 | 1333 | void updateScoreboard() { 1334 | byte sl1 = scoreboardLeft / 10; 1335 | byte sl2 = scoreboardLeft % 10; 1336 | byte sr1 = scoreboardRight / 10; 1337 | byte sr2 = scoreboardRight % 10; 1338 | byte vLen = scoreboardTeamLeft.length() + 1; 1339 | byte hLen = scoreboardTeamRight.length() + 1; 1340 | char v_array[vLen]; 1341 | char h_array[hLen]; 1342 | byte letternum = 0; //Default to blank space 1343 | scoreboardTeamLeft.toCharArray(v_array, vLen); 1344 | scoreboardTeamRight.toCharArray(h_array, hLen); 1345 | 1346 | displayNumber(sl1,11,scoreboardColorLeft); 1347 | displayNumber(sl2,10,scoreboardColorLeft); 1348 | displayNumber(sr1,9,scoreboardColorRight); 1349 | displayNumber(sr2,8,scoreboardColorRight); 1350 | 1351 | //Show Team Names 1352 | // Left - visitor 1353 | for (byte i=0; i<3; i++) { 1354 | yield(); 1355 | if (i <= (vLen-1)) { 1356 | letternum = getLetterIndex(v_array[i]); 1357 | } else { 1358 | letternum = 0; 1359 | } 1360 | displayLetter(letternum, (17-i), scoreboardColorLeft); 1361 | } 1362 | // Right - home 1363 | for (byte i=0; i<3; i++) { 1364 | yield(); 1365 | if (i <= (hLen-1)) { 1366 | letternum = getLetterIndex(h_array[i]); 1367 | } else { 1368 | letternum = 0; 1369 | } 1370 | displayLetter(letternum, (14-i), scoreboardColorRight); 1371 | } 1372 | 1373 | hideDots(); 1374 | } 1375 | 1376 | void updateText() { 1377 | byte topLen = textTop.length() + 1; 1378 | byte botLen = textBottom.length() + 1; 1379 | char top_array[topLen]; 1380 | char bottom_array[botLen]; 1381 | textTop.toCharArray(top_array, topLen); 1382 | textBottom.toCharArray(bottom_array, botLen); 1383 | byte letternum = 0; //Default to blank space 1384 | 1385 | //Top row 1386 | for (byte i=0; i<6; i++){ 1387 | yield(); 1388 | if (i <= (topLen-1)) { 1389 | letternum = getLetterIndex(top_array[i]); 1390 | } else { 1391 | letternum = 0; 1392 | } 1393 | displayLetter(letternum, (5-i), textColorTop); 1394 | } 1395 | //Bottom row 1396 | for (byte i=0; i<6; i++){ 1397 | yield(); 1398 | if (i <= (botLen-1)) { 1399 | letternum = getLetterIndex(bottom_array[i]); 1400 | } else { 1401 | letternum = 0; 1402 | } 1403 | displayLetter(letternum, (11-i), textColorBottom); 1404 | } 1405 | 1406 | } 1407 | 1408 | //Text Effect Mathods 1409 | //Flash (both rows on/off together) 1410 | void updateTextFlash() { 1411 | yield(); 1412 | if (effectTextHide) { 1413 | allBlank(); 1414 | } else { 1415 | updateText(); 1416 | } 1417 | effectTextHide = !effectTextHide; 1418 | } 1419 | 1420 | //Flash Alternate (one row on, one row off) 1421 | void updateTextFlashAlternate() { 1422 | byte letternum = 0; 1423 | allBlank(); 1424 | //Show 1st row when effectTextHide false 1425 | if (effectTextHide) { 1426 | byte topLen = textTop.length() + 1; 1427 | char top_array[topLen]; 1428 | textTop.toCharArray(top_array, topLen); 1429 | for (byte i=0; i<6; i++){ 1430 | yield(); 1431 | if (i <= (topLen-1)) { 1432 | letternum = getLetterIndex(top_array[i]); 1433 | } else { 1434 | letternum = 0; 1435 | } 1436 | displayLetter(letternum, (5-i), textColorTop); 1437 | } 1438 | } else { 1439 | byte botLen = textBottom.length() + 1; 1440 | char bottom_array[botLen]; 1441 | textBottom.toCharArray(bottom_array, botLen); 1442 | for (byte i=0; i<6; i++){ 1443 | yield(); 1444 | if (i <= (botLen-1)) { 1445 | letternum = getLetterIndex(bottom_array[i]); 1446 | } else { 1447 | letternum = 0; 1448 | } 1449 | displayLetter(letternum, (11-i), textColorBottom); 1450 | } 1451 | } 1452 | effectTextHide = !effectTextHide; 1453 | } 1454 | 1455 | void updateTextFadeIn(){ 1456 | if (effectBrightness == 0) { 1457 | allBlank(); 1458 | effectBrightness += int(brightness / 5); 1459 | } else if (effectBrightness > brightness) { 1460 | effectBrightness = 0; 1461 | } else { 1462 | updateText(); 1463 | effectBrightness += int(brightness / 5); 1464 | } 1465 | } 1466 | //Fade Out 1467 | void updateTextFadeOut() { 1468 | if (effectBrightness <= 0) { 1469 | allBlank(); 1470 | effectBrightness = brightness; 1471 | } else { 1472 | updateText(); 1473 | effectBrightness = (effectBrightness - int((brightness / 5))); 1474 | } 1475 | } 1476 | 1477 | //Appear and Appear Flash 1478 | void updateTextAppear(bool flash) { 1479 | byte topLen = textTop.length() + 1; 1480 | byte botLen = textBottom.length() + 1; 1481 | byte letternum = 0; 1482 | char top_array[topLen]; 1483 | char bottom_array[botLen]; 1484 | textTop.toCharArray(top_array, topLen); 1485 | textBottom.toCharArray(bottom_array, botLen); 1486 | yield(); 1487 | if (appearCount >= 99) { 1488 | allBlank(); 1489 | appearCount = 5; 1490 | } else if ((appearCount >= 14) && (appearCount <= 20)) { 1491 | // just skip and hold or start flash 1492 | if (flash) { //Start flash for 6 loops (3 on 3 off) 1493 | updateTextFlash(); 1494 | appearCount = appearCount-1; 1495 | } else { 1496 | appearCount = 99; 1497 | } 1498 | } else if ((appearCount == 12) || (appearCount == 13)) { 1499 | appearCount = 99; 1500 | } else if ((appearCount >= 6) && (appearCount <= 11)) { //Bottom row 1501 | letternum = getLetterIndex(bottom_array[11 - appearCount]); 1502 | displayLetter(letternum, appearCount, textColorBottom); 1503 | if (appearCount == 6) { 1504 | appearCount = 20; //finished display 1505 | } else { 1506 | appearCount = appearCount-1; 1507 | } 1508 | 1509 | } else if ((appearCount >= 0) && (appearCount <= 5)) { //Top row 1510 | letternum = getLetterIndex(top_array[5 - appearCount]); 1511 | displayLetter(letternum, appearCount, textColorTop); 1512 | if (appearCount == 0) { 1513 | appearCount = 11; //Move to bottom row 1514 | } else { 1515 | appearCount = appearCount - 1; 1516 | } 1517 | } 1518 | return; 1519 | } 1520 | 1521 | //Rainbow (random colors) 1522 | void updateTextRainbow() { 1523 | byte topLen = textTop.length() + 1; 1524 | byte botLen = textBottom.length() + 1; 1525 | char top_array[topLen]; 1526 | char bottom_array[botLen]; 1527 | textTop.toCharArray(top_array, topLen); 1528 | textBottom.toCharArray(bottom_array, botLen); 1529 | byte letternum = 0; //Default to blank space 1530 | byte r_val = 255; 1531 | byte g_val = 255; 1532 | byte b_val = 255; 1533 | //Top row 1534 | for (byte i=0; i<6; i++){ 1535 | yield(); 1536 | if (i <= (topLen-1)) { 1537 | letternum = getLetterIndex(top_array[i]); 1538 | } else { 1539 | letternum = 0; 1540 | } 1541 | r_val = getRandomColor(); 1542 | g_val = getRandomColor(); 1543 | b_val = getRandomColor(); 1544 | displayLetter(letternum, (5-i), CRGB(r_val, g_val, b_val)); 1545 | } 1546 | //Bottom row 1547 | for (byte i=0; i<6; i++){ 1548 | yield(); 1549 | if (i <= (botLen-1)) { 1550 | letternum = getLetterIndex(bottom_array[i]); 1551 | } else { 1552 | letternum = 0; 1553 | } 1554 | r_val = getRandomColor(); 1555 | g_val = getRandomColor(); 1556 | b_val = getRandomColor(); 1557 | displayLetter(letternum, (11-i), CRGB(r_val, g_val, b_val)); 1558 | } 1559 | return; 1560 | } 1561 | 1562 | 1563 | byte getLetterIndex(byte letterval) { 1564 | byte index = 0; 1565 | if ((letterval >= 32) && (letterval <=96)) { //Symbols, numbers & uppercase letters 1566 | index = letterval - 32; 1567 | } else if ((letterval >= 97) && (letterval <= 122)) { //lowercase letters 1568 | index = letterval - 64; 1569 | } 1570 | return index; 1571 | } 1572 | 1573 | byte getRandomColor() { 1574 | byte colorVal = 0; 1575 | colorVal = random(0,256); 1576 | return colorVal; 1577 | } 1578 | // =============================================================== 1579 | // DISPLAY FUNCTIONS 1580 | // For all LED[x] calls, subtract 1 since array is 0-based, 1581 | // but segments are based on actual pixel number which is 1-based. 1582 | // =============================================================== 1583 | void displayNumber(byte number, byte segment, CRGB color) { 1584 | /* 1585 | number passed from numbers[x][y] array. [x] defines 'font' number. [y] is the digit/character 1586 | Segment is defined in the fullnumPixelPos[] array 1587 | */ 1588 | unsigned int pixelPos = 0; 1589 | for (byte i=0; i<32; i++){ 1590 | yield(); 1591 | pixelPos = (fullnumPixelPos[segment][i] - 1); 1592 | LEDs[pixelPos] = ((numbers[numFont][number] & 1 << i) == 1 << i) ? color : alternateColor; 1593 | } 1594 | } 1595 | 1596 | void displaySmallNum(byte number, byte segment, CRGB color) { 1597 | /* 1598 | * 7 8 9 Segments 0,1 Temperature digits 1599 | * 6 10 Segment 2 Temperature Symbol (F/C) 1600 | * 5 12 11 Segment 3 A/P time symbol 1601 | * 4 0 Segment 4 V(istor) symbol 1602 | * 3 2 1 Segment 5 H(ome) symbol 1603 | */ 1604 | unsigned int pixelPos = 0; 1605 | for (byte i=0; i<13; i++) { 1606 | yield(); 1607 | pixelPos = (smallPixelPos[segment][i] - 1); 1608 | LEDs[pixelPos] = ((smallNums[number] & 1 << i) == 1 << i) ? color : alternateColor; 1609 | } 1610 | } 1611 | 1612 | void displayLetter(byte number, byte segment, CRGB color) { 1613 | /* 1614 | * 7 8 9 Segments 0-5 Top row, right to left 1615 | * 6 13 10 Segments 6-11 Bottom row, right to left 1616 | * 5 12 11 1617 | * 4 14 0 1618 | * 3 2 1 1619 | */ 1620 | unsigned int pixelPos = 0; 1621 | for (byte i=0; i<15; i++) { 1622 | yield(); 1623 | pixelPos = (letterPixelPos[segment][i] - 1); 1624 | LEDs[pixelPos] = ((letters[number] & 1 << i) == 1 << i) ? color : alternateColor; 1625 | } 1626 | } 1627 | 1628 | void allBlank() { 1629 | for (int i=0; i 61 | {% set num = states('input_select.matrix_number_font') %} 62 | {{ num[1:2] | int }} 63 | - alias: Matrix Number Font Get 64 | trigger: 65 | platform: mqtt 66 | topic: "stat/matrix/font" 67 | action: 68 | - service: input_select.select_option 69 | target: 70 | entity_id: input_select.matrix_number_font 71 | data_template: 72 | option: > 73 | {% if trigger.payload.strip() == '0' %} 74 | [0] - Seven Segment 75 | {% elif trigger.payload.strip() == '1' %} 76 | [1] - Modern 77 | {% elif trigger.payload.strip() == '2' %} 78 | [2] - Hybrid 79 | {% endif %} 80 | 81 | # ----------------------- 82 | # Auto-Dimming 83 | # ----------------------- 84 | - alias: Matrix Auto Dim On 85 | trigger: 86 | platform: state 87 | entity_id: input_boolean.matrix_auto_dim 88 | from: "off" 89 | to: "on" 90 | action: 91 | - service: mqtt.publish 92 | data: 93 | topic: "cmnd/matrix/brightness" 94 | payload: > 95 | {% if is_state('light.basement', 'on') %} 96 | {{ (((state_attr('light.basement', 'brightness')) | default(0)) / 5) | int + 5 }} 97 | {% else %} 98 | 5 99 | {% endif %} 100 | 101 | - alias: Matrix Auto Dim Update 102 | trigger: 103 | - platform: state 104 | entity_id: light.basement 105 | attribute: brightness 106 | condition: 107 | condition: and 108 | conditions: 109 | - condition: state 110 | entity_id: input_boolean.matrix_auto_dim 111 | state: "on" 112 | - condition: state 113 | entity_id: light.basement 114 | state: "on" 115 | action: 116 | service: mqtt.publish 117 | data: 118 | topic: "cmnd/matrix/brightness" 119 | payload: "{{ (((state_attr('light.basement', 'brightness')) | default(0)) / 5) | int + 5 }}" 120 | 121 | - alias: Matrix Auto Dim Light Off 122 | trigger: 123 | platform: state 124 | entity_id: light.basement 125 | from: "on" 126 | to: "off" 127 | condition: 128 | condition: state 129 | entity_id: input_boolean.matrix_auto_dim 130 | state: "on" 131 | action: 132 | service: mqtt.publish 133 | data: 134 | topic: "cmnd/matrix/brightness" 135 | payload: "5" 136 | 137 | # ----------------------- 138 | # Countdown Start Time 139 | # ----------------------- 140 | - alias: Matrix Countdown Start Set 141 | trigger: 142 | platform: state 143 | entity_id: input_text.matrix_countdown_start_time 144 | action: 145 | service: mqtt.publish 146 | data: 147 | topic: "cmnd/matrix/countdown/starttime" 148 | payload: "{{ states('input_text.matrix_countdown_start_time') }}" 149 | - alias: Matrix Countdown Start Get 150 | trigger: 151 | platform: mqtt 152 | topic: "stat/matrix/countdown/starttime" 153 | action: 154 | service: input_text.set_value 155 | data: 156 | entity_id: input_text.matrix_countdown_start_time 157 | value: "{{ trigger.payload }}" 158 | # ---------------------- 159 | # Scoreboard Team Names 160 | # ---------------------- 161 | - alias: Matrix Scoreboard Team Left Set 162 | trigger: 163 | platform: state 164 | entity_id: input_text.matrix_scoreboard_team_left 165 | action: 166 | service: mqtt.publish 167 | data: 168 | topic: "cmnd/matrix/scoreboard/teamleft" 169 | payload: "{{ states('input_text.matrix_scoreboard_team_left') }}" 170 | - alias: Matrix Scoreboard Team Left Get 171 | trigger: 172 | platform: mqtt 173 | topic: "stat/matrix/scoreboard/teamleft" 174 | action: 175 | service: input_text.set_value 176 | data: 177 | entity_id: input_text.matrix_scoreboard_team_left 178 | value: "{{ trigger.payload }}" 179 | - alias: Matrix Scoreboard Team Right Set 180 | trigger: 181 | platform: state 182 | entity_id: input_text.matrix_scoreboard_team_right 183 | action: 184 | service: mqtt.publish 185 | data: 186 | topic: "cmnd/matrix/scoreboard/teamright" 187 | payload: "{{ states('input_text.matrix_scoreboard_team_right') }}" 188 | - alias: Matrix Scoreboard Team Right Get 189 | trigger: 190 | platform: mqtt 191 | topic: "stat/matrix/scoreboard/teamright" 192 | action: 193 | service: input_text.set_value 194 | data: 195 | entity_id: input_text.matrix_scorebard_team_right 196 | value: "{{ trigger.payload }}" 197 | # ---------------------- 198 | # Temperature Correction 199 | # ---------------------- 200 | - alias: Matrix Temp Correction Set 201 | trigger: 202 | platform: state 203 | entity_id: input_text.matrix_temperature_correction 204 | action: 205 | service: mqtt.publish 206 | data: 207 | topic: "cmnd/matrix/temperature/correction" 208 | payload: "{{ states('input_text.matrix_temperature_correction') }}" 209 | - alias: Matrix Temp Correction Get 210 | trigger: 211 | platform: mqtt 212 | topic: "stat/matrix/temperature/correction" 213 | action: 214 | service: input_text.set_value 215 | data: 216 | entity_id: input_text.matrix_temperature_correction 217 | value: "{{ trigger.payload }}" 218 | 219 | # ---------------------------- 220 | # Text Message, Effect & Speed 221 | # ---------------------------- 222 | # Message 223 | - alias: Matrix Text Set 224 | trigger: 225 | platform: state 226 | entity_id: input_text.matrix_text_message 227 | action: 228 | service: mqtt.publish 229 | data: 230 | topic: "cmnd/matrix/text/message" 231 | payload: "{{ states('input_text.matrix_text_message') }}" 232 | - alias: Matrix Text Get 233 | trigger: 234 | platform: mqtt 235 | topic: "stat/matrix/text/message" 236 | action: 237 | service: input_text.set_value 238 | data: 239 | entity_id: input_text.matrix_text_message 240 | value: "{{ trigger.payload }}" 241 | # Effect 242 | - alias: Matrix Text Effect Set 243 | trigger: 244 | platform: state 245 | entity_id: input_select.matrix_text_effect 246 | action: 247 | service: mqtt.publish 248 | data: 249 | topic: "cmnd/matrix/text/effect" 250 | payload: > 251 | {% set num = states('input_select.matrix_text_effect') %} 252 | {{ num[1:3] | int }} 253 | 254 | - alias: Matrix Text Effect Get 255 | trigger: 256 | platform: mqtt 257 | topic: "stat/matrix/text/effect" 258 | action: 259 | - service: input_select.select_option 260 | target: 261 | entity_id: input_select.matrix_text_effect 262 | data_template: 263 | option: > 264 | {% if trigger.payload.strip() == '0' %} 265 | [00] - None 266 | {% elif trigger.payload.strip() == '1' %} 267 | [01] - Flash 268 | {% elif trigger.payload.strip() == '2' %} 269 | [02] - Flash Alternate 270 | {% elif trigger.payload.strip() == '3' %} 271 | [03] - Fade In 272 | {% elif trigger.payload.strip() == '4' %} 273 | [04] - Fade Out 274 | {% elif trigger.payload.strip() == '5' %} 275 | [05] - Appear 276 | {% elif trigger.payload.strip() == '6' %} 277 | [06] - Appear Flash 278 | {% elif trigger.payload.strip() == '7' %} 279 | [07] - Rainbow 280 | {% endif %} 281 | 282 | # Speed 283 | - alias: Matrix Text Speed Set 284 | trigger: 285 | platform: state 286 | entity_id: input_number.matrix_text_effect_speed 287 | action: 288 | - service: mqtt.publish 289 | data: 290 | topic: "cmnd/matrix/text/speed" 291 | payload: "{{ states('input_number.matrix_text_effect_speed') | int }}" 292 | - alias: Matrix Text Speed Get 293 | trigger: 294 | platform: mqtt 295 | topic: "stat/matrix/text/speed" 296 | action: 297 | service: input_number.set_value 298 | data: 299 | entity_id: input_number.matrix_text_effect_speed 300 | value: "{{ trigger.payload }}" 301 | # ---------------------- 302 | # Clock Colors 303 | # ----------------------- 304 | - alias: Matrix Clock Color Red Set 305 | trigger: 306 | platform: state 307 | entity_id: input_number.matrix_clock_color_red 308 | action: 309 | - delay: "00:00:01" #brief delay needed to allow all RGB values to be updated when external changes/clock reboot occurs 310 | - service: mqtt.publish 311 | data: 312 | topic: "cmnd/matrix/clock/color" 313 | payload: "{{ states('input_number.matrix_clock_color_red') ~ ',' ~ states('input_number.matrix_clock_color_green') ~ ',' ~ states('input_number.matrix_clock_color_blue') }}" 314 | - alias: Matrix Clock Color Red Get 315 | trigger: 316 | platform: mqtt 317 | topic: "stat/matrix/clock/color/red" 318 | action: 319 | service: input_number.set_value 320 | data: 321 | entity_id: input_number.matrix_clock_color_red 322 | value: "{{ trigger.payload }}" 323 | - alias: Matrix Clock Color Green Set 324 | trigger: 325 | platform: state 326 | entity_id: input_number.matrix_clock_color_green 327 | action: 328 | - delay: "00:00:01" 329 | - service: mqtt.publish 330 | data: 331 | topic: "cmnd/matrix/clock/color" 332 | payload: "{{ states('input_number.matrix_clock_color_red') ~ ',' ~ states('input_number.matrix_clock_color_green') ~ ',' ~ states('input_number.matrix_clock_color_blue') }}" 333 | - alias: Matrix Clock Color Green Get 334 | trigger: 335 | platform: mqtt 336 | topic: "stat/matrix/clock/color/green" 337 | action: 338 | service: input_number.set_value 339 | data: 340 | entity_id: input_number.matrix_clock_color_green 341 | value: "{{ trigger.payload }}" 342 | - alias: Matrix Clock Color Blue Set 343 | trigger: 344 | platform: state 345 | entity_id: input_number.matrix_clock_color_blue 346 | action: 347 | - delay: "00:00:01" 348 | - service: mqtt.publish 349 | data: 350 | topic: "cmnd/matrix/clock/color" 351 | payload: "{{ states('input_number.matrix_clock_color_red') ~ ',' ~ states('input_number.matrix_clock_color_green') ~ ',' ~ states('input_number.matrix_clock_color_blue') }}" 352 | - alias: Matrix Clock Color Blue Get 353 | trigger: 354 | platform: mqtt 355 | topic: "stat/matrix/clock/color/blue" 356 | action: 357 | service: input_number.set_value 358 | data: 359 | entity_id: input_number.matrix_clock_color_blue 360 | value: "{{ trigger.payload }}" 361 | # ----------------------- 362 | # Countdown Colors 363 | # ----------------------- 364 | # Active 365 | - alias: Matrix Countdown Color Red Set 366 | trigger: 367 | platform: state 368 | entity_id: input_number.matrix_countdown_color_red 369 | action: 370 | - delay: "00:00:01" 371 | - service: mqtt.publish 372 | data: 373 | topic: "cmnd/matrix/countdown/color" 374 | payload: "{{ states('input_number.matrix_countdown_color_red') ~ ',' ~ states('input_number.matrix_countdown_color_green') ~ ',' ~ states('input_number.matrix_countdown_color_blue') }}" 375 | - alias: Matrix Countdown Color Red Get 376 | trigger: 377 | platform: mqtt 378 | topic: "stat/matrix/countdown/color/red" 379 | action: 380 | service: input_number.set_value 381 | data: 382 | entity_id: input_number.matrix_countdown_color_red 383 | value: "{{ trigger.payload }}" 384 | - alias: Matrix Countdown Color Green Set 385 | trigger: 386 | platform: state 387 | entity_id: input_number.matrix_countdown_color_green 388 | action: 389 | - delay: "00:00:01" 390 | - service: mqtt.publish 391 | data: 392 | topic: "cmnd/matrix/countdown/color" 393 | payload: "{{ states('input_number.matrix_countdown_color_red') ~ ',' ~ states('input_number.matrix_countdown_color_green') ~ ',' ~ states('input_number.matrix_countdown_color_blue') }}" 394 | - alias: Matrix Countdown Color Green Get 395 | trigger: 396 | platform: mqtt 397 | topic: "stat/matrix/countdown/color/green" 398 | action: 399 | service: input_number.set_value 400 | data: 401 | entity_id: input_number.matrix_countdown_color_green 402 | value: "{{ trigger.payload }}" 403 | - alias: Matrix Countdown Color Blue Set 404 | trigger: 405 | platform: state 406 | entity_id: input_number.matrix_countdown_color_blue 407 | action: 408 | - delay: "00:00:01" 409 | - service: mqtt.publish 410 | data: 411 | topic: "cmnd/matrix/countdown/color" 412 | payload: "{{ states('input_number.matrix_countdown_color_red') ~ ',' ~ states('input_number.matrix_countdown_color_green') ~ ',' ~ states('input_number.matrix_countdown_color_blue') }}" 413 | - alias: Matrix Countdown Color Blue Get 414 | trigger: 415 | platform: mqtt 416 | topic: "stat/matrix/countdown/color/blue" 417 | action: 418 | service: input_number.set_value 419 | data: 420 | entity_id: input_number.matrix_countdown_color_blue 421 | value: "{{ trigger.payload }}" 422 | # Paused 423 | - alias: Matrix Countdown Color Paused Red Set 424 | trigger: 425 | platform: state 426 | entity_id: input_number.matrix_countdown_color_paused_red 427 | action: 428 | - delay: "00:00:01" 429 | - service: mqtt.publish 430 | data: 431 | topic: "cmnd/matrix/countdown/colorpaused" 432 | payload: "{{ states('input_number.matrix_countdown_color_paused_red') ~ ',' ~ states('input_number.matrix_countdown_color_paused_green') ~ ',' ~ states('input_number.matrix_countdown_color_paused_blue') }}" 433 | - alias: Matrix Countdown Color Paused Red Get 434 | trigger: 435 | platform: mqtt 436 | topic: "stat/matrix/countdown/colorpaused/red" 437 | action: 438 | service: input_number.set_value 439 | data: 440 | entity_id: input_number.matrix_countdown_color_paused_red 441 | value: "{{ trigger.payload }}" 442 | - alias: Matrix Countdown Color Paused Green Set 443 | trigger: 444 | platform: state 445 | entity_id: input_number.matrix_countdown_color_paused_green 446 | action: 447 | - delay: "00:00:01" 448 | - service: mqtt.publish 449 | data: 450 | topic: "cmnd/matrix/countdown/colorpaused" 451 | payload: "{{ states('input_number.matrix_countdown_color_paused_red') ~ ',' ~ states('input_number.matrix_countdown_color_paused_green') ~ ',' ~ states('input_number.matrix_countdown_color_paused_blue') }}" 452 | - alias: Matrix Countdown Color Paused Green Get 453 | trigger: 454 | platform: mqtt 455 | topic: "stat/matrix/countdown/colorpaused/green" 456 | action: 457 | service: input_number.set_value 458 | data: 459 | entity_id: input_number.matrix_countdown_color_paused_green 460 | value: "{{ trigger.payload }}" 461 | - alias: Matrix Countdown Color Paused Blue Set 462 | trigger: 463 | platform: state 464 | entity_id: input_number.matrix_countdown_color_paused_blue 465 | action: 466 | - delay: "00:00:01" 467 | - service: mqtt.publish 468 | data: 469 | topic: "cmnd/matrix/countdown/colorpaused" 470 | payload: "{{ states('input_number.matrix_countdown_color_paused_red') ~ ',' ~ states('input_number.matrix_countdown_color_paused_green') ~ ',' ~ states('input_number.matrix_countdown_color_paused_blue') }}" 471 | - alias: Matrix Countdown Color Paused Blue Get 472 | trigger: 473 | platform: mqtt 474 | topic: "stat/matrix/countdown/colorpaused/blue" 475 | action: 476 | service: input_number.set_value 477 | data: 478 | entity_id: input_number.matrix_countdown_color_paused_blue 479 | value: "{{ trigger.payload }}" 480 | # Final Min 481 | - alias: Matrix Countdown Color Last Min Red Set 482 | trigger: 483 | platform: state 484 | entity_id: input_number.matrix_countdown_color_lastmin_red 485 | action: 486 | - delay: "00:00:01" 487 | - service: mqtt.publish 488 | data: 489 | topic: "cmnd/matrix/countdown/colorfinalmin" 490 | payload: "{{ states('input_number.matrix_countdown_color_lastmin_red') ~ ',' ~ states('input_number.matrix_countdown_color_lastmin_green') ~ ',' ~ states('input_number.matrix_countdown_color_lastmin_blue') }}" 491 | - alias: Matrix Countdown Color Last Min Red Get 492 | trigger: 493 | platform: mqtt 494 | topic: "stat/matrix/countdown/colorfinalmin/red" 495 | action: 496 | service: input_number.set_value 497 | data: 498 | entity_id: input_number.matrix_countdown_color_lastmin_red 499 | value: "{{ trigger.payload }}" 500 | - alias: Matrix Countdown Color Last Min Green Set 501 | trigger: 502 | platform: state 503 | entity_id: input_number.matrix_countdown_color_lastmin_green 504 | action: 505 | - delay: "00:00:01" 506 | - service: mqtt.publish 507 | data: 508 | topic: "cmnd/matrix/countdown/colorfinalmin" 509 | payload: "{{ states('input_number.matrix_countdown_color_lastmin_red') ~ ',' ~ states('input_number.matrix_countdown_color_lastmin_green') ~ ',' ~ states('input_number.matrix_countdown_color_lastmin_blue') }}" 510 | - alias: Matrix Countdown Color Last Min Green Get 511 | trigger: 512 | platform: mqtt 513 | topic: "stat/matrix/countdown/colorfinalmin/green" 514 | action: 515 | service: input_number.set_value 516 | data: 517 | entity_id: input_number.matrix_countdown_color_lastmin_green 518 | value: "{{ trigger.payload }}" 519 | - alias: Matrix Countdown Color Last Min Blue Set 520 | trigger: 521 | platform: state 522 | entity_id: input_number.matrix_countdown_color_lastmin_blue 523 | action: 524 | - delay: "00:00:01" 525 | - service: mqtt.publish 526 | data: 527 | topic: "cmnd/matrix/countdown/colorfinalmin" 528 | payload: "{{ states('input_number.matrix_countdown_color_lastmin_red') ~ ',' ~ states('input_number.matrix_countdown_color_lastmin_green') ~ ',' ~ states('input_number.matrix_countdown_color_lastmin_blue') }}" 529 | - alias: Matrix Countdown Color Last Min Blue Get 530 | trigger: 531 | platform: mqtt 532 | topic: "stat/matrix/countdown/colorfinalmin/blue" 533 | action: 534 | service: input_number.set_value 535 | data: 536 | entity_id: input_number.matrix_countdown_color_lastmin_blue 537 | value: "{{ trigger.payload }}" 538 | # ----------------------- 539 | # Temperature Colors 540 | # ----------------------- 541 | - alias: Matrix Temperature Color Red Set 542 | trigger: 543 | platform: state 544 | entity_id: input_number.matrix_temperature_color_red 545 | action: 546 | - delay: "00:00:01" 547 | - service: mqtt.publish 548 | data: 549 | topic: "cmnd/matrix/temperature/color" 550 | payload: "{{ states('input_number.matrix_temperature_color_red') ~ ',' ~ states('input_number.matrix_temperature_color_green') ~ ',' ~ states('input_number.matrix_temperature_color_blue') }}" 551 | - alias: Matrix Temperature Color Red Get 552 | trigger: 553 | platform: mqtt 554 | topic: "stat/matrix/temperature/color/red" 555 | action: 556 | service: input_number.set_value 557 | data: 558 | entity_id: input_number.matrix_temperature_color_red 559 | value: "{{ trigger.payload }}" 560 | - alias: Matrix Temperature Color Green Set 561 | trigger: 562 | platform: state 563 | entity_id: input_number.matrix_temperature_color_green 564 | action: 565 | - delay: "00:00:01" 566 | - service: mqtt.publish 567 | data: 568 | topic: "cmnd/matrix/temperature/color" 569 | payload: "{{ states('input_number.matrix_temperature_color_red') ~ ',' ~ states('input_number.matrix_temperature_color_green') ~ ',' ~ states('input_number.matrix_temperature_color_blue') }}" 570 | - alias: Matrix Temperature Color Green Get 571 | trigger: 572 | platform: mqtt 573 | topic: "stat/matrix/temperature/color/green" 574 | action: 575 | service: input_number.set_value 576 | data: 577 | entity_id: input_number.matrix_temperature_color_green 578 | value: "{{ trigger.payload }}" 579 | - alias: Matrix Temperature Color Blue Set 580 | trigger: 581 | platform: state 582 | entity_id: input_number.matrix_temperature_color_blue 583 | action: 584 | - delay: "00:00:01" 585 | - service: mqtt.publish 586 | data: 587 | topic: "cmnd/matrix/temperature/color" 588 | payload: "{{ states('input_number.matrix_temperature_color_red') ~ ',' ~ states('input_number.matrix_temperature_color_green') ~ ',' ~ states('input_number.matrix_temperature_color_blue') }}" 589 | - alias: Matrix Temperature Color Blue Get 590 | trigger: 591 | platform: mqtt 592 | topic: "stat/matrix/temperature/color/blue" 593 | action: 594 | service: input_number.set_value 595 | data: 596 | entity_id: input_number.matrix_temperature_color_blue 597 | value: "{{ trigger.payload }}" 598 | # ----------------------- 599 | # Scoreboard Colors 600 | # ----------------------- 601 | # Left 602 | - alias: Matrix Scoreboard Color Left Red Set 603 | trigger: 604 | platform: state 605 | entity_id: input_number.matrix_scoreboard_color_left_red 606 | action: 607 | - delay: "00:00:01" 608 | - service: mqtt.publish 609 | data: 610 | topic: "cmnd/matrix/scoreboard/colorleft" 611 | payload: "{{ states('input_number.matrix_scoreboard_color_left_red') ~ ',' ~ states('input_number.matrix_scoreboard_color_left_green') ~ ',' ~ states('input_number.matrix_scoreboard_color_left_blue') }}" 612 | - alias: Matrix Scoreboard Color Left Red Get 613 | trigger: 614 | platform: mqtt 615 | topic: "stat/matrix/scoreboard/colorleft/red" 616 | action: 617 | service: input_number.set_value 618 | data: 619 | entity_id: input_number.matrix_scoreboard_color_left_red 620 | value: "{{ trigger.payload }}" 621 | - alias: Matrix Scoreboard Color Left Green Set 622 | trigger: 623 | platform: state 624 | entity_id: input_number.matrix_scoreboard_color_left_green 625 | action: 626 | - delay: "00:00:01" 627 | - service: mqtt.publish 628 | data: 629 | topic: "cmnd/matrix/scoreboard/colorleft" 630 | payload: "{{ states('input_number.matrix_scoreboard_color_left_red') ~ ',' ~ states('input_number.matrix_scoreboard_color_left_green') ~ ',' ~ states('input_number.matrix_scoreboard_color_left_blue') }}" 631 | - alias: Matrix Scoreboard Color Left Green Get 632 | trigger: 633 | platform: mqtt 634 | topic: "stat/matrix/scoreboard/colorleft/green" 635 | action: 636 | service: input_number.set_value 637 | data: 638 | entity_id: input_number.matrix_scoreboard_color_left_green 639 | value: "{{ trigger.payload }}" 640 | - alias: Matrix Scoreboard Color Left Blue Set 641 | trigger: 642 | platform: state 643 | entity_id: input_number.matrix_scoreboard_color_left_blue 644 | action: 645 | - delay: "00:00:01" 646 | - service: mqtt.publish 647 | data: 648 | topic: "cmnd/matrix/scoreboard/colorleft" 649 | payload: "{{ states('input_number.matrix_scoreboard_color_left_red') ~ ',' ~ states('input_number.matrix_scoreboard_color_left_green') ~ ',' ~ states('input_number.matrix_scoreboard_color_left_blue') }}" 650 | - alias: Matrix Scoreboard Color Left Blue Get 651 | trigger: 652 | platform: mqtt 653 | topic: "stat/matrix/scoreboard/colorleft/blue" 654 | action: 655 | service: input_number.set_value 656 | data: 657 | entity_id: input_number.matrix_scoreboard_color_left_blue 658 | value: "{{ trigger.payload }}" 659 | # Right 660 | - alias: Matrix Scoreboard Color Right Red Set 661 | trigger: 662 | platform: state 663 | entity_id: input_number.matrix_scoreboard_color_right_red 664 | action: 665 | - delay: "00:00:01" 666 | - service: mqtt.publish 667 | data: 668 | topic: "cmnd/matrix/scoreboard/colorright" 669 | payload: "{{ states('input_number.matrix_scoreboard_color_right_red') ~ ',' ~ states('input_number.matrix_scoreboard_color_right_green') ~ ',' ~ states('input_number.matrix_scoreboard_color_right_blue') }}" 670 | - alias: Matrix Scoreboard Color Right Red Get 671 | trigger: 672 | platform: mqtt 673 | topic: "stat/matrix/scoreboard/colorright/red" 674 | action: 675 | service: input_number.set_value 676 | data: 677 | entity_id: input_number.matrix_scoreboard_color_right_red 678 | value: "{{ trigger.payload }}" 679 | - alias: Matrix Scoreboard Color Right Green Set 680 | trigger: 681 | platform: state 682 | entity_id: input_number.matrix_scoreboard_color_right_green 683 | action: 684 | - delay: "00:00:01" 685 | - service: mqtt.publish 686 | data: 687 | topic: "cmnd/matrix/scoreboard/colorright" 688 | payload: "{{ states('input_number.matrix_scoreboard_color_right_red') ~ ',' ~ states('input_number.matrix_scoreboard_color_right_green') ~ ',' ~ states('input_number.matrix_scoreboard_color_right_blue') }}" 689 | - alias: Matrix Scoreboard Color Right Green Get 690 | trigger: 691 | platform: mqtt 692 | topic: "stat/matrix/scoreboard/colorright/green" 693 | action: 694 | service: input_number.set_value 695 | data: 696 | entity_id: input_number.matrix_scoreboard_color_right_green 697 | value: "{{ trigger.payload }}" 698 | - alias: Matrix Scoreboard Color Right Blue Set 699 | trigger: 700 | platform: state 701 | entity_id: input_number.matrix_scoreboard_color_right_blue 702 | action: 703 | - delay: "00:00:01" 704 | - service: mqtt.publish 705 | data: 706 | topic: "cmnd/matrix/scoreboard/colorright" 707 | payload: "{{ states('input_number.matrix_scoreboard_color_right_red') ~ ',' ~ states('input_number.matrix_scoreboard_color_right_green') ~ ',' ~ states('input_number.matrix_scoreboard_color_right_blue') }}" 708 | - alias: Matrix Scoreboard Color Right Blue Get 709 | trigger: 710 | platform: mqtt 711 | topic: "stat/matrix/scoreboard/colorright/blue" 712 | action: 713 | service: input_number.set_value 714 | data: 715 | entity_id: input_number.matrix_scoreboard_color_right_blue 716 | value: "{{ trigger.payload }}" 717 | # ============================ 718 | # Text Colors 719 | # ============================ 720 | # Top 721 | - alias: Matrix Text Color Top Red Set 722 | trigger: 723 | platform: state 724 | entity_id: input_number.matrix_text_color_top_red 725 | action: 726 | - delay: "00:00:01" 727 | - service: mqtt.publish 728 | data: 729 | topic: "cmnd/matrix/text/colortop" 730 | payload: "{{ states('input_number.matrix_text_color_top_red') ~ ',' ~ states('input_number.matrix_text_color_top_green') ~ ',' ~ states('input_number.matrix_text_color_top_blue') }}" 731 | - alias: Matrix Text Color Top Red Get 732 | trigger: 733 | platform: mqtt 734 | topic: "stat/matrix/text/colortop/red" 735 | action: 736 | service: input_number.set_value 737 | data: 738 | entity_id: input_number.matrix_text_color_top_red 739 | value: "{{ trigger.payload }}" 740 | - alias: Matrix Text Color Top Green Set 741 | trigger: 742 | platform: state 743 | entity_id: input_number.matrix_text_color_top_green 744 | action: 745 | - delay: "00:00:01" 746 | - service: mqtt.publish 747 | data: 748 | topic: "cmnd/matrix/text/colortop" 749 | payload: "{{ states('input_number.matrix_text_color_top_red') ~ ',' ~ states('input_number.matrix_text_color_top_green') ~ ',' ~ states('input_number.matrix_text_color_top_blue') }}" 750 | - alias: Matrix Text Color Top Green Get 751 | trigger: 752 | platform: mqtt 753 | topic: "stat/matrix/text/colortop/green" 754 | action: 755 | service: input_number.set_value 756 | data: 757 | entity_id: input_number.matrix_text_color_top_green 758 | value: "{{ trigger.payload }}" 759 | - alias: Matrix Text Color Top Blue Set 760 | trigger: 761 | platform: state 762 | entity_id: input_number.matrix_text_color_top_blue 763 | action: 764 | - delay: "00:00:01" 765 | - service: mqtt.publish 766 | data: 767 | topic: "cmnd/matrix/text/colortop" 768 | payload: "{{ states('input_number.matrix_text_color_top_red') ~ ',' ~ states('input_number.matrix_text_color_top_green') ~ ',' ~ states('input_number.matrix_text_color_top_blue') }}" 769 | - alias: Matrix Text Color Top blue Get 770 | trigger: 771 | platform: mqtt 772 | topic: "stat/matrix/text/colortop/blue" 773 | action: 774 | service: input_number.set_value 775 | data: 776 | entity_id: input_number.matrix_text_color_top_blue 777 | value: "{{ trigger.payload }}" 778 | # Bottom 779 | - alias: Matrix Text Color Bottom Red Set 780 | trigger: 781 | platform: state 782 | entity_id: input_number.matrix_text_color_bottom_red 783 | action: 784 | - delay: "00:00:01" 785 | - service: mqtt.publish 786 | data: 787 | topic: "cmnd/matrix/text/colorbottom" 788 | payload: "{{ states('input_number.matrix_text_color_bottom_red') ~ ',' ~ states('input_number.matrix_text_color_bottom_green') ~ ',' ~ states('input_number.matrix_text_color_bottom_blue') }}" 789 | - alias: Matrix Text Color Bottom Red Get 790 | trigger: 791 | platform: mqtt 792 | topic: "stat/matrix/text/colorbottom/red" 793 | action: 794 | service: input_number.set_value 795 | data: 796 | entity_id: input_number.matrix_text_color_bottom_red 797 | value: "{{ trigger.payload }}" 798 | - alias: Matrix Text Color Bottom Green Set 799 | trigger: 800 | platform: state 801 | entity_id: input_number.matrix_text_color_bottom_green 802 | action: 803 | - delay: "00:00:01" 804 | - service: mqtt.publish 805 | data: 806 | topic: "cmnd/matrix/text/colorbottom" 807 | payload: "{{ states('input_number.matrix_text_color_bottom_red') ~ ',' ~ states('input_number.matrix_text_color_bottom_green') ~ ',' ~ states('input_number.matrix_text_color_bottom_blue') }}" 808 | - alias: Matrix Text Color Bottom Green Get 809 | trigger: 810 | platform: mqtt 811 | topic: "stat/matrix/text/colorbottom/green" 812 | action: 813 | service: input_number.set_value 814 | data: 815 | entity_id: input_number.matrix_text_color_bottom_green 816 | value: "{{ trigger.payload }}" 817 | - alias: Matrix Text Color Bottom Blue Set 818 | trigger: 819 | platform: state 820 | entity_id: input_number.matrix_text_color_bottom_blue 821 | action: 822 | - delay: "00:00:01" 823 | - service: mqtt.publish 824 | data: 825 | topic: "cmnd/matrix/text/colorbottom" 826 | payload: "{{ states('input_number.matrix_text_color_bottom_red') ~ ',' ~ states('input_number.matrix_text_color_bottom_green') ~ ',' ~ states('input_number.matrix_text_color_bottom_blue') }}" 827 | - alias: Matrix Text Color Bottom blue Get 828 | trigger: 829 | platform: mqtt 830 | topic: "stat/matrix/text/colorbottom/blue" 831 | action: 832 | service: input_number.set_value 833 | data: 834 | entity_id: input_number.matrix_text_color_bottom_blue 835 | value: "{{ trigger.payload }}" 836 | 837 | # ============================ 838 | # External Temperature Update 839 | # ============================ 840 | # - Requires NWS integration 841 | - alias: Matrix Clock External Temp Update 842 | trigger: 843 | platform: state 844 | entity_id: sensor.nws_temperature 845 | action: 846 | service: mqtt.publish 847 | data: 848 | topic: "cmnd/matrix/temperature/external" 849 | payload: "{{ states('sensor.nws_temperature') | int }}" 850 | 851 | # == Called from clock boot process to force MQTT update 852 | - alias: Matrix Clock External Temp Refresh 853 | trigger: 854 | platform: mqtt 855 | topic: "cmnd/matrix/temperature/refresh" 856 | action: 857 | service: mqtt.publish 858 | data: 859 | topic: "cmnd/matrix/temperature/external" 860 | payload: "{{ states('sensor.nws_temperature') | int }}" 861 | -------------------------------------------------------------------------------- /homeassistant/input_boolean.yaml: -------------------------------------------------------------------------------- 1 | # ================ 2 | # Matrix Auto-Dim 3 | # ================ 4 | matrix_auto_dim: 5 | name: Matrix Auto-Dim 6 | initial: on 7 | icon: mdi:brightness-auto 8 | -------------------------------------------------------------------------------- /homeassistant/input_number.yaml: -------------------------------------------------------------------------------- 1 | # ========================================================== 2 | # Matrix Brightness and Color and Text Effect Speed Sliders 3 | # ========================================================== 4 | # Matrix Brightness: 5 | matrix_brightness: 6 | name: Brightness 7 | min: 0 8 | max: 255 9 | step: 1 10 | mode: slider 11 | # Matrix Clock Colors (R, G, B) 12 | matrix_clock_color_red: 13 | name: Clock Color Red 14 | min: 0 15 | max: 255 16 | step: 1 17 | mode: slider 18 | matrix_clock_color_green: 19 | name: Clock Color Green 20 | min: 0 21 | max: 255 22 | step: 1 23 | mode: slider 24 | matrix_clock_color_blue: 25 | name: Clock Color Blue 26 | min: 0 27 | max: 255 28 | step: 1 29 | mode: slider 30 | # Matrix Countdown Color 31 | matrix_countdown_color_red: 32 | name: Countdown Active Color Red 33 | min: 0 34 | max: 255 35 | step: 1 36 | mode: slider 37 | matrix_countdown_color_green: 38 | name: Countdown Active Color Green 39 | min: 0 40 | max: 255 41 | step: 1 42 | mode: slider 43 | matrix_countdown_color_blue: 44 | name: Countdown Active Color Blue 45 | min: 0 46 | max: 255 47 | step: 1 48 | mode: slider 49 | matrix_countdown_color_paused_red: 50 | name: Countdown Paused Color Red 51 | min: 0 52 | max: 255 53 | step: 1 54 | mode: slider 55 | matrix_countdown_color_paused_green: 56 | name: Countdown Paused Color Green 57 | min: 0 58 | max: 255 59 | step: 1 60 | mode: slider 61 | matrix_countdown_color_paused_blue: 62 | name: Countdown Paused Color Blue 63 | min: 0 64 | max: 255 65 | step: 1 66 | mode: slider 67 | matrix_countdown_color_lastmin_red: 68 | name: Countdown Last Minute Color Red 69 | min: 0 70 | max: 255 71 | step: 1 72 | mode: slider 73 | matrix_countdown_color_lastmin_green: 74 | name: Countdown Last Minute Color Green 75 | min: 0 76 | max: 255 77 | step: 1 78 | mode: slider 79 | matrix_countdown_color_lastmin_blue: 80 | name: Countdown Last Minute Color Blue 81 | min: 0 82 | max: 255 83 | step: 1 84 | mode: slider 85 | # Matrix Temperature Color 86 | matrix_temperature_color_red: 87 | name: Temperature Color Red 88 | min: 0 89 | max: 255 90 | step: 1 91 | mode: slider 92 | matrix_temperature_color_green: 93 | name: Temperature Color Green 94 | min: 0 95 | max: 255 96 | step: 1 97 | mode: slider 98 | matrix_temperature_color_blue: 99 | name: Temperature Color Blue 100 | min: 0 101 | max: 255 102 | step: 1 103 | mode: slider 104 | # Matrix Scoreboard Colors 105 | matrix_scoreboard_color_left_red: 106 | name: Scoreboard Left Color Red 107 | min: 0 108 | max: 255 109 | step: 1 110 | mode: slider 111 | matrix_scoreboard_color_left_green: 112 | name: Scoreboard Left Color Green 113 | min: 0 114 | max: 255 115 | step: 1 116 | mode: slider 117 | matrix_scoreboard_color_left_blue: 118 | name: Scoreboard Left Color Blue 119 | min: 0 120 | max: 255 121 | step: 1 122 | mode: slider 123 | matrix_scoreboard_color_right_red: 124 | name: Scoreboard Right Color Red 125 | min: 0 126 | max: 255 127 | step: 1 128 | mode: slider 129 | matrix_scoreboard_color_right_green: 130 | name: Scoreboard Right Color Green 131 | min: 0 132 | max: 255 133 | step: 1 134 | mode: slider 135 | matrix_scoreboard_color_right_blue: 136 | name: Scoreboard Right Color Blue 137 | min: 0 138 | max: 255 139 | step: 1 140 | mode: slider 141 | matrix_text_color_top_red: 142 | name: Text Top Color Red 143 | min: 0 144 | max: 255 145 | step: 1 146 | mode: slider 147 | matrix_text_color_top_green: 148 | name: Text Top Color Green 149 | min: 0 150 | max: 255 151 | step: 1 152 | mode: slider 153 | matrix_text_color_top_blue: 154 | name: Text Top Color Blue 155 | min: 0 156 | max: 255 157 | step: 1 158 | mode: slider 159 | matrix_text_color_bottom_red: 160 | name: Text Bottom Color Red 161 | min: 0 162 | max: 255 163 | step: 1 164 | mode: slider 165 | matrix_text_color_bottom_green: 166 | name: Text Bottom Color Green 167 | min: 0 168 | max: 255 169 | step: 1 170 | mode: slider 171 | matrix_text_color_bottom_blue: 172 | name: Text Bottom Color Blue 173 | min: 0 174 | max: 255 175 | step: 1 176 | mode: slider 177 | matrix_text_effect_speed: 178 | name: Text Effect Speed 179 | min: 1 180 | max: 10 181 | step: 1 182 | mode: slider 183 | -------------------------------------------------------------------------------- /homeassistant/input_select.yaml: -------------------------------------------------------------------------------- 1 | # ========================= 2 | # Matrix Large Number Font 3 | # ========================= 4 | matrix_number_font: 5 | name: Matrix Number Font 6 | options: 7 | - '[0] - Seven Segment' 8 | - '[1] - Modern' 9 | - '[2] - Hybrid' 10 | initial: '[1] - Modern' 11 | 12 | # ========================= 13 | # Matrix Clock Text Effects 14 | # ========================= 15 | matrix_text_effect: 16 | name: Matrix Text Effect 17 | options: 18 | - '[00] - None' 19 | - '[01] - Flash' 20 | - '[02] - Flash Alternate' 21 | - '[03] - Fade In' 22 | - '[04] - Fade Out' 23 | - '[05] - Appear' 24 | - '[06] - Appear Flash' 25 | - '[07] - Rainbow' 26 | initial: '[00] - None' 27 | -------------------------------------------------------------------------------- /homeassistant/input_text.yaml: -------------------------------------------------------------------------------- 1 | # ================================== 2 | # Matrix Text Inputs 3 | # ================================== 4 | # Date/Time for setting clock time 5 | matrix_time_load: 6 | name: Date/Time 7 | initial: "MMM dd yyyy;hh:mm:ss" 8 | min: 20 9 | max: 20 10 | # Start Time for Countdown 11 | matrix_countdown_start_time: 12 | name: Countdown Start Time 13 | min: 8 14 | max: 8 15 | pattern: "[0-2][0-9]:[0-5][0-9]:[0-5][0-9]" 16 | # Tmperature Correction 17 | matrix_temperature_correction: 18 | name: Temperature Correction 19 | min: 3 20 | max: 5 21 | pattern: '^[\-]?\d{1,2}[\.]?[\d]{0,1}$' 22 | # Scoreboard Team Names 23 | matrix_scoreboard_team_left: 24 | name: Team Name Left 25 | min: 0 26 | max: 3 27 | matrix_scoreboard_team_right: 28 | name: Team Name Right 29 | min: 0 30 | max: 3 31 | # Text Message 32 | matrix_text_message: 33 | name: Text Message 34 | min: 0 35 | max: 30 36 | -------------------------------------------------------------------------------- /homeassistant/lovelace.yaml: -------------------------------------------------------------------------------- 1 | - title: Matrix 2 | path: matrix 3 | badges: [] 4 | cards: 5 | - type: vertical-stack 6 | cards: 7 | - text: LED Matrix 8 | type: 'custom:text-divider-row' 9 | - cards: 10 | - aspect_ratio: 1.2/1 11 | color: 'rgb(0,255,158)' 12 | color_type: card 13 | entity: switch.matrix_mode_clock 14 | icon: 'mdi:clock-outline' 15 | name: Clock 16 | tap_action: 17 | action: toggle 18 | type: 'custom:button-card' 19 | - aspect_ratio: 1.2/1 20 | color: 'rgb(0,255,158)' 21 | color_type: card 22 | entity: switch.matrix_mode_countdown 23 | icon: 'mdi:timer-outline' 24 | name: Countdown 25 | tap_action: 26 | action: toggle 27 | type: 'custom:button-card' 28 | - aspect_ratio: 1.2/1 29 | color: 'rgb(0,255,158)' 30 | color_type: card 31 | entity: switch.matrix_mode_scoreboard 32 | icon: 'mdi:basketball-hoop-outline' 33 | name: Scoreboard 34 | tap_action: 35 | action: toggle 36 | type: 'custom:button-card' 37 | - aspect_ratio: 1.2/1 38 | color: 'rgb(0,255,158)' 39 | color_type: card 40 | entity: switch.matrix_mode_text 41 | icon: 'mdi:format-text-variant-outline' 42 | name: Text 43 | tap_action: 44 | action: toggle 45 | type: 'custom:button-card' 46 | type: horizontal-stack 47 | - type: entities 48 | entities: 49 | - entity: input_number.matrix_brightness 50 | name: Brightness 51 | icon: 'mdi:brightness-5' 52 | - entity: input_select.matrix_number_font 53 | icon: 'mdi:format-font' 54 | - entity: input_boolean.matrix_auto_dim 55 | name: Auto-Dimming 56 | - entity: input_boolean.matrix_sync_led_clock 57 | name: Sync Actions to LED Clock 58 | show_header_toggle: false 59 | - type: horizontal-stack 60 | cards: 61 | - type: 'custom:button-card' 62 | aspect-ratio: 2/1 63 | name: Sound Buzzer 64 | show_icon: true 65 | show_name: true 66 | show_state: false 67 | tap_action: 68 | action: call-service 69 | service: script.matrix_buzzer_sound 70 | - type: vertical-stack 71 | cards: 72 | - type: conditional 73 | conditions: 74 | - entity: switch.matrix_mode_clock 75 | state: 'on' 76 | card: 77 | type: vertical-stack 78 | cards: 79 | - type: 'custom:text-divider-row' 80 | text: Clock Options 81 | - type: horizontal-stack 82 | cards: 83 | - type: 'custom:button-card' 84 | aspect-ratio: 2/1 85 | name: 12-Hour Display 86 | color_type: card 87 | color: 'rgb(128,0,128)' 88 | show_icon: false 89 | show_name: true 90 | show_state: false 91 | entity: switch.matrix_display_12_hour 92 | tap_action: 93 | action: toggle 94 | - type: 'custom:button-card' 95 | aspect-ratio: 2/1 96 | name: 24-Hour Display 97 | color_type: card 98 | color: 'rgb(128,0,128)' 99 | show_icon: false 100 | show_name: true 101 | show_state: false 102 | entity: switch.matrix_display_24_hour 103 | tap_action: 104 | action: toggle 105 | - type: entities 106 | entities: 107 | - entity: input_text.matrix_time_load 108 | name: 'Set Clock Date/Time To:' 109 | icon: 'mdi:clock-outline' 110 | - type: horizontal-stack 111 | cards: 112 | - type: 'custom:button-card' 113 | aspect-ratio: 2/1 114 | name: Load System Time 115 | show_icon: true 116 | show_name: true 117 | show_state: false 118 | tap_action: 119 | action: call-service 120 | service: script.matrix_load_systime 121 | - type: 'custom:button-card' 122 | aspect-ratio: 2/1 123 | name: Update Clock Time 124 | tap_action: 125 | action: call-service 126 | service: script.matrix_upload_time 127 | - type: entities 128 | entities: 129 | - entity: input_number.matrix_clock_color_red 130 | name: Red 131 | icon: 'mdi:alpha-r-circle' 132 | - entity: input_number.matrix_clock_color_green 133 | name: Green 134 | icon: 'mdi:alpha-g-circle' 135 | - entity: input_number.matrix_clock_color_blue 136 | name: Blue 137 | icon: 'mdi:alpha-b-circle' 138 | title: Clock Color 139 | state_color: false 140 | show_header_toggle: false 141 | - type: 'custom:text-divider-row' 142 | text: Temperature Options 143 | - type: entity 144 | entity: sensor.matrix_temperature_measured 145 | name: Current Temperature 146 | unit: ° 147 | - type: horizontal-stack 148 | cards: 149 | - type: 'custom:button-card' 150 | aspect-ratio: 2/1 151 | name: °F 152 | color_type: card 153 | color: 'rgb(66, 108, 255)' 154 | show_icon: false 155 | show_name: true 156 | show_state: false 157 | entity: switch.matrix_temp_display_f 158 | tap_action: 159 | action: toggle 160 | - type: 'custom:button-card' 161 | aspect-ratio: 2/1 162 | name: °C 163 | color_type: card 164 | color: 'rgb(66, 108, 255)' 165 | show_icon: false 166 | show_name: true 167 | show_state: false 168 | entity: switch.matrix_temp_display_c 169 | tap_action: 170 | action: toggle 171 | - type: entities 172 | entities: 173 | - entity: input_text.matrix_temperature_correction 174 | icon: 'mdi:thermometer-minus' 175 | show_header_toggle: false 176 | - type: entities 177 | entities: 178 | - entity: input_number.matrix_temperature_color_red 179 | name: Red 180 | icon: 'mdi:alpha-r-circle' 181 | - entity: input_number.matrix_temperature_color_green 182 | name: Green 183 | icon: 'mdi:alpha-g-circle' 184 | - entity: input_number.matrix_temperature_color_blue 185 | name: Blue 186 | icon: 'mdi:alpha-b-circle' 187 | title: Temperature Color 188 | show_header_toggle: false 189 | - type: conditional 190 | conditions: 191 | - entity: sensor.matrix_mode 192 | state: Countdown 193 | card: 194 | type: vertical-stack 195 | cards: 196 | - type: 'custom:text-divider-row' 197 | text: Countdown Options 198 | - type: entities 199 | entities: 200 | - entity: input_text.matrix_countdown_start_time 201 | name: 'Start time (hh:mm:ss):' 202 | icon: 'mdi:timer-outline' 203 | - type: horizontal-stack 204 | cards: 205 | - aspect_ratio: 2/1 206 | color: 'rgb(0,255,0)' 207 | color_type: card 208 | entity: switch.matrix_countdown_running 209 | icon: 'mdi:timer-outline' 210 | name: Start 211 | tap_action: 212 | action: toggle 213 | type: 'custom:button-card' 214 | - aspect_ratio: 2/1 215 | color: 'rgb(255,0,0)' 216 | color_type: card 217 | entity: switch.matrix_countdown_stopped 218 | icon: 'mdi:timer-off-outline' 219 | name: Stop/Pause 220 | tap_action: 221 | action: toggle 222 | type: 'custom:button-card' 223 | - aspect_ratio: 2/1 224 | icon: 'mdi:autorenew' 225 | name: Reset 226 | tap_action: 227 | action: call-service 228 | service: script.matrix_countdown_reset 229 | type: 'custom:button-card' 230 | - aspect_ratio: 2/1 231 | icon: 'mdi:numeric-0-box-outline' 232 | name: Clear 233 | tap_action: 234 | action: call-service 235 | service: script.matrix_countdown_clear 236 | type: 'custom:button-card' 237 | - type: entities 238 | entities: 239 | - entity: input_number.matrix_countdown_color_red 240 | name: Red 241 | icon: 'mdi:alpha-r-circle' 242 | - entity: input_number.matrix_countdown_color_green 243 | name: Green 244 | icon: 'mdi:alpha-g-circle' 245 | - entity: input_number.matrix_countdown_color_blue 246 | name: Blue 247 | icon: 'mdi:alpha-b-circle' 248 | title: Countdown Active Color 249 | show_header_toggle: false 250 | - type: entities 251 | entities: 252 | - entity: input_number.matrix_countdown_color_paused_red 253 | name: Red 254 | icon: 'mdi:alpha-r-circle' 255 | - entity: input_number.matrix_countdown_color_paused_green 256 | name: Green 257 | icon: 'mdi:alpha-g-circle' 258 | - entity: input_number.matrix_countdown_color_paused_blue 259 | name: Blue 260 | icon: 'mdi:alpha-b-circle' 261 | show_header_toggle: false 262 | title: Countdown Paused Color 263 | - type: entities 264 | entities: 265 | - entity: input_number.matrix_countdown_color_lastmin_red 266 | name: Red 267 | icon: 'mdi:alpha-r-circle' 268 | - entity: input_number.matrix_countdown_color_lastmin_green 269 | name: Green 270 | icon: 'mdi:alpha-g-circle' 271 | - entity: input_number.matrix_countdown_color_lastmin_blue 272 | name: Blue 273 | icon: 'mdi:alpha-b-circle' 274 | title: Countdown Final Minute Color 275 | show_header_toggle: false 276 | - type: conditional 277 | conditions: 278 | - entity: sensor.matrix_mode 279 | state: Scoreboard 280 | card: 281 | type: vertical-stack 282 | cards: 283 | - type: 'custom:text-divider-row' 284 | text: Scoreboard Options 285 | - type: horizontal-stack 286 | cards: 287 | - type: entities 288 | entities: 289 | - entity: input_text.matrix_scoreboard_team_left 290 | name: Team 291 | icon: 'mdi:alpha-v-circle-outline' 292 | - type: entities 293 | entities: 294 | - entity: input_text.matrix_scoreboard_team_right 295 | name: Team 296 | icon: 'mdi:alpha-h-circle-outline' 297 | - type: horizontal-stack 298 | cards: 299 | - type: entity 300 | entity: sensor.matrix_scoreboard_score_left 301 | name: Visitors (left) 302 | icon: ' ' 303 | - type: entity 304 | entity: sensor.matrix_scoreboard_score_right 305 | name: Home (right) 306 | icon: ' ' 307 | - type: horizontal-stack 308 | cards: 309 | - aspect_ratio: 2/1 310 | icon: 'mdi:minus' 311 | tap_action: 312 | action: call-service 313 | service: script.matrix_scoreboard_left_down 314 | type: 'custom:button-card' 315 | - aspect_ratio: 2/1 316 | icon: 'mdi:plus' 317 | tap_action: 318 | action: call-service 319 | service: script.matrix_scoreboard_left_up 320 | type: 'custom:button-card' 321 | - aspect_ratio: 2/1 322 | icon: 'mdi:minus' 323 | tap_action: 324 | action: call-service 325 | service: script.matrix_scoreboard_right_down 326 | type: 'custom:button-card' 327 | - aspect_ratio: 2/1 328 | icon: 'mdi:plus' 329 | tap_action: 330 | action: call-service 331 | service: script.matrix_scoreboard_right_up 332 | type: 'custom:button-card' 333 | - type: horizontal-stack 334 | cards: 335 | - aspect_ratio: 3/1 336 | name: Reset Vistors 337 | tap_action: 338 | action: call-service 339 | service: script.matrix_scoreboard_left_reset 340 | type: 'custom:button-card' 341 | - aspect_ratio: 3/1 342 | name: Reset All 343 | tap_action: 344 | action: call-service 345 | service: script.matrix_scoreboard_reset 346 | type: 'custom:button-card' 347 | - aspect_ratio: 3/1 348 | name: Reset Home 349 | tap_action: 350 | action: call-service 351 | service: script.matrix_scoreboard_right_reset 352 | type: 'custom:button-card' 353 | - type: entities 354 | entities: 355 | - entity: input_number.matrix_scoreboard_color_left_red 356 | name: Red 357 | icon: 'mdi:alpha-r-circle' 358 | - entity: input_number.matrix_scoreboard_color_left_green 359 | name: Green 360 | icon: 'mdi:alpha-g-circle' 361 | - entity: input_number.matrix_scoreboard_color_left_blue 362 | name: Blue 363 | icon: 'mdi:alpha-b-circle' 364 | title: Color Visitors (left) 365 | show_header_toggle: false 366 | - type: entities 367 | entities: 368 | - entity: input_number.matrix_scoreboard_color_right_red 369 | name: Red 370 | icon: 'mdi:alpha-r-circle' 371 | - entity: input_number.matrix_scoreboard_color_right_green 372 | name: Green 373 | icon: 'mdi:alpha-g-circle' 374 | - entity: input_number.matrix_scoreboard_color_right_blue 375 | name: Blue 376 | icon: 'mdi:alpha-b-circle' 377 | title: Color Home (right) 378 | show_header_toggle: false 379 | - type: conditional 380 | conditions: 381 | - entity: sensor.matrix_mode 382 | state: Text 383 | card: 384 | type: vertical-stack 385 | cards: 386 | - type: 'custom:text-divider-row' 387 | text: Text Mode Options 388 | - type: entities 389 | entities: 390 | - entity: input_text.matrix_text_message 391 | name: 'Text to Display:' 392 | icon: 'mdi:format-text-variant-outline' 393 | - type: entities 394 | entities: 395 | - entity: input_select.matrix_text_effect 396 | - entity: input_number.matrix_text_effect_speed 397 | - type: entities 398 | entities: 399 | - entity: input_number.matrix_text_color_top_red 400 | name: Red 401 | icon: 'mdi:alpha-r-circle' 402 | - entity: input_number.matrix_text_color_top_green 403 | name: Green 404 | icon: 'mdi:alpha-g-circle' 405 | - entity: input_number.matrix_text_color_top_blue 406 | name: Blue 407 | icon: 'mdi:alpha-b-circle' 408 | title: Text Color (Top Row) 409 | show_header_toggle: false 410 | - type: entities 411 | entities: 412 | - entity: input_number.matrix_text_color_bottom_red 413 | name: Red 414 | icon: 'mdi:alpha-r-circle' 415 | - entity: input_number.matrix_text_color_bottom_green 416 | name: Green 417 | icon: 'mdi:alpha-g-circle' 418 | - entity: input_number.matrix_text_color_bottom_blue 419 | name: Blue 420 | icon: 'mdi:alpha-b-circle' 421 | title: Text Color (Bottom Row) 422 | show_header_toggle: false 423 | -------------------------------------------------------------------------------- /homeassistant/script.yaml: -------------------------------------------------------------------------------- 1 | # ================================================= 2 | # Scripts for controlling/publishing to Matrix 3 | # ================================================= 4 | # Clock 5 | # ------------- 6 | # Load system time to input text for setting clock time 7 | matrix_load_systime: 8 | alias: 'Load system time' 9 | sequence: 10 | - service: input_text.set_value 11 | data: 12 | entity_id: input_text.matrix_time_load 13 | value: "{{ states('sensor.matrix_settime') }}" 14 | # Push time to clock 15 | matrix_upload_time: 16 | alias: 'Update Clock Time' 17 | sequence: 18 | - service: mqtt.publish 19 | data: 20 | topic: 'cmnd/matrix/clock/settime' 21 | payload_template: "{{ states('input_text.matrix_time_load') }}" 22 | 23 | # Immediately sync time from local system - can be called from automaion, button, etc. 24 | matrix_sync_time: 25 | alias: 'Matrix Sync Time' 26 | sequence: 27 | - service: mqtt.publish 28 | data: 29 | topic: 'cmnd/matrix/clock/settime' 30 | payload_template: "{{ as_timestamp(now()) | timestamp_custom('%b %d %Y;%H:%M:%S') }}" 31 | # ------------------------ 32 | # Countdown 33 | # ------------------------ 34 | matrix_countdown_reset: 35 | alias: 'Reset Countdown' 36 | sequence: 37 | - service: mqtt.publish 38 | data: 39 | topic: "cmnd/matrix/countdown/action" 40 | payload: "2" 41 | matrix_countdown_clear: 42 | alias: 'Clear Countdown Start Time' 43 | sequence: 44 | - service: mqtt.publish 45 | data: 46 | topic: "cmnd/matrix/countdown/action" 47 | payload: "3" 48 | # ----------------------- 49 | # Buzzer 50 | # ----------------------- 51 | # Length is in milliseconds (1000 = 1 sec) 52 | matrix_buzzer_sound: 53 | alias: 'Sound Matrix Buzzer' 54 | sequence: 55 | - service: mqtt.publish 56 | data: 57 | topic: "cmnd/matrix/buzzer" 58 | payload: "2000" 59 | # ----------------------- 60 | # Scoreboard 61 | # ----------------------- 62 | matrix_scoreboard_left_up: 63 | alias: 'Increase Left Score' 64 | sequence: 65 | - service: mqtt.publish 66 | data: 67 | topic: "cmnd/matrix/scoreboard/scoreup" 68 | payload: "0" 69 | matrix_scoreboard_right_up: 70 | alias: 'Increase Right Score' 71 | sequence: 72 | - service: mqtt.publish 73 | data: 74 | topic: "cmnd/matrix/scoreboard/scoreup" 75 | payload: "1" 76 | matrix_scoreboard_left_down: 77 | alias: 'Decrease Left Score' 78 | sequence: 79 | - service: mqtt.publish 80 | data: 81 | topic: "cmnd/matrix/scoreboard/scoredown" 82 | payload: "0" 83 | matrix_scoreboard_right_down: 84 | alias: 'Decrease Right Score' 85 | sequence: 86 | - service: mqtt.publish 87 | data: 88 | topic: "cmnd/matrix/scoreboard/scoredown" 89 | payload: "1" 90 | matrix_scoreboard_left_reset: 91 | alias: 'Reset Left Score' 92 | sequence: 93 | - service: mqtt.publish 94 | data: 95 | topic: "cmnd/matrix/scoreboard/reset" 96 | payload: "0" 97 | matrix_scoreboard_right_reset: 98 | alias: 'Reset Right Score' 99 | sequence: 100 | - service: mqtt.publish 101 | data: 102 | topic: "cmnd/matrix/scoreboard/reset" 103 | payload: "1" 104 | matrix_scoreboard_reset: 105 | alias: 'Reset Both Scores' 106 | sequence: 107 | - service: mqtt.publish 108 | data: 109 | topic: "cmnd/matrix/scoreboard/reset" 110 | payload: "2" 111 | -------------------------------------------------------------------------------- /homeassistant/sensor.yaml: -------------------------------------------------------------------------------- 1 | # ================================= 2 | # Matrix MQTT State Sensors 3 | # ================================= 4 | # -------------- 5 | # Global States 6 | # -------------- 7 | - platform: mqtt 8 | name: "Matrix Mode" 9 | state_topic: "stat/matrix/mode" 10 | icon: mdi:clock-digitial 11 | - platform: mqtt 12 | name: "Matrix Brightness" 13 | state_topic: "stat/matrix/brightness" 14 | icon: mdi:brightness-5 15 | # -------------- 16 | # Clock 17 | # -------------- 18 | - platform: mqtt 19 | name: "Matrix Time Display" 20 | state_topic: "stat/matrix/clock/display" 21 | icon: mdi:clock-time-four-outline 22 | # ---------------- 23 | # Countdown Timer 24 | # ----------------- 25 | # Starting Time 26 | - platform: mqtt 27 | name: "Matrix Countdown Start Time" 28 | state_topic: "stat/matrix/countdown/starttime" 29 | icon: mdi:timer-outline 30 | # Running Status 31 | - platform: mqtt 32 | name: "Matrix Countdown Run Status" 33 | state_topic: "stat/matrix/countdown/status" 34 | icon: mdi:run 35 | # ------------- 36 | # Temperature 37 | # ------------- 38 | # Display type (°C or °F) 39 | - platform: mqtt 40 | name: "Matrix Temperature Display" 41 | state_topic: "stat/matrix/temperature/symbol" 42 | icon: mdi:cogs 43 | # Correction value 44 | - platform: mqtt 45 | name: "Matrix Temperature Correction" 46 | state_topic: "stat/matrix/temperature/correction" 47 | icon: mdi:compare-vertical 48 | # Reported Temperature (after correction applied) 49 | - platform: mqtt 50 | name: "Matrix Temperature Measured" 51 | state_topic: "stat/matrix/temperature" 52 | unit_of_measurement: "°" 53 | icon: mdi:thermometer 54 | # ----------------- 55 | # Scoreboard 56 | # ----------------- 57 | # Score Left 58 | - platform: mqtt 59 | name: "Matrix Scoreboard Score Left" 60 | state_topic: "stat/matrix/scoreboard/scoreleft" 61 | icon: mdi:alpha-v-box-outline 62 | # Score Right 63 | - platform: mqtt 64 | name: "Matrix Scoreboard Score Right" 65 | state_topic: "stat/matrix/scoreboard/scoreright" 66 | icon: mdi:alpha-h-box-outline 67 | # ------------------------------------------------------ 68 | # Color states, as individual red, green and blue values 69 | # ------------------------------------------------------ 70 | # Clock color 71 | - platform: mqtt 72 | name: "Matrix Clock Color Val Red" 73 | state_topic: "stat/matrix/clock/color/red" 74 | icon: mdi:palette 75 | - platform: mqtt 76 | name: "Matrix Clock Color Val Green" 77 | state_topic: "stat/matrix/clock/color/green" 78 | icon: mdi:palette 79 | - platform: mqtt 80 | name: "Matrix Clock Color Val Blue" 81 | state_topic: "stat/matrix/clock/color/blue" 82 | icon: mdi:palette 83 | # Countdown Colors 84 | - platform: mqtt 85 | name: "Matrix Countdown Color Val Red" 86 | state_topic: "stat/matrix/countdown/color/red" 87 | icon: mdi:palette 88 | - platform: mqtt 89 | name: "Matrix Countdown Color Val Green" 90 | state_topic: "stat/matrix/countdown/color/green" 91 | icon: mdi:palette 92 | - platform: mqtt 93 | name: "Matrix Countdown Color Val Blue" 94 | state_topic: "stat/matrix/countdown/color/blue" 95 | icon: mdi:palette 96 | - platform: mqtt 97 | name: "Matrix Countdown Color Final Min Val Red" 98 | state_topic: "stat/matrix/countdown/colorfinalmin/red" 99 | icon: mdi:palette 100 | - platform: mqtt 101 | name: "Matrix Countdown Color Final Min Val Green" 102 | state_topic: "stat/matrix/countdown/colorfinalmin/green" 103 | icon: mdi:palette 104 | - platform: mqtt 105 | name: "Matrix Countdown Color Final Min Val Blue" 106 | state_topic: "stat/matrix/countdown/colorfinalmin/blue" 107 | icon: mdi:palette 108 | - platform: mqtt 109 | name: "Matrix Countdown Color Paused Val Red" 110 | state_topic: "stat/matrix/countdown/colorpaused/red" 111 | icon: mdi:palette 112 | - platform: mqtt 113 | name: "Matrix Countdown Color Paused Val Green" 114 | state_topic: "stat/matrix/countdown/colorpaused/green" 115 | icon: mdi:palette 116 | - platform: mqtt 117 | name: "Matrix Countdown Color Paused Val Blue" 118 | state_topic: "stat/matrix/countdown/colorpaused/blue" 119 | icon: mdi:palette 120 | # Temperature Color 121 | - platform: mqtt 122 | name: "Matrix Temperature Color Val Red" 123 | state_topic: "stat/matrix/temperature/color/red" 124 | icon: mdi:palette 125 | - platform: mqtt 126 | name: "Matrix Temperature Color Val Green" 127 | state_topic: "stat/matrix/temperature/color/green" 128 | icon: mdi:palette 129 | - platform: mqtt 130 | name: "Matrix Temperature Color Val Blue" 131 | state_topic: "stat/matrix/temperature/color/blue" 132 | icon: mdi:palette 133 | # Scoreboard Colors 134 | - platform: mqtt 135 | name: "Matrix Scoreboard Color Left Val Red" 136 | state_topic: "stat/matrix/scoreboard/colorleft/red" 137 | icon: mdi:palette 138 | - platform: mqtt 139 | name: "Matrix Scoreboard Color Left Val Green" 140 | state_topic: "stat/matrix/scoreboard/colorleft/green" 141 | icon: mdi:palette 142 | - platform: mqtt 143 | name: "Matrix Scoreboard Color Left Val Blue" 144 | state_topic: "stat/matrix/scoreboard/colorleft/blue" 145 | icon: mdi:palette 146 | - platform: mqtt 147 | name: "Matrix Scoreboard Color Right Val Red" 148 | state_topic: "stat/matrix/scoreboard/colorright/red" 149 | icon: mdi:palette 150 | - platform: mqtt 151 | name: "Matrix Scoreboard Color Right Val Green" 152 | state_topic: "stat/matrix/scoreboard/colorright/green" 153 | icon: mdi:palette 154 | - platform: mqtt 155 | name: "Matrix Scoreboard Color Right Val Blue" 156 | state_topic: "stat/matrix/scoreboard/colorright/blue" 157 | icon: mdi:palette 158 | 159 | # ---------------------------------------------------- 160 | # Color states returned as single RGB value: (r, g, b) 161 | # ---------------------------------------------------- 162 | - platform: template 163 | sensors: 164 | matrix_color_rgb: 165 | friendly_name: "Matrix Clock RGB Color" 166 | value_template: > 167 | "{{ '(' ~ (states('sensor.matrix_clock_color_val_red')) ~ ', ' ~ (states('sensor.matrix_clock_color_val_green')) ~ ', ' ~ (states('sensor.matrix_clock_color_val_blue')) ~ ')'}}" 168 | icon_template: mdi:palette 169 | 170 | matrix_countdown_color_rgb: 171 | friendly_name: "Matrix Countdown RGB Color" 172 | value_template: > 173 | "{{ '(' ~ (states('sensor.matrix_countdown_color_val_red')) ~ ', ' ~ (states('sensor.matrix_countdown_color_val_green')) ~ ', ' ~ (states('sensor.matrix_countdown_color_val_blue')) ~ ')'}}" 174 | icon_template: mdi:palette 175 | 176 | matrix_countdown_color_final_min_rgb: 177 | friendly_name: "Matrix Countdown Final Min RGB Color" 178 | value_template: > 179 | "{{ '(' ~ (states('sensor.matrix_countdown_color_final_min_val_red')) ~ ', ' ~ (states('sensor.matrix_countdown_color_final_min_val_green')) ~ ', ' ~ (states('sensor.matrix_countdown_color_final_min_val_blue')) ~ ')'}}" 180 | icon_template: mdi:palette 181 | 182 | matrix_countdown_color_paused_rgb: 183 | friendly_name: "Matrix Countdown Paused RGB Color" 184 | value_template: > 185 | "{{ '(' ~ (states('sensor.matrix_countdown_color_paused_val_red')) ~ ', ' ~ (states('sensor.matrix_countdown_color_paused_val_green')) ~ ', ' ~ (states('sensor.matrix_countdown_color_paused_val_blue')) ~ ')'}}" 186 | icon_template: mdi:palette 187 | 188 | matrix_temperature_color_rgb: 189 | friendly_name: "Matrix Temperature RGB Color" 190 | value_template: > 191 | "{{ '(' ~ (states('sensor.matrix_temperature_color_val_red')) ~ ', ' ~ (states('sensor.matrix_temperature_color_val_green')) ~ ', ' ~ (states('sensor.matrix_temperature_color_val_blue')) ~ ')'}}" 192 | icon_template: mdi:palette 193 | 194 | matrix_scoreboard_color_left_rgb: 195 | friendly_name: "Matrix Scoreboard RGB Color Left" 196 | value_template: > 197 | "{{ '(' ~ (states('sensor.matrix_scoreboard_color_left_val_red')) ~ ', ' ~ (states('sensor.matrix_scoreboard_color_left_val_green')) ~ ', ' ~ (states('sensor.matrix_scoreboard_color_left_val_blue')) ~ ')'}}" 198 | icon_template: mdi:palette 199 | 200 | matrix_scoreboard_color_right_rgb: 201 | friendly_name: "Matrix Scoreboard RGB Color Right" 202 | value_template: > 203 | "{{ '(' ~ (states('sensor.matrix_scoreboard_color_right_val_red')) ~ ', ' ~ (states('sensor.matrix_scoreboard_color_right_val_green')) ~ ', ' ~ (states('sensor.matrix_scoreboard_color_right_val_blue')) ~ ')'}}" 204 | icon_template: mdi:palette 205 | -------------------------------------------------------------------------------- /homeassistant/switch.yaml: -------------------------------------------------------------------------------- 1 | # =================== 2 | # Matrix Switches 3 | # =================== 4 | # Mode 5 | - platform: mqtt 6 | name: Matrix Mode Clock 7 | state_topic: "stat/matrix/mode" 8 | value_template: "{{ value == 'Clock' }}" 9 | command_topic: "cmnd/matrix/mode" 10 | payload_on: "0" 11 | #payload_off: "OFF" 12 | state_on: true 13 | state_off: false 14 | retain: false 15 | - platform: mqtt 16 | name: Matrix Mode Countdown 17 | state_topic: "stat/matrix/mode" 18 | value_template: "{{ value == 'Countdown' }}" 19 | command_topic: "cmnd/matrix/mode" 20 | payload_on: "1" 21 | #payload_off: "OFF" 22 | state_on: true 23 | state_off: false 24 | retain: false 25 | - platform: mqtt 26 | name: Matrix Mode Scoreboard 27 | state_topic: "stat/matrix/mode" 28 | command_topic: "cmnd/matrix/mode" 29 | payload_on: "2" 30 | #payload_off: "OFF" 31 | state_on: true 32 | state_off: false 33 | value_template: "{{ value == 'Scoreboard' }}" 34 | retain: false 35 | - platform: mqtt 36 | name: Matrix Mode Text 37 | state_topic: "stat/matrix/mode" 38 | command_topic: "cmnd/matrix/mode" 39 | payload_on: "3" 40 | #payload_off: "OFF" 41 | state_on: true 42 | state_off: false 43 | value_template: "{{ value == 'Text' }}" 44 | retain: false 45 | 46 | # 12/24 hour display 47 | - platform: mqtt 48 | name: Matrix Display 12 Hour 49 | state_topic: "stat/matrix/clock/display" 50 | value_template: "{{ value | int == 12 }}" 51 | state_on: true 52 | state_off: false 53 | command_topic: "cmnd/matrix/clock/display" 54 | payload_on: "12" 55 | retain: false 56 | - platform: mqtt 57 | name: Matrix Display 24 Hour 58 | state_topic: "stat/matrix/clock/display" 59 | value_template: "{{ value | int == 24 }}" 60 | state_on: true 61 | state_off: false 62 | command_topic: "cmnd/matrix/clock/display" 63 | payload_on: "24" 64 | retain: false 65 | 66 | # Temperature C/F Display 67 | - platform: mqtt 68 | name: Matrix Temp Display F 69 | state_topic: "stat/matrix/temperature/symbol" 70 | value_template: "{{ value == 'F' }}" 71 | state_on: true 72 | state_off: false 73 | command_topic: "cmnd/matrix/temperature/symbol" 74 | payload_on: "13" 75 | retain: false 76 | - platform: mqtt 77 | name: Matrix Temp Display C 78 | state_topic: "stat/matrix/temperature/symbol" 79 | value_template: "{{ value == 'C' }}" 80 | state_on: true 81 | state_off: false 82 | command_topic: "cmnd/matrix/temperature/symbol" 83 | payload_on: "12" 84 | retain: false 85 | 86 | # Countdown Running/Stopped Status 87 | - platform: mqtt 88 | name: Matrix Countdown Running 89 | state_topic: "stat/matrix/countdown/status" 90 | value_template: "{{ value == 'Running' }}" 91 | state_on: true 92 | state_off: false 93 | command_topic: "cmnd/matrix/countdown/action" 94 | payload_on: "0" 95 | retain: false 96 | - platform: mqtt 97 | name: Matrix Countdown Stopped 98 | state_topic: "stat/matrix/countdown/status" 99 | value_template: "{{ value == 'Stopped' }}" 100 | state_on: true 101 | state_off: false 102 | command_topic: "cmnd/matrix/countdown/action" 103 | payload_on: "1" 104 | retain: false 105 | -------------------------------------------------------------------------------- /mqtt/MQTT_Topics.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Resinchem/LED-Matrix-Clock_WS2812b/efc0655020edddbbd5b87c8a93def0bfdb43e4cc/mqtt/MQTT_Topics.pdf -------------------------------------------------------------------------------- /mqtt/README.md: -------------------------------------------------------------------------------- 1 | # MQTT Topic Commands and States 2 | 3 | ### MQTT use is technically optional, but many features (such as text mode) can only be used via MQTT 4 | 5 | See the [MQTT page](https://github.com/Resinchem/LED-Matrix-Clock_WS2812b/wiki/MQTT-%5C---Home-Assistant-Integration) of the wiki for additional information. 6 | 7 | The full list of topic states and commands, and valid payloads, can be found in the MQTT_Topics.pdf file. Currently, there are over 40 states reported by the clock and over 30 commands that can be issued to the clock over MQTT. 8 | -------------------------------------------------------------------------------- /stl/3-Button Case.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Resinchem/LED-Matrix-Clock_WS2812b/efc0655020edddbbd5b87c8a93def0bfdb43e4cc/stl/3-Button Case.stl -------------------------------------------------------------------------------- /stl/3_Button_1_Toggle Case.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Resinchem/LED-Matrix-Clock_WS2812b/efc0655020edddbbd5b87c8a93def0bfdb43e4cc/stl/3_Button_1_Toggle Case.stl -------------------------------------------------------------------------------- /stl/Clock_Control_Enclosure V3.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Resinchem/LED-Matrix-Clock_WS2812b/efc0655020edddbbd5b87c8a93def0bfdb43e4cc/stl/Clock_Control_Enclosure V3.stl -------------------------------------------------------------------------------- /stl/README.md: -------------------------------------------------------------------------------- 1 | # 3D Models 2 | 3 | #### None of these 3D parts are required to build the matrix. They are optional enclosures and mounts if you have access to a 3D printer and opt to utilize them. Adapt as needed. 4 | 5 | | File Name | Description | 6 | |-----------|-------------| 7 | |3-Button-Case.stl| Enclosure for the 3 push button controls| 8 | |3_Button_1_Toggle Case.stl| Enclosure for 3 buttons plus a toggle switch for dual-controller builds| 9 | |Clock_Control_Enclosure V3.stl|Enclosure for clock control module ElectroCookie protoboard)| 10 | |RTC_Mount.stl| Enclosure for the real-time clock module| 11 | |Toggle_Switch_Enclosure.stl| Enclosure for a toggle switch for dual-controller builds| 12 | |WLED_Small_ Enclosure V2.stl| Enclosure for WLED control board (ElectroCookie mini-board)| 13 | -------------------------------------------------------------------------------- /stl/RTC_Mount.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Resinchem/LED-Matrix-Clock_WS2812b/efc0655020edddbbd5b87c8a93def0bfdb43e4cc/stl/RTC_Mount.STL -------------------------------------------------------------------------------- /stl/Toggle_Switch_Enclosure.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Resinchem/LED-Matrix-Clock_WS2812b/efc0655020edddbbd5b87c8a93def0bfdb43e4cc/stl/Toggle_Switch_Enclosure.stl -------------------------------------------------------------------------------- /stl/WLED_Small_ Enclosure V2.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Resinchem/LED-Matrix-Clock_WS2812b/efc0655020edddbbd5b87c8a93def0bfdb43e4cc/stl/WLED_Small_ Enclosure V2.stl --------------------------------------------------------------------------------