[RELEASE] Value Tiles - DTH for displaying webCoRE variables / Stats in a 'Thing'


#104

I would love to be able to test this but i’m getting an odd error when creating the DTH.

Groovy.lang.GroovyRuntimeException: Ambiguous method overloading for method physicalgraph.device.CapabilityService#syncCustomCapabilityWithDuplo. Cannot resolve which method to invoke for [null] due to overlapping prototypes between: [class physicalgraph.device.Capability] [class physicalgraph.device.cache.CapabilityDTO]

Maybe a copy paste issue?

Edit: I was finally able to save, had to a replace with ASCII double quotes.


#105

You are the man. I’m happy to see that someone has figured out the custom attributes piece. Makes my somewhat more optimistic for the future of ST in general.

I didn’t adjust the piston other than to switch to local variables for testing purposes. Values loaded correctly upon initial piston test. Piston also successfully ran and updated following a value adjustment. Reflected in both new and classic apps.


#106

It was a bit of a headache but it turned out the biggest problem is I had to start from scratch. Editing an existing DTH didn’t work as there is some cache issue. But I got the process down now, I think. Really glad it worked for you.

I thought I used double quotes everywhere. Where was it messed up for you?


#107

I’m going to try and apply it to a device that displays my WAN IP. I need to rework my current piston and won’t need the adjustment aspect.

I did not go through line by line, I copied to notepad first, did a replace all changing “curly”
double quotes to straight double quotes (if that makes sense). It happens sometimes with copy/paste of code.

Update: Removed all the new app capabilities except TextA and TextB to display my IP Address, worked like a charm. Going to remove B as well…IP Address is somewhat redundant since the device name is also displayed.


#108

@fieldsjm can you share the ip address DTH? And your method for getting the ip address into WebCoRE please.


#109

Is it possible to perform value changes in decimal rather then a whole number? If so, what do I need to change in the DTH?


#110

I used patrick stuarts dth for a long time but it seemed to stop polling. Since then i’ve used webcore to run the show, pulling my ip locally from my router using a web request similar to this

The rest was a simple DTH using changeValue. I am out of town for the week so I haven’t had much opportunity to tinker with guxdude’s custom attributes other than what you see above.


#111

Sorry I’m out of town for a few weeks. Need to change the data type from integer to number but it has to be done in the CLI and a new DTH built. I can do it when I get back but don’t have my computer with me on the road. Sorry.


#112

@Alwas - Just following up, my edited DTH is below. Works in both apps but the IP does not appear on the home screen in the new app, you have to click into the device to see it. This is related to vid and since I am using a striped down version of the DTH/vid created by @guxdude; it cannot be directly edited (valueA is set to appear not labelA in his vid).

Once you figure out a way to pull your IP, you only need to use the setLabelA command in webCore. (cIP is my current IP)


image

DTH:

/**
 *  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.
 *
 */

preferences {
    }
    
metadata {
	definition (name: "WAN-IP", namespace: "fieldsjm", "vid": "7f05b788-4020-3d65-9977-a76c6ebb049e","mnmn": "SmartThingsDHs") {
		capability "dictionaryfabric11101.adjustTextA"
		capability "Health Check"
        capability "Refresh"
                
		attribute "labelA", "string"
	    attribute "GVstatus", "string"

		command ResetGV
    }

	simulator {
    
    }

	tiles {
        valueTile("labelA", "device.labelA", inactiveLabel: false, decoration: "flat", width: 3, height: 1) {
        	state "default", label:'${currentValue}'
        }
        
        main "labelA"
        details(["labelA"])
	}
}

def installed() {
    sendEvent(name: "labelA", value: "--", displayed: false)
    sendEvent(name: "GVstatus", value: "start", displayed: false, isStateChange: true)
}

def updated() {
    sendEvent(name: "GVstatus", value: "update", displayed: false, isStateChange: true)
}

def setLabelA(value) {
	sendEvent(name: "labelA", value: "labelA")
    textChange("labelA", value)
}

