Multi-Color LED Bulb Range APP

Multi-Color LED Bulb Range APP

I am using color changing LED Zigbee bulb (Sengled smart LED) to indicate quickly outside
temperatures ranges from Open Weather.

Temperature ranges

-100 to 0 F. (Dark blue) Extremely cold
0 to 32 F. (Light blue) Freezing
32 to 40 F. (Yellow) Getting cold
40 to 80 F. (Green) Normal temperature range
80 to 100 F. (Yellow) Getting hot
100 F. (Red) Extremely hot

Using HE built-in Rule-5.1 - I was able to process Open Weather outside temperature into LED "range colors" The downside is the amount of rules needed to produce color ranges for just "one" LED bulb is immense. Is there a better way? Maybe a Multi-Color LED Bulb Range APP ?

Here's an app I wrote for changing light colors based on temperature, maybe it will work for you. You set a few colors you want at certain temperatures and then it smoothly transitions from one color to the next based on temp.

/**
 *  Weather Lights
 *	Set light color based on current temperature
 * 
 *  Copyright 2020 Peter Miller
 */

definition(
	name: "Weather Light",
	namespace: "hyposphere.net",
	author: "Peter Miller",
	description: "Set light color based on current temperature",
	iconUrl: "",
	iconX2Url: "",
)

preferences {
	section() {
		input "isPaused", "bool", title: "Pause app", defaultValue: false
	}
	section("Devices") {
		input "thermoOut", "capability.temperatureMeasurement", title: "Thermometer", required: true, multiple: false
		input "myLights", "capability.colorControl", title: "Lights", required: true, multiple: true
	}
	section("Settings") {
		input "colorOption", "enum", title: "Color scale", options: ["Pete's", "Weather Channel", "Spectrum", "Spectrum 2", "Smartthings"]
		input "saturationOption", "decimal", title: "Saturation (0..1) ", required: true, defaultValue: 0.95, range: "0..1"
	}
	section("Debug") {
		input "debugMode", "bool", title: "Debug Mode", submitOnChange: true
		if (debugMode) {
			input "debugLevel", "enum", title: "Debug level", options: [0, 1, 2], required: true, defaultValue: 0
			input "tempDebug", "number", title: "Temp override"
		}
	}
}

List<Map> tempScaleSmartthings() { /* Standard SmartThings temperature colors */
	return [
		[temp: 31, hsv: [62, 86, 0], name: "indigo"],
		[temp: 44, hsv: [53, 84, 0], name: "turquoise"],
		[temp: 59, hsv: [39, 31, 0], name: "mint"],
		[temp: 74, hsv: [29, 82, 0], name: "green"],
		[temp: 84, hsv: [15, 100, 0], name: "yellow"],
		[temp: 95, hsv: [6, 100, 0], name: "orange"],
		[temp: 96, hsv: [0, 81, 0], name: "red"]
	]
}

List<Map> tempScaleWeatherchannel() { /* Source: https://s.w-x.co/staticmaps/acttemp_1280x720.jpg */
	return [
		[temp: -40, hsv: [74, 44, 0], name: "lavender"],
		[temp: -30, hsv: [75, 30, 0], name: "gray-purple"],
		[temp: -20, hsv: [76, 23, 0], name: "light purple"],
		[temp: -10, hsv: [89, 94, 0], name: "burgundy"],
		[temp: 0, hsv: [85, 65, 0], name: "dark purple"],
		[temp: 10, hsv: [82, 46, 0], name: "purple"],
		[temp: 20, hsv: [55, 33, 0], name: "light blue"],
		[temp: 30, hsv: [60, 45, 0], name: "blue"],
		[temp: 40, hsv: [66, 84, 0], name: "dark blue"],
		[temp: 50, hsv: [70, 34, 0], name: "dark gray"],
		[temp: 60, hsv: [16, 76, 0], name: "yellow"],
		[temp: 70, hsv: [10, 100, 0], name: "orange"],
		[temp: 80, hsv: [2, 98, 0], name: "red-orange"],
		[temp: 90, hsv: [0, 100, 0], name: "dark red"],
		[temp: 100, hsv: [91, 53, 0], name: "pink"],
		[temp: 110, hsv: [88, 5, 0], name: "eggshell"],
		[temp: 120, hsv: [17, 24, 0], name: "sand"]
	]
}

