Merge pull request #2285 from Aircoookie/CCT-support
CCT (color white balance support)
This commit is contained in:
commit
f6e5b67f0d
@ -434,6 +434,13 @@ board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_flags = ${common.build_flags_esp8266} -D LEDPIN=3 -D BTNPIN=2 -D IRPIN=5 -D WLED_MAX_BUTTONS=3
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:athom7w]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_MAX_CCT_BLEND=0 -D BTNPIN=-1 -D IRPIN=-1 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# travis test board configurations
|
||||
# ------------------------------------------------------------------------------
|
||||
|
@ -1150,10 +1150,10 @@ uint16_t WS2812FX::mode_fire_flicker(void) {
|
||||
uint32_t it = now / cycleTime;
|
||||
if (SEGENV.step == it) return FRAMETIME;
|
||||
|
||||
byte w = (SEGCOLOR(0) >> 24) & 0xFF;
|
||||
byte r = (SEGCOLOR(0) >> 16) & 0xFF;
|
||||
byte g = (SEGCOLOR(0) >> 8) & 0xFF;
|
||||
byte b = (SEGCOLOR(0) & 0xFF);
|
||||
byte w = (SEGCOLOR(0) >> 24);
|
||||
byte r = (SEGCOLOR(0) >> 16);
|
||||
byte g = (SEGCOLOR(0) >> 8);
|
||||
byte b = (SEGCOLOR(0) );
|
||||
byte lum = (SEGMENT.palette == 0) ? MAX(w, MAX(r, MAX(g, b))) : 255;
|
||||
lum /= (((256-SEGMENT.intensity)/16)+1);
|
||||
for(uint16_t i = 0; i < SEGLEN; i++) {
|
||||
@ -2117,7 +2117,7 @@ typedef struct Ripple {
|
||||
#endif
|
||||
uint16_t WS2812FX::ripple_base(bool rainbow)
|
||||
{
|
||||
uint16_t maxRipples = min(1 + (SEGLEN >> 2), MAX_RIPPLES); // 56 max for 18 segment ESP8266
|
||||
uint16_t maxRipples = min(1 + (SEGLEN >> 2), MAX_RIPPLES); // 56 max for 16 segment ESP8266
|
||||
uint16_t dataSize = sizeof(ripple) * maxRipples;
|
||||
|
||||
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
|
||||
@ -2900,7 +2900,6 @@ uint16_t WS2812FX::mode_starburst(void) {
|
||||
return FRAMETIME;
|
||||
}
|
||||
#undef STARBURST_MAX_FRAG
|
||||
#undef STARBURST_MAX_STARS
|
||||
|
||||
/*
|
||||
* Exploding fireworks effect
|
||||
@ -3645,7 +3644,7 @@ typedef struct Spotlight {
|
||||
*/
|
||||
uint16_t WS2812FX::mode_dancing_shadows(void)
|
||||
{
|
||||
uint8_t numSpotlights = map(SEGMENT.intensity, 0, 255, 2, SPOT_MAX_COUNT); // 49 on 32 segment ESP32, 17 on 18 segment ESP8266
|
||||
uint8_t numSpotlights = map(SEGMENT.intensity, 0, 255, 2, SPOT_MAX_COUNT); // 49 on 32 segment ESP32, 17 on 16 segment ESP8266
|
||||
bool initialize = SEGENV.aux0 != numSpotlights;
|
||||
SEGENV.aux0 = numSpotlights;
|
||||
|
||||
@ -3784,7 +3783,7 @@ uint16_t WS2812FX::mode_washing_machine(void) {
|
||||
Modified, originally by Mark Kriegsman https://gist.github.com/kriegsman/1f7ccbbfa492a73c015e
|
||||
*/
|
||||
uint16_t WS2812FX::mode_blends(void) {
|
||||
uint16_t dataSize = sizeof(uint32_t) * SEGLEN; // max segment length of 56 pixels on 18 segment ESP8266
|
||||
uint16_t dataSize = sizeof(uint32_t) * SEGLEN; // max segment length of 56 pixels on 16 segment ESP8266
|
||||
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
|
||||
uint32_t* pixels = reinterpret_cast<uint32_t*>(SEGENV.data);
|
||||
uint8_t blendSpeed = map(SEGMENT.intensity, 0, UINT8_MAX, 10, 128);
|
||||
@ -4037,7 +4036,7 @@ uint16_t WS2812FX::mode_aurora(void) {
|
||||
SEGENV.aux1 = map(SEGMENT.intensity, 0, 255, 2, W_MAX_COUNT);
|
||||
SEGENV.aux0 = SEGMENT.intensity;
|
||||
|
||||
if(!SEGENV.allocateData(sizeof(AuroraWave) * SEGENV.aux1)) { // 26 on 32 segment ESP32, 9 on 18 segment ESP8266
|
||||
if(!SEGENV.allocateData(sizeof(AuroraWave) * SEGENV.aux1)) { // 26 on 32 segment ESP32, 9 on 16 segment ESP8266
|
||||
return mode_static(); //allocation failed
|
||||
}
|
||||
|
||||
|
58
wled00/FX.h
58
wled00/FX.h
@ -165,12 +165,12 @@
|
||||
#define FX_MODE_FIRE_FLICKER 45
|
||||
#define FX_MODE_GRADIENT 46
|
||||
#define FX_MODE_LOADING 47
|
||||
#define FX_MODE_POLICE 48
|
||||
#define FX_MODE_POLICE_ALL 49
|
||||
#define FX_MODE_POLICE 48 // candidate for removal (after below three)
|
||||
#define FX_MODE_POLICE_ALL 49 // candidate for removal
|
||||
#define FX_MODE_TWO_DOTS 50
|
||||
#define FX_MODE_TWO_AREAS 51
|
||||
#define FX_MODE_TWO_AREAS 51 // candidate for removal
|
||||
#define FX_MODE_RUNNING_DUAL 52
|
||||
#define FX_MODE_HALLOWEEN 53
|
||||
#define FX_MODE_HALLOWEEN 53 // candidate for removal
|
||||
#define FX_MODE_TRICOLOR_CHASE 54
|
||||
#define FX_MODE_TRICOLOR_WIPE 55
|
||||
#define FX_MODE_TRICOLOR_FADE 56
|
||||
@ -231,7 +231,7 @@
|
||||
#define FX_MODE_CHUNCHUN 111
|
||||
#define FX_MODE_DANCING_SHADOWS 112
|
||||
#define FX_MODE_WASHING_MACHINE 113
|
||||
#define FX_MODE_CANDY_CANE 114
|
||||
#define FX_MODE_CANDY_CANE 114 // candidate for removal
|
||||
#define FX_MODE_BLENDS 115
|
||||
#define FX_MODE_TV_SIMULATOR 116
|
||||
#define FX_MODE_DYNAMIC_SMOOTH 117
|
||||
@ -247,35 +247,44 @@ class WS2812FX {
|
||||
|
||||
// segment parameters
|
||||
public:
|
||||
typedef struct Segment { // 29 (32 in memory?) bytes
|
||||
typedef struct Segment { // 30 (32 in memory) bytes
|
||||
uint16_t start;
|
||||
uint16_t stop; //segment invalid if stop == 0
|
||||
uint16_t offset;
|
||||
uint8_t speed;
|
||||
uint8_t intensity;
|
||||
uint8_t palette;
|
||||
uint8_t mode;
|
||||
uint8_t options; //bit pattern: msb first: transitional needspixelstate tbd tbd (paused) on reverse selected
|
||||
uint8_t grouping, spacing;
|
||||
uint8_t opacity;
|
||||
uint8_t speed;
|
||||
uint8_t intensity;
|
||||
uint8_t palette;
|
||||
uint8_t mode;
|
||||
uint8_t options; //bit pattern: msb first: transitional needspixelstate tbd tbd (paused) on reverse selected
|
||||
uint8_t grouping, spacing;
|
||||
uint8_t opacity;
|
||||
uint32_t colors[NUM_COLORS];
|
||||
uint8_t cct; //0==1900K, 255==10091K
|
||||
char *name;
|
||||
bool setColor(uint8_t slot, uint32_t c, uint8_t segn) { //returns true if changed
|
||||
if (slot >= NUM_COLORS || segn >= MAX_NUM_SEGMENTS) return false;
|
||||
if (c == colors[slot]) return false;
|
||||
ColorTransition::startTransition(opacity, colors[slot], instance->_transitionDur, segn, slot);
|
||||
uint8_t b = (slot == 1) ? cct : opacity;
|
||||
ColorTransition::startTransition(b, colors[slot], instance->_transitionDur, segn, slot);
|
||||
colors[slot] = c; return true;
|
||||
}
|
||||
void setCCT(uint16_t k, uint8_t segn) {
|
||||
if (segn >= MAX_NUM_SEGMENTS) return;
|
||||
if (k > 255) { //kelvin value, convert to 0-255
|
||||
if (k < 1900) k = 1900;
|
||||
if (k > 10091) k = 10091;
|
||||
k = (k - 1900) >> 5;
|
||||
}
|
||||
if (cct == k) return;
|
||||
ColorTransition::startTransition(cct, colors[1], instance->_transitionDur, segn, 1);
|
||||
cct = k;
|
||||
}
|
||||
void setOpacity(uint8_t o, uint8_t segn) {
|
||||
if (segn >= MAX_NUM_SEGMENTS) return;
|
||||
if (opacity == o) return;
|
||||
ColorTransition::startTransition(opacity, colors[0], instance->_transitionDur, segn, 0);
|
||||
opacity = o;
|
||||
}
|
||||
/*uint8_t actualOpacity() { //respects On/Off state
|
||||
if (!getOption(SEG_OPTION_ON)) return 0;
|
||||
return opacity;
|
||||
}*/
|
||||
void setOption(uint8_t n, bool val, uint8_t segn = 255)
|
||||
{
|
||||
bool prevOn = false;
|
||||
@ -445,7 +454,7 @@ class WS2812FX {
|
||||
if (t.segment == s) //this is an active transition on the same segment+color
|
||||
{
|
||||
bool wasTurningOff = (oldBri == 0);
|
||||
t.briOld = t.currentBri(wasTurningOff);
|
||||
t.briOld = t.currentBri(wasTurningOff, slot);
|
||||
t.colorOld = t.currentColor(oldCol);
|
||||
} else {
|
||||
t.briOld = oldBri;
|
||||
@ -477,11 +486,15 @@ class WS2812FX {
|
||||
uint32_t currentColor(uint32_t colorNew) {
|
||||
return instance->color_blend(colorOld, colorNew, progress(true), true);
|
||||
}
|
||||
uint8_t currentBri(bool turningOff = false) {
|
||||
uint8_t currentBri(bool turningOff = false, uint8_t slot = 0) {
|
||||
uint8_t segn = segment & 0x3F;
|
||||
if (segn >= MAX_NUM_SEGMENTS) return 0;
|
||||
uint8_t briNew = instance->_segments[segn].opacity;
|
||||
if (!instance->_segments[segn].getOption(SEG_OPTION_ON) || turningOff) briNew = 0;
|
||||
if (slot == 0) {
|
||||
if (!instance->_segments[segn].getOption(SEG_OPTION_ON) || turningOff) briNew = 0;
|
||||
} else { //transition slot 1 brightness for CCT transition
|
||||
briNew = instance->_segments[segn].cct;
|
||||
}
|
||||
uint32_t prog = progress() + 1;
|
||||
return ((briNew * prog) + (briOld * (0x10000 - prog))) >> 16;
|
||||
}
|
||||
@ -652,15 +665,16 @@ class WS2812FX {
|
||||
applyToAllSelected = true,
|
||||
setEffectConfig(uint8_t m, uint8_t s, uint8_t i, uint8_t p),
|
||||
checkSegmentAlignment(void),
|
||||
hasCCTBus(void),
|
||||
// return true if the strip is being sent pixel updates
|
||||
isUpdating(void);
|
||||
|
||||
uint8_t
|
||||
mainSegment = 0,
|
||||
rgbwMode = RGBW_MODE_DUAL,
|
||||
paletteFade = 0,
|
||||
paletteBlend = 0,
|
||||
milliampsPerLed = 55,
|
||||
cctBlending = 0,
|
||||
getBrightness(void),
|
||||
getMode(void),
|
||||
getSpeed(void),
|
||||
|
@ -139,13 +139,16 @@ void WS2812FX::service() {
|
||||
if (!SEGMENT.getOption(SEG_OPTION_FREEZE)) { //only run effect function if not frozen
|
||||
_virtualSegmentLength = SEGMENT.virtualLength();
|
||||
_bri_t = SEGMENT.opacity; _colors_t[0] = SEGMENT.colors[0]; _colors_t[1] = SEGMENT.colors[1]; _colors_t[2] = SEGMENT.colors[2];
|
||||
uint8_t _cct_t = SEGMENT.cct;
|
||||
if (!IS_SEGMENT_ON) _bri_t = 0;
|
||||
for (uint8_t t = 0; t < MAX_NUM_TRANSITIONS; t++) {
|
||||
if ((transitions[t].segment & 0x3F) != i) continue;
|
||||
uint8_t slot = transitions[t].segment >> 6;
|
||||
if (slot == 0) _bri_t = transitions[t].currentBri();
|
||||
if (slot == 1) _cct_t = transitions[t].currentBri(false, 1);
|
||||
_colors_t[slot] = transitions[t].currentColor(SEGMENT.colors[slot]);
|
||||
}
|
||||
if (!cctFromRgb || correctWB) busses.setSegmentCCT(_cct_t, correctWB);
|
||||
for (uint8_t c = 0; c < 3; c++) _colors_t[c] = gamma32(_colors_t[c]);
|
||||
handle_palette();
|
||||
delay = (this->*_mode[SEGMENT.mode])(); //effect function
|
||||
@ -156,6 +159,7 @@ void WS2812FX::service() {
|
||||
}
|
||||
}
|
||||
_virtualSegmentLength = 0;
|
||||
busses.setSegmentCCT(-1);
|
||||
if(doShow) {
|
||||
yield();
|
||||
show();
|
||||
@ -164,11 +168,7 @@ void WS2812FX::service() {
|
||||
}
|
||||
|
||||
void WS2812FX::setPixelColor(uint16_t n, uint32_t c) {
|
||||
uint8_t w = (c >> 24);
|
||||
uint8_t r = (c >> 16);
|
||||
uint8_t g = (c >> 8);
|
||||
uint8_t b = c ;
|
||||
setPixelColor(n, r, g, b, w);
|
||||
setPixelColor(n, R(c), G(c), B(c), W(c));
|
||||
}
|
||||
|
||||
//used to map from segment index to physical pixel, taking into account grouping, offsets, reverse and mirroring
|
||||
@ -191,21 +191,10 @@ uint16_t WS2812FX::realPixelIndex(uint16_t i) {
|
||||
|
||||
void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
|
||||
{
|
||||
//auto calculate white channel value if enabled
|
||||
if (isRgbw) {
|
||||
if (rgbwMode == RGBW_MODE_AUTO_BRIGHTER || (w == 0 && (rgbwMode == RGBW_MODE_DUAL || rgbwMode == RGBW_MODE_LEGACY)))
|
||||
{
|
||||
//white value is set to lowest RGB channel
|
||||
//thank you to @Def3nder!
|
||||
w = r < g ? (r < b ? r : b) : (g < b ? g : b);
|
||||
} else if (rgbwMode == RGBW_MODE_AUTO_ACCURATE && w == 0)
|
||||
{
|
||||
w = r < g ? (r < b ? r : b) : (g < b ? g : b);
|
||||
r -= w; g -= w; b -= w;
|
||||
}
|
||||
}
|
||||
|
||||
if (SEGLEN) {//from segment
|
||||
uint16_t realIndex = realPixelIndex(i);
|
||||
uint16_t len = SEGMENT.length();
|
||||
|
||||
//color_blend(getpixel, col, _bri_t); (pseudocode for future blending of segments)
|
||||
if (_bri_t < 255) {
|
||||
r = scale8(r, _bri_t);
|
||||
@ -213,12 +202,9 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
|
||||
b = scale8(b, _bri_t);
|
||||
w = scale8(w, _bri_t);
|
||||
}
|
||||
uint32_t col = ((w << 24) | (r << 16) | (g << 8) | (b));
|
||||
uint32_t col = RGBW32(r, g, b, w);
|
||||
|
||||
/* Set all the pixels in the group */
|
||||
uint16_t realIndex = realPixelIndex(i);
|
||||
uint16_t len = SEGMENT.length();
|
||||
|
||||
for (uint16_t j = 0; j < SEGMENT.grouping; j++) {
|
||||
uint16_t indexSet = realIndex + (IS_REVERSE ? -j : j);
|
||||
if (indexSet >= SEGMENT.start && indexSet < SEGMENT.stop) {
|
||||
@ -241,8 +227,7 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
|
||||
}
|
||||
} else { //live data, etc.
|
||||
if (i < customMappingSize) i = customMappingTable[i];
|
||||
uint32_t col = ((w << 24) | (r << 16) | (g << 8) | (b));
|
||||
busses.setPixelColor(i, col);
|
||||
busses.setPixelColor(i, RGBW32(r, g, b, w));
|
||||
}
|
||||
}
|
||||
|
||||
@ -295,7 +280,7 @@ void WS2812FX::estimateCurrentAndLimitBri() {
|
||||
uint32_t busPowerSum = 0;
|
||||
for (uint16_t i = 0; i < len; i++) { //sum up the usage of each LED
|
||||
uint32_t c = bus->getPixelColor(i);
|
||||
byte r = c >> 16, g = c >> 8, b = c, w = c >> 24;
|
||||
byte r = R(c), g = G(c), b = B(c), w = W(c);
|
||||
|
||||
if(useWackyWS2815PowerModel) { //ignore white component on WS2815 power calculation
|
||||
busPowerSum += (MAX(MAX(r,g),b)) * 3;
|
||||
@ -431,7 +416,7 @@ bool WS2812FX::setEffectConfig(uint8_t m, uint8_t s, uint8_t in, uint8_t p) {
|
||||
}
|
||||
|
||||
void WS2812FX::setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
|
||||
setColor(slot, ((uint32_t)w << 24) |((uint32_t)r << 16) | ((uint32_t)g << 8) | b);
|
||||
setColor(slot, RGBW32(r, g, b, w));
|
||||
}
|
||||
|
||||
void WS2812FX::setColor(uint8_t slot, uint32_t c) {
|
||||
@ -568,6 +553,20 @@ uint16_t WS2812FX::getLengthPhysical(void) {
|
||||
return len;
|
||||
}
|
||||
|
||||
bool WS2812FX::hasCCTBus(void) {
|
||||
if (cctFromRgb && !correctWB) return false;
|
||||
for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
|
||||
Bus *bus = busses.getBus(b);
|
||||
if (bus == nullptr || bus->getLength()==0) break;
|
||||
switch (bus->getType()) {
|
||||
case TYPE_ANALOG_5CH:
|
||||
case TYPE_ANALOG_2CH:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing) {
|
||||
if (n >= MAX_NUM_SEGMENTS) return;
|
||||
Segment& seg = _segments[n];
|
||||
@ -622,6 +621,7 @@ void WS2812FX::resetSegments() {
|
||||
_segments[0].setOption(SEG_OPTION_SELECTED, 1);
|
||||
_segments[0].setOption(SEG_OPTION_ON, 1);
|
||||
_segments[0].opacity = 255;
|
||||
_segments[0].cct = 127;
|
||||
|
||||
for (uint16_t i = 1; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
@ -629,6 +629,7 @@ void WS2812FX::resetSegments() {
|
||||
_segments[i].grouping = 1;
|
||||
_segments[i].setOption(SEG_OPTION_ON, 1);
|
||||
_segments[i].opacity = 255;
|
||||
_segments[i].cct = 127;
|
||||
_segments[i].speed = DEFAULT_SPEED;
|
||||
_segments[i].intensity = DEFAULT_INTENSITY;
|
||||
_segment_runtimes[i].reset();
|
||||
@ -752,22 +753,22 @@ uint32_t WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint16_t blend,
|
||||
if(blend == blendmax) return color2;
|
||||
uint8_t shift = b16 ? 16 : 8;
|
||||
|
||||
uint32_t w1 = (color1 >> 24) & 0xFF;
|
||||
uint32_t r1 = (color1 >> 16) & 0xFF;
|
||||
uint32_t g1 = (color1 >> 8) & 0xFF;
|
||||
uint32_t b1 = color1 & 0xFF;
|
||||
uint32_t w1 = W(color1);
|
||||
uint32_t r1 = R(color1);
|
||||
uint32_t g1 = G(color1);
|
||||
uint32_t b1 = B(color1);
|
||||
|
||||
uint32_t w2 = (color2 >> 24) & 0xFF;
|
||||
uint32_t r2 = (color2 >> 16) & 0xFF;
|
||||
uint32_t g2 = (color2 >> 8) & 0xFF;
|
||||
uint32_t b2 = color2 & 0xFF;
|
||||
uint32_t w2 = W(color2);
|
||||
uint32_t r2 = R(color2);
|
||||
uint32_t g2 = G(color2);
|
||||
uint32_t b2 = B(color2);
|
||||
|
||||
uint32_t w3 = ((w2 * blend) + (w1 * (blendmax - blend))) >> shift;
|
||||
uint32_t r3 = ((r2 * blend) + (r1 * (blendmax - blend))) >> shift;
|
||||
uint32_t g3 = ((g2 * blend) + (g1 * (blendmax - blend))) >> shift;
|
||||
uint32_t b3 = ((b2 * blend) + (b1 * (blendmax - blend))) >> shift;
|
||||
|
||||
return ((w3 << 24) | (r3 << 16) | (g3 << 8) | (b3));
|
||||
return RGBW32(r3, g3, b3, w3);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -795,17 +796,17 @@ void WS2812FX::fade_out(uint8_t rate) {
|
||||
float mappedRate = float(rate) +1.1;
|
||||
|
||||
uint32_t color = SEGCOLOR(1); // target color
|
||||
int w2 = (color >> 24) & 0xff;
|
||||
int r2 = (color >> 16) & 0xff;
|
||||
int g2 = (color >> 8) & 0xff;
|
||||
int b2 = color & 0xff;
|
||||
int w2 = W(color);
|
||||
int r2 = R(color);
|
||||
int g2 = G(color);
|
||||
int b2 = B(color);
|
||||
|
||||
for(uint16_t i = 0; i < SEGLEN; i++) {
|
||||
color = getPixelColor(i);
|
||||
int w1 = (color >> 24) & 0xff;
|
||||
int r1 = (color >> 16) & 0xff;
|
||||
int g1 = (color >> 8) & 0xff;
|
||||
int b1 = color & 0xff;
|
||||
int w1 = W(color);
|
||||
int r1 = R(color);
|
||||
int g1 = G(color);
|
||||
int b1 = B(color);
|
||||
|
||||
int wdelta = (w2 - w1) / mappedRate;
|
||||
int rdelta = (r2 - r1) / mappedRate;
|
||||
@ -839,9 +840,9 @@ void WS2812FX::blur(uint8_t blur_amount)
|
||||
cur += carryover;
|
||||
if(i > 0) {
|
||||
uint32_t c = getPixelColor(i-1);
|
||||
uint8_t r = (c >> 16 & 0xFF);
|
||||
uint8_t g = (c >> 8 & 0xFF);
|
||||
uint8_t b = (c & 0xFF);
|
||||
uint8_t r = R(c);
|
||||
uint8_t g = G(c);
|
||||
uint8_t b = B(c);
|
||||
setPixelColor(i-1, qadd8(r, part.red), qadd8(g, part.green), qadd8(b, part.blue));
|
||||
}
|
||||
setPixelColor(i,cur.red, cur.green, cur.blue);
|
||||
@ -924,16 +925,16 @@ uint8_t WS2812FX::get_random_wheel_index(uint8_t pos) {
|
||||
|
||||
uint32_t WS2812FX::crgb_to_col(CRGB fastled)
|
||||
{
|
||||
return (((uint32_t)fastled.red << 16) | ((uint32_t)fastled.green << 8) | fastled.blue);
|
||||
return RGBW32(fastled.red, fastled.green, fastled.blue, 0);
|
||||
}
|
||||
|
||||
|
||||
CRGB WS2812FX::col_to_crgb(uint32_t color)
|
||||
{
|
||||
CRGB fastled_col;
|
||||
fastled_col.red = (color >> 16 & 0xFF);
|
||||
fastled_col.green = (color >> 8 & 0xFF);
|
||||
fastled_col.blue = (color & 0xFF);
|
||||
fastled_col.red = R(color);
|
||||
fastled_col.green = G(color);
|
||||
fastled_col.blue = B(color);
|
||||
return fastled_col;
|
||||
}
|
||||
|
||||
@ -1153,15 +1154,20 @@ uint8_t WS2812FX::gamma8(uint8_t b)
|
||||
uint32_t WS2812FX::gamma32(uint32_t color)
|
||||
{
|
||||
if (!gammaCorrectCol) return color;
|
||||
uint8_t w = (color >> 24);
|
||||
uint8_t r = (color >> 16);
|
||||
uint8_t g = (color >> 8);
|
||||
uint8_t b = color;
|
||||
uint8_t w = W(color);
|
||||
uint8_t r = R(color);
|
||||
uint8_t g = G(color);
|
||||
uint8_t b = B(color);
|
||||
w = gammaT[w];
|
||||
r = gammaT[r];
|
||||
g = gammaT[g];
|
||||
b = gammaT[b];
|
||||
return ((w << 24) | (r << 16) | (g << 8) | (b));
|
||||
return RGBW32(r, g, b, w);
|
||||
}
|
||||
|
||||
WS2812FX* WS2812FX::instance = nullptr;
|
||||
WS2812FX* WS2812FX::instance = nullptr;
|
||||
|
||||
//Bus static member definition, would belong in bus_manager.cpp
|
||||
int16_t Bus::_cct = -1;
|
||||
uint8_t Bus::_cctBlend = 0;
|
||||
uint8_t Bus::_autoWhiteMode = RGBW_MODE_DUAL;
|
@ -73,17 +73,26 @@ void onAlexaChange(EspalexaDevice* dev)
|
||||
if (espalexaDevice->getColorMode() == EspalexaColorMode::ct) //shade of white
|
||||
{
|
||||
uint16_t ct = espalexaDevice->getCt();
|
||||
if (strip.isRgbw)
|
||||
{
|
||||
if (!ct) return;
|
||||
uint16_t k = 1000000 / ct; //mireds to kelvin
|
||||
|
||||
if (strip.hasCCTBus()) {
|
||||
uint8_t segid = strip.getMainSegmentId();
|
||||
WS2812FX::Segment& seg = strip.getSegment(segid);
|
||||
uint8_t cctPrev = seg.cct;
|
||||
seg.setCCT(k, segid);
|
||||
if (seg.cct != cctPrev) effectChanged = true; //send UDP
|
||||
} else if (strip.isRgbw) {
|
||||
switch (ct) { //these values empirically look good on RGBW
|
||||
case 199: col[0]=255; col[1]=255; col[2]=255; col[3]=255; break;
|
||||
case 234: col[0]=127; col[1]=127; col[2]=127; col[3]=255; break;
|
||||
case 284: col[0]= 0; col[1]= 0; col[2]= 0; col[3]=255; break;
|
||||
case 350: col[0]=130; col[1]= 90; col[2]= 0; col[3]=255; break;
|
||||
case 383: col[0]=255; col[1]=153; col[2]= 0; col[3]=255; break;
|
||||
default : colorKtoRGB(k, col);
|
||||
}
|
||||
} else {
|
||||
colorCTtoRGB(ct, col);
|
||||
colorKtoRGB(k, col);
|
||||
}
|
||||
} else {
|
||||
uint32_t color = espalexaDevice->getRGB();
|
||||
|
@ -10,6 +10,10 @@
|
||||
#include "bus_wrapper.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
//colors.cpp
|
||||
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
|
||||
void colorRGBtoRGBW(byte* rgb);
|
||||
|
||||
// enable additional debug output
|
||||
#ifdef WLED_DEBUG
|
||||
#ifndef ESP8266
|
||||
@ -28,13 +32,20 @@
|
||||
#define SET_BIT(var,bit) ((var)|=(uint16_t)(0x0001<<(bit)))
|
||||
#define UNSET_BIT(var,bit) ((var)&=(~(uint16_t)(0x0001<<(bit))))
|
||||
|
||||
//color mangling macros
|
||||
#define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b))))
|
||||
#define R(c) (byte((c) >> 16))
|
||||
#define G(c) (byte((c) >> 8))
|
||||
#define B(c) (byte(c))
|
||||
#define W(c) (byte((c) >> 24))
|
||||
|
||||
//temporary struct for passing bus configuration to bus
|
||||
struct BusConfig {
|
||||
uint8_t type = TYPE_WS2812_RGB;
|
||||
uint16_t count = 1;
|
||||
uint16_t start = 0;
|
||||
uint8_t colorOrder = COL_ORDER_GRB;
|
||||
bool reversed = false;
|
||||
uint16_t count;
|
||||
uint16_t start;
|
||||
uint8_t colorOrder;
|
||||
bool reversed;
|
||||
uint8_t skipAmount;
|
||||
bool refreshReq;
|
||||
uint8_t pins[5] = {LEDPIN, 255, 255, 255, 255};
|
||||
@ -62,82 +73,80 @@ struct BusConfig {
|
||||
}
|
||||
};
|
||||
|
||||
//parent class of BusDigital and BusPwm
|
||||
//parent class of BusDigital, BusPwm, and BusNetwork
|
||||
class Bus {
|
||||
public:
|
||||
Bus(uint8_t type, uint16_t start) {
|
||||
_type = type;
|
||||
_start = start;
|
||||
};
|
||||
|
||||
virtual void show() {}
|
||||
virtual bool canShow() { return true; }
|
||||
Bus(uint8_t type, uint16_t start) {
|
||||
_type = type;
|
||||
_start = start;
|
||||
};
|
||||
|
||||
virtual void setPixelColor(uint16_t pix, uint32_t c) {};
|
||||
virtual ~Bus() {} //throw the bus under the bus
|
||||
|
||||
virtual void setBrightness(uint8_t b) {};
|
||||
virtual void show() {}
|
||||
virtual bool canShow() { return true; }
|
||||
virtual void setStatusPixel(uint32_t c) {}
|
||||
virtual void setPixelColor(uint16_t pix, uint32_t c) {}
|
||||
virtual uint32_t getPixelColor(uint16_t pix) { return 0; }
|
||||
virtual void setBrightness(uint8_t b) {}
|
||||
virtual void cleanup() {}
|
||||
virtual uint8_t getPins(uint8_t* pinArray) { return 0; }
|
||||
inline uint16_t getLength() { return _len; }
|
||||
virtual void setColorOrder() {}
|
||||
virtual uint8_t getColorOrder() { return COL_ORDER_RGB; }
|
||||
virtual uint8_t skippedLeds() { return 0; }
|
||||
inline uint16_t getStart() { return _start; }
|
||||
inline void setStart(uint16_t start) { _start = start; }
|
||||
inline uint8_t getType() { return _type; }
|
||||
inline bool isOk() { return _valid; }
|
||||
inline bool isOffRefreshRequired() { return _needsRefresh; }
|
||||
bool containsPixel(uint16_t pix) { return pix >= _start && pix < _start+_len; }
|
||||
|
||||
virtual uint32_t getPixelColor(uint16_t pix) { return 0; };
|
||||
virtual bool isRgbw() { return Bus::isRgbw(_type); }
|
||||
static bool isRgbw(uint8_t type) {
|
||||
if (type == TYPE_SK6812_RGBW || type == TYPE_TM1814) return true;
|
||||
if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true;
|
||||
return false;
|
||||
}
|
||||
static void setCCT(uint16_t cct) {
|
||||
_cct = cct;
|
||||
}
|
||||
static void setCCTBlend(uint8_t b) {
|
||||
if (b > 100) b = 100;
|
||||
_cctBlend = (b * 127) / 100;
|
||||
//compile-time limiter for hardware that can't power both white channels at max
|
||||
#ifdef WLED_MAX_CCT_BLEND
|
||||
if (_cctBlend > WLED_MAX_CCT_BLEND) _cctBlend = WLED_MAX_CCT_BLEND;
|
||||
#endif
|
||||
}
|
||||
inline static void setAutoWhiteMode(uint8_t m) { if (m < 4) _autoWhiteMode = m; }
|
||||
inline static uint8_t getAutoWhiteMode() { return _autoWhiteMode; }
|
||||
|
||||
virtual void cleanup() {};
|
||||
|
||||
virtual ~Bus() { //throw the bus under the bus
|
||||
}
|
||||
|
||||
virtual uint8_t getPins(uint8_t* pinArray) { return 0; }
|
||||
|
||||
inline uint16_t getStart() {
|
||||
return _start;
|
||||
}
|
||||
|
||||
inline void setStart(uint16_t start) {
|
||||
_start = start;
|
||||
}
|
||||
|
||||
virtual uint16_t getLength() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
virtual void setColorOrder() {}
|
||||
|
||||
virtual uint8_t getColorOrder() {
|
||||
return COL_ORDER_RGB;
|
||||
}
|
||||
|
||||
virtual bool isRgbw() {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual uint8_t skippedLeds() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline uint8_t getType() {
|
||||
return _type;
|
||||
}
|
||||
|
||||
inline bool isOk() {
|
||||
return _valid;
|
||||
}
|
||||
|
||||
static bool isRgbw(uint8_t type) {
|
||||
if (type == TYPE_SK6812_RGBW || type == TYPE_TM1814) return true;
|
||||
if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool isOffRefreshRequired() {
|
||||
return _needsRefresh;
|
||||
}
|
||||
|
||||
bool reversed = false;
|
||||
bool reversed = false;
|
||||
|
||||
protected:
|
||||
uint8_t _type = TYPE_NONE;
|
||||
uint8_t _bri = 255;
|
||||
uint16_t _start = 0;
|
||||
bool _valid = false;
|
||||
bool _needsRefresh = false;
|
||||
uint8_t _type = TYPE_NONE;
|
||||
uint8_t _bri = 255;
|
||||
uint16_t _start = 0;
|
||||
uint16_t _len = 1;
|
||||
bool _valid = false;
|
||||
bool _needsRefresh = false;
|
||||
static uint8_t _autoWhiteMode;
|
||||
static int16_t _cct;
|
||||
static uint8_t _cctBlend;
|
||||
|
||||
uint32_t autoWhiteCalc(uint32_t c) {
|
||||
if (_autoWhiteMode == RGBW_MODE_MANUAL_ONLY) return c;
|
||||
uint8_t w = W(c);
|
||||
//ignore auto-white calculation if w>0 and mode DUAL (DUAL behaves as BRIGHTER if w==0)
|
||||
if (w > 0 && _autoWhiteMode == RGBW_MODE_DUAL) return c;
|
||||
uint8_t r = R(c);
|
||||
uint8_t g = G(c);
|
||||
uint8_t b = B(c);
|
||||
w = r < g ? (r < b ? r : b) : (g < b ? g : b);
|
||||
if (_autoWhiteMode == RGBW_MODE_AUTO_ACCURATE) { r -= w; g -= w; b -= w; } //subtract w in ACCURATE mode
|
||||
return RGBW32(r, g, b, w);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -184,7 +193,18 @@ class BusDigital : public Bus {
|
||||
PolyBus::setBrightness(_busPtr, _iType, b);
|
||||
}
|
||||
|
||||
//If LEDs are skipped, it is possible to use the first as a status LED.
|
||||
//TODO only show if no new show due in the next 50ms
|
||||
void setStatusPixel(uint32_t c) {
|
||||
if (_skip && canShow()) {
|
||||
PolyBus::setPixelColor(_busPtr, _iType, 0, c, _colorOrder);
|
||||
PolyBus::show(_busPtr, _iType);
|
||||
}
|
||||
}
|
||||
|
||||
void setPixelColor(uint16_t pix, uint32_t c) {
|
||||
if (_type == TYPE_SK6812_RGBW || _type == TYPE_TM1814) c = autoWhiteCalc(c);
|
||||
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
|
||||
if (reversed) pix = _len - pix -1;
|
||||
else pix += _skip;
|
||||
PolyBus::setPixelColor(_busPtr, _iType, pix, c, _colorOrder);
|
||||
@ -215,10 +235,6 @@ class BusDigital : public Bus {
|
||||
_colorOrder = colorOrder;
|
||||
}
|
||||
|
||||
inline bool isRgbw() {
|
||||
return Bus::isRgbw(_type);
|
||||
}
|
||||
|
||||
inline uint8_t skippedLeds() {
|
||||
return _skip;
|
||||
}
|
||||
@ -245,7 +261,6 @@ class BusDigital : public Bus {
|
||||
uint8_t _colorOrder = COL_ORDER_GRB;
|
||||
uint8_t _pins[2] = {255, 255};
|
||||
uint8_t _iType = I_NONE;
|
||||
uint16_t _len = 0;
|
||||
uint8_t _skip = 0;
|
||||
void * _busPtr = nullptr;
|
||||
};
|
||||
@ -287,29 +302,57 @@ class BusPwm : public Bus {
|
||||
|
||||
void setPixelColor(uint16_t pix, uint32_t c) {
|
||||
if (pix != 0 || !_valid) return; //only react to first pixel
|
||||
uint8_t r = c >> 16;
|
||||
uint8_t g = c >> 8;
|
||||
uint8_t b = c ;
|
||||
uint8_t w = c >> 24;
|
||||
if (_type != TYPE_ANALOG_3CH) c = autoWhiteCalc(c);
|
||||
if (_cct >= 1900 && (_type == TYPE_ANALOG_3CH || _type == TYPE_ANALOG_4CH)) {
|
||||
c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
|
||||
}
|
||||
uint8_t r = R(c);
|
||||
uint8_t g = G(c);
|
||||
uint8_t b = B(c);
|
||||
uint8_t w = W(c);
|
||||
uint8_t cct = 0; //0 - full warm white, 255 - full cold white
|
||||
if (_cct > -1) {
|
||||
if (_cct >= 1900) cct = (_cct - 1900) >> 5;
|
||||
else if (_cct < 256) cct = _cct;
|
||||
} else {
|
||||
cct = (approximateKelvinFromRGB(c) - 1900) >> 5;
|
||||
}
|
||||
|
||||
//0 - linear (CCT 127 = 50% warm, 50% cold), 127 - additive CCT blending (CCT 127 = 100% warm, 100% cold)
|
||||
uint8_t ww, cw;
|
||||
if (cct < _cctBlend) ww = 255;
|
||||
else ww = ((255-cct) * 255) / (255 - _cctBlend);
|
||||
|
||||
if ((255-cct) < _cctBlend) cw = 255;
|
||||
else cw = (cct * 255) / (255 - _cctBlend);
|
||||
|
||||
ww = (w * ww) / 255; //brightness scaling
|
||||
cw = (w * cw) / 255;
|
||||
|
||||
switch (_type) {
|
||||
case TYPE_ANALOG_1CH: //one channel (white), use highest RGBW value
|
||||
_data[0] = max(r, max(g, max(b, w))); break;
|
||||
|
||||
case TYPE_ANALOG_2CH: //warm white + cold white, we'll need some nice handling here, for now just R+G channels
|
||||
case TYPE_ANALOG_3CH: //standard dumb RGB
|
||||
case TYPE_ANALOG_1CH: //one channel (white), relies on auto white calculation
|
||||
_data[0] = w;
|
||||
break;
|
||||
case TYPE_ANALOG_2CH: //warm white + cold white
|
||||
_data[1] = cw;
|
||||
_data[0] = ww;
|
||||
break;
|
||||
case TYPE_ANALOG_5CH: //RGB + warm white + cold white
|
||||
// perhaps a non-linear adjustment would be in order. need to test
|
||||
_data[4] = cw;
|
||||
w = ww;
|
||||
case TYPE_ANALOG_4CH: //RGBW
|
||||
case TYPE_ANALOG_5CH: //we'll want the white handling from 2CH here + RGB
|
||||
_data[0] = r; _data[1] = g; _data[2] = b; _data[3] = w; _data[4] = 0; break;
|
||||
|
||||
default: return;
|
||||
_data[3] = w;
|
||||
case TYPE_ANALOG_3CH: //standard dumb RGB
|
||||
_data[0] = r; _data[1] = g; _data[2] = b;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//does no index check
|
||||
uint32_t getPixelColor(uint16_t pix) {
|
||||
if (!_valid) return 0;
|
||||
return ((_data[3] << 24) | (_data[0] << 16) | (_data[1] << 8) | (_data[2]));
|
||||
return RGBW32(_data[0], _data[1], _data[2], _data[3]);
|
||||
}
|
||||
|
||||
void show() {
|
||||
@ -333,14 +376,12 @@ class BusPwm : public Bus {
|
||||
uint8_t getPins(uint8_t* pinArray) {
|
||||
if (!_valid) return 0;
|
||||
uint8_t numPins = NUM_PWM_PINS(_type);
|
||||
for (uint8_t i = 0; i < numPins; i++) pinArray[i] = _pins[i];
|
||||
for (uint8_t i = 0; i < numPins; i++) {
|
||||
pinArray[i] = _pins[i];
|
||||
}
|
||||
return numPins;
|
||||
}
|
||||
|
||||
bool isRgbw() {
|
||||
return Bus::isRgbw(_type);
|
||||
}
|
||||
|
||||
inline void cleanup() {
|
||||
deallocatePins();
|
||||
}
|
||||
@ -397,12 +438,10 @@ class BusNetwork : public Bus {
|
||||
// break;
|
||||
// }
|
||||
_UDPchannels = _rgbw ? 4 : 3;
|
||||
//_rgbw |= bc.rgbwOverride; // RGBW override in bit 7 or can have a special type
|
||||
_data = (byte *)malloc(bc.count * _UDPchannels);
|
||||
if (_data == nullptr) return;
|
||||
memset(_data, 0, bc.count * _UDPchannels);
|
||||
_len = bc.count;
|
||||
//_colorOrder = bc.colorOrder;
|
||||
_client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]);
|
||||
_broadcastLock = false;
|
||||
_valid = true;
|
||||
@ -410,22 +449,19 @@ class BusNetwork : public Bus {
|
||||
|
||||
void setPixelColor(uint16_t pix, uint32_t c) {
|
||||
if (!_valid || pix >= _len) return;
|
||||
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
|
||||
c = autoWhiteCalc(c);
|
||||
uint16_t offset = pix * _UDPchannels;
|
||||
_data[offset] = 0xFF & (c >> 16);
|
||||
_data[offset+1] = 0xFF & (c >> 8);
|
||||
_data[offset+2] = 0xFF & (c );
|
||||
if (_rgbw) _data[offset+3] = 0xFF & (c >> 24);
|
||||
_data[offset] = R(c);
|
||||
_data[offset+1] = G(c);
|
||||
_data[offset+2] = B(c);
|
||||
if (_rgbw) _data[offset+3] = W(c);
|
||||
}
|
||||
|
||||
uint32_t getPixelColor(uint16_t pix) {
|
||||
if (!_valid || pix >= _len) return 0;
|
||||
uint16_t offset = pix * _UDPchannels;
|
||||
return (
|
||||
(_rgbw ? (_data[offset+3] << 24) : 0)
|
||||
| (_data[offset] << 16)
|
||||
| (_data[offset+1] << 8)
|
||||
| (_data[offset+2] )
|
||||
);
|
||||
return RGBW32(_data[offset], _data[offset+1], _data[offset+2], _rgbw ? (_data[offset+3] << 24) : 0);
|
||||
}
|
||||
|
||||
void show() {
|
||||
@ -472,8 +508,6 @@ class BusNetwork : public Bus {
|
||||
|
||||
private:
|
||||
IPAddress _client;
|
||||
uint16_t _len = 0;
|
||||
//uint8_t _colorOrder;
|
||||
uint8_t _bri = 255;
|
||||
uint8_t _UDPtype;
|
||||
uint8_t _UDPchannels;
|
||||
@ -538,7 +572,13 @@ class BusManager {
|
||||
}
|
||||
}
|
||||
|
||||
void setPixelColor(uint16_t pix, uint32_t c) {
|
||||
void setStatusPixel(uint32_t c) {
|
||||
for (uint8_t i = 0; i < numBusses; i++) {
|
||||
busses[i]->setStatusPixel(c);
|
||||
}
|
||||
}
|
||||
|
||||
void setPixelColor(uint16_t pix, uint32_t c, int16_t cct=-1) {
|
||||
for (uint8_t i = 0; i < numBusses; i++) {
|
||||
Bus* b = busses[i];
|
||||
uint16_t bstart = b->getStart();
|
||||
@ -553,6 +593,15 @@ class BusManager {
|
||||
}
|
||||
}
|
||||
|
||||
void setSegmentCCT(int16_t cct, bool allowWBCorrection = false) {
|
||||
if (cct > 255) cct = 255;
|
||||
if (cct >= 0) {
|
||||
//if white balance correction allowed, save as kelvin value instead of 0-255
|
||||
if (allowWBCorrection) cct = 1900 + (cct << 5);
|
||||
} else cct = -1;
|
||||
Bus::setCCT(cct);
|
||||
}
|
||||
|
||||
uint32_t getPixelColor(uint16_t pix) {
|
||||
for (uint8_t i = 0; i < numBusses; i++) {
|
||||
Bus* b = busses[i];
|
||||
|
@ -61,7 +61,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
|
||||
CJSON(apBehavior, ap[F("behav")]);
|
||||
|
||||
|
||||
/*
|
||||
JsonArray ap_ip = ap["ip"];
|
||||
for (byte i = 0; i < 4; i++) {
|
||||
@ -80,7 +79,11 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
|
||||
CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]);
|
||||
CJSON(strip.milliampsPerLed, hw_led[F("ledma")]);
|
||||
CJSON(strip.rgbwMode, hw_led[F("rgbwm")]);
|
||||
Bus::setAutoWhiteMode(hw_led[F("rgbwm")] | Bus::getAutoWhiteMode());
|
||||
CJSON(correctWB, hw_led["cct"]);
|
||||
CJSON(cctFromRgb, hw_led[F("cr")]);
|
||||
CJSON(strip.cctBlending, hw_led[F("cb")]);
|
||||
Bus::setCCTBlend(strip.cctBlending);
|
||||
|
||||
JsonArray ins = hw_led["ins"];
|
||||
|
||||
@ -521,7 +524,9 @@ void serializeConfig() {
|
||||
hw_led[F("total")] = strip.getLengthTotal(); //no longer read, but provided for compatibility on downgrade
|
||||
hw_led[F("maxpwr")] = strip.ablMilliampsMax;
|
||||
hw_led[F("ledma")] = strip.milliampsPerLed;
|
||||
hw_led[F("rgbwm")] = strip.rgbwMode;
|
||||
hw_led["cct"] = correctWB;
|
||||
hw_led[F("cr")] = cctFromRgb;
|
||||
hw_led[F("cb")] = strip.cctBlending;
|
||||
|
||||
JsonArray hw_led_ins = hw_led.createNestedArray("ins");
|
||||
|
||||
@ -538,9 +543,10 @@ void serializeConfig() {
|
||||
ins[F("order")] = bus->getColorOrder();
|
||||
ins["rev"] = bus->reversed;
|
||||
ins[F("skip")] = bus->skippedLeds();
|
||||
ins["type"] = bus->getType() & 0x7F;;
|
||||
ins["type"] = bus->getType() & 0x7F;
|
||||
ins["ref"] = bus->isOffRefreshRequired();
|
||||
ins[F("rgbw")] = bus->isRgbw();
|
||||
ins[F("rgbwm")] = bus->getAutoWhiteMode();
|
||||
}
|
||||
|
||||
// button(s)
|
||||
|
@ -6,36 +6,25 @@
|
||||
|
||||
void colorFromUint32(uint32_t in, bool secondary)
|
||||
{
|
||||
if (secondary) {
|
||||
colSec[3] = in >> 24 & 0xFF;
|
||||
colSec[0] = in >> 16 & 0xFF;
|
||||
colSec[1] = in >> 8 & 0xFF;
|
||||
colSec[2] = in & 0xFF;
|
||||
} else {
|
||||
col[3] = in >> 24 & 0xFF;
|
||||
col[0] = in >> 16 & 0xFF;
|
||||
col[1] = in >> 8 & 0xFF;
|
||||
col[2] = in & 0xFF;
|
||||
}
|
||||
byte *_col = secondary ? colSec : col;
|
||||
_col[0] = R(in);
|
||||
_col[1] = G(in);
|
||||
_col[2] = B(in);
|
||||
_col[3] = W(in);
|
||||
}
|
||||
|
||||
//load a color without affecting the white channel
|
||||
void colorFromUint24(uint32_t in, bool secondary)
|
||||
{
|
||||
if (secondary) {
|
||||
colSec[0] = in >> 16 & 0xFF;
|
||||
colSec[1] = in >> 8 & 0xFF;
|
||||
colSec[2] = in & 0xFF;
|
||||
} else {
|
||||
col[0] = in >> 16 & 0xFF;
|
||||
col[1] = in >> 8 & 0xFF;
|
||||
col[2] = in & 0xFF;
|
||||
}
|
||||
byte *_col = secondary ? colSec : col;
|
||||
_col[0] = R(in);
|
||||
_col[1] = G(in);
|
||||
_col[2] = B(in);
|
||||
}
|
||||
|
||||
//store color components in uint32_t
|
||||
uint32_t colorFromRgbw(byte* rgbw) {
|
||||
return (rgbw[0] << 16) + (rgbw[1] << 8) + rgbw[2] + (rgbw[3] << 24);
|
||||
return RGBW32(rgbw[0], rgbw[1], rgbw[2], rgbw[3]);
|
||||
}
|
||||
|
||||
//relatively change white brightness, minumum A=5
|
||||
@ -64,9 +53,9 @@ void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) //hue, sat to rgb
|
||||
case 4: rgb[0]=t,rgb[1]=p,rgb[2]=255;break;
|
||||
case 5: rgb[0]=255,rgb[1]=p,rgb[2]=q;
|
||||
}
|
||||
if (strip.isRgbw && strip.rgbwMode == RGBW_MODE_LEGACY) colorRGBtoRGBW(col);
|
||||
}
|
||||
|
||||
//get RGB values from color temperature in K (https://tannerhelland.com/2012/09/18/convert-temperature-rgb-algorithm-code.html)
|
||||
void colorKtoRGB(uint16_t kelvin, byte* rgb) //white spectrum to rgb, calc
|
||||
{
|
||||
float r = 0, g = 0, b = 0;
|
||||
@ -84,7 +73,7 @@ void colorKtoRGB(uint16_t kelvin, byte* rgb) //white spectrum to rgb, calc
|
||||
g = round(288.1221695283 * pow((temp - 60), -0.0755148492));
|
||||
b = 255;
|
||||
}
|
||||
g += 15; //mod by Aircoookie, a bit less accurate but visibly less pinkish
|
||||
//g += 12; //mod by Aircoookie, a bit less accurate but visibly less pinkish
|
||||
rgb[0] = (uint8_t) constrain(r, 0, 255);
|
||||
rgb[1] = (uint8_t) constrain(g, 0, 255);
|
||||
rgb[2] = (uint8_t) constrain(b, 0, 255);
|
||||
@ -111,7 +100,6 @@ void colorCTtoRGB(uint16_t mired, byte* rgb) //white spectrum to rgb, bins
|
||||
} else {
|
||||
rgb[0]=237;rgb[1]=255;rgb[2]=239;//150
|
||||
}
|
||||
if (strip.isRgbw && strip.rgbwMode == RGBW_MODE_LEGACY) colorRGBtoRGBW(col);
|
||||
}
|
||||
|
||||
#ifndef WLED_DISABLE_HUESYNC
|
||||
@ -169,7 +157,6 @@ void colorXYtoRGB(float x, float y, byte* rgb) //coordinates to rgb (https://www
|
||||
rgb[0] = 255.0*r;
|
||||
rgb[1] = 255.0*g;
|
||||
rgb[2] = 255.0*b;
|
||||
if (strip.isRgbw && strip.rgbwMode == RGBW_MODE_LEGACY) colorRGBtoRGBW(col);
|
||||
}
|
||||
|
||||
void colorRGBtoXY(byte* rgb, float* xy) //rgb to coordinates (https://www.developers.meethue.com/documentation/color-conversions-rgb-xy)
|
||||
@ -197,10 +184,10 @@ void colorFromDecOrHexString(byte* rgb, char* in)
|
||||
c = strtoul(in, NULL, 10);
|
||||
}
|
||||
|
||||
rgb[3] = (c >> 24) & 0xFF;
|
||||
rgb[0] = (c >> 16) & 0xFF;
|
||||
rgb[1] = (c >> 8) & 0xFF;
|
||||
rgb[2] = c & 0xFF;
|
||||
rgb[0] = R(c);
|
||||
rgb[1] = G(c);
|
||||
rgb[2] = B(c);
|
||||
rgb[3] = W(c);
|
||||
}
|
||||
|
||||
//contrary to the colorFromDecOrHexString() function, this uses the more standard RRGGBB / RRGGBBWW order
|
||||
@ -212,14 +199,14 @@ bool colorFromHexString(byte* rgb, const char* in) {
|
||||
uint32_t c = strtoul(in, NULL, 16);
|
||||
|
||||
if (inputSize == 6) {
|
||||
rgb[0] = (c >> 16) & 0xFF;
|
||||
rgb[1] = (c >> 8) & 0xFF;
|
||||
rgb[2] = c & 0xFF;
|
||||
rgb[0] = (c >> 16);
|
||||
rgb[1] = (c >> 8);
|
||||
rgb[2] = c ;
|
||||
} else {
|
||||
rgb[0] = (c >> 24) & 0xFF;
|
||||
rgb[1] = (c >> 16) & 0xFF;
|
||||
rgb[2] = (c >> 8) & 0xFF;
|
||||
rgb[3] = c & 0xFF;
|
||||
rgb[0] = (c >> 24);
|
||||
rgb[1] = (c >> 16);
|
||||
rgb[2] = (c >> 8);
|
||||
rgb[3] = c ;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -236,6 +223,18 @@ float maxf (float v, float w)
|
||||
return v;
|
||||
}
|
||||
|
||||
/*
|
||||
uint32_t colorRGBtoRGBW(uint32_t c)
|
||||
{
|
||||
byte rgb[4];
|
||||
rgb[0] = R(c);
|
||||
rgb[1] = G(c);
|
||||
rgb[2] = B(c);
|
||||
rgb[3] = W(c);
|
||||
colorRGBtoRGBW(rgb);
|
||||
return RGBW32(rgb[0], rgb[1], rgb[2], rgb[3]);
|
||||
}
|
||||
|
||||
void colorRGBtoRGBW(byte* rgb) //rgb to rgbw (http://codewelt.com/rgbw). (RGBW_MODE_LEGACY)
|
||||
{
|
||||
float low = minf(rgb[0],minf(rgb[1],rgb[2]));
|
||||
@ -244,3 +243,60 @@ void colorRGBtoRGBW(byte* rgb) //rgb to rgbw (http://codewelt.com/rgbw). (RGBW_M
|
||||
float sat = 100.0f * ((high - low) / high);; // maximum saturation is 100 (corrected from 255)
|
||||
rgb[3] = (byte)((255.0f - sat) / 255.0f * (rgb[0] + rgb[1] + rgb[2]) / 3);
|
||||
}
|
||||
*/
|
||||
|
||||
byte correctionRGB[4] = {0,0,0,0};
|
||||
uint16_t lastKelvin = 0;
|
||||
|
||||
// adjust RGB values based on color temperature in K (range [2800-10200]) (https://en.wikipedia.org/wiki/Color_balance)
|
||||
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb)
|
||||
{
|
||||
//remember so that slow colorKtoRGB() doesn't have to run for every setPixelColor()
|
||||
if (lastKelvin != kelvin) colorKtoRGB(kelvin, correctionRGB); // convert Kelvin to RGB
|
||||
lastKelvin = kelvin;
|
||||
byte rgbw[4];
|
||||
rgbw[0] = ((uint16_t) correctionRGB[0] * R(rgb)) /255; // correct R
|
||||
rgbw[1] = ((uint16_t) correctionRGB[1] * G(rgb)) /255; // correct G
|
||||
rgbw[2] = ((uint16_t) correctionRGB[2] * B(rgb)) /255; // correct B
|
||||
rgbw[3] = W(rgb);
|
||||
return colorFromRgbw(rgbw);
|
||||
}
|
||||
|
||||
//approximates a Kelvin color temperature from an RGB color.
|
||||
//this does no check for the "whiteness" of the color,
|
||||
//so should be used combined with a saturation check (as done by auto-white)
|
||||
//values from http://www.vendian.org/mncharity/dir3/blackbody/UnstableURLs/bbr_color.html (10deg)
|
||||
//equation spreadsheet at https://bit.ly/30RkHaN
|
||||
//accuracy +-50K from 1900K up to 8000K
|
||||
//minimum returned: 1900K, maximum returned: 10091K (range of 8192)
|
||||
uint16_t approximateKelvinFromRGB(uint32_t rgb) {
|
||||
//if not either red or blue is 255, color is dimmed. Scale up
|
||||
uint8_t r = R(rgb), b = B(rgb);
|
||||
if (r == b) return 6550; //red == blue at about 6600K (also can't go further if both R and B are 0)
|
||||
|
||||
if (r > b) {
|
||||
//scale blue up as if red was at 255
|
||||
uint16_t scale = 0xFFFF / r; //get scale factor (range 257-65535)
|
||||
b = ((uint16_t)b * scale) >> 8;
|
||||
//For all temps K<6600 R is bigger than B (for full bri colors R=255)
|
||||
//-> Use 9 linear approximations for blackbody radiation blue values from 2000-6600K (blue is always 0 below 2000K)
|
||||
if (b < 33) return 1900 + b *6;
|
||||
if (b < 72) return 2100 + (b-33) *10;
|
||||
if (b < 101) return 2492 + (b-72) *14;
|
||||
if (b < 132) return 2900 + (b-101) *16;
|
||||
if (b < 159) return 3398 + (b-132) *19;
|
||||
if (b < 186) return 3906 + (b-159) *22;
|
||||
if (b < 210) return 4500 + (b-186) *25;
|
||||
if (b < 230) return 5100 + (b-210) *30;
|
||||
return 5700 + (b-230) *34;
|
||||
} else {
|
||||
//scale red up as if blue was at 255
|
||||
uint16_t scale = 0xFFFF / b; //get scale factor (range 257-65535)
|
||||
r = ((uint16_t)r * scale) >> 8;
|
||||
//For all temps K>6600 B is bigger than R (for full bri colors B=255)
|
||||
//-> Use 2 linear approximations for blackbody radiation red values from 6600-10091K (blue is always 0 below 2000K)
|
||||
if (r > 225) return 6600 + (254-r) *50;
|
||||
uint16_t k = 8080 + (225-r) *86;
|
||||
return (k > 10091) ? 10091 : k;
|
||||
}
|
||||
}
|
@ -124,7 +124,7 @@
|
||||
// - 0b010 (dec. 32-47) analog (PWM)
|
||||
// - 0b011 (dec. 48-63) digital (data + clock / SPI)
|
||||
// - 0b100 (dec. 64-79) unused/reserved
|
||||
// - 0b101 (dec. 80-95) digital (data + clock / SPI)
|
||||
// - 0b101 (dec. 80-95) virtual network busses
|
||||
// - 0b110 (dec. 96-111) unused/reserved
|
||||
// - 0b111 (dec. 112-127) unused/reserved
|
||||
//bit 7 is reserved and set to 0
|
||||
|
@ -459,12 +459,20 @@ img {
|
||||
.sliderdisplay {
|
||||
content:'';
|
||||
position: absolute;
|
||||
top: 13px; bottom: 13px;
|
||||
left: 10px; right: 10px;
|
||||
top: 12.5px; bottom: 12.5px;
|
||||
left: 13px; right: 13px;
|
||||
background: var(--c-4);
|
||||
border-radius: 17px;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
--bg: var(--c-f);
|
||||
}
|
||||
|
||||
#rwrap .sliderdisplay { --bg: #f00; }
|
||||
#gwrap .sliderdisplay { --bg: #0f0; }
|
||||
#bwrap .sliderdisplay { --bg: #00f; }
|
||||
#wbal .sliderdisplay, #kwrap .sliderdisplay {
|
||||
background: linear-gradient(90deg, #ff8f1f 0%, #fff 50%, #cbdbff);
|
||||
}
|
||||
|
||||
.sliderbubble {
|
||||
@ -492,6 +500,7 @@ input[type=range] {
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type=range]:focus {
|
||||
outline: none;
|
||||
}
|
||||
@ -527,8 +536,7 @@ input[type=range]:active + .sliderbubble {
|
||||
display: inline;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
#wwrap {
|
||||
#wwrap, #wbal {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
@ -45,17 +45,30 @@
|
||||
<div class ="container">
|
||||
<div id="Colors" class="tabcontent">
|
||||
<div id="picker" class="noslide"></div>
|
||||
<div id="vwrap">
|
||||
<div class="sliderwrap il" id="vwrap">
|
||||
<input id="sliderV" class="noslide" oninput="fromV()" onchange="setColor(0)" max="100" min="0" type="range" value="128" step="any" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div><br>
|
||||
</div>
|
||||
<div id="kwrap">
|
||||
<div class="sliderwrap il">
|
||||
<input id="sliderK" class="noslide" oninput="fromK()" onchange="setColor(0)" max="10091" min="1900" type="range" value="6550" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="rgbwrap">
|
||||
<div class="sliderwrap il">
|
||||
<input id="sliderR" class="noslide" onchange="fromRgb()" oninput="updateTrail(this,1)" max="255" min="0" type="range" value="128" />
|
||||
<p class="labels">RGB color</p>
|
||||
<div class="sliderwrap il" id="rwrap">
|
||||
<input id="sliderR" class="noslide" onchange="setColor(0)" oninput="fromRgb()" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div><br>
|
||||
<div class="sliderwrap il">
|
||||
<input id="sliderG" class="noslide" onchange="fromRgb()" oninput="updateTrail(this,2)" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderwrap il" id="gwrap">
|
||||
<input id="sliderG" class="noslide" onchange="setColor(0)" oninput="fromRgb()" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div><br>
|
||||
<div class="sliderwrap il">
|
||||
<input id="sliderB" class="noslide" onchange="fromRgb()" oninput="updateTrail(this,3)" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderwrap il" id="bwrap">
|
||||
<input id="sliderB" class="noslide" onchange="setColor(0)" oninput="fromRgb()" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div><br>
|
||||
</div>
|
||||
@ -66,6 +79,13 @@
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="wbal">
|
||||
<p class="labels">White balance</p>
|
||||
<div class="sliderwrap il">
|
||||
<input id="sliderA" class="noslide" onchange="setBalance(this.value)" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="qcs-w">
|
||||
<div class="qcs" onclick="pC('#ff0000');" title="Red" style="background-color:#ff0000;"></div>
|
||||
<div class="qcs" onclick="pC('#ffa000');" title="Orange" style="background-color:#ffa000;"></div>
|
||||
@ -98,7 +118,7 @@
|
||||
<label class="check schkl">
|
||||
|
||||
<input type="radio" value="${palettes[i].id}" name="palette" onChange="setPalette()">
|
||||
<span class="checkmark schk"></span>
|
||||
<span class="radiomark schk"></span>
|
||||
</label>
|
||||
<div class="lstIcontent">
|
||||
<span class="lstIname">
|
||||
|
@ -40,25 +40,12 @@ var hol = [
|
||||
var cpick = new iro.ColorPicker("#picker", {
|
||||
width: 260,
|
||||
wheelLightness: false,
|
||||
wheelAngle: 90,
|
||||
wheelAngle: 270,
|
||||
wheelDirection: "clockwise",
|
||||
layout: [
|
||||
{
|
||||
component: iro.ui.Wheel,
|
||||
options: {}
|
||||
},
|
||||
{
|
||||
component: iro.ui.Slider,
|
||||
options: {
|
||||
sliderType: 'value'
|
||||
}
|
||||
},
|
||||
{
|
||||
component: iro.ui.Slider,
|
||||
options: {
|
||||
sliderType: 'kelvin',
|
||||
minTemperature: 2100,
|
||||
maxTemperature: 10000
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
@ -81,6 +68,8 @@ function applyCfg()
|
||||
var ccfg = cfg.comp.colors;
|
||||
d.getElementById('hexw').style.display = ccfg.hex ? "block":"none";
|
||||
d.getElementById('picker').style.display = ccfg.picker ? "block":"none";
|
||||
d.getElementById('vwrap').style.display = ccfg.picker ? "block":"none";
|
||||
d.getElementById('kwrap').style.display = ccfg.picker ? "block":"none";
|
||||
d.getElementById('rgbwrap').style.display = ccfg.rgb ? "block":"none";
|
||||
d.getElementById('qcs-w').style.display = ccfg.quick ? "block":"none";
|
||||
var l = cfg.comp.labels;
|
||||
@ -202,11 +191,10 @@ function onLoad() {
|
||||
if (window.location.protocol == "file:") {
|
||||
loc = true;
|
||||
locip = localStorage.getItem('locIp');
|
||||
if (!locip)
|
||||
{
|
||||
locip = prompt("File Mode. Please enter WLED IP!");
|
||||
localStorage.setItem('locIp', locip);
|
||||
}
|
||||
if (!locip) {
|
||||
locip = prompt("File Mode. Please enter WLED IP!");
|
||||
localStorage.setItem('locIp', locip);
|
||||
}
|
||||
}
|
||||
var sett = localStorage.getItem('wledUiCfg');
|
||||
if (sett) cfg = mergeDeep(cfg, JSON.parse(sett));
|
||||
@ -229,9 +217,9 @@ function onLoad() {
|
||||
.catch(function (error) {
|
||||
console.log("holidays.json does not contain array of holidays. Defaults loaded.");
|
||||
})
|
||||
.finally(function(){
|
||||
loadBg(cfg.theme.bg.url);
|
||||
});
|
||||
.finally(function(){
|
||||
loadBg(cfg.theme.bg.url);
|
||||
});
|
||||
} else
|
||||
loadBg(cfg.theme.bg.url);
|
||||
if (cfg.comp.css) loadSkinCSS('skinCss');
|
||||
@ -246,6 +234,7 @@ function onLoad() {
|
||||
cpick.on("input:end", function() {
|
||||
setColor(1);
|
||||
});
|
||||
cpick.on("color:change", updatePSliders);
|
||||
pmtLS = localStorage.getItem('wledPmt');
|
||||
setTimeout(function(){requestJson(null, false);}, 50);
|
||||
d.addEventListener("visibilitychange", handleVisibilityChange, false);
|
||||
@ -320,11 +309,10 @@ function inforow(key, val, unit = "")
|
||||
function getLowestUnusedP()
|
||||
{
|
||||
var l = 1;
|
||||
for (var key in pJson)
|
||||
{
|
||||
for (var key in pJson) {
|
||||
if (key == l) l++;
|
||||
}
|
||||
if (l > 250) l = 250;
|
||||
}
|
||||
if (l > 250) l = 250;
|
||||
return l;
|
||||
}
|
||||
|
||||
@ -357,16 +345,16 @@ function papiVal(i) {
|
||||
|
||||
function qlName(i) {
|
||||
if (!pJson[i]) return "";
|
||||
if (!pJson[i].ql) return "";
|
||||
return pJson[i].ql;
|
||||
if (!pJson[i].ql) return "";
|
||||
return pJson[i].ql;
|
||||
}
|
||||
|
||||
function cpBck() {
|
||||
var copyText = d.getElementById("bck");
|
||||
|
||||
copyText.select();
|
||||
copyText.setSelectionRange(0, 999999);
|
||||
d.execCommand("copy");
|
||||
copyText.select();
|
||||
copyText.setSelectionRange(0, 999999);
|
||||
d.execCommand("copy");
|
||||
|
||||
showToast("Copied to clipboard!");
|
||||
}
|
||||
@ -452,21 +440,20 @@ function populateQL()
|
||||
{
|
||||
var cn = "";
|
||||
if (pQL.length > 0) {
|
||||
cn += `<p class="labels">Quick load</p>`;
|
||||
cn += `<p class="labels">Quick load</p>`;
|
||||
|
||||
var it = 0;
|
||||
for (var key of (pQL||[]))
|
||||
{
|
||||
cn += `<button class="xxs btn psts" id="p${key[0]}qlb" onclick="setPreset(${key[0]});">${key[1]}</button>`;
|
||||
it++;
|
||||
if (it > 4) {
|
||||
it = 0;
|
||||
cn += '<br>';
|
||||
}
|
||||
}
|
||||
if (it != 0) cn+= '<br>';
|
||||
var it = 0;
|
||||
for (var key of (pQL||[])) {
|
||||
cn += `<button class="xxs btn psts" id="p${key[0]}qlb" onclick="setPreset(${key[0]});">${key[1]}</button>`;
|
||||
it++;
|
||||
if (it > 4) {
|
||||
it = 0;
|
||||
cn += '<br>';
|
||||
}
|
||||
}
|
||||
if (it != 0) cn+= '<br>';
|
||||
|
||||
cn += `<p class="labels">All presets</p>`;
|
||||
cn += `<p class="labels">All presets</p>`;
|
||||
}
|
||||
d.getElementById('pql').innerHTML = cn;
|
||||
}
|
||||
@ -478,25 +465,25 @@ function populatePresets(fromls)
|
||||
var cn = "";
|
||||
var arr = Object.entries(pJson);
|
||||
arr.sort(cmpP);
|
||||
pQL = [];
|
||||
var is = [];
|
||||
pNum = 0;
|
||||
pQL = [];
|
||||
var is = [];
|
||||
pNum = 0;
|
||||
|
||||
for (var key of (arr||[]))
|
||||
{
|
||||
if (!isObject(key[1])) continue;
|
||||
let i = parseInt(key[0]);
|
||||
var qll = key[1].ql;
|
||||
if (qll) pQL.push([i, qll]);
|
||||
is.push(i);
|
||||
if (qll) pQL.push([i, qll]);
|
||||
is.push(i);
|
||||
|
||||
cn += `<div class="seg pres" id="p${i}o">`;
|
||||
if (cfg.comp.pid) cn += `<div class="pid">${i}</div>`;
|
||||
cn += `<div class="segname pname" onclick="setPreset(${i})">${isPlaylist(i)?"<i class='icons btn-icon'></i>":""}${pName(i)}</div>
|
||||
<i class="icons e-icon flr ${expanded[i+100] ? "exp":""}" id="sege${i+100}" onclick="expand(${i+100})"></i>
|
||||
<div class="segin" id="seg${i+100}"></div>
|
||||
</div><br>`;
|
||||
pNum++;
|
||||
cn += `<div class="seg pres" id="p${i}o">`;
|
||||
if (cfg.comp.pid) cn += `<div class="pid">${i}</div>`;
|
||||
cn += `<div class="segname pname" onclick="setPreset(${i})">${isPlaylist(i)?"<i class='icons btn-icon'></i>":""}${pName(i)}</div>
|
||||
<i class="icons e-icon flr ${expanded[i+100] ? "exp":""}" id="sege${i+100}" onclick="expand(${i+100})"></i>
|
||||
<div class="segin" id="seg${i+100}"></div>
|
||||
</div><br>`;
|
||||
pNum++;
|
||||
}
|
||||
|
||||
d.getElementById('pcont').innerHTML = cn;
|
||||
@ -526,16 +513,15 @@ function populateInfo(i)
|
||||
var pwru = "Not calculated";
|
||||
if (pwr > 1000) {pwr /= 1000; pwr = pwr.toFixed((pwr > 10) ? 0 : 1); pwru = pwr + " A";}
|
||||
else if (pwr > 0) {pwr = 50 * Math.round(pwr/50); pwru = pwr + " mA";}
|
||||
var urows="";
|
||||
if (i.u) {
|
||||
for (const [k, val] of Object.entries(i.u))
|
||||
{
|
||||
if (val[1]) {
|
||||
urows += inforow(k,val[0],val[1]);
|
||||
} else {
|
||||
urows += inforow(k,val);
|
||||
}
|
||||
}
|
||||
var urows="";
|
||||
if (i.u) {
|
||||
for (const [k, val] of Object.entries(i.u)) {
|
||||
if (val[1]) {
|
||||
urows += inforow(k,val[0],val[1]);
|
||||
} else {
|
||||
urows += inforow(k,val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var vcn = "Kuuhaku";
|
||||
@ -580,7 +566,7 @@ function populateSegments(s)
|
||||
</label>
|
||||
<div class="segname">
|
||||
<div class="segntxt" onclick="selSegEx(${i})">${inst.n ? inst.n : "Segment "+i}</div>
|
||||
<i class="icons edit-icon ${expanded[i] ? "expanded":""}" id="seg${i}nedit" onclick="tglSegn(${i})"></i>
|
||||
<i class="icons edit-icon ${expanded[i] ? "expanded":""}" id="seg${i}nedit" onclick="tglSegn(${i})"></i>
|
||||
</div>
|
||||
<i class="icons e-icon flr ${expanded[i] ? "exp":""}" id="sege${i}" onclick="expand(${i})"></i>
|
||||
<div class="segin ${expanded[i] ? "expanded":""}" id="seg${i}">
|
||||
@ -651,7 +637,7 @@ function populateSegments(s)
|
||||
function populateEffects(effects)
|
||||
{
|
||||
var html = `<div class="searchbar"><input type="text" class="search" placeholder="Search" oninput="search(this)" />
|
||||
<i class="icons search-icon"></i><i class="icons search-cancel-icon" onclick="cancelSearch(this)"></i></div>`;
|
||||
<i class="icons search-icon"></i><i class="icons search-cancel-icon" onclick="cancelSearch(this)"></i></div>`;
|
||||
|
||||
effects.shift(); //remove solid
|
||||
for (let i = 0; i < effects.length; i++) {
|
||||
@ -697,7 +683,7 @@ function populatePalettes(palettes)
|
||||
});
|
||||
|
||||
var html = `<div class="searchbar"><input type="text" class="search" placeholder="Search" oninput="search(this)" />
|
||||
<i class="icons search-icon"></i><i class="icons search-cancel-icon" onclick="cancelSearch(this)"></i></div>`;
|
||||
<i class="icons search-icon"></i><i class="icons search-cancel-icon" onclick="cancelSearch(this)"></i></div>`;
|
||||
for (let i = 0; i < palettes.length; i++) {
|
||||
html += generateListItemHtml(
|
||||
'palette',
|
||||
@ -780,15 +766,15 @@ function genPalPrevCss(id)
|
||||
function generateListItemHtml(listName, id, name, clickAction, extraHtml = '', extraClass = '')
|
||||
{
|
||||
return `<div class="lstI btn fxbtn ${extraClass}" data-id="${id}" onClick="${clickAction}(${id})">
|
||||
<label class="radio fxchkl">
|
||||
<input type="radio" value="${id}" name="${listName}">
|
||||
<span class="radiomark"></span>
|
||||
</label>
|
||||
<span class="lstIname">
|
||||
${name}
|
||||
</span>
|
||||
${extraHtml}
|
||||
</div>`;
|
||||
<label class="radio fxchkl">
|
||||
<input type="radio" value="${id}" name="${listName}">
|
||||
<span class="radiomark"></span>
|
||||
</label>
|
||||
<span class="lstIname">
|
||||
${name}
|
||||
</span>
|
||||
${extraHtml}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function btype(b){
|
||||
@ -815,16 +801,16 @@ function populateNodes(i,n)
|
||||
if (o.name) {
|
||||
var url = `<button class="btn btna-icon tab" onclick="location.assign('http://${o.ip}');">${bname(o)}</button>`;
|
||||
urows += inforow(url,`${btype(o.type)}<br><i>${o.vid==0?"N/A":o.vid}</i>`);
|
||||
nnodes++;
|
||||
nnodes++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i.ndc < 0) cn += `Instance List is disabled.`;
|
||||
else if (nnodes == 0) cn += `No other instances found.`;
|
||||
if (i.ndc < 0) cn += `Instance List is disabled.`;
|
||||
else if (nnodes == 0) cn += `No other instances found.`;
|
||||
cn += `<table class="infot">
|
||||
${urows}
|
||||
${inforow("Current instance:",i.name)}
|
||||
</table>`;
|
||||
${urows}
|
||||
${inforow("Current instance:",i.name)}
|
||||
</table>`;
|
||||
d.getElementById('kn').innerHTML = cn;
|
||||
}
|
||||
|
||||
@ -854,28 +840,20 @@ function loadNodes()
|
||||
});
|
||||
}
|
||||
|
||||
function updateTrail(e, slidercol)
|
||||
function updateTrail(e)
|
||||
{
|
||||
if (e==null) return;
|
||||
var max = e.hasAttribute('max') ? e.attributes.max.value : 255;
|
||||
var perc = e.value * 100 / max;
|
||||
perc = parseInt(perc);
|
||||
if (perc < 50) perc += 2;
|
||||
var scol;
|
||||
switch (slidercol) {
|
||||
case 1: scol = "#f00"; break;
|
||||
case 2: scol = "#0f0"; break;
|
||||
case 3: scol = "#00f"; break;
|
||||
default: scol = "var(--c-f)";
|
||||
}
|
||||
var val = `linear-gradient(90deg, ${scol} ${perc}%, var(--c-4) ${perc}%)`;
|
||||
if (perc < 50) perc += 2;
|
||||
var val = `linear-gradient(90deg, var(--bg) ${perc}%, var(--c-4) ${perc}%)`;
|
||||
e.parentNode.getElementsByClassName('sliderdisplay')[0].style.background = val;
|
||||
}
|
||||
|
||||
function updateBubble(e)
|
||||
{
|
||||
var bubble = e.target.parentNode.getElementsByTagName('output')[0];
|
||||
|
||||
if (bubble) {
|
||||
bubble.innerHTML = e.target.value;
|
||||
}
|
||||
@ -939,12 +917,13 @@ function updateUI()
|
||||
updateTrail(d.getElementById('sliderBri'));
|
||||
updateTrail(d.getElementById('sliderSpeed'));
|
||||
updateTrail(d.getElementById('sliderIntensity'));
|
||||
updateTrail(d.getElementById('sliderW'));
|
||||
if (isRgbw) d.getElementById('wwrap').style.display = "block";
|
||||
d.getElementById('wwrap').style.display = (isRgbw) ? "block":"none";
|
||||
d.getElementById('wbal').style.display = (lastinfo.leds.cct) ? "block":"none";
|
||||
d.getElementById('kwrap').style.display = (lastinfo.leds.cct) ? "none":"block";
|
||||
|
||||
updatePA();
|
||||
updateHex();
|
||||
updateRgb();
|
||||
//updateHex();
|
||||
//updateRgb();
|
||||
}
|
||||
|
||||
function displayRover(i,s)
|
||||
@ -961,32 +940,32 @@ function compare(a, b) {
|
||||
}
|
||||
function cmpP(a, b) {
|
||||
if (!a[1].n) return (a[0] > b[0]);
|
||||
return a[1].n.localeCompare(b[1].n,undefined, {numeric: true});
|
||||
return a[1].n.localeCompare(b[1].n,undefined, {numeric: true});
|
||||
}
|
||||
|
||||
function makeWS() {
|
||||
if (ws) return;
|
||||
ws = new WebSocket('ws://'+(loc?locip:window.location.hostname)+'/ws');
|
||||
ws.onmessage = function(event) {
|
||||
var json = JSON.parse(event.data);
|
||||
if (json.leds) return; //liveview packet
|
||||
clearTimeout(jsonTimeout);
|
||||
if (ws) return;
|
||||
ws = new WebSocket('ws://'+(loc?locip:window.location.hostname)+'/ws');
|
||||
ws.onmessage = function(event) {
|
||||
var json = JSON.parse(event.data);
|
||||
if (json.leds) return; //liveview packet
|
||||
clearTimeout(jsonTimeout);
|
||||
jsonTimeout = null;
|
||||
clearErrorToast();
|
||||
d.getElementById('connind').style.backgroundColor = "#079";
|
||||
var info = json.info;
|
||||
d.getElementById('buttonNodes').style.display = (info.ndc > 0 && window.innerWidth > 770) ? "block":"none";
|
||||
lastinfo = info;
|
||||
if (isInfo) {
|
||||
populateInfo(info);
|
||||
}
|
||||
s = json.state;
|
||||
displayRover(info, s);
|
||||
d.getElementById('connind').style.backgroundColor = "#079";
|
||||
var info = json.info;
|
||||
d.getElementById('buttonNodes').style.display = (info.ndc > 0 && window.innerWidth > 770) ? "block":"none";
|
||||
lastinfo = info;
|
||||
if (isInfo) {
|
||||
populateInfo(info);
|
||||
}
|
||||
s = json.state;
|
||||
displayRover(info, s);
|
||||
readState(json.state);
|
||||
};
|
||||
ws.onclose = function(event) {
|
||||
d.getElementById('connind').style.backgroundColor = "#831";
|
||||
}
|
||||
ws.onclose = function(event) {
|
||||
d.getElementById('connind').style.backgroundColor = "#831";
|
||||
}
|
||||
}
|
||||
|
||||
function readState(s,command=false) {
|
||||
@ -1022,7 +1001,8 @@ function readState(s,command=false) {
|
||||
if (isRgbw) whites[e] = parseInt(i.col[e][3]);
|
||||
selectSlot(csel);
|
||||
}
|
||||
d.getElementById('sliderW').value = whites[csel];
|
||||
//d.getElementById('sliderW').value = whites[csel];
|
||||
if (i.cct != null && i.cct>=0) d.getElementById("sliderA").value = i.cct;
|
||||
|
||||
d.getElementById('sliderSpeed').value = i.sx;
|
||||
d.getElementById('sliderIntensity').value = i.ix;
|
||||
@ -1081,7 +1061,7 @@ var reqsLegal = false;
|
||||
|
||||
function requestJson(command, rinfo = true) {
|
||||
d.getElementById('connind').style.backgroundColor = "#a90";
|
||||
if (command && !reqsLegal) return; //stop post requests from chrome onchange event on page restore
|
||||
if (command && !reqsLegal) return; //stop post requests from chrome onchange event on page restore
|
||||
lastUpdate = new Date();
|
||||
if (!jsonTimeout) jsonTimeout = setTimeout(showErrorToast, 3000);
|
||||
var req = null;
|
||||
@ -1091,7 +1071,7 @@ function requestJson(command, rinfo = true) {
|
||||
url = `http://${locip}${url}`;
|
||||
}
|
||||
|
||||
var useWs = ((command || rinfo) && ws && ws.readyState === WebSocket.OPEN);
|
||||
var useWs = ((command || rinfo) && ws && ws.readyState === WebSocket.OPEN);
|
||||
|
||||
var type = command ? 'post':'get';
|
||||
if (command)
|
||||
@ -1107,10 +1087,10 @@ function requestJson(command, rinfo = true) {
|
||||
if (req.length > 1000) useWs = false; //do not send very long requests over websocket
|
||||
}
|
||||
|
||||
if (useWs) {
|
||||
ws.send(req?req:'{"v":true}');
|
||||
return;
|
||||
}
|
||||
if (useWs) {
|
||||
ws.send(req?req:'{"v":true}');
|
||||
return;
|
||||
}
|
||||
|
||||
fetch
|
||||
(url, {
|
||||
@ -1153,7 +1133,7 @@ function requestJson(command, rinfo = true) {
|
||||
});
|
||||
},25);
|
||||
|
||||
reqsLegal = true;
|
||||
reqsLegal = true;
|
||||
}
|
||||
|
||||
var info = json.info;
|
||||
@ -1172,12 +1152,12 @@ function requestJson(command, rinfo = true) {
|
||||
isRgbw = info.leds.wv;
|
||||
ledCount = info.leds.count;
|
||||
syncTglRecv = info.str;
|
||||
maxSeg = info.leds.maxseg;
|
||||
maxSeg = info.leds.maxseg;
|
||||
pmt = info.fs.pmt;
|
||||
|
||||
if (!command && rinfo) setTimeout(loadPresets, 99);
|
||||
|
||||
d.getElementById('buttonNodes').style.display = (info.ndc > 0 && window.innerWidth > 770) ? "block":"none";
|
||||
d.getElementById('buttonNodes').style.display = (info.ndc > 0 && window.innerWidth > 770) ? "block":"none";
|
||||
lastinfo = info;
|
||||
if (isInfo) {
|
||||
populateInfo(info);
|
||||
@ -1186,7 +1166,7 @@ function requestJson(command, rinfo = true) {
|
||||
displayRover(info, s);
|
||||
}
|
||||
|
||||
readState(s,command);
|
||||
readState(s,command);
|
||||
})
|
||||
.catch(function (error) {
|
||||
showToast(error, true);
|
||||
@ -1231,7 +1211,7 @@ function toggleLiveview() {
|
||||
var url = loc ? `http://${locip}/liveview`:"/liveview";
|
||||
d.getElementById('liveview').src = (isLv) ? url:"about:blank";
|
||||
d.getElementById('buttonSr').className = (isLv) ? "active":"";
|
||||
if (!isLv && ws && ws.readyState === WebSocket.OPEN) ws.send('{"lv":false}');
|
||||
if (!isLv && ws && ws.readyState === WebSocket.OPEN) ws.send('{"lv":false}');
|
||||
size();
|
||||
}
|
||||
|
||||
@ -1244,11 +1224,11 @@ function toggleInfo() {
|
||||
}
|
||||
|
||||
function toggleNodes() {
|
||||
if (isInfo) toggleInfo();
|
||||
if (isInfo) toggleInfo();
|
||||
isNodes = !isNodes;
|
||||
d.getElementById('nodes').style.transform = (isNodes) ? "translateY(0px)":"translateY(100%)";
|
||||
d.getElementById('buttonNodes').className = (isNodes) ? "active":"";
|
||||
if (isNodes) loadNodes();
|
||||
d.getElementById('buttonNodes').className = (isNodes) ? "active":"";
|
||||
if (isNodes) loadNodes();
|
||||
}
|
||||
|
||||
function makeSeg() {
|
||||
@ -1258,27 +1238,27 @@ function makeSeg() {
|
||||
if (pend < ledCount) ns = pend;
|
||||
}
|
||||
var cn = `<div class="seg">
|
||||
<div class="segname newseg">
|
||||
New segment ${lowestUnused}
|
||||
<i class="icons edit-icon expanded" onclick="tglSegn(${lowestUnused})"></i>
|
||||
</div>
|
||||
<br>
|
||||
<div class="segin expanded">
|
||||
<input type="text" class="ptxt stxt noslide" id="seg${lowestUnused}t" autocomplete="off" maxlength=32 value="" placeholder="Enter name..."/>
|
||||
<table class="segt">
|
||||
<tr>
|
||||
<td class="segtd">Start LED</td>
|
||||
<td class="segtd">${cfg.comp.seglen?"Length":"Stop LED"}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="segtd"><input class="noslide segn" id="seg${lowestUnused}s" type="number" min="0" max="${ledCount-1}" value="${ns}" oninput="updateLen(${lowestUnused})"></td>
|
||||
<td class="segtd"><input class="noslide segn" id="seg${lowestUnused}e" type="number" min="0" max="${ledCount-(cfg.comp.seglen?ns:0)}" value="${ledCount-(cfg.comp.seglen?ns:0)}" oninput="updateLen(${lowestUnused})"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="h" id="seg${lowestUnused}len">${ledCount - ns} LED${ledCount - ns >1 ? "s":""}</div>
|
||||
<i class="icons e-icon cnf cnf-s half" id="segc${lowestUnused}" onclick="setSeg(${lowestUnused}); resetUtil();"></i>
|
||||
</div>
|
||||
</div>`;
|
||||
<div class="segname newseg">
|
||||
New segment ${lowestUnused}
|
||||
<i class="icons edit-icon expanded" onclick="tglSegn(${lowestUnused})"></i>
|
||||
</div>
|
||||
<br>
|
||||
<div class="segin expanded">
|
||||
<input type="text" class="ptxt stxt noslide" id="seg${lowestUnused}t" autocomplete="off" maxlength=32 value="" placeholder="Enter name..."/>
|
||||
<table class="segt">
|
||||
<tr>
|
||||
<td class="segtd">Start LED</td>
|
||||
<td class="segtd">${cfg.comp.seglen?"Length":"Stop LED"}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="segtd"><input class="noslide segn" id="seg${lowestUnused}s" type="number" min="0" max="${ledCount-1}" value="${ns}" oninput="updateLen(${lowestUnused})"></td>
|
||||
<td class="segtd"><input class="noslide segn" id="seg${lowestUnused}e" type="number" min="0" max="${ledCount-(cfg.comp.seglen?ns:0)}" value="${ledCount-(cfg.comp.seglen?ns:0)}" oninput="updateLen(${lowestUnused})"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="h" id="seg${lowestUnused}len">${ledCount - ns} LED${ledCount - ns >1 ? "s":""}</div>
|
||||
<i class="icons e-icon cnf cnf-s half" id="segc${lowestUnused}" onclick="setSeg(${lowestUnused}); resetUtil();"></i>
|
||||
</div>
|
||||
</div>`;
|
||||
d.getElementById('segutil').innerHTML = cn;
|
||||
}
|
||||
|
||||
@ -1376,75 +1356,74 @@ function makeP(i,pl) {
|
||||
var content = "";
|
||||
if (pl) {
|
||||
var rep = plJson[i].repeat ? plJson[i].repeat : 0;
|
||||
content = `
|
||||
<div class="first c">Playlist Entries</div>
|
||||
<div id="ple${i}"></div><label class="check revchkl">
|
||||
Shuffle
|
||||
<input type="checkbox" id="pl${i}rtgl" onchange="plR(${i})" ${plJson[i].r?"checked":""}>
|
||||
<span class="checkmark schk"></span>
|
||||
</label>
|
||||
<label class="check revchkl">
|
||||
Repeat indefinitely
|
||||
<input type="checkbox" id="pl${i}rptgl" onchange="plR(${i})" ${rep?"":"checked"}>
|
||||
<span class="checkmark schk"></span>
|
||||
</label>
|
||||
<div id="pl${i}o1" style="display:${rep?"block":"none"}">
|
||||
<div class="c">Repeat <input class="noslide" type="number" id="pl${i}rp" oninput="plR(${i})" max=127 min=0 value=${rep>0?rep:1}> times</div>
|
||||
End preset:<br>
|
||||
<select class="btn sel sel-ple" id="pl${i}selEnd" onchange="plR(${i})" data-val=${plJson[i].end?plJson[i].end:0}>
|
||||
content = `<div class="first c">Playlist Entries</div>
|
||||
<div id="ple${i}"></div>
|
||||
<label class="check revchkl">
|
||||
Shuffle
|
||||
<input type="checkbox" id="pl${i}rtgl" onchange="plR(${i})" ${plJson[i].r?"checked":""}>
|
||||
<span class="checkmark schk"></span>
|
||||
</label>
|
||||
<label class="check revchkl">
|
||||
Repeat indefinitely
|
||||
<input type="checkbox" id="pl${i}rptgl" onchange="plR(${i})" ${rep?"":"checked"}>
|
||||
<span class="checkmark schk"></span>
|
||||
</label>
|
||||
<div id="pl${i}o1" style="display:${rep?"block":"none"}">
|
||||
<div class="c">Repeat <input class="noslide" type="number" id="pl${i}rp" oninput="plR(${i})" max=127 min=0 value=${rep>0?rep:1}> times</div>
|
||||
End preset:<br>
|
||||
<select class="btn sel sel-ple" id="pl${i}selEnd" onchange="plR(${i})" data-val=${plJson[i].end?plJson[i].end:0}>
|
||||
<option value=0>None</option>
|
||||
${makePlSel(true)}
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn btn-i btn-p" onclick="testPl(${i}, this)"><i class='icons btn-icon'></i>Test</button>`;
|
||||
${makePlSel(true)}
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn btn-i btn-p" onclick="testPl(${i}, this)"><i class='icons btn-icon'></i>Test</button>`;
|
||||
}
|
||||
else content = `<label class="check revchkl">
|
||||
Include brightness
|
||||
<input type="checkbox" id="p${i}ibtgl" checked>
|
||||
<span class="checkmark schk"></span>
|
||||
</label>
|
||||
<label class="check revchkl">
|
||||
Save segment bounds
|
||||
<input type="checkbox" id="p${i}sbtgl" checked>
|
||||
<span class="checkmark schk"></span>
|
||||
</label>`;
|
||||
Include brightness
|
||||
<input type="checkbox" id="p${i}ibtgl" checked>
|
||||
<span class="checkmark schk"></span>
|
||||
</label>
|
||||
<label class="check revchkl">
|
||||
Save segment bounds
|
||||
<input type="checkbox" id="p${i}sbtgl" checked>
|
||||
<span class="checkmark schk"></span>
|
||||
</label>`;
|
||||
|
||||
return `
|
||||
<input type="text" class="ptxt noslide" id="p${i}txt" autocomplete="off" maxlength=32 value="${(i>0)?pName(i):""}" placeholder="Enter name..."/><br>
|
||||
<div class="c">Quick load label: <input type="text" class="qltxt noslide" maxlength=2 value="${qlName(i)}" id="p${i}ql" autocomplete="off"/></div>
|
||||
<div class="h">(leave empty for no Quick load button)</div>
|
||||
<div ${pl&&i==0?"style='display:none'":""}>
|
||||
return `<input type="text" class="ptxt noslide" id="p${i}txt" autocomplete="off" maxlength=32 value="${(i>0)?pName(i):""}" placeholder="Enter name..."/><br>
|
||||
<div class="c">Quick load label: <input type="text" class="qltxt noslide" maxlength=2 value="${qlName(i)}" id="p${i}ql" autocomplete="off"/></div>
|
||||
<div class="h">(leave empty for no Quick load button)</div>
|
||||
<div ${pl&&i==0?"style='display:none'":""}>
|
||||
<label class="check revchkl">
|
||||
${pl?"Show playlist editor":(i>0)?"Overwrite with state":"Use current state"}
|
||||
<input type="checkbox" id="p${i}cstgl" onchange="tglCs(${i})" ${(i==0||pl)?"checked":""}>
|
||||
<span class="checkmark schk"></span>
|
||||
</label><br>
|
||||
</div>
|
||||
<div class="po2" id="p${i}o2">
|
||||
API command<br>
|
||||
<textarea class="noslide" id="p${i}api"></textarea>
|
||||
</div>
|
||||
<div class="po1" id="p${i}o1">
|
||||
${content}
|
||||
</div>
|
||||
<div class="c">Save to ID <input class="noslide" id="p${i}id" type="number" oninput="checkUsed(${i})" max=250 min=1 value=${(i>0)?i:getLowestUnusedP()}></div>
|
||||
<div class="c">
|
||||
<button class="btn btn-i btn-p" onclick="saveP(${i},${pl})"><i class="icons btn-icon"></i>Save ${(pl)?"playlist":(i>0)?"changes":"preset"}</button>
|
||||
${(i>0)?'<button class="btn btn-i btn-p" id="p'+i+'del" onclick="delP('+i+')"><i class="icons btn-icon"></i>Delete '+(pl?"playlist":"preset"):
|
||||
'<button class="btn btn-p" onclick="resetPUtil()">Cancel'}</button>
|
||||
</div>
|
||||
<div class="pwarn ${(i>0)?"bp":""} c" id="p${i}warn">
|
||||
${pl?"Show playlist editor":(i>0)?"Overwrite with state":"Use current state"}
|
||||
<input type="checkbox" id="p${i}cstgl" onchange="tglCs(${i})" ${(i==0||pl)?"checked":""}>
|
||||
<span class="checkmark schk"></span>
|
||||
</label><br>
|
||||
</div>
|
||||
<div class="po2" id="p${i}o2">
|
||||
API command<br>
|
||||
<textarea class="noslide" id="p${i}api"></textarea>
|
||||
</div>
|
||||
<div class="po1" id="p${i}o1">
|
||||
${content}
|
||||
</div>
|
||||
<div class="c">Save to ID <input class="noslide" id="p${i}id" type="number" oninput="checkUsed(${i})" max=250 min=1 value=${(i>0)?i:getLowestUnusedP()}></div>
|
||||
<div class="c">
|
||||
<button class="btn btn-i btn-p" onclick="saveP(${i},${pl})"><i class="icons btn-icon"></i>Save ${(pl)?"playlist":(i>0)?"changes":"preset"}</button>
|
||||
${(i>0)?'<button class="btn btn-i btn-p" id="p'+i+'del" onclick="delP('+i+')"><i class="icons btn-icon"></i>Delete '+(pl?"playlist":"preset"):
|
||||
'<button class="btn btn-p" onclick="resetPUtil()">Cancel'}</button>
|
||||
</div>
|
||||
<div class="pwarn ${(i>0)?"bp":""} c" id="p${i}warn">
|
||||
|
||||
</div>
|
||||
${(i>0)? ('<div class="h">ID ' +i+ '</div>'):""}`;
|
||||
</div>
|
||||
${(i>0)? ('<div class="h">ID ' +i+ '</div>'):""}`;
|
||||
}
|
||||
|
||||
function makePUtil() {
|
||||
d.getElementById('putil').innerHTML = `<div class="seg pres">
|
||||
<div class="segname newseg">
|
||||
New preset</div>
|
||||
<div class="segin expanded">
|
||||
${makeP(0)}</div></div>`;
|
||||
<div class="segname newseg">
|
||||
New preset</div>
|
||||
<div class="segin expanded">
|
||||
${makeP(0)}</div></div>`;
|
||||
}
|
||||
|
||||
function makePlEntry(p,i) {
|
||||
@ -1478,7 +1457,7 @@ function makePlUtil() {
|
||||
|
||||
function resetPUtil() {
|
||||
var cn = `<button class="btn btn-s btn-i" onclick="makePUtil()"><i class="icons btn-icon"></i>Create preset</button><br>
|
||||
<button class="btn btn-s btn-i" onclick="makePlUtil()"><i class='icons btn-icon'></i>Create playlist</button><br>`;
|
||||
<button class="btn btn-s btn-i" onclick="makePlUtil()"><i class='icons btn-icon'></i>Create playlist</button><br>`;
|
||||
d.getElementById('putil').innerHTML = cn;
|
||||
}
|
||||
|
||||
@ -1490,7 +1469,7 @@ function tglCs(i){
|
||||
|
||||
function tglSegn(s)
|
||||
{
|
||||
d.getElementById(`seg${s}t`).style.display =
|
||||
d.getElementById(`seg${s}t`).style.display =
|
||||
(window.getComputedStyle(d.getElementById(`seg${s}t`)).display === "none") ? "inline":"none";
|
||||
}
|
||||
|
||||
@ -1660,7 +1639,7 @@ function saveP(i,pl) {
|
||||
var pQN = d.getElementById(`p${i}ql`).value;
|
||||
if (pQN.length > 0) obj.ql = pQN;
|
||||
|
||||
showToast("Saving " + pN +" (" + pI + ")");
|
||||
showToast("Saving " + pN +" (" + pI + ")");
|
||||
requestJson(obj);
|
||||
if (obj.o) {
|
||||
pJson[pI] = obj;
|
||||
@ -1724,8 +1703,8 @@ function selectSlot(b) {
|
||||
cpick.color.set(cd[csel].style.backgroundColor);
|
||||
d.getElementById('sliderW').value = whites[csel];
|
||||
updateTrail(d.getElementById('sliderW'));
|
||||
updateHex();
|
||||
updateRgb();
|
||||
//updateHex();
|
||||
//updateRgb();
|
||||
redrawPalPrev();
|
||||
}
|
||||
|
||||
@ -1745,6 +1724,19 @@ function pC(col)
|
||||
setColor(0);
|
||||
}
|
||||
|
||||
function updatePSliders() {
|
||||
updateRgb();
|
||||
updateHex();
|
||||
var v = d.getElementById('sliderV');
|
||||
v.value = cpick.color.value;
|
||||
var hsv = {"h":cpick.color.hue,"s":cpick.color.saturation,"v":100};
|
||||
var c = iro.Color.hsvToRgb(hsv);
|
||||
var cs = 'rgb('+c.r+','+c.g+','+c.b+')';
|
||||
v.parentNode.getElementsByClassName('sliderdisplay')[0].style.setProperty('--bg',cs);
|
||||
updateTrail(v);
|
||||
d.getElementById('sliderK').value = cpick.color.kelvin;
|
||||
}
|
||||
|
||||
function updateRgb()
|
||||
{
|
||||
var col = cpick.color.rgb;
|
||||
@ -1784,20 +1776,30 @@ function fromHex()
|
||||
setColor(2);
|
||||
}
|
||||
|
||||
function fromV()
|
||||
{
|
||||
cpick.color.setChannel('hsv', 'v', d.getElementById('sliderV').value);
|
||||
}
|
||||
|
||||
function fromK()
|
||||
{
|
||||
cpick.color.set({ kelvin: d.getElementById('sliderK').value });
|
||||
}
|
||||
|
||||
function fromRgb()
|
||||
{
|
||||
var r = d.getElementById('sliderR').value;
|
||||
var g = d.getElementById('sliderG').value;
|
||||
var b = d.getElementById('sliderB').value;
|
||||
cpick.color.set(`rgb(${r},${g},${b})`);
|
||||
setColor(0);
|
||||
}
|
||||
|
||||
//sr 0: from RGB sliders, 1: from picker, 2: from hex
|
||||
function setColor(sr) {
|
||||
var cd = d.getElementById('csl').children;
|
||||
if (sr == 1 && cd[csel].style.backgroundColor == 'rgb(0, 0, 0)') cpick.color.setChannel('hsv', 'v', 100);
|
||||
cd[csel].style.backgroundColor = cpick.color.rgbString;
|
||||
if (sr != 2) whites[csel] = d.getElementById('sliderW').value;
|
||||
if (sr != 2) whites[csel] = parseInt(d.getElementById('sliderW').value);
|
||||
var col = cpick.color.rgb;
|
||||
var obj = {"seg": {"col": [[col.r, col.g, col.b, whites[csel]],[],[]]}};
|
||||
if (csel == 1) {
|
||||
@ -1805,8 +1807,14 @@ function setColor(sr) {
|
||||
} else if (csel == 2) {
|
||||
obj = {"seg": {"col": [[],[],[col.r, col.g, col.b, whites[csel]]]}};
|
||||
}
|
||||
updateHex();
|
||||
updateRgb();
|
||||
//updateHex();
|
||||
//updateRgb();
|
||||
requestJson(obj);
|
||||
}
|
||||
|
||||
function setBalance(b)
|
||||
{
|
||||
var obj = {"seg": {"cct": parseInt(b)}};
|
||||
requestJson(obj);
|
||||
}
|
||||
|
||||
@ -1825,9 +1833,9 @@ function cnfReset()
|
||||
if (!cnfr)
|
||||
{
|
||||
var bt = d.getElementById('resetbtn');
|
||||
bt.style.color = "#f00";
|
||||
bt.innerHTML = "Confirm Reboot";
|
||||
cnfr = true; return;
|
||||
bt.style.color = "#f00";
|
||||
bt.innerHTML = "Confirm Reboot";
|
||||
cnfr = true; return;
|
||||
}
|
||||
window.location.href = "/reset";
|
||||
}
|
||||
@ -1838,9 +1846,9 @@ function rSegs()
|
||||
var bt = d.getElementById('rsbtn');
|
||||
if (!cnfrS)
|
||||
{
|
||||
bt.style.color = "#f00";
|
||||
bt.innerHTML = "Confirm reset";
|
||||
cnfrS = true; return;
|
||||
bt.style.color = "#f00";
|
||||
bt.innerHTML = "Confirm reset";
|
||||
cnfrS = true; return;
|
||||
}
|
||||
cnfrS = false;
|
||||
bt.style.color = "#fff";
|
||||
@ -1916,7 +1924,7 @@ function getPalettesData(page, callback)
|
||||
|
||||
function search(searchField) {
|
||||
var searchText = searchField.value.toUpperCase();
|
||||
searchField.parentElement.getElementsByClassName('search-cancel-icon')[0].style.display = (searchText.length < 1)?"none":"inline";
|
||||
searchField.parentElement.getElementsByClassName('search-cancel-icon')[0].style.display = (searchText.length < 1)?"none":"inline";
|
||||
var elements = searchField.parentElement.parentElement.querySelectorAll('.lstI');
|
||||
for (i = 0; i < elements.length; i++) {
|
||||
var item = elements[i];
|
||||
@ -2039,10 +2047,10 @@ function move(e) {
|
||||
var s = Math.sign(dx);
|
||||
var f = +(s*dx/w).toFixed(2);
|
||||
|
||||
if((clientX != 0) &&
|
||||
(iSlide > 0 || s < 0) && (iSlide < N - 1 || s > 0) &&
|
||||
f > 0.12 &&
|
||||
d.getElementsByClassName("tabcontent")[iSlide].scrollTop == scrollS) {
|
||||
if ((clientX != 0) &&
|
||||
(iSlide > 0 || s < 0) && (iSlide < N - 1 || s > 0) &&
|
||||
f > 0.12 &&
|
||||
d.getElementsByClassName("tabcontent")[iSlide].scrollTop == scrollS) {
|
||||
_C.style.setProperty('--i', iSlide -= s);
|
||||
f = 1 - f;
|
||||
updateTablinks(iSlide);
|
||||
@ -2054,7 +2062,7 @@ function move(e) {
|
||||
|
||||
function size() {
|
||||
w = window.innerWidth;
|
||||
d.getElementById('buttonNodes').style.display = (lastinfo.ndc > 0 && w > 770) ? "block":"none";
|
||||
d.getElementById('buttonNodes').style.display = (lastinfo.ndc > 0 && w > 770) ? "block":"none";
|
||||
var h = d.getElementById('top').clientHeight;
|
||||
sCol('--th', h + "px");
|
||||
sCol('--bh', d.getElementById('bot').clientHeight + "px");
|
||||
@ -2079,7 +2087,7 @@ function togglePcMode(fromB = false)
|
||||
d.getElementById('buttonPcm').className = (pcMode) ? "active":"";
|
||||
d.getElementById('bot').style.height = (pcMode && !cfg.comp.pcmbot) ? "0":"auto";
|
||||
sCol('--bh', d.getElementById('bot').clientHeight + "px");
|
||||
_C.style.width = (pcMode)?'100%':'400%';
|
||||
_C.style.width = (pcMode)?'100%':'400%';
|
||||
lastw = w;
|
||||
}
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -17,23 +17,15 @@
|
||||
color: #fff;
|
||||
font-family: Verdana, Helvetica, sans-serif;
|
||||
border: 1px solid #333;
|
||||
border-radius: var(--h);
|
||||
font-size: 6vmin;
|
||||
height: var(--h);
|
||||
width: 95%;
|
||||
width: calc(100% - 40px);
|
||||
margin-top: 2vh;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function BB()
|
||||
{
|
||||
if (window.frameElement) {
|
||||
document.getElementById("b").style.display = "none";
|
||||
document.documentElement.style.setProperty('--h',"13.86vh");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="BB()">
|
||||
<body>
|
||||
<form action="/"><button type=submit id="b">Back</button></form>
|
||||
<form action="/settings/wifi"><button type="submit">WiFi Setup</button></form>
|
||||
<form action="/settings/leds"><button type="submit">LED Preferences</button></form>
|
||||
|
@ -247,8 +247,8 @@
|
||||
gId('m0').innerHTML = memu;
|
||||
bquot = memu / maxM * 100;
|
||||
gId('dbar').style.background = `linear-gradient(90deg, ${bquot > 60 ? (bquot > 90 ? "red":"orange"):"#ccc"} 0 ${bquot}%%, #444 ${bquot}%% 100%%)`;
|
||||
gId('ledwarning').style.display = (sLC > maxPB || maxLC > 800 || bquot > 80) ? 'inline':'none';
|
||||
gId('ledwarning').style.color = (sLC > maxPB || maxLC > maxPB || bquot > 100) ? 'red':'orange';
|
||||
gId('ledwarning').style.display = (maxLC > Math.min(maxPB,800) || bquot > 80) ? 'inline':'none';
|
||||
gId('ledwarning').style.color = (maxLC > Math.max(maxPB,800) || bquot > 100) ? 'red':'orange';
|
||||
gId('wreason').innerHTML = (bquot > 80) ? "80% of max. LED memory" +(bquot>100 ? ` (<b>ERROR: Using over ${maxM}B!</b>)` : "") : "800 LEDs per output";
|
||||
// calculate power
|
||||
var val = Math.ceil((100 + sPC * laprev)/500)/2;
|
||||
@ -305,10 +305,11 @@ ${i+1}:
|
||||
<option value="52">LPD8806</option>
|
||||
<option value="53">P9813</option>
|
||||
<option value="41">PWM White</option>
|
||||
<option value="42">PWM WWCW</option>
|
||||
<option value="42">PWM CCT</option>
|
||||
<option value="43">PWM RGB</option>
|
||||
<option value="44">PWM RGBW</option>
|
||||
<option value="45">PWM RGBWC</option>
|
||||
<option value="45">PWM RGB+CCT</option>
|
||||
<!--option value="46">PWM RGB+DCCT</option-->
|
||||
<option value="80">DDP RGB (network)</option>
|
||||
<!--option value="81">E1.31 RGB (network)</option-->
|
||||
<!--option value="82">ArtNet RGB (network)</option-->
|
||||
@ -333,7 +334,7 @@ ${i+1}:
|
||||
<span id="p4d${i}"></span><input type="number" name="L4${i}" min="0" max="33" class="xs" onchange="UI()"/>
|
||||
<div id="dig${i}r" style="display:inline"><br><span id="rev${i}">Reversed</span>: <input type="checkbox" name="CV${i}"></div>
|
||||
<div id="dig${i}s" style="display:inline"><br>Skip 1<sup>st</sup> LED: <input id="sl${i}" type="checkbox" name="SL${i}"></div>
|
||||
<div id="dig${i}f" style="display:inline"><br>Off Refresh: <input id="rf${i}" type="checkbox" name="RF${i}"> </div>
|
||||
<div id="dig${i}f" style="display:inline"><br>Off Refresh: <input id="rf${i}" type="checkbox" name="RF${i}"></div>
|
||||
</div>`;
|
||||
f.insertAdjacentHTML("beforeend", cn);
|
||||
}
|
||||
@ -389,11 +390,71 @@ ${i+1}:
|
||||
req.send(formData);
|
||||
d.Sf.data.value = '';
|
||||
return false;
|
||||
}
|
||||
// https://stackoverflow.com/questions/7346563/loading-local-json-file
|
||||
function loadCfg(o) {
|
||||
var f, fr;
|
||||
|
||||
if (typeof window.FileReader !== 'function') {
|
||||
alert("The file API isn't supported on this browser yet.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!o.files) {
|
||||
alert("This browser doesn't support the `files` property of file inputs.");
|
||||
} else if (!o.files[0]) {
|
||||
alert("Please select a JSON file first!");
|
||||
} else {
|
||||
f = o.files[0];
|
||||
fr = new FileReader();
|
||||
fr.onload = receivedText;
|
||||
fr.readAsText(f);
|
||||
}
|
||||
o.value = '';
|
||||
|
||||
function receivedText(e) {
|
||||
let lines = e.target.result;
|
||||
var c = JSON.parse(lines);
|
||||
if (c.hw) {
|
||||
if (c.hw.led) {
|
||||
for (var i=0; i<10; i++) addLEDs(-1);
|
||||
var l = c.hw.led;
|
||||
l.ins.forEach((v,i,a)=>{
|
||||
addLEDs(1);
|
||||
for (var j=0; j<v.pin.length; j++) d.getElementsByName(`L${j}${i}`)[0].value = v.pin[j];
|
||||
d.getElementsByName("LT"+i)[0].value = v.type;
|
||||
d.getElementsByName("LS"+i)[0].value = v.start;
|
||||
d.getElementsByName("LC"+i)[0].value = v.len;
|
||||
d.getElementsByName("CO"+i)[0].value = v.order;
|
||||
d.getElementsByName("SL"+i)[0].checked = v.skip;
|
||||
d.getElementsByName("RF"+i)[0].checked = v.ref;
|
||||
d.getElementsByName("CV"+i)[0].checked = v.rev;
|
||||
});
|
||||
}
|
||||
if (c.hw.btn) {
|
||||
var b = c.hw.btn;
|
||||
if (Array.isArray(b.ins)) gId("btns").innerHTML = "";
|
||||
b.ins.forEach((v,i,a)=>{
|
||||
addBtn(i,v.pin[0],v.type);
|
||||
});
|
||||
d.getElementsByName("TT")[0].value = b.tt;
|
||||
}
|
||||
if (c.hw.ir) {
|
||||
d.getElementsByName("IR")[0].value = c.hw.ir.pin;
|
||||
d.getElementsByName("IT")[0].value = c.hw.ir.type;
|
||||
}
|
||||
if (c.hw.relay) {
|
||||
d.getElementsByName("RL")[0].value = c.hw.relay.pin;
|
||||
d.getElementsByName("RM")[0].checked = c.hw.relay.inv;
|
||||
}
|
||||
UI();
|
||||
}
|
||||
}
|
||||
}
|
||||
function GetV()
|
||||
{
|
||||
//values injected by server while sending HTML
|
||||
//d.um_p=[6,7,8,9,10,11,1];bLimits(3,4096,4000,1664);d.Sf.MS.checked=1;addLEDs(1);d.Sf.L00.value=2;d.Sf.LC0.value=30;d.Sf.LT0.value=22;d.Sf.CO0.value=0;d.Sf.LS0.value=15;d.Sf.CV0.checked=1;d.Sf.SL0.checked=0;addLEDs(1);d.Sf.L01.value=10;d.Sf.L11.value=10;d.Sf.L21.value=1;d.Sf.L31.value=10;d.Sf.LC1.value=60;d.Sf.LT1.value=80;d.Sf.CO1.value=1;d.Sf.LS1.value=0;d.Sf.CV1.checked=0;d.Sf.SL1.checked=0;d.Sf.MA.value=850;d.Sf.LA.value=0;d.Sf.CA.value=56;d.Sf.AW.value=3;d.Sf.BO.checked=1;d.Sf.BP.value=80;d.Sf.GB.checked=0;d.Sf.GC.checked=1;d.Sf.TF.checked=1;d.Sf.TD.value=700;d.Sf.PF.checked=0;d.Sf.BF.value=100;d.Sf.TB.value=0;d.Sf.TL.value=60;d.Sf.TW.value=0;d.Sf.PB.selectedIndex=0;d.Sf.RL.value=12;d.Sf.RM.checked=1;addBtn(0,0,0);addBtn(1,-1,0);d.Sf.TT.value=32;d.Sf.IR.value=-1;d.Sf.IT.value=0;
|
||||
//d.um_p=[6,7,8,9,10,11,14,15,13,1,21,19,22,25,26,27,5,23,18,17];bLimits(10,2048,64000,8192);d.Sf.MS.checked=1;d.Sf.CCT.checked=0;addLEDs(1);d.Sf.L00.value=192;d.Sf.L10.value=168;d.Sf.L20.value=0;d.Sf.L30.value=61;d.Sf.LC0.value=421;d.Sf.LT0.value=80;d.Sf.CO0.value=1;d.Sf.LS0.value=0;d.Sf.CV0.checked=0;d.Sf.SL0.checked=0;d.Sf.RF0.checked=0;d.Sf.MA.value=850;d.Sf.LA.value=0;d.Sf.CA.value=127;d.Sf.AW.value=3;d.Sf.BO.checked=0;d.Sf.BP.value=0;d.Sf.GB.checked=0;d.Sf.GC.checked=1;d.Sf.TF.checked=1;d.Sf.TD.value=700;d.Sf.PF.checked=1;d.Sf.BF.value=100;d.Sf.TB.value=0;d.Sf.TL.value=60;d.Sf.TW.value=1;d.Sf.PB.selectedIndex=0;d.Sf.RL.value=-1;d.Sf.RM.checked=1;addBtn(0,-1,0);addBtn(1,-1,0);addBtn(2,-1,0);addBtn(3,-1,0);d.Sf.TT.value=32;d.Sf.IR.value=-1;d.Sf.IT.value=8;
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
@ -474,7 +535,7 @@ ${i+1}:
|
||||
<br><br>
|
||||
Use Gamma correction for color: <input type="checkbox" name="GC"> (strongly recommended)<br>
|
||||
Use Gamma correction for brightness: <input type="checkbox" name="GB"> (not recommended)<br><br>
|
||||
Brightness factor: <input name="BF" type="number" class="s" min="1" max="255" required> %
|
||||
Brightness factor: <input name="BF" type="number" class="s" min="1" max="255" required> %%
|
||||
<h3>Transitions</h3>
|
||||
Crossfade: <input type="checkbox" name="TF"><br>
|
||||
Transition Time: <input name="TD" type="number" class="l" min="0" max="65500"> ms<br>
|
||||
@ -489,6 +550,19 @@ ${i+1}:
|
||||
<option value="2">Fade Color</option>
|
||||
<option value="3">Sunrise</option>
|
||||
</select>
|
||||
<h3>White management</h3>
|
||||
White Balance correction: <input type="checkbox" name="CCT"> <br>
|
||||
<span class="wc">
|
||||
Auto-calculate white channel from RGB:<br>
|
||||
<select name="AW">
|
||||
<option value=0>None</option>
|
||||
<option value=1>Brighter</option>
|
||||
<option value=2>Accurate</option>
|
||||
<option value=3>Dual</option>
|
||||
</select>
|
||||
<br>
|
||||
Calculate CCT from RGB: <input type="checkbox" name="CR"> <br>
|
||||
CCT additive blending: <input type="number" class="s" min="0" max="100" name="CB" required> %%</span>
|
||||
<h3>Advanced</h3>
|
||||
Palette blending:
|
||||
<select name="PB">
|
||||
@ -497,16 +571,9 @@ ${i+1}:
|
||||
<option value="2">Linear (never wrap)</option>
|
||||
<option value="3">None (not recommended)</option>
|
||||
</select><br>
|
||||
<span class="wc">
|
||||
Auto-calculate white channel from RGB:<br>
|
||||
<select name="AW">
|
||||
<option value=0>None</option>
|
||||
<option value=1>Brighter</option>
|
||||
<option value=2>Accurate</option>
|
||||
<option value=3>Dual</option>
|
||||
<option value=4>Legacy</option>
|
||||
</select>
|
||||
<br></span><hr>
|
||||
<hr style="width:260px">
|
||||
<div id="cfg">Config template: <input type="file" name="data2" accept=".json"> <input type="button" value="Apply" onclick="loadCfg(d.Sf.data2);"><br></div>
|
||||
<hr>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
|
||||
</form>
|
||||
</body>
|
||||
|
@ -22,10 +22,10 @@ void handleDMX()
|
||||
for (int i = DMXStartLED; i < len; i++) { // uses the amount of LEDs as fixture count
|
||||
|
||||
uint32_t in = strip.getPixelColor(i); // get the colors for the individual fixtures as suggested by Aircoookie in issue #462
|
||||
byte w = in >> 24 & 0xFF;
|
||||
byte r = in >> 16 & 0xFF;
|
||||
byte g = in >> 8 & 0xFF;
|
||||
byte b = in & 0xFF;
|
||||
byte w = W(in);
|
||||
byte r = R(in);
|
||||
byte g = G(in);
|
||||
byte b = B(in);
|
||||
|
||||
int DMXFixtureStart = DMXStart + (DMXGap * (i - DMXStartLED));
|
||||
for (int j = 0; j < DMXChannels; j++) {
|
||||
|
@ -67,7 +67,9 @@ void colorRGBtoXY(byte* rgb, float* xy); // only defined if huesync disabled TOD
|
||||
|
||||
void colorFromDecOrHexString(byte* rgb, char* in);
|
||||
bool colorFromHexString(byte* rgb, const char* in);
|
||||
void colorRGBtoRGBW(byte* rgb); //rgb to rgbw (http://codewelt.com/rgbw). (RGBW_MODE_LEGACY)
|
||||
|
||||
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
|
||||
uint16_t approximateKelvinFromRGB(uint32_t rgb);
|
||||
|
||||
//dmx.cpp
|
||||
void initDMX();
|
||||
|
File diff suppressed because one or more lines are too long
4334
wled00/html_ui.h
4334
wled00/html_ui.h
File diff suppressed because it is too large
Load Diff
@ -86,6 +86,10 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
if (elem["on"].is<const char*>() && elem["on"].as<const char*>()[0] == 't') on = !on;
|
||||
seg.setOption(SEG_OPTION_ON, on, id);
|
||||
|
||||
uint8_t cctPrev = seg.cct;
|
||||
seg.setCCT(elem["cct"] | seg.cct, id);
|
||||
if (seg.cct != cctPrev && id == strip.getMainSegmentId()) effectChanged = true; //send UDP
|
||||
|
||||
JsonArray colarr = elem["col"];
|
||||
if (!colarr.isNull())
|
||||
{
|
||||
@ -143,7 +147,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
|
||||
//if (pal != seg.palette && pal < strip.getPaletteCount()) strip.setPalette(pal);
|
||||
seg.setOption(SEG_OPTION_SELECTED, elem[F("sel")] | seg.getOption(SEG_OPTION_SELECTED));
|
||||
seg.setOption(SEG_OPTION_REVERSED, elem["rev"] | seg.getOption(SEG_OPTION_REVERSED));
|
||||
seg.setOption(SEG_OPTION_REVERSED, elem["rev"] | seg.getOption(SEG_OPTION_REVERSED));
|
||||
seg.setOption(SEG_OPTION_MIRROR , elem[F("mi")] | seg.getOption(SEG_OPTION_MIRROR ));
|
||||
|
||||
//temporary, strip object gets updated via colorUpdated()
|
||||
@ -383,6 +387,7 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo
|
||||
root["on"] = seg.getOption(SEG_OPTION_ON);
|
||||
byte segbri = seg.opacity;
|
||||
root["bri"] = (segbri) ? segbri : 255;
|
||||
root["cct"] = seg.cct;
|
||||
|
||||
if (segmentBounds && seg.name != nullptr) root["n"] = reinterpret_cast<const char *>(seg.name); //not good practice, but decreases required JSON buffer
|
||||
|
||||
@ -498,7 +503,15 @@ void serializeInfo(JsonObject root)
|
||||
JsonObject leds = root.createNestedObject("leds");
|
||||
leds[F("count")] = strip.getLengthTotal();
|
||||
leds[F("rgbw")] = strip.isRgbw;
|
||||
leds[F("wv")] = strip.isRgbw && (strip.rgbwMode == RGBW_MODE_MANUAL_ONLY || strip.rgbwMode == RGBW_MODE_DUAL); //should a white channel slider be displayed?
|
||||
leds[F("wv")] = false;
|
||||
leds["cct"] = correctWB || strip.hasCCTBus();
|
||||
switch (Bus::getAutoWhiteMode()) {
|
||||
case RGBW_MODE_MANUAL_ONLY:
|
||||
case RGBW_MODE_DUAL:
|
||||
if (strip.isRgbw) leds[F("wv")] = true;
|
||||
break;
|
||||
}
|
||||
|
||||
leds[F("pwr")] = strip.currentMilliamps;
|
||||
leds[F("fps")] = strip.getFps();
|
||||
leds[F("maxpwr")] = (strip.currentMilliamps)? strip.ablMilliampsMax : 0;
|
||||
|
@ -30,7 +30,6 @@ void toggleOnOff()
|
||||
{
|
||||
briLast = bri;
|
||||
bri = 0;
|
||||
unloadPlaylist();
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,25 +37,15 @@ void toggleOnOff()
|
||||
//scales the brightness with the briMultiplier factor
|
||||
byte scaledBri(byte in)
|
||||
{
|
||||
uint32_t d = in*briMultiplier;
|
||||
uint32_t val = d/100;
|
||||
uint16_t val = ((uint16_t)in*briMultiplier)/100;
|
||||
if (val > 255) val = 255;
|
||||
return (byte)val;
|
||||
}
|
||||
|
||||
|
||||
void setAllLeds() {
|
||||
if (strip.isRgbw && strip.rgbwMode == RGBW_MODE_LEGACY)
|
||||
{
|
||||
colorRGBtoRGBW(col);
|
||||
colorRGBtoRGBW(colSec);
|
||||
}
|
||||
strip.setColor(0, col[0], col[1], col[2], col[3]);
|
||||
strip.setColor(1, colSec[0], colSec[1], colSec[2], colSec[3]);
|
||||
if (strip.isRgbw && strip.rgbwMode == RGBW_MODE_LEGACY)
|
||||
{
|
||||
col[3] = 0; colSec[3] = 0;
|
||||
}
|
||||
if (!realtimeMode || !arlsForceMaxBri)
|
||||
{
|
||||
strip.setBrightness(scaledBri(briT));
|
||||
@ -190,8 +179,10 @@ void updateInterfaces(uint8_t callMode)
|
||||
espalexaDevice->setColor(col[0], col[1], col[2]);
|
||||
}
|
||||
#endif
|
||||
#ifndef WLED_DISABLE_BLYNK
|
||||
if (callMode != CALL_MODE_BLYNK &&
|
||||
callMode != CALL_MODE_NO_NOTIFY) updateBlynk();
|
||||
#endif
|
||||
doPublishMqtt = true;
|
||||
lastInterfaceUpdate = millis();
|
||||
}
|
||||
@ -285,7 +276,9 @@ void handleNightlight()
|
||||
setLedsStandard();
|
||||
}
|
||||
}
|
||||
#ifndef WLED_DISABLE_BLYNK
|
||||
updateBlynk();
|
||||
#endif
|
||||
if (macroNl > 0)
|
||||
applyPreset(macroNl);
|
||||
nightlightActiveOld = false;
|
||||
|
@ -118,6 +118,7 @@ int16_t loadPlaylist(JsonObject playlistObj, byte presetId) {
|
||||
|
||||
|
||||
void handlePlaylist() {
|
||||
static unsigned long presetCycledTime = 0;
|
||||
if (currentPlaylist < 0 || playlistEntries == nullptr) return;
|
||||
|
||||
if (millis() - presetCycledTime > (100*playlistEntryDur)) {
|
||||
|
@ -95,6 +95,11 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
uint8_t pins[5] = {255, 255, 255, 255, 255};
|
||||
|
||||
autoSegments = request->hasArg(F("MS"));
|
||||
correctWB = request->hasArg(F("CCT"));
|
||||
cctFromRgb = request->hasArg(F("CR"));
|
||||
strip.cctBlending = request->arg(F("CB")).toInt();
|
||||
Bus::setCCTBlend(strip.cctBlending);
|
||||
Bus::setAutoWhiteMode(request->arg(F("AW")).toInt());
|
||||
|
||||
for (uint8_t s = 0; s < WLED_MAX_BUSSES; s++) {
|
||||
char lp[4] = "L0"; lp[2] = 48+s; lp[3] = 0; //ascii 0-9 //strip data pin
|
||||
@ -116,7 +121,6 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
type = request->arg(lt).toInt();
|
||||
type |= request->hasArg(rf) << 7; // off refresh override
|
||||
skip = request->hasArg(sl) ? LED_SKIP_AMOUNT : 0;
|
||||
|
||||
colorOrder = request->arg(co).toInt();
|
||||
start = (request->hasArg(ls)) ? request->arg(ls).toInt() : t;
|
||||
if (request->hasArg(lc) && request->arg(lc).toInt() > 0) {
|
||||
@ -166,8 +170,6 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
strip.ablMilliampsMax = request->arg(F("MA")).toInt();
|
||||
strip.milliampsPerLed = request->arg(F("LA")).toInt();
|
||||
|
||||
strip.rgbwMode = request->arg(F("AW")).toInt();
|
||||
|
||||
briS = request->arg(F("CA")).toInt();
|
||||
|
||||
turnOnAtBoot = request->hasArg(F("BO"));
|
||||
|
@ -22,7 +22,7 @@ private:
|
||||
ColorCallbackFunction _callbackCol = nullptr;
|
||||
uint8_t _val, _val_last, _sat = 0;
|
||||
uint16_t _hue = 0, _ct = 0;
|
||||
float _x = 0.5, _y = 0.5;
|
||||
float _x = 0.5f, _y = 0.5f;
|
||||
uint32_t _rgb = 0;
|
||||
uint8_t _id = 0;
|
||||
EspalexaDeviceType _type;
|
||||
|
@ -4,7 +4,7 @@
|
||||
* UDP sync notifier / Realtime / Hyperion / TPM2.NET
|
||||
*/
|
||||
|
||||
#define WLEDPACKETSIZE 37
|
||||
#define WLEDPACKETSIZE 39
|
||||
#define UDP_IN_MAXSIZE 1472
|
||||
#define PRESUMED_NETWORK_DELAY 3 //how many ms could it take on avg to reach the receiver? This will be added to transmitted times
|
||||
|
||||
@ -25,6 +25,7 @@ void notify(byte callMode, bool followUp)
|
||||
default: return;
|
||||
}
|
||||
byte udpOut[WLEDPACKETSIZE];
|
||||
WS2812FX::Segment& mainseg = strip.getSegment(strip.getMainSegmentId());
|
||||
udpOut[0] = 0; //0: wled notifier protocol 1: WARLS protocol
|
||||
udpOut[1] = callMode;
|
||||
udpOut[2] = bri;
|
||||
@ -40,8 +41,8 @@ void notify(byte callMode, bool followUp)
|
||||
//0: old 1: supports white 2: supports secondary color
|
||||
//3: supports FX intensity, 24 byte packet 4: supports transitionDelay 5: sup palette
|
||||
//6: supports timebase syncing, 29 byte packet 7: supports tertiary color 8: supports sys time sync, 36 byte packet
|
||||
//9: supports sync groups, 37 byte packet
|
||||
udpOut[11] = 9;
|
||||
//9: supports sync groups, 37 byte packet 10: supports CCT, 39 byte packet
|
||||
udpOut[11] = 10;
|
||||
udpOut[12] = colSec[0];
|
||||
udpOut[13] = colSec[1];
|
||||
udpOut[14] = colSec[2];
|
||||
@ -50,7 +51,7 @@ void notify(byte callMode, bool followUp)
|
||||
udpOut[17] = (transitionDelay >> 0) & 0xFF;
|
||||
udpOut[18] = (transitionDelay >> 8) & 0xFF;
|
||||
udpOut[19] = effectPalette;
|
||||
uint32_t colTer = strip.getSegment(strip.getMainSegmentId()).colors[2];
|
||||
uint32_t colTer = mainseg.colors[2];
|
||||
udpOut[20] = (colTer >> 16) & 0xFF;
|
||||
udpOut[21] = (colTer >> 8) & 0xFF;
|
||||
udpOut[22] = (colTer >> 0) & 0xFF;
|
||||
@ -77,6 +78,11 @@ void notify(byte callMode, bool followUp)
|
||||
|
||||
//sync groups
|
||||
udpOut[36] = syncGroups;
|
||||
|
||||
//Might be changed to Kelvin in the future, receiver code should handle that case
|
||||
//0: byte 38 contains 0-255 value, 255: no valid CCT, 1-254: Kelvin value MSB
|
||||
udpOut[37] = strip.hasCCTBus() ? 0 : 255; //check this is 0 for the next value to be significant
|
||||
udpOut[38] = mainseg.cct;
|
||||
|
||||
IPAddress broadcastIp;
|
||||
broadcastIp = ~uint32_t(Network.subnetMask()) | uint32_t(Network.gatewayIP());
|
||||
@ -260,6 +266,14 @@ void handleNotifications()
|
||||
{
|
||||
strip.setColor(2, udpIn[20], udpIn[21], udpIn[22], udpIn[23]); //tertiary color
|
||||
}
|
||||
if (version > 9 && version < 200 && udpIn[37] < 255) { //valid CCT/Kelvin value
|
||||
uint8_t cct = udpIn[38];
|
||||
if (udpIn[37] > 0) { //Kelvin
|
||||
cct = (((udpIn[37] << 8) + udpIn[38]) - 1900) >> 5;
|
||||
}
|
||||
uint8_t segid = strip.getMainSegmentId();
|
||||
strip.getSegment(segid).setCCT(cct, segid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
// version code in format yymmddb (b = daily build)
|
||||
#define VERSION 2111220
|
||||
#define VERSION 2111280
|
||||
|
||||
//uncomment this if you have a "my_config.h" file you'd like to use
|
||||
//#define WLED_USE_MY_CONFIG
|
||||
@ -31,7 +31,7 @@
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
#define WLED_ENABLE_MQTT // saves 12kb
|
||||
#endif
|
||||
#define WLED_ENABLE_ADALIGHT // saves 500b only
|
||||
#define WLED_ENABLE_ADALIGHT // saves 500b only (uses GPIO3 (RX) for serial)
|
||||
//#define WLED_ENABLE_DMX // uses 3.5kb (use LEDPIN other than 2)
|
||||
#ifndef WLED_DISABLE_LOXONE
|
||||
#define WLED_ENABLE_LOXONE // uses 1.2kb
|
||||
@ -269,6 +269,8 @@ WLED_GLOBAL byte bootPreset _INIT(0); // save preset to load
|
||||
//if false, only one segment spanning the total LEDs is created,
|
||||
//but not on LED settings save if there is more than one segment currently
|
||||
WLED_GLOBAL bool autoSegments _INIT(false);
|
||||
WLED_GLOBAL bool correctWB _INIT(false); //CCT color correction of RGB color
|
||||
WLED_GLOBAL bool cctFromRgb _INIT(true); //CCT is calculated from RGB instead of using seg.cct
|
||||
|
||||
WLED_GLOBAL byte col[] _INIT_N(({ 255, 160, 0, 0 })); // current RGB(W) primary color. col[] should be updated if you want to change the color.
|
||||
WLED_GLOBAL byte colSec[] _INIT_N(({ 0, 0, 0, 0 })); // current RGB(W) secondary color
|
||||
@ -511,7 +513,6 @@ WLED_GLOBAL byte improvActive _INIT(0); //0: no improv packet received, 1: impro
|
||||
WLED_GLOBAL byte improvError _INIT(0);
|
||||
|
||||
//playlists
|
||||
WLED_GLOBAL unsigned long presetCycledTime _INIT(0);
|
||||
WLED_GLOBAL int16_t currentPlaylist _INIT(-1);
|
||||
//still used for "PL=~" HTTP API command
|
||||
WLED_GLOBAL byte presetCycCurr _INIT(0);
|
||||
@ -642,6 +643,13 @@ WLED_GLOBAL UsermodManager usermods _INIT(UsermodManager());
|
||||
#define WLED_WIFI_CONFIGURED (strlen(clientSSID) >= 1 && strcmp(clientSSID, DEFAULT_CLIENT_SSID) != 0)
|
||||
#define WLED_MQTT_CONNECTED (mqtt != nullptr && mqtt->connected())
|
||||
|
||||
//color mangling macros
|
||||
#define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b))))
|
||||
#define R(c) (byte((c) >> 16))
|
||||
#define G(c) (byte((c) >> 8))
|
||||
#define B(c) (byte(c))
|
||||
#define W(c) (byte((c) >> 24))
|
||||
|
||||
// append new c string to temp buffer efficiently
|
||||
bool oappend(const char* txt);
|
||||
// append new number to temp buffer efficiently
|
||||
|
@ -329,7 +329,7 @@ void loadSettingsFromEEPROM()
|
||||
receiveDirect = !EEPROM.read(2200);
|
||||
notifyMacro = EEPROM.read(2201);
|
||||
|
||||
strip.rgbwMode = EEPROM.read(2203);
|
||||
//strip.rgbwMode = EEPROM.read(2203);
|
||||
//skipFirstLed = EEPROM.read(2204);
|
||||
|
||||
bootPreset = EEPROM.read(389);
|
||||
|
@ -370,9 +370,14 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
oappend(SET_F(");"));
|
||||
|
||||
sappend('c',SET_F("MS"),autoSegments);
|
||||
sappend('c',SET_F("CCT"),correctWB);
|
||||
sappend('c',SET_F("CR"),cctFromRgb);
|
||||
sappend('v',SET_F("CB"),strip.cctBlending);
|
||||
sappend('v',SET_F("AW"),Bus::getAutoWhiteMode());
|
||||
|
||||
for (uint8_t s=0; s < busses.getNumBusses(); s++) {
|
||||
Bus* bus = busses.getBus(s);
|
||||
if (bus == nullptr) continue;
|
||||
char lp[4] = "L0"; lp[2] = 48+s; lp[3] = 0; //ascii 0-9 //strip data pin
|
||||
char lc[4] = "LC"; lc[2] = 48+s; lc[3] = 0; //strip length
|
||||
char co[4] = "CO"; co[2] = 48+s; co[3] = 0; //strip color order
|
||||
@ -407,7 +412,6 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
}
|
||||
|
||||
sappend('v',SET_F("CA"),briS);
|
||||
sappend('v',SET_F("AW"),strip.rgbwMode);
|
||||
|
||||
sappend('c',SET_F("BO"),turnOnAtBoot);
|
||||
sappend('v',SET_F("BP"),bootPreset);
|
||||
|
Loading…
Reference in New Issue
Block a user