ST Virtual Thermometer that reads a ThingSpeak channel

I’ve researched how to get this done and have played with various GitHub and WebCoRE ideas for way too much time, but I’m failing at this.

I want to use a virtual thermometer that obtains the current temperature from a ThingSpeak channel.

Is there a way to do this and I’m just somehow missing it or not understanding how to go about it? It seems like the http requests will only work if getting data from the ST hub.

You can write a Device Handler for yourself by using some of these examples. You need a simple http get, I believe.

Thank you for the reply. I saw that forum before but disregarded it because it ultimately talks about hub requests, but a link in there did end up getting me started on the right track! I’m not new to making and coding stuff (websites, arduino smart stuff), but SmartThings, handlers, groovy, and http requests are all new to me. I’ve been on a massive learning curve the last few days!

I took another crack at editing a few virtual temperature sensor and thermostat device handlers, and I eventually ended up getting one to sort of work! I used SmartThings-VirtualThermostat-WithDTH by Elliot Stocker.

I still have questions though…

In the device handler code, I only changed the “def getTemperature()” to the following:

def getTemperature() {
   try {
       httpGet(uri: "https://api.thingspeak.com/channels/904289/fields/1.json?results=1") {resp ->
        // get the data from the response body
        log.debug "response data: ${resp.data}"
        log.debug "temperature: ${resp.data.feeds.field1}"
        return "${resp.data.feeds.field1}"
       }
   } catch (e) {
           log.debug "something went wrong: $e"
   }
}

Sorry if this isn’t posted properly - I see code inside boxes for forums, but I don’t know how to do that.

Note: Using “def params” returned an error, and I found a thread stating that certain characters, like “?” and “=” distorted the url. Plugging it in after httpGet fixed the issue.

Also Note: Safari failed to log anything, which made the simulator appear worthless until I switched to Chrome.

I played with several lines to return the value and update the temperature. The problem I’m having here is that the value is updated with brackets, like this: [72.3]. Any thoughts on how to get rid of this? I’m also not sure this is the best way to return the value. I thought I’d be updating a variable. Let me know if there’s a better way, but this does work…

The other problem is that the thermostat is not updating unless I hit refresh. I’m assuming this needs to be fixed in the App code and may have something to do with the fact that I haven’t properly attached a sensor device as the app expects. I’ll delve into that later and share again, unless someone beats me to the punch.

Searh for the SmartThings public github repo. In there you will find the Smart Weather tile DH. It is basically a DH which utilizes WeatherUnderground through the implemented API, but it has an example to update the values periodically. It is probably a runIn(), but I am not sure. You need it to read your source time to time, without hitting the refresh. It will run a refresh for you.
To post code here you need to paste the code in the text box, select it and press the formatting icon on the top </> That will put the text into a code box. (You can edit your previous post too.)
What you really need in your DH is a big tile with the temperature, a single refresh button and the code to get the temperature value from the http get to the temperature variable, what is part of the Temperature measurement capability.
Dig into the Smartthings Classic documentation how to write a Device Handler. It is really easy. It has a lot of examples.
You can search on google how to remove , there are a lot of good sources of groovy code out there. You might going to find the same question on stackexchange or some other Q/A site.

SUCCESS!!

Yes, a google search quickly solved the bracket problem. Just added [0] as seen in the code I pasted below. I tried several strategies in the device handler to get it to refresh, but none of them worked. Creating a custom piston in WebCoRE solved it, though. I have it refreshing every 30 seconds. I did find that WU code and used it as a reference:)

I wrote a new Device Handler so that it works as a stand-alone Virtual ThingSpeak Sensor. If anyone else wants to use it, just replace my ThingSpeak uri with your own. I expect it will be easy enough to customize for humidity, lux levels, or whatever else you might be tracking on a ThingSpeak-like service rather than on a hub.

Also, I tested this as my sensor device with the SmartThings-VirtualThermostat-WithDTH thermostat, and it is working perfectly, updating the temperature in that app immediately after my Virtual ThingSpeak Sensor updates (I initially tried seeing the the thermostat would regularly refresh on it’s own before jumping into WebCoRE, but it didn’t)

Here’s the device handler code:

metadata {
	definition (name: "Virtual ThingSpeak Sensor", namespace: "MN", author: "Michelle N.") {
		capability "Refresh"
		capability "Sensor"
		capability "Temperature Measurement"
		capability "Health Check"

		command "refresh"
        command "poll"
		command "setVirtualTemperature", ["number"]

		attribute "temperatureUnit", "string"
		attribute "debugOn", "string"

	}

	simulator {
		// TODO: define status and reply messages here
	}

	tiles(scale: 2) {
		valueTile("temperature", "device.temperature", width: 2, height: 2, decoration: "flat") {
			state("temperature", label:'${currentValue}°', unit: unitString(),
				backgroundColors: getTempColors())
		}
		standardTile("refresh", "device.refresh", width:2, height:2, decoration: "flat") {
			state "Refresh", action:"refresh.refresh", icon:"st.secondary.refresh"
		}
	}
	/*preferences {
		input "resetHistoryOnly", "bool", title: "Reset History Data", description: "", displayDuringSetup: false
		input "resetAllData", "bool", title: "Reset All Stored Event Data", description: "", displayDuringSetup: false
	}*/
}