def textChange(String valString, String newval) {
    Logger("valueChange: ${valString} Target ${newval}")
    sendEvent("name":valString, "value":newval, displayed: true)
    sendEvent(name: "GVstatus", value: valString, displayed: false, isStateChange: true)
}

def ResetGV() {
    sendEvent(name: "GVstatus", value: "clear", displayed: false, isStateChange: true)
}
def Logger(String logString) {
	log.info(logString)
}

#113

@Alwas and @fieldsjm, Here is a DTH which only has text values (5 of them). The first text value will be displayed on the home screen. Due to a bug in the new ST implementation, you cannot edit the values to feed back into webcore but you can display anything from webcore. Hope this helps.

/*
 Value tiles with adjustment capability-with new app support
*/

metadata {
 	definition (name: "Adjust Text Values", namespace: "guxdude", author: "guxdude" ,"vid": "d55576bc-0a21-3f23-ba43-d04d9e485d8e","mnmn": "SmartThingsCommunity"
    					) { 
        capability "dictionaryfabric11101.adjustTextA"
        capability "dictionaryfabric11101.adjustTextB"
        capability "dictionaryfabric11101.adjustTextC"
        capability "dictionaryfabric11101.adjustTextD"
        capability "dictionaryfabric11101.adjustTextE"

		capability "Health Check"
        capability "Refresh"
        
        attribute "GVstatus", "string"

		command ResetGV
     } // End of definition
     
    tiles(scale:2) {
        valueTile("Label1", "labelA", width: 6, height: 2, decoration: "flat") {
			state("val", label:'${currentValue}', foregroundColor: "#000000", backgroundColor: "#FFFFFF", defaultState: true )
		}
        valueTile("Label2", "labelB", width: 6, height: 2, decoration: "flat") {
			state("default", label:'${currentValue}', foregroundColor: "#000000", backgroundColor: "#FFFFFF" )
		}
        valueTile("Label3", "labelC", width: 6, height: 2, decoration: "flat") {
			state("default", label:'${currentValue}', foregroundColor: "#000000", backgroundColor: "#FFFFFF" )
		}
        valueTile("Label4", "labelD", width: 6, height: 2, decoration: "flat") {
			state("default", label:'${currentValue}', foregroundColor: "#000000", backgroundColor: "#FFFFFF" )
		}
        valueTile("Label5", "labelE", width: 6, height: 2, decoration: "flat") {
			state("default", label:'${currentValue}', foregroundColor: "#000000", backgroundColor: "#FFFFFF" )
		}

		// "Value1" will appear in the things view
 		main(["Label1"])
        // these tiles will appear in the Device Details view
        // (order is left-to-right, top-to-bottom)
 		details([
        "Label1",
        "Label2",
        "Label3",
        "Label4",
        "Label5" ])
 	}  // End of tiles
    
 	preferences {
      //  input name: "GVup", type: "boolean", title: "Updated", description: "Settings updated", required: true, defaultValue: false
	}   // End of preferences   
 }   // End of metadata
 
 
def installed() {
    sendEvent(name: "labelA", value: "Value A", displayed: false)
    sendEvent(name: "labelB", value: "Value B", displayed: false)
    sendEvent(name: "labelC", value: "Value C", displayed: false)
    sendEvent(name: "labelD", value: "Value D", displayed: false)
    sendEvent(name: "labelE", value: "Value E", displayed: false)

    sendEvent(name: "GVstatus", value: "start", displayed: false, isStateChange: true)
}

def updated() {
    sendEvent(name: "GVstatus", value: "update", displayed: false, isStateChange: true)
}

def setLabelA(value) {
	textChange("labelA", value)
}

def setLabelB(value) {
	textChange("labelB", value)
}

def setLabelC(value) {
	textChange("labelC", value)
}

def setLabelD(value) {
	textChange("labelD", value)
}

def setLabelE(value) {
	textChange("labelE", value)
}

def textChange(String valString, String newval) {
    Logger("valueChange: ${valString} Target ${newval}")
    sendEvent("name":valString, "value":newval, displayed: true)
    sendEvent(name: "GVstatus", value: valString, displayed: false, isStateChange: true)
}

