Creating a color changing bulb/bulbs app

Hi,

I want to write a simple app to change colors of selected bulbs according to a preprogrammed color loop.
I wrote it partially. Bulbs are selected and the first color command is set to the bulbs.

Now I want to change the color to the second in the queue. But I don’t know how to create the queue and how to read from it with a given period of time.

What could be the best practice to achieve this ?

Currently my app asks the bulbs to use in the program and number of actions.
Then each action is configured with 3 parameters (color, level, duration)
these parameters are stored in variables :

p_1_color, p_1_level, p_1_duration
p_2_color, p_2_level, p_2_duration

where 1,2,… are the action numbers.

these are read from the settings page with “input” commands.

also an event is configured (motion , button etc.)
when the event is triggered, it calls a function.

the problems are ;

  1. I don’t know how to read these values which are set
  2. how to use them according to the duration configured in each action (I have to set a timer but how)
  3. how can I pass to the next color when the timer is expired ?

also, can I make it looping forever ? (if yes, how can I stop it)

If you post your code so far I will try and help you

Andy

1 Like

sure , here it is :

definition(
    name: "Color Changing Bulbs",
    namespace: "smartthings",
    author: "SmartThings",
    description: "Change color of bulbs in a defined pattern",
    category: "SmartThings Labs",
    iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/sonos.png",
    iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/sonos@2x.png"
)

preferences {
	page(name: "mainPage", title: "Color Changing Bulbs", install: true, uninstall: true)
    page(name: "configureAction")
	page(name: "timeIntervalInput", title: "Only during a certain time") {
		section {
			input "starting", "time", title: "Starting", required: false
			input "ending", "time", title: "Ending", required: false
		}
	}
}

def mainPage() {
	dynamicPage(name: "mainPage") {
        section("Control these bulbs...") {
		input "hues", "capability.colorControl", title: "Which Hue Bulbs?", required:false, multiple:true
		}
                section("Actions") {
                input "numberOfActions", "enum", title: "Number of Actions?", required: true, submitOnChange: true, options: [
                1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24]
                def configDescription = ""
                for (int i = 1; i <= (settings["numberOfActions"] as Integer); i++){
                   configDescription = ""
                   if (settings["p_${i}_color"] != null) { 
                      configDescription = configDescription + "p_${i}_color" +":"
					  }
                   if (settings["p_${i}_lightLevel"] != null) { 
                      configDescription = configDescription + "p_${i}_lightLevel" +":" 
                   }
                       if (settings["p_${i}_duration"] != null) {
                           configDescription = configDescription + "p_${i}_duration" +":" 
                       }
                   if (configDescription == ""){
                      configDescription = "Click to configure"
                   }
                   href "configureAction", title:"Configure Action", description:"$configDescription", params: [paction: i]
                }
        }
		def anythingSet = anythingSet()
		if (anythingSet) {
			section("Play weather report when"){
				ifSet "motion", "capability.motionSensor", title: "Motion Here", required: false, multiple: true
				ifSet "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true
				ifSet "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true
				ifSet "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true
				ifSet "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true
				ifSet "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true
				ifSet "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true
				ifSet "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true
				ifSet "smoke", "capability.smokeDetector", title: "Smoke Detected", required: false, multiple: true
				ifSet "water", "capability.waterSensor", title: "Water Sensor Wet", required: false, multiple: true
				ifSet "button1", "capability.button", title: "Button Press", required:false, multiple:true //remove from production
				ifSet "triggerModes", "mode", title: "System Changes Mode", required: false, multiple: true
				ifSet "timeOfDay", "time", title: "At a Scheduled Time", required: false
			}
		}
		def hideable = anythingSet || app.installationState == "COMPLETE"
		def sectionTitle = anythingSet ? "Select additional triggers" : "Play weather report when..."
		section(sectionTitle, hideable: hideable, hidden: true){
			ifUnset "motion", "capability.motionSensor", title: "Motion Here", required: false, multiple: true
			ifUnset "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true
			ifUnset "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true
			ifUnset "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true
			ifUnset "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true
			ifUnset "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true
			ifUnset "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true
			ifUnset "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true
			ifUnset "smoke", "capability.smokeDetector", title: "Smoke Detected", required: false, multiple: true
			ifUnset "water", "capability.waterSensor", title: "Water Sensor Wet", required: false, multiple: true
			ifUnset "button1", "capability.button", title: "Button Press", required:false, multiple:true //remove from production
			ifUnset "triggerModes", "mode", title: "System Changes Mode", required: false, multiple: true
			ifUnset "timeOfDay", "time", title: "At a Scheduled Time", required: false
		}

		section("More options", hideable: true, hidden: true) {
			input "frequency", "decimal", title: "Minimum time between actions (defaults to every event)", description: "Minutes", required: false
			href "timeIntervalInput", title: "Only during a certain time", description: timeLabel ?: "Tap to set", state: timeLabel ? "complete" : "incomplete"
			input "days", "enum", title: "Only on certain days of the week", multiple: true, required: false,
				options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
			if (settings.modes) {
            	input "modes", "mode", title: "Only when mode is", multiple: true, required: false
            }
			input "oncePerDay", "bool", title: "Only once per day", required: false, defaultValue: false
		}
		section([mobileOnly:true]) {
			label title: "Assign a name", required: false
			mode title: "Set for specific mode(s)"
		}
	}
}