def shouldReportInCentigrade() {
    //there is no way to do this dynamically right now, a number of the functions that call this function are compile time evaluated :(
	return false //Set this to true for Centigrade, false for Fahrenheit  so that enums and colors are correct (due to ST issue of compile time evaluation)
	/*try {
    	def ts = getTemperatureScale();
    	retVal = ts == "C"
    } finally {
		return retVal
    }*/
}

def installed() {
    initialize()
}

def configure() {
    initialize()
}

def initialize() {
	state.tempScale = "F"
}

def getTempColors() {
	def colorMap
		colorMap = [
			// Set your own Color Range based on C/F and personal comfort ranges
			[value: 40, color: "#153591"],
			[value: 68, color: "#1e9cbb"],
			[value: 70, color: "#90d2a7"],
			[value: 73, color: "#44b621"],
			[value: 76, color: "#f1d801"],
			[value: 80, color: "#d04e00"],
			[value: 90, color: "#bc2323"]
		]
}

def unitString() {  return shouldReportInCentigrade() ? "°C": "°F" }
def defaultTemp() { return shouldReportInCentigrade() ? 20 : 70 }

def getTemperature() {
   try {
       httpGet(uri: "https://api.thingspeak.com/channels/******/fields/1.json?results=1") {resp ->
        // get the data from the response body
        // log.debug "response data: ${resp.data}"
        log.debug "temperature: ${resp.data.feeds.field1[0]}"
        return "${resp.data.feeds.field1[0]}"
       }
   } catch (e) {
           log.debug "something went wrong: $e"
   }
}

def ping() {
    log.trace "Executing ping"
    refresh()
}
def parse(data) {
    log.debug "parse data: $data"
}

def refresh() {
    log.trace "Executing refresh"
    sendEvent(name: "temperature", value: getTemperature(), unit: unitString())
}

def poll() {
	refresh()
}

def setVirtualTemperature(temp) {
	sendEvent(name:"temperature", value: temp, unit: unitString(), displayed: false)
}

def setTemperatureScale(val) {
	log.debug "set temp scale to: $val"
	sendEvent(name:"tempScale", value: val, displayed: false)
}

Here’s my code in WebCoRE:

/**************************************************************/
/* Refresh Temperature                                        */
/**************************************************************/
/* Author     : foxymichelle                                  */
/* Created    : 11/13/2019, 8:31:21 AM                        */
/* Modified   : 11/13/2019, 8:32:15 AM                        */
/* Build      : 1                                             */
/* UI version : v0.3.110.20191009                             */
/**************************************************************/
 
execute
 
every 30 seconds
do
with
Virtual ThingSpeak Sensor
do
  Refresh;
end with;
end every;
end execute;

Now it’s time for me to start building some more smart crap for my otherwise stupid home!

2 Likes

Look at the new Smartthings developer documentation. That is the way forward with the new app. It has a nice examples with the new MQTT integration to SmartThings. There is an example project for an ESP device as a switch. You can redevelop your device with direct integration into Smartthings if you want. :wink:

By the way this is how it refreshes every 30 minutes in the DH.

def installed() {
    poll()
    runEvery30Minutes(poll)
}

def updated() {
    poll
}

def uninstalled() {
    unschedule()
}

You need to use this too, to make sure to delete the polling when you remove the DH.

I tried the run every x minutes code as you have it, but it wouldn’t work… in the simulator. I just tried again, published and made a new device. It works on the device. Perfect for anyone to throw into the code if they don’t want the hassle of WebCoRE, or to use WebCoRE if you need updates more frequently than every minute.

Thanks for the pointers on adding updated and installed events. I guess in WebCoRE I’d also want to have it run poll rather than refresh.

It doesn’t make a difference. It is just how you named your functions. Poll runs only refresh in your code. I think in the other code refresh calls poll and poll has all the real functionality to run the http get command.
For groovy it worth to read along the Smartthings classic documentation. You will understand the structure of the code better. There are examples for all functions. I started originally from there and picked up the rest of it from other codes.
You don’t really need temperature updates more than a minute, only if your temperature changes really quick. Smartthings by default is not even logging any events if the value is not changing ( state change is false). You might going to realize that your logged events are minutes apart, when the temperature has really changed.
How often is your real device updates Thingspeak? You should match your refresh rate with that.