def ResetGV() {
    sendEvent(name: "GVstatus", value: "clear", displayed: false, isStateChange: true)
}

def Logger(String logString) {
	log.info(logString)
}

#114

I checked again after some hours and it works :+1:


#115

Worked perfectly, thanks for sharing.

I did have to create a new device as there still seems to be some cache issues in the new app.


#116

@guxdude This has rapidly become one of my favourite dth’s and pistons.
Probably like other people I’m using it as a rolling alert system, with the latest alert always showing at the top (in the dth and piston), like if my IP address changes, wind rises too high, motion in garden etc all called to a single Global variable across my pistons (@zValueStatus). Here’s the piston if anybody wants a go. Also created 5 other global variables zValueA through zValueE.


#117

That’s great. Glad this is helping. And realize you can create as many of these “devices” as you want to if you want more than 5 items.


#118

I may just be doing that. Because I have some custom Zigbee environmental sensors with child devices of motion, water, sound that don’t display in the new app correctly, I may have to use your dth as a stop gap measure, especially as the custom capabilities are not working correctly yet, with 2 weeks to go!


#119

For anyone who is interested, I created a new DTH for time values. Not pretty but it works. I use this for my sprinkler control so I can modify start times in the app. This one only works in the new app as I didn’t copy my old DTH into this one.

screenshot:

DTH:

/*
 Time tiles with adjustment capability-with new app support
*/

metadata {
 	definition (name: "Adjust Time Values", namespace: "guxdude", author: "guxdude"
               ,"vid": "bcccec64-8bbc-3719-829f-a00a0ac8072f","mnmn": "SmartThingsCommunity"
    					) { 
        capability "dictionaryfabric11101.adjustTextA"
        capability "dictionaryfabric11101.adjustHourA"
        capability "dictionaryfabric11101.adjustMinuteA"

        capability "dictionaryfabric11101.adjustTextB"
        capability "dictionaryfabric11101.adjustHourB"
        capability "dictionaryfabric11101.adjustMinuteB"

        capability "dictionaryfabric11101.adjustTextC"
        capability "dictionaryfabric11101.adjustHourC"
        capability "dictionaryfabric11101.adjustMinuteC"

        capability "dictionaryfabric11101.adjustTextD"
        capability "dictionaryfabric11101.adjustHourD"
        capability "dictionaryfabric11101.adjustMinuteD"

       // Need 3 more start times

		capability "Health Check"
        capability "Refresh"
        
        attribute "GVstatus", "string"
        attribute "currentMeridianA", "string"
        attribute "currentMeridianB", "string"
        attribute "currentMeridianC", "string"
        attribute "currentMeridianD", "string"
        
    	attribute "enableTimeA","enum",["true","false"]
    	attribute "enableTimeB","enum",["true","false"]
    	attribute "enableTimeC","enum",["true","false"]
    	attribute "enableTimeD","enum",["true","false"]

        
		command ResetGV
        command "changeTimeA", ["string"]
        command "changeTimeB", ["string"]
        command "changeTimeC", ["string"]
        command "changeTimeD", ["string"]

     } // End of definition
     
    tiles(scale:2) {
 	}  // End of tiles
    
 	preferences {
        input (
        	name: "meridianAP", type: "enum", title: "AM/PM A", description: "Morning or afternoon", required: true, 
            options: ["AM": "A Morning (AM)", "PM": "A Afternoon (PM)"]
            // defaultValue not actually set until user enters so not usefull:  , defaultValue: "AM"
        )
        input (
        	name: "meridianBP", type: "enum", title: "AM/PM B", description: "Morning or afternoon", required: true, 
            options: ["AM": "B Morning (AM)", "PM": "B Afternoon (PM)"]
            // defaultValue not actually set until user enters so not usefull:  , defaultValue: "AM"
        )
        input (
        	name: "meridianCP", type: "enum", title: "AM/PM C", description: "Morning or afternoon", required: true, 
            options: ["AM": "C Morning (AM)", "PM": "C Afternoon (PM)"]
            // defaultValue not actually set until user enters so not usefull:  , defaultValue: "AM"
        )
        input (
        	name: "meridianDP", type: "enum", title: "AM/PM D", description: "Morning or afternoon", required: true, 
            options: ["AM": "D Morning (AM)", "PM": "D Afternoon (PM)"]
            // defaultValue not actually set until user enters so not usefull:  , defaultValue: "AM"
        )
    	input name: "enableTimeAP", type: "bool", title: "Time A Enable", description: "Use time A start?", required: true //, displayDuringSetup: true
    	input name: "enableTimeBP", type: "bool", title: "Time B Enable", description: "Use time B start?", required: true //, displayDuringSetup: true
    	input name: "enableTimeCP", type: "bool", title: "Time C Enable", description: "Use time C start?", required: true //, displayDuringSetup: true
    	input name: "enableTimeDP", type: "bool", title: "Time D Enable", description: "Use time D start?", required: true //, displayDuringSetup: true
	}   // End of preferences   
 }   // End of metadata
 
 
