Awesome SmartApp: Mood Cube (with critical bug)

Anyone tried the Mood Cube app? This is what it does: you take a SmartSense Multisensor and put it inside a cube/box. Then, you can assign lighting configurations for each of the 6 sides. Any side that is currently facing up gets activated. It is awesome.

Bug: There is one critical feature that does not work for me. For each of the 6 scenes, you can capture the current state of your lights and save it to that scene. It worked for me the first time, but every capture since then has only reproduced colors from the first capture. I’m using it with Philips Hue bulbs + light strip.

The only way to set my colors is to manually enter in the hue/saturation numbers, which I wouldn’t even know how to go about figuring out.

If you haven’t tried this app out yet, give it a shot. If you have any issues, share here. I’ve put in a bug report to ST and I hope they work it out soon.

Peter

3 Likes

I’m posting the code here for reference, if you don’t mind:

Perhaps we can debug it.

/**
 *  Mood Cube
 *
 *  Copyright 2014 SmartThings, Inc.
 *
 *  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.
 *
 */

/************
 * Metadata *
 ************/
definition(
	name: "Mood Cube",
	namespace: "smartthings",
	author: "SmartThings",
	description: "Set your lighting by rotating a cube containing a SmartSense Multi",
	category: "SmartThings Labs",
	iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/App-LightUpMyWorld.png",
	iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/App-LightUpMyWorld@2x.png"
)

/**********
 * Setup  *
 **********/
preferences {
	page(name: "mainPage", title: "", nextPage: "scenesPage", uninstall: true) {
		section("Use the orientation of this cube") {
			input "cube", "capability.threeAxis", required: false, title: "SmartSense Multi sensor"
		}
		section("To control these lights") {
			input "lights", "capability.switch", multiple: true, required: false, title: "Lights, switches & dimmers"
		}
		section([title: " ", mobileOnly:true]) {
			label title: "Assign a name", required: false
			mode title: "Set for specific mode(s)", required: false
		}
	}
	page(name: "scenesPage", title: "Scenes", install: true, uninstall: true)
	page(name: "scenePage", title: "Scene", install: false, uninstall: false, previousPage: "scenesPage")
	page(name: "devicePage", install: false, uninstall: false, previousPage: "scenePage")
	page(name: "saveStatesPage", install: false, uninstall: false, previousPage: "scenePage")
}


def scenesPage() {
	log.debug "scenesPage()"
	def sceneId = getOrientation()
	dynamicPage(name:"scenesPage") {
		section {
			for (num in 1..6) {
				href "scenePage", title: "${num}. ${sceneName(num)}${sceneId==num ? ' (current)' : ''}", params: [sceneId:num], description: "", state: sceneIsDefined(num) ? "complete" : "incomplete"
			}
		}
		section {
			href "scenesPage", title: "Refresh", description: ""
		}
	}
}

def scenePage(params=[:]) {
	log.debug "scenePage($params)"
	def currentSceneId = getOrientation()
	def sceneId = params.sceneId as Integer ?: state.lastDisplayedSceneId
	state.lastDisplayedSceneId = sceneId
	dynamicPage(name:"scenePage", title: "${sceneId}. ${sceneName(sceneId)}") {
		section {
			input "sceneName${sceneId}", "text", title: "Scene Name", required: false
		}

		section {
			href "devicePage", title: "Show Device States", params: [sceneId:sceneId], description: "", state: sceneIsDefined(sceneId) ? "complete" : "incomplete"
		}

		if (sceneId == currentSceneId) {
			section {
				href "saveStatesPage", title: "Record Current Device States", params: [sceneId:sceneId], description: ""
			}
		}

	}
}

def devicePage(params) {
	log.debug "devicePage($params)"

	getDeviceCapabilities()

	def sceneId = params.sceneId as Integer ?: state.lastDisplayedSceneId

	dynamicPage(name:"devicePage", title: "${sceneId}. ${sceneName(sceneId)} Device States") {
		section("Lights") {
			lights.each {light ->
				input "onoff_${sceneId}_${light.id}", "boolean", title: light.displayName
			}
		}

		section("Dimmers") {
			lights.each {light ->
				if (state.lightCapabilities[light.id] in ["level", "color"]) {
					input "level_${sceneId}_${light.id}", "enum", title: light.displayName, options: levels, description: "", required: false
				}
			}
		}

		section("Colors (hue/saturation)") {
			lights.each {light ->
				if (state.lightCapabilities[light.id] == "color") {
					input "color_${sceneId}_${light.id}", "text", title: light.displayName, description: "", required: false
				}
			}
		}
	}
}

def saveStatesPage(params) {
	saveStates(params)
	devicePage(params)
}


/*************************
 * Installation & update *
 *************************/
