@ -40,10 +40,11 @@ bool _light_use_transitions = false;
unsigned int _light_transition_time = LIGHT_TRANSITION_TIME ;
unsigned int _light_transition_time = LIGHT_TRANSITION_TIME ;
bool _light_has_color = false ;
bool _light_has_color = false ;
bool _light_use_white = false ;
bool _light_use_white = false ;
bool _light_use_cct = false ;
bool _light_use_gamma = false ;
bool _light_use_gamma = false ;
unsigned long _light_steps_left = 1 ;
unsigned long _light_steps_left = 1 ;
unsigned char _light_brightness = LIGHT_MAX_BRIGHTNESS ;
unsigned char _light_brightness = LIGHT_MAX_BRIGHTNESS ;
unsigned int _light_mireds = LIGHT_DEFAULT_MIREDS ;
unsigned int _light_mireds = round ( ( LIGHT_COLDWHITE_MIRED + LIGHT_WARMWHITE_MIRED ) / 2 ) ;
# if LIGHT_PROVIDER == LIGHT_PROVIDER_MY92XX
# if LIGHT_PROVIDER == LIGHT_PROVIDER_MY92XX
# include <my92xx.h>
# include <my92xx.h>
@ -77,45 +78,70 @@ const unsigned char _light_gamma_table[] = {
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
void _setRGBInputValue ( unsigned char red , unsigned char green , unsigned char blue ) {
void _setRGBInputValue ( unsigned char red , unsigned char green , unsigned char blue ) {
_light_channel [ 0 ] . inputValue = red ;
_light_channel [ 1 ] . inputValue = green ;
_light_channel [ 2 ] . inputValue = blue ;
_light_channel [ 0 ] . inputValue = constrain ( red , 0 , LIGHT_MAX_VALUE ) ;
_light_channel [ 1 ] . inputValue = constrain ( green , 0 , LIGHT_MAX_VALUE ) ; ;
_light_channel [ 2 ] . inputValue = constrain ( blue , 0 , LIGHT_MAX_VALUE ) ; ;
}
}
void _generateBrightness ( ) {
void _generateBrightness ( ) {
double brightness = ( double ) _light_brightness / LIGHT_MAX_BRIGHTNESS ;
double brightness = ( double ) _light_brightness / LIGHT_MAX_BRIGHTNESS ;
// Convert RGB to RGBW
// Convert RGB to RGBW(W)
if ( _light_has_color & & _light_use_white ) {
if ( _light_has_color & & _light_use_white ) {
// Substract the common part from RGB channels and add it to white channel. So [250,150,50] -> [200,100,0,50
// Substract the common part from RGB channels and add it to white channel. So [250,150,50] -> [200,100,0,50]
unsigned char white = std : : min ( _light_channel [ 0 ] . inputValue , std : : min ( _light_channel [ 1 ] . inputValue , _light_channel [ 2 ] . inputValue ) ) ;
unsigned char white = std : : min ( _light_channel [ 0 ] . inputValue , std : : min ( _light_channel [ 1 ] . inputValue , _light_channel [ 2 ] . inputValue ) ) ;
for ( unsigned int i = 0 ; i < 3 ; i + + ) {
for ( unsigned int i = 0 ; i < 3 ; i + + ) {
_light_channel [ i ] . value = _light_channel [ i ] . inputValue - white ;
_light_channel [ i ] . value = _light_channel [ i ] . inputValue - white ;
}
}
_light_channel [ 3 ] . value = white ;
_light_channel [ 3 ] . inputValue = 0 ;
// Split the White Value across 2 White LED Strips.
if ( _light_use_cct ) {
// This change the range from 153-500 to 0-347 so we get a value between 0 and 1 in the end.
double miredFactor = ( ( double ) _light_mireds - ( double ) LIGHT_COLDWHITE_MIRED ) / ( ( double ) LIGHT_WARMWHITE_MIRED - ( double ) LIGHT_COLDWHITE_MIRED ) ;
// set cold white
_light_channel [ 3 ] . inputValue = 0 ;
_light_channel [ 3 ] . value = round ( ( ( double ) 1.0 - miredFactor ) * white ) ;
// set warm white
_light_channel [ 4 ] . inputValue = 0 ;
_light_channel [ 4 ] . value = round ( miredFactor * white ) ;
} else {
_light_channel [ 3 ] . inputValue = 0 ;
_light_channel [ 3 ] . value = white ;
}
// Scale up to equal input values. So [250,150,50] -> [200,100,0,50] -> [250, 125, 0, 63]
// Scale up to equal input values. So [250,150,50] -> [200,100,0,50] -> [250, 125, 0, 63]
unsigned char max_in = std : : max ( _light_channel [ 0 ] . inputValue , std : : max ( _light_channel [ 1 ] . inputValue , _light_channel [ 2 ] . inputValue ) ) ;
unsigned char max_in = std : : max ( _light_channel [ 0 ] . inputValue , std : : max ( _light_channel [ 1 ] . inputValue , _light_channel [ 2 ] . inputValue ) ) ;
unsigned char max_out = std : : max ( std : : max ( _light_channel [ 0 ] . value , _light_channel [ 1 ] . value ) , std : : max ( _light_channel [ 2 ] . value , _light_channel [ 3 ] . value ) ) ;
unsigned char max_out = std : : max ( std : : max ( _light_channel [ 0 ] . value , _light_channel [ 1 ] . value ) , std : : max ( _light_channel [ 2 ] . value , _light_channel [ 3 ] . value ) ) ;
unsigned char channelSize = _light_use_cct ? 5 : 4 ;
if ( _light_use_cct ) {
max_out = std : : max ( max_out , _light_channel [ 4 ] . value ) ;
}
double factor = ( max_out > 0 ) ? ( double ) ( max_in / max_out ) : 0 ;
double factor = ( max_out > 0 ) ? ( double ) ( max_in / max_out ) : 0 ;
for ( unsigned int i = 0 ; i < 4 ; i + + ) {
for ( unsigned char i = 0 ; i < channelSize ; i + + ) {
_light_channel [ i ] . value = round ( ( double ) _light_channel [ i ] . value * factor * brightness ) ;
_light_channel [ i ] . value = round ( ( double ) _light_channel [ i ] . value * factor * brightness ) ;
}
}
// Scale white channel to match brightness
// Scale white channel to match brightness
_light_channel [ 3 ] . value = constrain ( _light_channel [ 3 ] . value * LIGHT_WHITE_FACTOR , 0 , 255 ) ;
for ( unsigned char i = 3 ; i < channelSize ; i + + ) {
_light_channel [ i ] . value = constrain ( _light_channel [ i ] . value * LIGHT_WHITE_FACTOR , 0 , LIGHT_MAX_BRIGHTNESS ) ;
}
// For the rest of channels, don't apply brightness, it is already in the inputValue:
for ( unsigned char i = 4 ; i < _light_channel . size ( ) ; i + + ) {
// For the rest of channels, don't apply brightness, it is already in the inputValue
// i should be 4 when RGBW and 5 when RGBWW
for ( unsigned char i = channelSize ; i < _light_channel . size ( ) ; i + + ) {
_light_channel [ i ] . value = _light_channel [ i ] . inputValue ;
_light_channel [ i ] . value = _light_channel [ i ] . inputValue ;
}
}
} else {
} else {
// Don't apply brightness, it is already in the inputValue :
// Don't apply brightness, it is already in the target :
for ( unsigned char i = 0 ; i < _light_channel . size ( ) ; i + + ) {
for ( unsigned char i = 0 ; i < _light_channel . size ( ) ; i + + ) {
if ( _light_has_color & ( i < 3 ) ) {
if ( _light_has_color & ( i < 3 ) ) {
_light_channel [ i ] . value = _light_channel [ i ] . inputValue * brightness ;
_light_channel [ i ] . value = _light_channel [ i ] . inputValue * brightness ;
@ -155,16 +181,10 @@ void _fromRGB(const char * rgb) {
}
}
break ;
break ;
case ' M ' : // Mired Value
case ' M ' : // Mired Value
if ( _light_has_color ) {
unsigned long mireds = atol ( p + 1 ) ;
_fromMireds ( mireds ) ;
}
_fromMireds ( atol ( p + 1 ) ) ;
break ;
break ;
case ' K ' : // Kelvin Value
case ' K ' : // Kelvin Value
if ( _light_has_color ) {
unsigned long kelvin = atol ( p + 1 ) ;
_fromKelvin ( kelvin ) ;
}
_fromKelvin ( atol ( p + 1 ) ) ;
break ;
break ;
default : // assume decimal values separated by commas
default : // assume decimal values separated by commas
char * tok ;
char * tok ;
@ -257,13 +277,18 @@ void _fromHSV(const char * hsv) {
// https://github.com/stelgenhof/AiLight
// https://github.com/stelgenhof/AiLight
void _fromKelvin ( unsigned long kelvin , bool setMireds ) {
void _fromKelvin ( unsigned long kelvin , bool setMireds ) {
// Check we have RGB channels
if ( ! _light_has_color ) return ;
if ( ! _light_has_color ) return ;
if ( setMireds ) {
if ( setMireds ) {
_light_mireds = round ( 1000000UL / kelvin ) ;
_light_mireds = constrain ( round ( 1000000UL / kelvin ) , LIGHT_MIN_MIREDS , LIGHT_MAX_MIREDS ) ;
}
}
if ( _light_use_cct ) {
_setRGBInputValue ( LIGHT_MAX_VALUE , LIGHT_MAX_VALUE , LIGHT_MAX_VALUE ) ;
return ;
}
// Calculate colors
// Calculate colors
unsigned int red = ( kelvin < = 66 )
unsigned int red = ( kelvin < = 66 )
? LIGHT_MAX_VALUE
? LIGHT_MAX_VALUE
@ -277,11 +302,7 @@ void _fromKelvin(unsigned long kelvin, bool setMireds) {
? 0
? 0
: 138.5177312231 * log ( kelvin - 10 ) - 305.0447927307 ) ;
: 138.5177312231 * log ( kelvin - 10 ) - 305.0447927307 ) ;
_setRGBInputValue (
constrain ( red , 0 , LIGHT_MAX_VALUE ) ,
constrain ( green , 0 , LIGHT_MAX_VALUE ) ,
constrain ( blue , 0 , LIGHT_MAX_VALUE )
) ;
_setRGBInputValue ( red , green , blue ) ;
}
}
void _fromKelvin ( unsigned long kelvin ) {
void _fromKelvin ( unsigned long kelvin ) {
@ -290,8 +311,15 @@ void _fromKelvin(unsigned long kelvin) {
// Color temperature is measured in mireds (kelvin = 1e6/mired)
// Color temperature is measured in mireds (kelvin = 1e6/mired)
void _fromMireds ( unsigned long mireds ) {
void _fromMireds ( unsigned long mireds ) {
if ( mireds = = 0 ) mireds = 1 ;
_light_mireds = mireds ;
if ( ! _light_has_color ) return ;
_light_mireds = mireds = constrain ( mireds , LIGHT_MIN_MIREDS , LIGHT_MAX_MIREDS ) ;
if ( _light_use_cct ) {
_setRGBInputValue ( LIGHT_MAX_VALUE , LIGHT_MAX_VALUE , LIGHT_MAX_VALUE ) ;
return ;
}
unsigned long kelvin = constrain ( 1000000UL / mireds , 1000 , 40000 ) / 100 ;
unsigned long kelvin = constrain ( 1000000UL / mireds , 1000 , 40000 ) / 100 ;
_fromKelvin ( kelvin , false ) ;
_fromKelvin ( kelvin , false ) ;
}
}
@ -313,15 +341,15 @@ void _toRGB(char * rgb, size_t len) {
}
}
void _toHSV ( char * hsv , size_t len ) {
void _toHSV ( char * hsv , size_t len ) {
double min , max , h , s , v ;
double h , s , v ;
double brightness = ( double ) _light_brightness / LIGHT_MAX_BRIGHTNESS ;
double brightness = ( double ) _light_brightness / LIGHT_MAX_BRIGHTNESS ;
double r = ( double ) ( _light_channel [ 0 ] . inputValue * brightness ) / 255.0 ;
double r = ( double ) ( _light_channel [ 0 ] . inputValue * brightness ) / 255.0 ;
double g = ( double ) ( _light_channel [ 1 ] . inputValue * brightness ) / 255.0 ;
double g = ( double ) ( _light_channel [ 1 ] . inputValue * brightness ) / 255.0 ;
double b = ( double ) ( _light_channel [ 2 ] . inputValue * brightness ) / 255.0 ;
double b = ( double ) ( _light_channel [ 2 ] . inputValue * brightness ) / 255.0 ;
min = std : : min ( r , std : : min ( g , b ) ) ;
max = std : : max ( r , std : : max ( g , b ) ) ;
double min = std : : min ( r , std : : min ( g , b ) ) ;
double max = std : : max ( r , std : : max ( g , b ) ) ;
v = 100.0 * max ;
v = 100.0 * max ;
if ( v = = 0 ) {
if ( v = = 0 ) {
@ -389,6 +417,7 @@ unsigned int _toPWM(unsigned char id) {
}
}
void _shadow ( ) {
void _shadow ( ) {
// Update transition ticker
// Update transition ticker
_light_steps_left - - ;
_light_steps_left - - ;
if ( _light_steps_left = = 0 ) _light_transition_ticker . detach ( ) ;
if ( _light_steps_left = = 0 ) _light_transition_ticker . detach ( ) ;
@ -396,15 +425,20 @@ void _shadow() {
// Transitions
// Transitions
unsigned char target ;
unsigned char target ;
for ( unsigned int i = 0 ; i < _light_channel . size ( ) ; i + + ) {
for ( unsigned int i = 0 ; i < _light_channel . size ( ) ; i + + ) {
target = _light_state ? _light_channel [ i ] . value : 0 ;
target = _light_state & & _light_channel [ i ] . state ? _light_channel [ i ] . value : 0 ;
if ( _light_steps_left = = 0 ) {
if ( _light_steps_left = = 0 ) {
_light_channel [ i ] . current = target ;
_light_channel [ i ] . current = target ;
} else {
} else {
double difference = ( double ) ( target - _light_channel [ i ] . current ) / ( _light_steps_left + 1 ) ;
double difference = ( double ) ( target - _light_channel [ i ] . current ) / ( _light_steps_left + 1 ) ;
_light_channel [ i ] . current = _light_channel [ i ] . current + difference ;
_light_channel [ i ] . current = _light_channel [ i ] . current + difference ;
}
}
_light_channel [ i ] . shadow = _light_channel [ i ] . current ;
_light_channel [ i ] . shadow = _light_channel [ i ] . current ;
}
}
}
}
void _lightProviderUpdate ( ) {
void _lightProviderUpdate ( ) {
@ -441,6 +475,7 @@ void _lightColorSave() {
setSetting ( " ch " , i , _light_channel [ i ] . inputValue ) ;
setSetting ( " ch " , i , _light_channel [ i ] . inputValue ) ;
}
}
setSetting ( " brightness " , _light_brightness ) ;
setSetting ( " brightness " , _light_brightness ) ;
setSetting ( " mireds " , _light_mireds ) ;
saveSettings ( ) ;
saveSettings ( ) ;
}
}
@ -449,6 +484,7 @@ void _lightColorRestore() {
_light_channel [ i ] . inputValue = getSetting ( " ch " , i , i = = 0 ? 255 : 0 ) . toInt ( ) ;
_light_channel [ i ] . inputValue = getSetting ( " ch " , i , i = = 0 ? 255 : 0 ) . toInt ( ) ;
}
}
_light_brightness = getSetting ( " brightness " , LIGHT_MAX_BRIGHTNESS ) . toInt ( ) ;
_light_brightness = getSetting ( " brightness " , LIGHT_MAX_BRIGHTNESS ) . toInt ( ) ;
_light_mireds = getSetting ( " mireds " , _light_mireds ) . toInt ( ) ;
lightUpdate ( false , false ) ;
lightUpdate ( false , false ) ;
}
}
@ -751,6 +787,10 @@ void _lightWebSocketOnSend(JsonObject& root) {
bool useRGB = getSetting ( " useRGB " , LIGHT_USE_RGB ) . toInt ( ) = = 1 ;
bool useRGB = getSetting ( " useRGB " , LIGHT_USE_RGB ) . toInt ( ) = = 1 ;
root [ " useRGB " ] = useRGB ;
root [ " useRGB " ] = useRGB ;
if ( _light_has_color ) {
if ( _light_has_color ) {
if ( _light_use_cct ) {
root [ " useCCT " ] = _light_use_cct ;
root [ " mireds " ] = _light_mireds ;
}
if ( useRGB ) {
if ( useRGB ) {
root [ " rgb " ] = lightColor ( true ) ;
root [ " rgb " ] = lightColor ( true ) ;
root [ " brightness " ] = lightBrightness ( ) ;
root [ " brightness " ] = lightBrightness ( ) ;
@ -780,6 +820,12 @@ void _lightWebSocketOnAction(uint32_t client_id, const char * action, JsonObject
lightUpdate ( true , true ) ;
lightUpdate ( true , true ) ;
}
}
}
}
if ( _light_use_cct ) {
if ( strcmp ( action , " mireds " ) = = 0 ) {
_fromMireds ( data [ " mireds " ] ) ;
lightUpdate ( true , true ) ;
}
}
}
}
if ( strcmp ( action , " channel " ) = = 0 ) {
if ( strcmp ( action , " channel " ) = = 0 ) {
@ -961,6 +1007,12 @@ void _lightConfigure() {
setSetting ( " useWhite " , _light_use_white ) ;
setSetting ( " useWhite " , _light_use_white ) ;
}
}
_light_use_cct = getSetting ( " useCCT " , LIGHT_USE_CCT ) . toInt ( ) = = 1 ;
if ( _light_use_cct & & ( ( _light_channel . size ( ) < 5 ) | | ! _light_use_white ) ) {
_light_use_cct = false ;
setSetting ( " useCCT " , _light_use_cct ) ;
}
_light_use_gamma = getSetting ( " useGamma " , LIGHT_USE_GAMMA ) . toInt ( ) = = 1 ;
_light_use_gamma = getSetting ( " useGamma " , LIGHT_USE_GAMMA ) . toInt ( ) = = 1 ;
_light_use_transitions = getSetting ( " useTransitions " , LIGHT_USE_TRANSITIONS ) . toInt ( ) = = 1 ;
_light_use_transitions = getSetting ( " useTransitions " , LIGHT_USE_TRANSITIONS ) . toInt ( ) = = 1 ;
_light_transition_time = getSetting ( " lightTime " , LIGHT_TRANSITION_TIME ) . toInt ( ) ;
_light_transition_time = getSetting ( " lightTime " , LIGHT_TRANSITION_TIME ) . toInt ( ) ;