List<Map> tempScalePetes() {
	return [
		[temp: -40, hsv: [74, 75, 0], name: "lavender"],
		[temp: -30, hsv: [75, 75, 0], name: "gray-purple"],
		[temp: -20, hsv: [76, 75, 0], name: "light purple"],
		[temp: -10, hsv: [89, 75, 0], name: "burgundy"],
		[temp: 0, hsv: [85, 80, 0], name: "dark purple"],
		[temp: 10, hsv: [82, 80, 0], name: "purple"],
		[temp: 20, hsv: [66, 90, 0], name: "dark blue"],
		[temp: 32, hsv: [52, 100, 0], name: "ice blue"],
		[temp: 56, hsv: [55, 100, 0], name: "light blue"],
		[temp: 68, hsv: [33, 100, 0], name: "green"],
		[temp: 80, hsv: [16, 100, 0], name: "yellow"],
		[temp: 90, hsv: [10, 100, 0], name: "orange"],
		[temp: 100, hsv: [0, 90, 0], name: "dark red"],
		[temp: 110, hsv: [91, 80, 0], name: "pink"],
		[temp: 120, hsv: [91, 75, 0], name: "pink"]
	]
}

List<Map> tempScaleSpectrum() {
	return [
		[temp: 0, hsv: [83, 75, 0], name: "purple"],
		[temp: 32, hsv: [83, 100, 0], name: "purple"],
//		[temp: 65, hsv: [43, 100, 0], name: ""],
//		[temp: 70, hsv: [37, 100, 0], name: ""],
//		[temp: 75, hsv: [31, 100, 0], name: ""],
		[temp: 100, hsv: [0, 100, 0], name: "red"],
		[temp: 120, hsv: [0, 75, 0], name: "red"]
	]
}

List<Map> tempScaleSpectrum2() {
	return [
		[temp: 0, hsv: [83, 75, 0], name: "purple"],
		[temp: 32, hsv: [83, 100, 0], name: "purple"],
		[temp: 65, hsv: [49, 100, 0], name: ""],
		[temp: 70, hsv: [37, 100, 0], name: ""],
		[temp: 75, hsv: [25, 100, 0], name: ""],
		[temp: 100, hsv: [0, 100, 0], name: "red"],
		[temp: 120, hsv: [0, 75, 0], name: "red"]
	]
}

void installed() {
	if (debugMode) log.debug "Installed: $settings"
	initialize()
}

void updated() {
	if (debugMode) log.debug "Updated: $settings"
	unsubscribe()
	initialize()
}

void initialize() {
	if (isPaused == false) {
		subscribe(thermoOut, "temperature", updateLight)
		subscribe(myLights, "switch.on", updateLight)

		if (colorOption == "Pete's") {
			if (debugMode) log.debug "Using Pete's colors"
			state.tempScale = tempScalePetes()
		} else if (colorOption == "Weather Channel") {
			if (debugMode) log.debug "Using Weather Channel colors"
			state.tempScale = tempScaleWeatherchannel()
		} else if (colorOption == "Spectrum") {
			if (debugMode) log.debug "Using Spectrum colors"
			state.tempScale = tempScaleSpectrum()
		} else if (colorOption == "Spectrum 2") {
			if (debugMode) log.debug "Using Spectrum2 colors"
			state.tempScale = tempScaleSpectrum2()
		} else if (colorOption == "Smartthings") {
			if (debugMode) log.debug "Using Smartthings colors"
			state.tempScale = tempScaleSmartthings()
		} else {
			if (debugMode) log.debug "Defaulting to Pete's colors: colorOption = $colorOption"	
			state.tempScale = tempScalePetes()
		}
		updateLight()
	}
}

