I am struggling inserting a string to get a driver attribute value. My code is below where i am trying to take an input value and pass to get a devices attribute value.
If i use the string variable either just the variable itself or by trying to impose the value as a string i get a null value. Even though sAttribute is "pm25"
Integer value = ChosenDevice.currentValue("${sAttribute}")
If i use the following code and put a string directly into the currentValue method i get the result and see the sensors value.
Integer value2 = ChosenDevice.currentValue("pm25")
First, have you verified that sAttribute is really "pm25"? Before this line:
Inset a line like:
log.debug "sAttribute = ${sAttribute}"
to see for sure.
Also, enum preference values will always be strings, so there should be no need to convert.
The other thing to make sure of is that ChosenDeivce is set to something (though since you aren't using null-safe navigation, you'd probably get a different error if it were, and you're using it fine later, though with only one line each, it's not possible to tell if this is in the same context). You could also log that, maybe right before or after the above, to see for sure:
log.debug "ChosenDevice = ${ChosenDevice}"
Finally, it's convention to follow camel case for Groovy variable names and also Hubitat inputs that ultimately get stored into these. I'd recommend changing this to chosenDevice but don't think it should cause problems (there are times where it does create things you you, like currentXyz based on an xyz attribute, and this would make this clearer then but still likely not a problem).
If that doesn't help, I would suggest providing an example of a minimal app that demonstrates this problem. Someone may be able to help better then -- or often when paring things down to just a demonstration of the problem, people will figure the problem out on their own.
Thank you. I included a cope snip and my app output.
I did change my variable naming convention and added a log output to validate. i had been using paragraphs to spit out values on the screen while i was running the app.
and here is the app output with log output. sAttribute displays properly in the log but when passed into currentValue it passes null. If i take what the value of sAttribute is and hardcode and pass to currentValue i get the sensor reading. I am sure this is a property syntax thing I am not understanding with groovy.
A screenshot probably isn't the best way to share code, but I guess it shows more context than a line or two.
For you problem: I wonder if it's a GString (interpolated string, more or less) versus String thing. This rarely matters but can for some things like Map keys, depending on how the comparison is made. There is way to convert that; perhaps that's what you were trying before.
But in your case, I don't see a reason for the intermediate variable at all. Why not use selectedAttribute directly?
If that doesn't help, try forming a minimal example as I suggested above (an app big enough to demonstrate only this specific problem) and share that code (as text--use the toolbar button or triple tick marks to format it).
Here is the code. I am not sure how to make it much simpler because i think it has to do with how a strings value is extracted in the currentValue method. I am not sure if i can extract. every example i see has a literal string value passed which doesnt help make the application dynamic. I would be assuming every person is using the same driver.
definition(
name: "AQI Calculator",
namespace: "DicksonJ",
author: "Jeff Dickson",
description: "This Application will read PM25 sensor from users device and calculates USA AQI",
category: "Air Quality",
iconUrl: "",
iconX2Url: "")
preferences {
page(name: "mainPage")
}
def mainPage(){
dynamicPage(name: "mainPage", title: "Calculate AQI", nextPage: null, install: true, uninstall: true, refreshinterval: 0){
section("Select your device"){
//get device from user
input "chosenDevice", "capability.polling", title: "Choose your Sensoring Device", multiple: false, required: true, submitOnChange:true
}
section("Display Attributes"){
//get attributes available from device and make a list
if(chosenDevice != null ){
String strAttributes = chosenDevice?.supportedAttributes
def attributeList = strAttributes.substring(1, strAttributes.size() -1).split(",").collect{it as String}
if(strAttributes != null){
// grab attribute from user
input (name: "selectedAttribute", title: "Select PM25 Sensor Attribute", type: "enum", options: attributeList.sort(), submitOnChange:true, width:3, defaultValue: 1)
// Run Calculation method
if(selectedAttribute != null){
Integer value = chosenDevice.currentValue("${selectedAttribute}".toString())
Integer value2 = chosenDevice.currentValue("pm25")
//Show Value of chosen sensor
paragraph "passed through value turned into ${value}"
paragraph "hardcoding pm25 string is ${value2}"
}
}
}
}
}
}
def installed() {
log.debug "Installed Application"
unsubscribe()
}
def updated(){
log.debug "Updated Application"
unsubscribe()
unschedule()
initialize()
}
def initialize(){
logi.info("Initializing with settings: ${settings}")
subscribe(settings.Device,"sensor", evenHandler)
runEvery15Minute(refresh)
}
def eventHandler(evt){
log.debug "Device was chosen ${evt.value}"
}
So i put a switch statement in for now that will give me the value. Two issues for some reason the value coming out of the attribute field on both drivers has a space prefixing the value so instead of "pm25" its needing " pm25" to enter the case statement. I still do not understand why the .currentValue() method is forcing a literal string passed and i cant pull that from a variable. In other languages i would use variable.ToSTring() to force or getvalue(variable) but you cant use a method call inside of the currentValue method so idk for now.
definition(
name: "AQI Calculator",
namespace: "DicksonJ",
author: "Jeff Dickson",
description: "This Application will read PM25 sensor from users device and calculates USA AQI",
category: "Air Quality",
iconUrl: "",
iconX2Url: "")
preferences {
page(name: "mainPage")
}
def mainPage(){
dynamicPage(name: "mainPage", title: "Calculate AQI", nextPage: null, install: true, uninstall: true, refreshinterval: 0){
section("Select your device"){
//get device from user
input "chosenDevice", "capability.polling", title: "Choose your Sensoring Device", multiple: false, required: true, submitOnChange:true
}
section("Display Attributes"){
//get attributes available from device and make a list
if(chosenDevice != null ){
String strAttributes = chosenDevice?.supportedAttributes
def attributeList = strAttributes.substring(1, strAttributes.size() -1).split(",").collect{it as String}
if(strAttributes != null){
// grab attribute from user
input (name: "selectedAttribute", title: "Select PM25 Sensor Attribute", type: "enum", options: attributeList.sort(), submitOnChange:true, width:3, defaultValue: 1)
// Run Calculation method
if(selectedAttribute != null){
switch(selectedAttribute){
case " pm25":
Integer value = chosenDevice.currentValue("pm25")
paragraph "hardcoding awair string is ${value}"
break;
case " Pm25":
Integer value = chosenDevice.currentValue("Pm25")
paragraph "hardcoding awair string is ${value}"
break;
case " PM25":
Integer value = chosenDevice.currentValue("PM25")
paragraph "hardcoding awair string is ${value}"
break;
}
}
}
}
}
}
}
def installed() {
log.debug "Installed Application"
unsubscribe()
}
def updated(){
log.debug "Updated Application"
unsubscribe()
unschedule()
initialize()
}
def initialize(){
logi.info("Initializing with settings: ${settings}")
subscribe(settings.Device,"sensor", evenHandler)
runEvery15Minute(refresh)
}
def eventHandler(evt){
log.debug "Device was chosen ${evt.value}"
}
Then, you're manually splitting that back into a list with the next line.
This is not necessary -- though it's theoretically fine. Groovy happily converts the list to the comma-separated string (with a space in between), which you then split back out into the list. The issue there is that you're splitting on "," and not ", " (or trimming the result), giving you the extra space. Of course, the best solution isn't to change that; it's to deal directly with the list in the first place.
I assume you're doing the above because you just want the name of each attribute, not the list of actual attribute objects. Here's a Groovy-idiomatic way you can do that without any of the above: