Trouble getting ThingShield device tiles to change color on events


(Joe Angell) #1

I resurrected a ThingShield project I had been working on some time ago. It’s just a couple of photosensors that detect if the “done” lights on my washer and dryer have come on. Since they only transmit a state, I thought it made sense to use the “Button” and “Sensor” capabilities.

Parsing data from the Arduino seems to work fine, and I see my “washer is done” and “dryer is done” message in the device’s Activity Log in the SmartThings app when I cover the photosensors. However, the thing’s tiles in the SmartThings iOS app never change color or label in response – they simply remain white.

I tried adding two matching, but that didn’t seem to have any effect on changing the tiles.

Here’s the complete source for the device type:

/**
 *  Washer/Dryer Finished Monitor
 */
// for the UI
metadata {
	definition (name: "Washer/Dryer Finished Monitor", author: "jangell") {
		capability "Button"
		capability "Sensor"

		attribute "washer", "enum", ["pending", "done"]
		attribute "dryer",  "enum", ["pending", "done"]
	}

	simulator {
		status "Washer Pending":  "washer:pending"
		status "Washer Done"   :  "washer:done"
		status "Dryer Pending" :  "dryer:pending"
		status "Dryer Done"    :  "dryer:done"
	}

	tiles {
        standardTile("washer", "device.washerLightState", width: 1, height: 1, canChangeIcon: true, canChangeBackground: true)
        {
            state "pending", label: 'Washer',       backgroundColor: "#ffffff", icon: "st.Appliances.appliances1"
            state "done",    label: 'Washer Done',  backgroundColor: "#00ff00", icon: "st.Appliances.appliances1"
        }

        standardTile("dryer", "device.dryerLightState", width: 1, height: 1, canChangeIcon: true, canChangeBackground: true)
        {
            state "pending", label: 'Dryer',       backgroundColor: "#ffffff", icon: "st.Appliances.appliances8"
            state "done",    label: 'Dryer Done',  backgroundColor: "#00ff00", icon: "st.Appliances.appliances8"
        }

		main(    "washer")
        details(["washer","dryer"])
    }
}

// Parse input and send events.
def parse(String description) {
//	log.debug "Parsing '${description}'"

	def cmd = zigbee.parse(description)
	if (!cmd) {
		log.debug "zigbee.parse() returned NULL:  ${description}"
        return
	}

	def parts = cmd.text.split( /:/ );

	if(parts[0] != "washer" && parts[0] != "dryer")
    	return

	if(parts[1] == "pending" || parts[1] == "done")
	{
		def result = createEvent(name:parts[0], value:parts[1]) 
		log.debug "Parse returned ${result?.descriptionText}"
		return result
    }
}

I’m pretty stumped about what I’m doing wrong. Any thoughts?

Thanks!

– Joe


(Mike Maxwell) #2

In order to update the tiles you need a custom command in the definition section that matches the state name in the tiles.
–command “pending”
–command "done"
Then add a matching method for each command
def pending(){
//use send event to update the tile
sendEvent(name: “washerLightState”, value: “pending”)
}
Currently however you are using the same state name for two separate devices, so you will have to separate them.

It helps me to think of the device in two separate parts, the preferences and tiles code section being on the phone, and the remainder of the code being resident on the back end.
When seen from this POV it makes sense that the back end must issue some sort of message (event) to update the app.


(Keith Croshaw) #3

This is pretty cool stuff. Makes me think that you could create a little UI without a separate webapp.


(Dan) #4

Joe,

I have created many custom Device Types that I use with the Arduino ThingShield. Below is an example of one that monitors 4 door contact sensors + 2 garage doors + Temp + Humidity. While I am not sure why your code is not working, I thought it might help to show you an example that I have been successfully running for many months.

Also, you may want to make your string parsing simpler in the parse() routine by modifying the Arduino code to simply send “washer pending”, “washer done”, “dryer pending”, & “dryer done” which lets you simplify your split() command.

I also find it best to try to implement the Arduino as standard SmartThings capabilities. You could emulate either a “contact sensor” or a “switch” pretty easily. It helps because you can then see similar syntax in other example Device Types.

Anyway, here’s my custom Device Types for monitor numerous doors within my home:

/**
 *  ST_Anything_Doors Device Type - ST_Anything_Doors.device.groovy
 *
 *  Copyright 2015 Daniel Ogorchock
 *
 *  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.
 * 
 *  Change History:
 *
 *    Date        Who            What
 *    ----        ---            ----
 *    2015-01-10  Dan Ogorchock  Original Creation
 *
 *
 */

