Weather & Holiday Lights (Change Color Based On Historical Ave Temp and Holiday)

smartapp_alerts
weather
smartapp_weather
smartapp_lighting
project_holidays

#1

First attempt at coding anything real in ST, and I suspect this may be functionality another app already provides. But throwing it out there in case it’s of any use to anyone.

This app checks to see how far today’s temperature forecast is from the daily historical average temperature (for your zipcode). It then turns an OSRAM RGBW A19 bulb on at a set time with a color that represents how far from normal today is. (Purple, indigo, light blue for the colder temps, green for average, then yellow, orange, red as it gets hotter.)

Now a certain kid has no excuse left for not dressing for the weather…

Would love to do this with an LED strip used for accenting lighting above a doorframe in the hallway. Unfortunately, I can’t find anything controllable with a nice thin wire and connector that’ll replace the Ikea color LED strip I’ve got in there now.

definition(
name: “WeatherLights”,
namespace: “Mykines”,
author: “Mykines”,
description: “Turn an OSRAM color bulb colors to indicate how different from normal today’s temperature will be.”,
category: “Convenience”,
iconUrl: “https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png”,
iconX2Url: “https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png”,
iconX3Url: “https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png”)

preferences {
section(“Where…”){
input “zipcode”, “text”, title: “Zipcode?”, required: true
}
section(“When…”) {
input “theOnTime”, “time”, title: “Turn on at what time?”, required: true
input “theOffTime”, “time”, title: “Turn off at what time?”, required: true
}
// section(“What…”) {
// input “thebutton”, “capability.button”
// input “thebulb”, “capability.colorControl”, title: “pick a bulb”, required: true, multiple: false
// }
// section(“TESTING”) {
// input “temperatureOffset”, “number”, title: “temperatureOffset”
// }
}

def installed() {
schedule(theOnTime, “startLightForecast”)
schedule(theOffTime, “stopLightForecast”)
log.debug “Installed with settings: ${settings}”
}

def updated() {
unsubscribe()
schedule(theOnTime, “startLightForecast”)
schedule(theOffTime, “stopLightForecast”)
log.debug “Updated with settings: ${settings}”
}

def startLightForecast() {
def forecastInput = getWeatherFeature(“forecast”, zipcode)
// log.debug "day forecastInput: $forecastInput"
def forecastHighString = forecastInput?.forecast?.simpleforecast?.forecastday?.high[0].fahrenheit
def forecastHigh = forecastHighString.toInteger()
log.debug “day forecastData: $forecastHigh”

// Get this stuff from the wunderground API: https://www.wunderground.com/weather/api/d/docs?d=data/index&MR=1
def response = getWeatherFeature(“almanac”, zipcode)
def almanacData = response?.values()
// log.debug "almanac: $almanacData"
def aveHighString = response?.almanac?.temp_high.normal.F
def aveHigh = aveHighString.toInteger()
log.debug “aveHigh: $aveHigh”

	def temperatureDelta = forecastHigh - aveHigh
 log.debug "temperatureDelta: $temperatureDelta"  
            
 def bulbColorText = "unknown"
 def bulbColor = 22  // icky yellow-green hue, should always be overridden

  if(temperatureDelta>9) {
     	bulbColorText = "Red"
      bulbColor = 0
  } else if(temperatureDelta>5) {
     	bulbColorText = "Orange"
      bulbColor = 3
  } else if(temperatureDelta>2) {
     	bulbColorText = "Yellow"
      bulbColor = 8
  } else if(temperatureDelta>-3) {
    	bulbColorText = "Green"
      bulbColor = 33
  } else if(temperatureDelta>-6) {
     	bulbColorText = "Light Blue"
      bulbColor = 60
  } else if(temperatureDelta>-10) {
     	bulbColorText = "Indigo"
      bulbColor = 66
  } else {
     	bulbColorText = "Purple"
      bulbColor = 95
  }
  log.debug "bulbColor: $bulbColor" 
  log.debug "bulbColorText: $bulbColorText" 

  def bulbStatus = thebulb.currentSwitch
  state.origBulbStatus = bulbStatus
    state.origColorTemperature = thebulb.currentColorTemperature
  log.debug "switch status: $bulbStatus"

// def bulbLevel = thebulb.currentLevel
// log.debug "switch level: $bulbLevel"
def bulbHue = thebulb.currentHue
log.debug "hue: $bulbHue"
thebulb.setLevel(100)
thebulb.setHue(bulbColor)
thebulb.setSaturation(100)

    if(bulbStatus == "off") {
		thebulb.on()
	}

}