void updateLight(evt) {
	Float tempOut
	List<Integer> hsvTempColor = null
	Float satLevel = saturationOption
	
	if (satLevel > 1) {
		satLevel = 1.0
	} else if (satLevel < 0) {
		satLevel = 0
	} 

	if (thermoOut.getStatus() != "ACTIVE") {
		log.warn "Temp sensor is not online, do nothing"	
	} else {
		if (!debugMode || tempDebug == null) {
			tempOut = thermoOut.latestValue("temperature")
			if (debugMode) log.debug "temp: $tempOut"
		} else {
			tempOut = tempDebug ? tempDebug : thermoOut.latestValue("temperature")
			if (debugMode) log.debug "DEBUG temp: $tempOut"
		}

		hsvTempColor = getColor(tempOut, state.tempScale)
		
		// Adjust saturation to configured level
		hsvTempColor[1] = (hsvTempColor[1] * satLevel) as Integer

		for (light in myLights) {
			if (light.latestValue("switch") == "on") {
				if (debugMode) log.debug "Changing light $light"
				light.setColor(["hue": hsvTempColor[0], "saturation": hsvTempColor[1], "level": light.latestValue("level")])
			}
		}
	}
}

List<Integer> getColor(Float temp, List<Map> colorScale) {
	Integer tempLow = null
	Integer tempHigh = null
	Integer tempLast = null
	List<Integer> colorLow = null
	List<Integer> colorHigh = null
	List<Integer> colorLast = null
	Float rangePercent

	for (i in colorScale) {
		//if (debugMode) log.debug "i = $i; tempLow = $tempLow; tempHigh = $tempHigh; tempLast = $tempLast"
		if (temp < i.temp && tempLast == null) {
			return i.hsv
		} else if (temp == i.temp) {
			return i.hsv
		} else if (temp > tempLast && temp < i.temp) {
			tempLow = tempLast
			colorLow = colorLast
			tempHigh = i.temp
			colorHigh = i.hsv
			break
		} else if (temp > i.temp) {
			tempLast = i.temp
			colorLast = i.hsv
		}
	}
    
	if (tempLow == null && tempHigh == null) {
		// Passed the end of the temp scale, use last color
		return colorLast
	} else {
		// Get percent in temp range between colors
		rangePercent = (temp - tempLow)/(tempHigh - tempLow)
	}
	if (debugMode) log.debug "tempLow: $tempLow, temp = $temp ($rangePercent); tempHigh: $tempHigh"
    
	return getColorOnRange(colorLow, colorHigh, rangePercent)
}

List<Integer> getColorOnRange(List<Integer> colorOne, List<Integer> colorTwo, Float percent) {
	List<Integer> hsvColorNew = [0, 0, 0]

	for (i in 0..1) { // for H and S (we don't need V)
		hsvColorNew[i] = Math.round(((colorTwo[i] - colorOne[i]) * percent) + colorOne[i])
	}
    
	if (debugMode) log.debug "colorOne = $colorOne; hsvColorNew = $hsvColorNew; colorTwo = $colorTwo"

	return hsvColorNew
}
3 Likes

What a slick APP - AWESOME - thanks Pete
I just used the spectrum2 selection and plopped in my OW temperature settings and colors.
Further testing is needed but it is a non-issue!

List tempScaleSpectrum2() {
return [
[temp: -100, hsv: [66, 90, 0], name: "dark blue"],
[temp: 0, hsv: [55, 100, 0], name: "light blue"],
[temp: 32, hsv: [55, 100, 0], name: "yellow"],
[temp: 40, hsv: [33, 100, 0], name: "green"],
[temp: 80, hsv: [55, 100, 0], name: "yellow"],
[temp: 100, hsv: [0, 90, 0], name: "dark red"],
[temp: 120, hsv: [0, 90, 0], name: "dark red"]
]
}

(correction)


Now if I can find an APP for Weather Windspeed (MPH) then my color LED bulb problems will be gone.

BTW ... Again thanks - Pete

2 Likes

Awesome, glad you like it!

You can do this in a single rule. Just use the trigger *changed*, and then a series of temperature-range conditions within that rule.

But @pfmiller's app makes it easier.

Regarding windspeed, you are looking for an app that does the same thing but just uses windspeed for input instead of temperature?

If so my app would work with some simple modifications. You'd just need to change the device type that is accepted and the name of the attribute that is being used.

1 Like