metadata {
	definition (name: "ST_Anything_Doors", namespace: "ogiewon", author: "Daniel Ogorchock") {
		capability "Configuration"
		capability "Contact Sensor"
		capability "Motion Sensor"
		capability "Temperature Measurement"
		capability "Relative Humidity Measurement"
		capability "Sensor"
		capability "Polling"

		attribute "leftDoor", "string"
		attribute "rightDoor", "string"
		attribute "frontDoor", "string"
		attribute "backDoor", "string"
		attribute "kitchenDoor", "string"
		attribute "garagesideDoor", "string"
        
		command "pushLeft"
		command "pushRight"
	}

    simulator {
        status "on":  "catchall: 0104 0000 01 01 0040 00 0A21 00 00 0000 0A 00 0A6F6E"
        status "off": "catchall: 0104 0000 01 01 0040 00 0A21 00 00 0000 0A 00 0A6F6666"

        // reply messages
        reply "raw 0x0 { 00 00 0a 0a 6f 6e }": "catchall: 0104 0000 01 01 0040 00 0A21 00 00 0000 0A 00 0A6F6E"
        reply "raw 0x0 { 00 00 0a 0a 6f 66 66 }": "catchall: 0104 0000 01 01 0040 00 0A21 00 00 0000 0A 00 0A6F6666"
    }
	
    // Preferences
	preferences {
    	input "temphumidSampleRate", "number", title: "Temperature/Humidity Sensor Sampling Interval (seconds)", description: "Sampling Interval (seconds)", defaultValue: 30, required: true, displayDuringSetup: true
	}

	// tile definitions
	tiles {
		standardTile("leftDoor", "device.leftDoor", width: 1, height: 1, canChangeIcon: true, canChangeBackground: true) {
			state "closed", label: 'Closed', action: "pushLeft", icon: "st.doors.garage.garage-closed", backgroundColor: "#79b821", nextState: "closed"
            state "open", label: 'Open', action: "pushLeft", icon: "st.doors.garage.garage-open", backgroundColor: "#ffa81e", nextState: "open"
            state "opening", label: 'Opening', action: "pushLeft", icon: "st.doors.garage.garage-opening", backgroundColor: "89C2E8", nextState: "opening"
            state "closing", label: 'Closing', action: "pushLeft", icon: "st.doors.garage.garage-closing", backgroundColor: "89C2E8", nextState: "closing"
 		}
        standardTile("rightDoor", "device.rightDoor", width: 1, height: 1, canChangeIcon: true, canChangeBackground: true) {
			state "closed", label: 'Closed', action: "pushRight", icon: "st.doors.garage.garage-closed", backgroundColor: "#79b821", nextState: "closed"
            state "open", label: 'Open', action: "pushRight", icon: "st.doors.garage.garage-open", backgroundColor: "#ffa81e", nextState: "open"
            state "opening", label: 'Opening', action: "pushRight", icon: "st.doors.garage.garage-opening", backgroundColor: "89C2E8", nextState: "opening"
            state "closing", label: 'Closing', action: "pushRight", icon: "st.doors.garage.garage-closing", backgroundColor: "89C2E8", nextState: "closing"
 		}
        standardTile("frontDoor", "device.frontDoor", width: 1, height: 1, canChangeIcon: true, canChangeBackground: true) {
			state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
			state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821")
 		}
        standardTile("backDoor", "device.backDoor", width: 1, height: 1, canChangeIcon: true, canChangeBackground: true) {
			state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
			state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821")
 		}
        standardTile("kitchenDoor", "device.kitchenDoor", width: 1, height: 1, canChangeIcon: true, canChangeBackground: true) {
			state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
			state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821")
 		}
        standardTile("garagesideDoor", "device.garagesideDoor", width: 1, height: 1, canChangeIcon: true, canChangeBackground: true) {
			state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
			state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821")
 		}
        valueTile("temperature", "device.temperature", width: 1, height: 1) {
			state("temperature", label:'${currentValue}°', 
				backgroundColors:[
					[value: 31, color: "#153591"],
					[value: 44, color: "#1e9cbb"],
					[value: 59, color: "#90d2a7"],
					[value: 74, color: "#44b621"],
					[value: 84, color: "#f1d801"],
					[value: 95, color: "#d04e00"],
					[value: 96, color: "#bc2323"]
				]
			)
		}
        valueTile("humidity", "device.humidity", inactiveLabel: false) {
			state "humidity", label:'${currentValue}% humidity', unit:""
		}
        standardTile("motion", "device.motion", width: 1, height: 1) {
			state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0")
			state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff")
		}
        standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat") {
			state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
		}
        
        main (["temperature","humidity","motion"])
        details(["temperature","humidity","motion","leftDoor","rightDoor","frontDoor","backDoor","kitchenDoor","garagesideDoor","configure"])
	}
}