def installed() {
	
    sendEvent(name: "currentMeridianA", value: "AM", displayed: false)
	setHourA(12)
    setMinuteA(0)
  //  buildTimeA()

    sendEvent(name: "currentMeridianB", value: "AM", displayed: false)
	setHourB(12)
    setMinuteB(0)
  //  buildTimeB()

    sendEvent(name: "currentMeridianC", value: "AM", displayed: false)
	setHourC(12)
    setMinuteC(0)
  //  buildTimeC()

    sendEvent(name: "currentMeridianD", value: "AM", displayed: false)
	setHourD(12)
    setMinuteD(0)
  //  buildTimeD()

    sendEvent(name: "GVstatus", value: "start", displayed: false, isStateChange: true)
}

def updated() {
    sendEvent(name: "currentMeridianA", value: meridianAP, displayed: false)
    sendEvent(name: "currentMeridianB", value: meridianBP, displayed: false)
    sendEvent(name: "currentMeridianC", value: meridianCP, displayed: false)
    sendEvent(name: "currentMeridianD", value: meridianDP, displayed: false)

    sendEvent(name: "enableTimeA", value: enableTimeAP, displayed: false)
    sendEvent(name: "enableTimeB", value: enableTimeBP, displayed: false)
    sendEvent(name: "enableTimeC", value: enableTimeCP, displayed: false)
    sendEvent(name: "enableTimeD", value: enableTimeDP, displayed: false)

    //log.debug("UPDATED: HourA= ${device.currentValue("currentMeridianA")}")

    sendEvent(name: "hourA", value: device.currentValue("hourA"), unit: device.currentValue("currentMeridianA"), displayed: false)
    buildTimeA()
    sendEvent(name: "hourB", value: device.currentValue("hourB"), unit: device.currentValue("currentMeridianB"), displayed: false)
    buildTimeB()
    sendEvent(name: "hourC", value: device.currentValue("hourC"), unit: device.currentValue("currentMeridianC"), displayed: false)
    buildTimeC()
    sendEvent(name: "hourD", value: device.currentValue("hourD"), unit: device.currentValue("currentMeridianD"), displayed: false)
    buildTimeD()
    
    sendEvent(name: "GVstatus", value: "update", displayed: false, isStateChange: true)
}

def buildTimeA() {
	def timeString = device.currentValue("hourA").toString() + ":" + ((device.currentValue("minuteA")<10)? "0" : "" ) + device.currentValue("minuteA").toString() + " " + device.currentValue("currentMeridianA")

    //log.debug("BUILD: HourA= ${device.currentValue("hourA")}")
    //log.debug("BUILD: timestring: $timeString")
     
    setTimeA(timeString)
}

def buildTimeB() {
	def timeString = device.currentValue("hourB").toString() + ":" + ((device.currentValue("minuteB")<10)? "0" : "" ) + device.currentValue("minuteB").toString() + " " + device.currentValue("currentMeridianB")

    setTimeB(timeString)
}