def installed() {
	log.debug "Installed with settings: ${settings}"

	initialize()
}

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

	unsubscribe()
	initialize()
}

def initialize() {
	subscribe cube, "threeAxis", positionHandler
}


/******************
 * Event handlers *
 ******************/
def positionHandler(evt) {

	def sceneId = getOrientation(evt.xyzValue)
	log.trace "orientation: $sceneId"

	if (sceneId != state.lastActiveSceneId) {
		restoreStates(sceneId)
	}
	else {
		log.trace "No status change"
	}
	state.lastActiveSceneId = sceneId
}


/******************
 * Helper methods *
 ******************/
private Boolean sceneIsDefined(sceneId) {
	def tgt = "onoff_${sceneId}".toString()
	settings.find{it.key.startsWith(tgt)} != null
}

private updateSetting(name, value) {
	app.updateSetting(name, value)
	settings[name] = value
}

private closestLevel(level) {
	level ? "${Math.round(level/5) * 5}%" : "0%"
}

private saveStates(params) {
	log.trace "saveStates($params)"
	def sceneId = params.sceneId as Integer
	getDeviceCapabilities()

	lights.each {light ->
		def type = state.lightCapabilities[light.id]

		updateSetting("onoff_${sceneId}_${light.id}", light.currentValue("switch") == "on")

		if (type == "level") {
			updateSetting("level_${sceneId}_${light.id}", closestLevel(light.currentValue('level')))
		}
		else if (type == "color") {
			updateSetting("level_${sceneId}_${light.id}", closestLevel(light.currentValue('level')))
			updateSetting("color_${sceneId}_${light.id}", "${light.currentValue("hue")}/${light.currentValue("saturation")}")
		}
	}
}


private restoreStates(sceneId) {
	log.trace "restoreStates($sceneId)"
	getDeviceCapabilities()

	lights.each {light ->
		def type = state.lightCapabilities[light.id]

		def isOn = settings."onoff_${sceneId}_${light.id}" == "true" ? true : false
		log.debug "${light.displayName} is '$isOn'"
		if (isOn) {
			light.on()
		}
		else {
			light.off()
		}

		if (type != "switch") {
			def level = switchLevel(sceneId, light)

			if (type == "level") {
				log.debug "${light.displayName} level is '$level'"
				if (level != null) {
					light.setLevel(value)
				}
			}
			else if (type == "color") {
				def segs = settings."color_${sceneId}_${light.id}"?.split("/")
				if (segs?.size() == 2) {
					def hue = segs[0].toInteger()
					def saturation = segs[1].toInteger()
					log.debug "${light.displayName} color is level: $level, hue: $hue, sat: $saturation"
					if (level != null) {
						light.setColor(level: level, hue: hue, saturation: saturation)
					}
					else {
						light.setColor(hue: hue, saturation: saturation)
					}
				}
				else {
					log.debug "${light.displayName} level is '$level'"
					if (level != null) {
						light.setLevel(level)
					}
				}
			}
			else {
				log.error "Unknown type '$type'"
			}
		}


	}
}

private switchLevel(sceneId, light) {
	def percent = settings."level_${sceneId}_${light.id}"
	if (percent) {
		percent[0..-2].toInteger()
	}
	else {
		null
	}
}

private getDeviceCapabilities() {
	def caps = [:]
	lights.each {
		if (it.hasCapability("Color Control")) {
			caps[it.id] = "color"
		}
		else if (it.hasCapability("Switch Level")) {
			caps[it.id] = "level"
		}
		else {
			caps[it.id] = "switch"
		}
	}
	state.lightCapabilities = caps
}

private getLevels() {
	def levels = []
	for (int i = 0; i <= 100; i += 5) {
		levels << "$i%"
	}
	levels
}

private getOrientation(xyz=null) {
	final threshold = 250

	def value = xyz ?: cube.currentValue("threeAxis")

	def x = Math.abs(value.x) > threshold ? (value.x > 0 ? 1 : -1) : 0
	def y = Math.abs(value.y) > threshold ? (value.y > 0 ? 1 : -1) : 0
	def z = Math.abs(value.z) > threshold ? (value.z > 0 ? 1 : -1) : 0

	def orientation = 0
	if (z > 0) {
		if (x == 0 && y == 0) {
			orientation = 1
		}
	}
	else if (z < 0) {
		if (x == 0 && y == 0) {
			orientation = 2
		}
	}
	else {
		if (x > 0) {
			if (y == 0) {
				orientation = 3
			}
		}
		else if (x < 0) {
			if (y == 0) {
				orientation = 4
			}
		}
		else {
			if (y > 0) {
				orientation = 5
			}
			else if (y < 0) {
				orientation = 6
			}
		}
	}

	orientation
}