def configureAction(params) {
   if (params.paction != null) state.currentAction = params.paction.toInteger() //log.debug "$params.pbutton"
   dynamicPage(name: "configureAction", title: "Choose the actions for Program${state.currentAction}.",
   getActionSections(state.currentAction))
}

def getActionSections(actionNumber) {
	return {
		section("Color") {
			input "p_${actionNumber}_color", "enum", title: "Light Strip Color?", required: false, multiple:false, submitOnChange: true, options: [
					["Soft White":"Soft White - Default"],
					["White":"White - Concentrate"],
					["Daylight":"Daylight - Energize"],
					["Warm White":"Warm White - Relax"],
					"Red","Green","Blue","Yellow","Orange","Purple","Pink","Cyan","W1","W2","Random","Custom","Off"]
            if (settings["p_${actionNumber}_color"] == "Custom"){
                input "p_${actionNumber}_custom", "text", title: "Custom Color in Hex (ie ffffff)", submitOnChange: false, required: false
            }
		}
        if (settings["p_${actionNumber}_color"] != "Custom"){
            section("Level"){
                input "p_${actionNumber}_lightLevel", "number", title: "Light Level?", required: false, range: "1..100"
            }
        }
        section ("Duration"){
                input "p_${actionNumber}_duration", "number", title: "Duration (milliseconds)", range: "100..1000000", submitOnChange: false, required: false
        }
	}
}

private anythingSet() {
	for (name in ["hues","motion","contact","contactClosed","acceleration","mySwitch","mySwitchOff","arrivalPresence","departurePresence","smoke","water","button1","timeOfDay","triggerModes"]) {
		if (settings[name]) {
			return true
		}
	}
	return false
}

private ifUnset(Map options, String name, String capability) {
	if (!settings[name]) {
		input(options, name, capability)
	}
}

private ifSet(Map options, String name, String capability) {
	if (settings[name]) {
		input(options, name, capability)
	}
}

def installed() {
	log.debug "Installed with settings: ${settings}"
	subscribeToEvents()
}

def updated() {
	log.debug "Updated with settings: ${settings}"
	unsubscribe()
	unschedule()
	subscribeToEvents()
}

def subscribeToEvents() {
	subscribe(app, appTouchHandler)
	subscribe(contact, "contact.open", eventHandler)
	subscribe(contactClosed, "contact.closed", eventHandler)
	subscribe(acceleration, "acceleration.active", eventHandler)
	subscribe(motion, "motion.active", eventHandler)
	subscribe(mySwitch, "switch.on", eventHandler)
	subscribe(mySwitchOff, "switch.off", eventHandler)
	subscribe(arrivalPresence, "presence.present", eventHandler)
	subscribe(departurePresence, "presence.not present", eventHandler)
	subscribe(smoke, "smoke.detected", eventHandler)
	subscribe(smoke, "smoke.tested", eventHandler)
	subscribe(smoke, "carbonMonoxide.detected", eventHandler)
	subscribe(water, "water.wet", eventHandler)
	subscribe(button1, "button.pushed", eventHandler)

	if (triggerModes) {
		subscribe(location,modeChangeHandler)
	}

	if (timeOfDay) {
		schedule(timeOfDay, scheduledTimeHandler)
	}

	if (song) {
		saveSelectedSong()
	}
}