def buildTimeC() {
	def timeString = device.currentValue("hourC").toString() + ":" + ((device.currentValue("minuteC")<10)? "0" : "" ) + device.currentValue("minuteC").toString() + " " + device.currentValue("currentMeridianC")

    setTimeC(timeString)
}

def buildTimeD() {
	def timeString = device.currentValue("hourD").toString() + ":" + ((device.currentValue("minuteD")<10)? "0" : "" ) + device.currentValue("minuteD").toString() + " " + device.currentValue("currentMeridianD")

    setTimeD(timeString)
}

def changeTimeA(value) {
	def newHour = (value.substring(0,value.indexOf(":"))).toInteger()
    def newMinute = (value.substring(value.indexOf(":")+1,value.indexOf(" "))).toInteger()
    def newMeridian = (value.substring(value.indexOf(" ")+1)).toString()
    //log.debug("CHANGE A: value: $value; hour: $newHour; minute: $newMinute; meridian: $newMeridian")
    
    // Only set times if all values are valid
    if ((newHour>0) && (newHour<13) && (newMinute>=0) && (newMinute<=60) && ((newMeridian=="AM") || (newMeridian=="PM"))) {
		sendEvent(name: "currentMeridianA", value: newMeridian, displayed: false)
        setMinuteA(newMinute)
        setHourA(newHour)
    }
}

def changeTimeB(value) {
	def newHour = (value.substring(0,value.indexOf(":"))).toInteger()
    def newMinute = (value.substring(value.indexOf(":")+1,value.indexOf(" "))).toInteger()
    def newMeridian = (value.substring(value.indexOf(" ")+1)).toString()
    
    // Only set times if all values are valid
    if ((newHour>0) && (newHour<13) && (newMinute>=0) && (newMinute<=60) && ((newMeridian=="AM") || (newMeridian=="PM"))) {
		sendEvent(name: "currentMeridianB", value: newMeridian, displayed: false)
        setMinuteB(newMinute)
        setHourB(newHour)
    }
}

def changeTimeC(value) {
	def newHour = (value.substring(0,value.indexOf(":"))).toInteger()
    def newMinute = (value.substring(value.indexOf(":")+1,value.indexOf(" "))).toInteger()
    def newMeridian = (value.substring(value.indexOf(" ")+1)).toString()
    
    // Only set times if all values are valid
    if ((newHour>0) && (newHour<13) && (newMinute>=0) && (newMinute<=60) && ((newMeridian=="AM") || (newMeridian=="PM"))) {
		sendEvent(name: "currentMeridianC", value: newMeridian, displayed: false)
        setMinuteC(newMinute)
        setHourC(newHour)
    }
}

def changeTimeD(value) {
	def newHour = (value.substring(0,value.indexOf(":"))).toInteger()
    def newMinute = (value.substring(value.indexOf(":")+1,value.indexOf(" "))).toInteger()
    def newMeridian = (value.substring(value.indexOf(" ")+1)).toString()
    
    // Only set times if all values are valid
    if ((newHour>0) && (newHour<13) && (newMinute>=0) && (newMinute<=60) && ((newMeridian=="AM") || (newMeridian=="PM"))) {
		sendEvent(name: "currentMeridianD", value: newMeridian, displayed: false)
        setMinuteD(newMinute)
        setHourD(newHour)
    }
}

def setTimeA(value) {
    textChange("labelA", value)
}
def setTimeB(value) {
    textChange("labelB", value)
}
def setTimeC(value) {
    textChange("labelC", value)
}
def setTimeD(value) {
    textChange("labelD", value)
}

def setHourA(value) {
	setHourGeneral(value, "hourA", "currentMeridianA")
    buildTimeA()
}

def setHourB(value) {
	setHourGeneral(value, "hourB", "currentMeridianB")
    buildTimeB()
}

def setHourC(value) {
	setHourGeneral(value, "hourC", "currentMeridianC")
    buildTimeC()
}