private sceneName(num) {
	final names = ["UNDEFINED","One","Two","Three","Four","Five","Six"]
	settings."sceneName${num}" ?: "Scene ${names[num]}"
}
1 Like

Sounds like a cool idea. Do you have a photo or video to share?

Here ya go:
Mood Cube Video Demo

I added the following code to the settings (devicePage(params)) - page. It’s not really a solution, but it does help me in setting the correct colors for each bulb. Got the colors from the Hue Mood Lighting smartapp.

	section("Soft white: (23/56)") 
	section("White: (52/19)") 
	section("Daylight: (53/91)") 
	section("Warm white: (20/80)") 
	section("Orange: (10/100)") 
	section("Yellow: (25/100)") 
	section("Green: (39/100)") 
	section("Blue: (70/100)") 
	section("Purple: (75/100)") 
	section("Pink: (83/100)") 
	section("Red: (100/100)")
1 Like

Thanks, I will test more today. I’m manually entering in values since the record state function doesn’t work at all for me. Also, one of the Hue lights in particular gets stuck on the same color most of the time. I have 4 lights used in the current configuration. Let me know if there’s anything you’d like me to try.

Try recording hex and level. Hue and saturation don;t work for me either.

	def switchState  = device.latestValue("switch")
        def level 	= device.latestValue("level")
        def color   	= device.latestValue("color")

       //do some things, execute some code, call a method with my values, then use them to recall color and level.
 
        setColor([hex: color, level: level])

HTH

This is a great idea!
Hope you can make it work because it would be a cheaper solution then hue taps.

Yea I can’t get the recording state feature to work. So I’m just typing in the hue/sat values in manually. The other issue I have is some bulbs get stuck on a color occasionally. But I can live with it.

I put it in an ugly cardboard box :smile:

1 Like

It looks like a great idea. So easy for non tech-savy guests that are over to control the environment and have some fun. But I am running into an issue with the refresh time on the lights themselves. While in the Mood Cube app, it shows that it reads the sensor and changes scenes. I’ve manually assigned the hue/saturation and also tried recording the current state of the individual lights. The lights don’t update to the specified color.

I suspect it is something with my hue smart app in ST. When I control the color of the lights directly from the “Things” section they have a 10-15 seconds delay in updating or don’t update at all. Has anybody else had issues with either this specific issue in the Mood Cube, or with the sporadic response with the Hue lights?

Thanks,
Thomas

Here’s a snippet of HUE color setting code that does work, it’s derived from a scene state app I’m working on.

def colorMap = [hue:myHueVar.toInteger(),saturation:mySaturationVar.toInteger(),myLevelVar]
stDevice.setColor(colorMap) 

Notice that level is included in the color map sent to the device.

A few things I’ve noticed in capturing the hue states.
Setting the hue colors via the hue app more than likely will not update those settings in ST in any reasonable time.
I’ve had better luck setting the colors in ST.
In other words when capturing a device state, you are capturing the state of the device as far as ST is concerned, this may or may not be the actual state of the device.
The color wheel is annoying, for me anyway when trying to set colors.
The hue bulb device has sliders for hue sat and level already in the preferences section, I added these to the details in the prefs and removed the color wheel. OK, so the hue bulb device isn’t as blingy without the color wheel, but I find it easier to use…

So i made a little cube of lego to encase the sensor:)
However i’m still confused how to type in the correct colour codes in the smartapp for each scene. So how should i go about finding them?

Big thanks for ze help!

If you run live logging while adjusting the hues, you’ll see the values in there.
The current values are also shown by clicking the hue bulb, in my devices in the IDE

So i found the values by clicking the bulb in things but i’m not sure how to type it in inside the mood smartapp.
inside the colors (hue/saturation) box i wrote the following 1C01Ff/99.6078431372549.

tried different ways to type it in with spacings around the / but im guessing there is a special way to do it^^

there and again BIG thanks!

Hue sat and level need to be integers, the three values are used to create a map, which is a groovy data structure.
Maps are a key: value list in the form [key: value, key: value…]. The order of the key value pairs within the map are unimportant, the names of the keys matter.

For anybody looking for more functions, I created a version of this to run Hello Home actions.

3 Likes

I am going to make both as cubes! Together, these would offer a lot of cool combinations.

Work in progress: Two halves of my wooden mood cube. There’s a slot inside the cube to hold the sensor. The two halves are held together by a bolt and nut through the center. Circle wooden inlays will be inserted in the center of each cube face, which also hides the ends of the bold/nut and will be colored to represent the active light scene. And will finish off with a nice stain and clear coat.

Any chance of working out the current state bug? I’m looking to set this up with about 10 hue bulbs based on scenes from the Hue app - it’d be great if I could just copy that scene into this instead of having to work it out manually.

It must be worked out cuz he finished his project and has a video of it somewhere. Just do a search.