├── 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 | 
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 | >[](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
--------------------------------------------------------------------------------