def eventHandler(evt) {
	if (allOk) {
		log.trace "allOk"
		def lastTime = state[frequencyKey(evt)]
		if (oncePerDayOk(lastTime)) {
			if (frequency) {
				if (lastTime == null || now() - lastTime >= frequency * 60000) {
					takeAction(evt)
				}
				else {
					log.debug "Not taking action because $frequency minutes have not elapsed since last action"
				}
			}
			else {
				takeAction(evt)
			}
		}
		else {
			log.debug "Not taking action because it was already taken today"
		}
	}
}

def modeChangeHandler(evt) {
	if (evt.value in triggerModes) {
		eventHandler(evt)
	}
}

def scheduledTimeHandler() {
	eventHandler(null)
}

def appTouchHandler(evt) {
	takeAction(evt)
}

private takeAction(evt) {
	loadText()

	if (frequency || oncePerDay) {
		state[frequencyKey(evt)] = now()
	}
}




private frequencyKey(evt) {
	"lastActionTimeStamp"
}

private dayString(Date date) {
	def df = new java.text.SimpleDateFormat("yyyy-MM-dd")
	if (location.timeZone) {
		df.setTimeZone(location.timeZone)
	}
	else {
		df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
	}
	df.format(date)
}

private oncePerDayOk(Long lastTime) {
	def result = true
	if (oncePerDay) {
		result = lastTime ? dayString(new Date()) != dayString(new Date(lastTime)) : true
		log.trace "oncePerDayOk = $result"
	}
	result
}

// TODO - centralize somehow
private getAllOk() {
	modeOk && daysOk && timeOk
}

private getModeOk() {
	def result = !modes || modes.contains(location.mode)
	log.trace "modeOk = $result"
	result
}

private getDaysOk() {
	def result = true
	if (days) {
		def df = new java.text.SimpleDateFormat("EEEE")
		if (location.timeZone) {
			df.setTimeZone(location.timeZone)
		}
		else {
			df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
		}
		def day = df.format(new Date())
		result = days.contains(day)
	}
	log.trace "daysOk = $result"
	result
}

private getTimeOk() {
	def result = true
	if (starting && ending) {
		def currTime = now()
		def start = timeToday(starting, location?.timeZone).time
		def stop = timeToday(ending, location?.timeZone).time
		result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
	}
	log.trace "timeOk = $result"
	result
}

private hhmm(time, fmt = "h:mm a")
{
	def t = timeToday(time, location.timeZone)
	def f = new java.text.SimpleDateFormat(fmt)
	f.setTimeZone(location.timeZone ?: timeZone(time))
	f.format(t)
}