def stopLightForecast() {
// if(state.origBulbStatus == “off”) { // I always want it off
thebulb.off()
// }
log.debug "orig color temp: $state.origColorTemperature"
thebulb.setColorTemperature(state.origColorTemperature)
}


(Michael Hess) #2

Wow, good work! I do this the super simple way for my kids. I have a Leeo in the living room, it sets it’s color via IFTTT based on the weather forcast.

I may give this a shot with my kids RGBW strips in their rooms, since they started ignoring the Leeo after about day 4.


#3

Updated version that:

  1. Uses the OSRAM Lightify RGBW LED Strips
  2. Sets the color based on local forecast temperature in the morning
  3. Sets the color to a default after that, or to holiday colors when near certain holidays. For some holidays, the colors alternate between two options every time the light turns off (such as red and green for Christmas).

The actual turning on and off of the lights is controlled by Smart Lighting or other separate SmartApps.

/**
 *  WeatherAndHolidayLights
 *
 *  Copyright 2016 Mykines
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 *
 */
definition(
    name: "WeatherAndHolidayLights",
    namespace: "Mykines",
    author: "Mykines",
    description: "Turn an OSRAM color bulb colors to indicate how different from normal today's temperature will be.",
    category: "Convenience",
    iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
    iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
    iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png")


preferences {
	section("Where..."){
		input "zipcode", "text", title: "Zipcode?", required: true		// ask instead of 
	}
    section("Devices") {
    input "thebulb", "capability.colorControl", title: "Pick a bulb", required: true, multiple: false
    }
    section("Settings") {
		input(name: "defaultColor", type: "number", title: "What Default Hue?", description: "0", required: true, defaultValue: 0)
    }
	section("When...") {
		input "startforecastTime", "time", title: "Start Forecast Mode at what time?", required: true
		input "endForecastTime", "time", title: "End Forecast Mode at what time?", required: true
}
//    section("testing...") {
//		input(name: "monthNumb", type: "number", title: "monthNumb", description: "0", required: true, defaultValue: 0)
//		input(name: "dateNumb", type: "number", title: "dateNumb", description: "0", required: true, defaultValue: 0)
//	}
}

def installed() {
	schedule(startforecastTime, "setForecastColor")
	schedule(endForecastTime, "setHolidayColor")
  	subscribeToCommand(thebulb, "off", uponOffCommand)
	state.colorToggle = 0
    log.debug "Installed with settings: ${settings}"
    		thebulb.setSaturation(100)
}

def updated() {
	unsubscribe()
    unschedule()
	schedule(startforecastTime, "setForecastColor")
	schedule(endForecastTime, "setHolidayColor")
	subscribeToCommand(thebulb, "off", uponOffCommand)
	state.colorToggle = 0
	log.debug "Updated with settings: ${settings}"
    		thebulb.setSaturation(100)
}

def setForecastColor() {
		log.debug "in setForecastColor"
		def forecastInput = getWeatherFeature("forecast", zipcode)
//		log.debug "day forecastInput: $forecastInput"
	    def forecastHighString = forecastInput?.forecast?.simpleforecast?.forecastday?.high[0].fahrenheit
	    def forecastHigh = forecastHighString.toInteger()
		log.debug "day forecastData: $forecastHigh"     
	
//		Get this stuff from the wunderground API:  https://www.wunderground.com/weather/api/d/docs?d=data/index&MR=1
		def response = getWeatherFeature("almanac", zipcode)
    	def almanacData = response?.values()
//		log.debug "almanac: $almanacData"      
    	def aveHighString = response?.almanac?.temp_high.normal.F
		def aveHigh = aveHighString.toInteger()
		log.debug "aveHigh: $aveHigh"      
        
    	def temperatureDelta = forecastHigh - aveHigh
    
 	   log.debug "temperatureDelta: $temperatureDelta"  
                
 	   def bulbColorText = "unknown"
 	   def bulbColor = 22  // icky yellow-green hue, should always be overridden
    
		if(temperatureDelta>9) {
	       	bulbColorText = "Red"
	        bulbColor = 0
	    } else if(temperatureDelta>5) {
	       	bulbColorText = "Orange"
	        bulbColor = 1.25
	    } else if(temperatureDelta>2) {
	       	bulbColorText = "Yellow"
	        bulbColor = 6.5
		} else if(temperatureDelta>-3) {
	      	bulbColorText = "Green"
	        bulbColor = 33
		} else if(temperatureDelta>-6) {
	       	bulbColorText = "Light Blue"
	        bulbColor = 60
	    } else if(temperatureDelta>-10) {
	       	bulbColorText = "Indigo"
	        bulbColor = 66
		} else {
	       	bulbColorText = "Purple"
	        bulbColor = 79
	    }

	    log.debug "bulbColor: $bulbColor" 
	    log.debug "Weather Forecast Changed the color to $bulbColorText" 
    
		thebulb.setHue(bulbColor)
		thebulb.setSaturation(100)
}

def uponOffCommand(evt) {
	log.debug "in onOffCommand"
    
	def time = now()
	def dateInfo = new Date(time + location.timeZone.getOffset(time))
    def thisHour = dateInfo.hours
    def thisMinutes = dateInfo.minutes 
	def currentTimeValue = thisHour.toInteger() * 60 + thisMinutes.toInteger()
   	log.debug "currentTimeValue: $currentTimeValue"
    
    def readyTime = timeToday(endForecastTime, location.timeZone)
    def readyHour = readyTime.format("H",location.timeZone)
    def readyMinutes = readyTime.format("m",location.timeZone)
	def endTimeValue = readyHour.toInteger() * 60 + readyMinutes.toInteger()
  	log.debug "endTimeValue: $endTimeValue"
    
    
    if (currentTimeValue >= endTimeValue) {
		if(state.colorToggle == 0) {	// Toggle color back and forth every time it turns off
	    	state.colorToggle = 1
	    } else {
	    	state.colorToggle = 0
	    }
	    log.debug "state.colorToggleReady: $state.colorToggle"  

		setHolidayColor()
	}
}

def setHolidayColor() {
	log.debug "in setHolidayColor"
//  def defaultColor = 55  // Light Bluish
    def todaysColor = null
    
    def	valentinesStartMonth = 2
    def valentinesStartDate = 10
    def valentinesColor = 89  // Pink
    def	valentinesEndMonth = 2
    def valentinesEndDate = 15
    
    def	easterStartMonth = 3  // April
    def easterStartDate = 9
    def easterColor1 = 6.5  // Yellow
    def easterColor2 = 79  // Purple
    def	easterEndMonth = 3
    def easterEndDate = 17  // April 16 is Easter 2017
    
    def	indepStartMonth = 6  // June
    def indepStartDate = 28
    def indepColor1 = 66  // Blue
    def indepColor2 = 0  // Red
    def	indepEndMonth = 7  // July
    def indepEndDate = 5  
    
    def	halloweenStartMonth = 10  // Oct
    def halloweenStartDate = 1
    def halloweenColor1 = 1.25  // Orange
    def halloweenColor2 = 79  // Purple
    def	halloweenEndMonth = 11  // Oct
    def halloweenEndDate = 1
    
    def	tgivingStartMonth = 11  // Nov
    def tgivingStartDate = 15
    def tgivingColor = 6.5  // Yellow
    def	tgivingEndMonth = 11  // Nov
    def tgivingEndDate = 25  // Nov 24 is Thanksgiving 2016
    
    def	christmasStartMonth = 12  // Dec
    def christmasStartDate = 1
    def christmasColor1 = 0  // Red
    def christmasColor2 = 33  // Green
    def	christmasEndMonth = 12  // Dec
    def christmasEndDate = 26
        
    def time = now()
	def dateInfo = new Date(time + location.timeZone.getOffset(time))
	def thisMonth = dateInfo.month + 1  // Have to add one as the month number stupidly starts at 0. Why does anyone start counting at 0?
    def thisDate = dateInfo.date
    
    def todayIndex = getDateIndex(thisMonth, thisDate)
//	def todayIndex = getDateIndex(monthNumb, dateNumb)  // For testing only
    log.debug "todayIndex: $todayIndex"
    
	if (todayIndex >= getDateIndex(christmasEndMonth,christmasEndDate)) {
    	todaysColor = defaultColor
    } else if (todayIndex >= getDateIndex(christmasStartMonth,christmasStartDate)) {
        if(state.colorToggle == 0) {	// Toggle color back and forth every day
    		todaysColor = christmasColor1
    	} else {
    		todaysColor = christmasColor2
    	}
    } else if (todayIndex >= getDateIndex(tgivingEndMonth,tgivingEndDate)) {
	    todaysColor = defaultColor
    } else if (todayIndex >= getDateIndex(tgivingStartMonth,tgivingStartDate)) {
	    todaysColor = tgivingColor
    } else if (todayIndex >= getDateIndex(halloweenEndMonth,halloweenEndDate)) {
	    todaysColor = defaultColor
    } else if (todayIndex >= getDateIndex(halloweenStartMonth,halloweenStartDate)) {
        if(state.colorToggle == 0) {	// Toggle color back and forth every day
    		todaysColor = halloweenColor1
    	} else {
    		todaysColor = halloweenColor2
    	}    
    } else if (todayIndex >= getDateIndex(indepEndMonth,indepEndDate)) {
	    todaysColor = defaultColor    
    } else if (todayIndex >= getDateIndex(indepStartMonth,indepStartDate)) {
        if(state.colorToggle == 0) {	// Toggle color back and forth every day
    		todaysColor = indepColor1
    	} else {
    		todaysColor = indepColor2
    	}        
    } else if (todayIndex >= getDateIndex(easterEndMonth,easterEndDate)) {
	    todaysColor = defaultColor       
    } else if (todayIndex >= getDateIndex(easterStartMonth,easterStartDate)) {
        if(state.colorToggle == 0) {	// Toggle color back and forth every day
    		todaysColor = easterColor1
    	} else {
    		todaysColor = easterColor2
    	}            
     } else if (todayIndex >= getDateIndex(valentinesEndMonth,valentinesEndDate)) {
	    todaysColor = defaultColor        
    } else if (todayIndex >= getDateIndex(valentinesStartMonth,valentinesStartDate)) {
	    todaysColor = valentinesColor    
    } else {
        todaysColor = defaultColor    
    }
    log.debug "todaysColor: $todaysColor"    

    thebulb.setHue(todaysColor)
	thebulb.setSaturation(100)	
}

def getDateIndex(inputMonth, inputDate) {
//    log.debug "inputMonth: $inputMonth  inputDate: $inputDate" 
	def result = (inputMonth-1) * 31 + inputDate	// Rough day of the year calc, wrong as all months dont have 31 days, but it doesn't matter
    return result
}

(A.J. Griglak) #4

@Mykines: I should have mentioned this a lot sooner, but I love this app! I’ve been using it since October and went through Halloween, Thanksgiving, and Christmas colors. It was also fun for the kids to see the weather colors in the mornings.

However, either the hub firmware update this week, or my first ever firmware update to the Osram flex strips have broken your SmartApp.

I noticed the same boring colors every morning this week. Tonight, I tried removing and adding the SmartApp and get “You are not authorized to perform the requested operation.”

Any ideas?


#5

Hmmm, not sure what’s going on here. I’m not much of a developer, and I’ve been at work pretty much nonstop so I couldn’t even tell you if mine’s working right. (But how cool that someone else used it - so glad you liked it!)
I did have occasional problems when I was setting it up with things not working right, but removing and re-adding it would clean that up. But not sure why it would tell you that you’re not authorized to do that… hopefully one of the people here with more ST expertise could explain that?
If you can reinstall it and that doesn’t clear it up, I’ll try to take a look at it next weekend after work hopefully slows down a bit.


(A.J. Griglak) #6

Thanks! I know well first-hand what that’s like.

Obviously not an urgent issue.


#7

I tried figuring out what’s wrong, but it seems to work without error still on my system. I’m still using the old firmware (0x01020205), so maybe that’s it? But I get both the weather prediction color changes and the holiday changes. Not sure why it won’t delete, but if you can figure that out and reinstall it, hopefully it comes back!