def setHourD(value) {
	setHourGeneral(value, "hourD", "currentMeridianD")
    buildTimeD()
}

def setHourGeneral( value,  whichHour,  whichMeridian) {
    //log.debug("SetHourGeneral: value: $value;  hour=$whichHour;  meridian=$whichMeridian")

	def hourVal = (device.currentValue(whichHour).toInteger())
    // reset to ensure we don't get out of sync with WebCoRE piston
    ResetGV()
    
    //log.debug("hourVal: $hourVal ; value: $value")
    
    if (hourVal==11) {
    	if (value==12) {
    		toggleUnit(whichHour, whichMeridian)
        }
    }
    if (hourVal==12) {
    	if (value==11) {
    		toggleUnit(whichHour, whichMeridian)
        }
    }
    
    //log.debug("SETHOUR: Updated hourVal: $hourVal")

	if (value>12) {
    	sendEvent(name: whichHour, value: 1, unit: device.currentValue(whichMeridian), displayed: false)
    } else if (value<1) {
    	sendEvent(name: whichHour, value: 12, unit: device.currentValue(whichMeridian), displayed: false)
    } else {
    	sendEvent(name: whichHour, value: value, unit: device.currentValue(whichMeridian), displayed: false)
    }
}

def setMinuteA(value) {
	setMinuteGeneral(value, "hourA", "minuteA", "currentMeridianA")
    buildTimeA()
}

def setMinuteB(value) {
	setMinuteGeneral(value, "hourB", "minuteB", "currentMeridianB")
    buildTimeB()
}

def setMinuteC(value) {
	setMinuteGeneral(value, "hourC", "minuteC", "currentMeridianC")
    buildTimeC()
}

def setMinuteD(value) {
	setMinuteGeneral(value, "hourD", "minuteD", "currentMeridianD")
    buildTimeD()
}

def setMinuteGeneral( value,  whichHour,  whichMinute, whichMeridian) {
    //log.debug("SETMINUTE: minuteVal: $value")
    // reset to ensure we don't get out of sync with WebCoRE piston
    ResetGV()

	if (value==60) {
    	valueChange(whichMinute, 0)
        setHourGeneral((device.currentValue(whichHour)+1), whichHour, whichMeridian)
    } else if (value<0) {
    	valueChange(whichMinute, 59)
        setHourGeneral((device.currentValue(whichHour)-1), whichHour, whichMeridian)
    } else {
    	valueChange(whichMinute, value)
    }
}

def toggleUnit( whichHour,  whichMeridian) {
    if (device.currentValue(whichMeridian)=="AM") {
    	sendEvent(name: whichHour, value: device.currentValue(whichHour), unit: "PM", displayed: false)
    	sendEvent(name: whichMeridian, value: "PM", displayed: true)
    } else {
    	sendEvent(name: whichHour, value: device.currentValue(whichHour), unit: "AM", displayed: false)
    	sendEvent(name: whichMeridian, value: "AM", displayed: true)
    }
}

def valueChange(String valString, Integer newval) {
    Logger("valueChange: ${valString} Target ${newval}")
    sendEvent("name":valString, "value":newval, displayed: true)
    // Don't flag integer value changes to minimize triggering piston
//    sendEvent(name: "GVstatus", value: valString, displayed: false, isStateChange: true)
}

def textChange(String valString, String newval) {
    Logger("textChange: ${valString} Target ${newval}")
    sendEvent("name":valString, "value":newval, displayed: true)
    sendEvent(name: "GVstatus", value: valString, displayed: false, isStateChange: true)
}

//def changeStep(Integer newval) {
	//valueA
//}

def ResetGV() {
    sendEvent(name: "GVstatus", value: "clear", displayed: false, isStateChange: true)
}

def Logger(String logString) {
	log.info(logString)
}

Piston to capture updates:


#120

Anyone using ActionTiles? Can I display custom text from this device type on an AT dashboard?
Thinking of these

  • an open windows list instead of having to show times for all windows
  • last door/motion activity location to know where everyone is
    Thanks