private getTimeLabel()
{
	(starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : ""
}

private loadText() {

    
def color ="Warm White"
sendcolor(color)

}

private list(String s) {
	[s]
}
private list(l) {
	l
}


def sendcolor(color) {
    def hueColor = 0
    def saturation = 100

	switch(color) {
		case "White":
			hueColor = 52
			saturation = 19
			break;
		case "Daylight":
			hueColor = 53
			saturation = 91
			break;
		case "Soft White":
			hueColor = 23
			saturation = 56
			break;
		case "Warm White":
			hueColor = 20
			saturation = 80 //83
			break;
		case "Blue":
			hueColor = 69
			saturation = 95
			break;
		case "DarkBlue":
			hueColor = 70
			break;
		case "Green":
			hueColor = 39
			break;
		case "Yellow":
			hueColor = 25
			break;
		case "Orange":
			hueColor = 10
			break;
		case "Purple":
			hueColor = 75
			break;
		case "Pink":
			hueColor = 83
			break;
		case "Red":
			hueColor = 100
			break;
	}

	state.previous = [:]

	hues.each {
		state.previous[it.id] = [
			"switch": it.currentValue("switch"),
			"level" : it.currentValue("level"),
			"hue": it.currentValue("hue"),
			"saturation": it.currentValue("saturation")
           
		]
	}
	     
	def newValue = [hue: hueColor, saturation: saturation, level:100]
	hues*.setColor(newValue)
}

Difficult to read and reply on an ipad but here goes…

I’ll answer your last question first

To switch on/off (to allow loop or not)
Create a virtual switch to turn the app on/off

Create a normal switch input then subscribe to it

input “switch1”, “capability.switch”, title: “Select switch to enable/disable app (Optional)”, required: false, multiple: false

// Subscriptions ********************************************************************
subscribe(switch1, “switch”, switchEnableNow) // Default - Enable/Disable switch
// **********************************************************************************

// Enable Switch Handler *********
// disable/enable switch
def switchEnableNow(evt){
state.enable = evt.value
}

You could add something like this to your allOk :

private getAllOk() {
modeOk && daysOk && timeOk && enableOk
}

private getenableOk(){
def result = true
if(state.enable == ‘on’){
result = true }

else if(state.enable == ‘off’){
result = false }

result
}

This would mean once the switch was off any looping would stop .
You could also set all lights off within the " else if(state.enable == ‘off’){ " section to turn off everything when switch is turned off

1 Like

ok. great. but I don’t know how I shall make it loop with the durations specified on the settings by user.

Have a look in the docs for the runIn() command

Basically…

myDelay = userInputdelay

runIn(myDelay, method)

ok. that I understand now. but I still can’t form the function to make my selected bulbs loop in a given color program.
I believe, I need a “for” loop for this , what else ?
how can I read the variables from the settings and set each bulb accordingly ?

I understand that you are out now. I am not in a hurry. If you can have a look later, that would be wonderful.

thx.

did you have some time to have a look at this ?
thx

this is not what I am looking for.
I want to create a program for all lights but each light will use different color at any point.
so ;
light1= color(i)
light2= color(i+1)
ight3= color(i+2)

take a look at this piston as an example. this sets each bulb to a different color so long as there are sufficient colors to bulbs:

1 Like

how can I create an array and use elements from it ?
And the array should be available as a global variable.

thx.

I’m sorry but I’m not very well at the moment so cannot concentrate on code

Have a look at this as it may help you

http://docs.smartthings.com/en/latest/smartapp-developers-guide/state.html?highlight=Array

I hope you get well soon.

thanks.

I understood usage of arrays and state

but I have a problem:

now I have array named state.colors which has color names in it.
and I also have a state.where integer which shows at which color we are.
but the following is not accepted:

log.debug "${state.colors[${state.where}]}"

I get this error:

error groovy.lang.MissingMethodException: No signature of method: script_app_Color_Chan_d9c31961_fc2b_479e_bb33_e8255ab4878a_ver_7_8.$() is applicable for argument types: (script_app_Color_Chan_d9c31961_fc2b_479e_bb33_e8255ab4878a_ver_7_8$_loadText_closure4) values: [script_app_Color_Chan_d9c31961_fc2b_479e_bb33_e8255ab4878a_ver_7_8$_loadText_closure4@42f09407]
Possible solutions: is(java.lang.Object), now(), url(), run(), any(), app(java.util.Map)

also, this produces the same result:
int i = ${state.where}

following command gives the color name as the output:
log.debug "${state.colors[0]}"

what is wrong with the usage of integer type state ?

I’ve finally managed to solve the issue with state variable.
and added switch to enable/disable
they all work fine.

But , the loop does not continue forever.
For example , I set 5 actions. after the 5th action, it goes back to the first one.
But does not go the the 2nd.

here’s the full code :

definition(
    name: "Color Changing Bulbs",
    namespace: "smartthings",
    author: "SmartThings",
    description: "Change color of bulbs in a defined pattern",
    category: "SmartThings Labs",
    iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/sonos.png",
    iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/sonos@2x.png"
)

preferences {
	page(name: "mainPage", title: "Color Changing Bulbs", install: true, uninstall: true)
    page(name: "configureAction")
	page(name: "timeIntervalInput", title: "Only during a certain time") {
		section {
			input "starting", "time", title: "Starting", required: false
			input "ending", "time", title: "Ending", required: false
		}
	}
}

def mainPage() {
	dynamicPage(name: "mainPage") {
        section("Control these bulbs...") {
		input "hues", "capability.colorControl", title: "Which Hue Bulbs?", required:false, multiple:true
		}
        section("enable/disable switch") {
        input "switch1", "capability.switch", title: "Select switch to enable/disable app (Optional)", required: false, multiple: false
        }
                section("Actions") {
                input "numberOfActions", "enum", title: "Number of Actions?", required: true, submitOnChange: true, options: [
                1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24]
                def configDescription = ""
                for (int i = 1; i <= (settings["numberOfActions"] as Integer); i++){
                   configDescription = ""
                   if (settings["p_${i}_color"] != null) { 
                      configDescription = configDescription + "p_${i}_color" +":"
					  }
                   if (settings["p_${i}_lightLevel"] != null) { 
                      configDescription = configDescription + "p_${i}_lightLevel" +":" 
                   }
                       if (settings["p_${i}_duration"] != null) {
                           configDescription = configDescription + "p_${i}_duration" +":" 
                       }
                   if (configDescription == ""){
                      configDescription = "Click to configure"
                   }
                   href "configureAction", title:"Configure Action", description:"$configDescription", params: [paction: i]
                }
        }
		def anythingSet = anythingSet()
		if (anythingSet) {
			section("Play weather report when"){
				ifSet "motion", "capability.motionSensor", title: "Motion Here", required: false, multiple: true
				ifSet "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true
				ifSet "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true
				ifSet "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true
				ifSet "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true
				ifSet "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true
				ifSet "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true
				ifSet "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true
				ifSet "smoke", "capability.smokeDetector", title: "Smoke Detected", required: false, multiple: true
				ifSet "water", "capability.waterSensor", title: "Water Sensor Wet", required: false, multiple: true
				ifSet "button1", "capability.button", title: "Button Press", required:false, multiple:true //remove from production
				ifSet "triggerModes", "mode", title: "System Changes Mode", required: false, multiple: true
				ifSet "timeOfDay", "time", title: "At a Scheduled Time", required: false
			}
		}
		def hideable = anythingSet || app.installationState == "COMPLETE"
		def sectionTitle = anythingSet ? "Select additional triggers" : "Play weather report when..."
		section(sectionTitle, hideable: hideable, hidden: true){
			ifUnset "motion", "capability.motionSensor", title: "Motion Here", required: false, multiple: true
			ifUnset "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true
			ifUnset "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true
			ifUnset "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true
			ifUnset "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true
			ifUnset "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true
			ifUnset "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true
			ifUnset "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true
			ifUnset "smoke", "capability.smokeDetector", title: "Smoke Detected", required: false, multiple: true
			ifUnset "water", "capability.waterSensor", title: "Water Sensor Wet", required: false, multiple: true
			ifUnset "button1", "capability.button", title: "Button Press", required:false, multiple:true //remove from production
			ifUnset "triggerModes", "mode", title: "System Changes Mode", required: false, multiple: true
			ifUnset "timeOfDay", "time", title: "At a Scheduled Time", required: false
		}

		section("More options", hideable: true, hidden: true) {
			input "frequency", "decimal", title: "Minimum time between actions (defaults to every event)", description: "Minutes", required: false
			href "timeIntervalInput", title: "Only during a certain time", description: timeLabel ?: "Tap to set", state: timeLabel ? "complete" : "incomplete"
			input "days", "enum", title: "Only on certain days of the week", multiple: true, required: false,
				options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
			if (settings.modes) {
            	input "modes", "mode", title: "Only when mode is", multiple: true, required: false
            }
			input "oncePerDay", "bool", title: "Only once per day", required: false, defaultValue: false
		}
		section([mobileOnly:true]) {
			label title: "Assign a name", required: false
			mode title: "Set for specific mode(s)"
		}
	}
}

def configureAction(params) {
   if (params.paction != null) state.currentAction = params.paction.toInteger() //log.debug "$params.pbutton"
   dynamicPage(name: "configureAction", title: "Choose the actions for Program${state.currentAction}.",
   getActionSections(state.currentAction))
}

def getActionSections(actionNumber) {
	return {
		section("Color") {
			input "p_${actionNumber}_color", "enum", title: "Light Strip Color?", required: false, multiple:false, submitOnChange: true, options: [
					["Soft White":"Soft White - Default"],
					["White":"White - Concentrate"],
					["Daylight":"Daylight - Energize"],
					["Warm White":"Warm White - Relax"],
					"Red","Green","Blue","Yellow","Orange","Purple","Pink","Cyan","W1","W2","Random","Custom","Off"]
            if (settings["p_${actionNumber}_color"] == "Custom"){
                input "p_${actionNumber}_custom", "text", title: "Custom Color in Hex (ie ffffff)", submitOnChange: false, required: false
            }
		}
            section("Level"){
                input "p_${actionNumber}_lightLevel", "number", title: "Light Level?", required: false, range: "1..100"
            }
        section ("Duration"){
                input "p_${actionNumber}_duration", "number", title: "Duration (milliseconds)", range: "100..1000000", submitOnChange: false, required: false
        }
	}
}

private anythingSet() {
	for (name in ["hues","motion","contact","contactClosed","acceleration","mySwitch","mySwitchOff","arrivalPresence","departurePresence","smoke","water","button1","timeOfDay","triggerModes"]) {
		if (settings[name]) {
			return true
		}
	}
	return false
}

private ifUnset(Map options, String name, String capability) {
	if (!settings[name]) {
		input(options, name, capability)
	}
}

private ifSet(Map options, String name, String capability) {
	if (settings[name]) {
		input(options, name, capability)
	}
}

def installed() {
	log.debug "Installed with settings: ${settings}"
	subscribeToEvents()
}

def updated() {
   def colors = []
   def levels = []
   def durations = []
state.noa=settings["numberOfActions"] as Integer
for (int i = 1; i <= (settings["numberOfActions"] as Integer); i++) {
   colors << settings["p_${i}_color"]
   levels << settings["p_${i}_lightLevel"]
   durations << settings["p_${i}_duration"]
}
state.colors = colors
state.levels = levels
state.durations = durations
state.where =  0 as Integer
//log.debug "$state.colors $state.levels  $state.durations"
	log.debug "Updated with settings: ${settings}"
	unsubscribe()
	unschedule()
	subscribeToEvents()
}

def subscribeToEvents() {
	subscribe(switch1, "switch", switchEnableNow) // Default - Enable/Disable switch
	subscribe(app, appTouchHandler)
	subscribe(contact, "contact.open", eventHandler)
	subscribe(contactClosed, "contact.closed", eventHandler)
	subscribe(acceleration, "acceleration.active", eventHandler)
	subscribe(motion, "motion.active", eventHandler)
	subscribe(mySwitch, "switch.on", eventHandler)
	subscribe(mySwitchOff, "switch.off", eventHandler)
	subscribe(arrivalPresence, "presence.present", eventHandler)
	subscribe(departurePresence, "presence.not present", eventHandler)
	subscribe(smoke, "smoke.detected", eventHandler)
	subscribe(smoke, "smoke.tested", eventHandler)
	subscribe(smoke, "carbonMonoxide.detected", eventHandler)
	subscribe(water, "water.wet", eventHandler)
	subscribe(button1, "button.pushed", eventHandler)

	if (triggerModes) {
		subscribe(location,modeChangeHandler)
	}

	if (timeOfDay) {
		schedule(timeOfDay, scheduledTimeHandler)
	}

	if (song) {
		saveSelectedSong()
	}
}

// Enable Switch Handler *********
// disable/enable switch
def switchEnableNow(evt){
state.enable = evt.value
}

private getenableOk(){
def result = true
if(state.enable == 'on'){
result = true }

else if(state.enable == 'off'){
result = false }

result
}

def eventHandler(evt) {
	if (allOk) {
		log.trace "allOk"
		def lastTime = state[frequencyKey(evt)]
		if (oncePerDayOk(lastTime)) {
			if (frequency) {
				if (lastTime == null || now() - lastTime >= frequency * 60000) {
					takeAction(evt)
				}
				else {
					log.debug "Not taking action because $frequency minutes have not elapsed since last action"
				}
			}
			else {
				takeAction(evt)
			}
		}
		else {
			log.debug "Not taking action because it was already taken today"
		}
	}
}

def modeChangeHandler(evt) {
	if (evt.value in triggerModes) {
		eventHandler(evt)
	}
}

def scheduledTimeHandler() {
	eventHandler(null)
}

def appTouchHandler(evt) {
	takeAction(evt)
}

private takeAction(evt) {
	loadText()

	if (frequency || oncePerDay) {
		state[frequencyKey(evt)] = now()
	}
}




private frequencyKey(evt) {
	"lastActionTimeStamp"
}

private dayString(Date date) {
	def df = new java.text.SimpleDateFormat("yyyy-MM-dd")
	if (location.timeZone) {
		df.setTimeZone(location.timeZone)
	}
	else {
		df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
	}
	df.format(date)
}

private oncePerDayOk(Long lastTime) {
	def result = true
	if (oncePerDay) {
		result = lastTime ? dayString(new Date()) != dayString(new Date(lastTime)) : true
		log.trace "oncePerDayOk = $result"
	}
	result
}

// TODO - centralize somehow
private getAllOk() {
	modeOk && daysOk && timeOk
}

private getModeOk() {
	def result = !modes || modes.contains(location.mode)
	log.trace "modeOk = $result"
	result
}

private getDaysOk() {
	def result = true
	if (days) {
		def df = new java.text.SimpleDateFormat("EEEE")
		if (location.timeZone) {
			df.setTimeZone(location.timeZone)
		}
		else {
			df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
		}
		def day = df.format(new Date())
		result = days.contains(day)
	}
	log.trace "daysOk = $result"
	result
}

private getTimeOk() {
	def result = true
	if (starting && ending) {
		def currTime = now()
		def start = timeToday(starting, location?.timeZone).time
		def stop = timeToday(ending, location?.timeZone).time
		result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
	}
	log.trace "timeOk = $result"
	result
}

private hhmm(time, fmt = "h:mm a")
{
	def t = timeToday(time, location.timeZone)
	def f = new java.text.SimpleDateFormat(fmt)
	f.setTimeZone(location.timeZone ?: timeZone(time))
	f.format(t)
}

private getTimeLabel()
{
	(starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : ""
}

private loadText() {
int i = state.where
int j = state.noa
//log.debug "$state.where"
//log.debug "$state.where ${state.colors[i]}"
	hues.each {
		sendcolor(state.colors[i],state.levels[i],it)
        pause(200)
	}  
 		pause(state.durations[i])
        i++
		state.where=i

   //if (state.where < state.noa) {
  			if (enableOk) {goagain()}
   //}
}

private goagain() {
   if (state.where >= state.noa) { 
   	state.where=0 as Integer
    }
    log.debug "$state.where"
    loadText()
}

def sendcolor(color,level,bulb) {
	def hueColor = 0
    def saturation = 100

	switch(color) {
		case "White":
			hueColor = 52
			saturation = 19
			break;
		case "Daylight":
			hueColor = 53
			saturation = 91
			break;
		case "Soft White":
			hueColor = 23
			saturation = 56
			break;
		case "Warm White":
			hueColor = 20
			saturation = 80 //83
			break;
		case "Blue":
			hueColor = 69
			saturation = 95
			break;
		case "DarkBlue":
			hueColor = 70
			break;
		case "Green":
			hueColor = 39
			break;
		case "Yellow":
			hueColor = 25
			break;
		case "Orange":
			hueColor = 10
			break;
		case "Purple":
			hueColor = 75
			break;
		case "Pink":
			hueColor = 83
			break;
		case "Red":
			hueColor = 100
			break;
	}

//	state.previous = [:]
/*
	hues.each {
		state.previous[it.id] = [
			"switch": it.currentValue("switch"),
			"level" : it.currentValue("level"),
			"hue": it.currentValue("hue"),
			"saturation": it.currentValue("saturation")
           
		]
	}
*/	     
	def newValue = [hue: hueColor, saturation: saturation, level:level]
    bulb.setColor(newValue)
	//hues*.setColor(newValue)
}