//Map parse(String description) {
def parse(String description) {
    def msg = zigbee.parse(description)?.text
    log.debug "Parse got '${msg}'"

    def parts = msg.split(" ")
    def name  = parts.length>0?parts[0].trim():null
    def value = parts.length>1?parts[1].trim():null

    name = value != "ping" ? name : null
	
    //if (name == "temperature") 
    //{
    //	value = fahrenheitToCelsius(value.toDouble())
    //}
    
    def result = createEvent(name: name, value: value, isStateChange: true)

    log.debug result

    return result
}

def pushLeft() {
	log.debug "Executing 'pushLeft' = 'leftDoor on'"
    zigbee.smartShield(text: "leftDoor on").format()
}

def pushRight() {
	log.debug "Executing 'pushRight' = 'rightDoor on'"
    zigbee.smartShield(text: "rightDoor on").format()
}

def poll() {
	//temporarily implement poll() to issue a configure() command to send the polling interval settings to the arduino
	configure()
}

def configure() {
	log.debug "Executing 'configure'"
	log.debug "temphumid " + temphumidSampleRate
	[
        zigbee.smartShield(text: "temphumid " + temphumidSampleRate).format()
    ]
}

(Dan) #5

Joe,

After looking at your code again, and your comments in your original post…

It appears your Arduino is sending “dryer is done” but your Device Type is expecting “dryer:done”

This may explain the issue.

Dan


(Dan) #6

Mike,

I would agree with you if he was trying to update the value of the tiles from a SmartApp. However, his parse() routine is what will create the events to update the tiles once data is received from the ThingShield. In this case, no custom commands should be required. (Unless I misinterpreted the original issue :wink:)

Dan


(Joe Angell) #7

Dan, the Arduino is sending “dryer:done”, and the device type seems to e parsing it correctly. Since I’m creating the event with the name “dryer” and the value “done”, the log is showing “dryer is done”.

Using a contact sensor as a basis is a good idea; my “is the light on” test operates very similarly to one as far as outputs go, so that seems like the best model.

It turns out my mistake was pretty simple – I thought the first argument (“washer”) to StandardTile() was the target of my createEvent, not the second argument (“device.washerLightState”). I first noticed this with Mike’s comment, and then ntoiced that your Standard Tile calls were using arguments like ("leftDoor, “device.leftDoor”). I mirrored that structure with mine (i.e.: "washer, “device.washer”), and now sending events to “washer” work as expected.

Now everything works great! No commands needed. Thanks Dan and Mike!

– Joe

Updated device type code (I’m still using the “:” in the split):

/**
 *  Washer/Dryer Finished Monitor
 *
 *  Author: jangell
 *  Date: 2013-06-30
 */
// for the UI
metadata {
	definition (name: "Washer/Dryer Finished Monitor", author: "jangell") {
		capability "Contact Sensor"
		capability "Sensor"

		attribute "washer", "string"
		attribute "dryer", "string"
	}

	tiles {
        standardTile("washer", "device.washer", width: 1, height: 1, canChangeIcon: true, canChangeBackground: true)
        {
            state "pending", label: 'Washer',       backgroundColor: "#ffffff", icon: "st.Appliances.appliances1"
            state "done",    label: 'Washer Done',  backgroundColor: "#69AF1A", icon: "st.Appliances.appliances1"
        }

        standardTile("dryer", "device.dryer", width: 1, height: 1, canChangeIcon: true, canChangeBackground: true)
        {
            state "pending", label: 'Dryer',       backgroundColor: "#ffffff", icon: "st.Appliances.appliances8"
            state "done",    label: 'Dryer Done',  backgroundColor: "#69AF1A", icon: "st.Appliances.appliances8"
        }

		main(    "washer")
        details(["washer","dryer"])
    }
}

// Parse input and send events.
def parse(String description) {
//	log.debug "Parsing '${description}'"

	def cmd = zigbee.parse(description)
	if (!cmd) {
		log.debug "zigbee.parse() returned NULL:  ${description}"
        return
	}

	def parts = cmd.text.split( /:/ );

	if(parts[0] != "washer" && parts[0] != "dryer")
    	return

    def result = createEvent(name:parts[0], value:parts[1]) 
    log.debug "Parse returned ${result?.descriptionText}"
    return result
}

(Joe Angell) #8

Now I just have to figure out how to make a SmartApp that can do something useful with it. :slight_smile: I added “washer” and “dryer” string attributes, which I presume I’ll be able to read from my SmartApp. Mostly I want my app to send a notification when the washer or dryer finishes done. Time to look through some examples and go over the app docs.

Thanks again!