How can I know which states a zwave thermostat exposes?

This goes back to a question someone else asked some time ago: “Is there a way to get the relative humidity value from a 2gig CT100 thermostat”.
More recently I was wondering “is there a way to get the battery level information from a 2gig CT100 thermostat”.

And I don’t know how to get a definite answer to those questions; I tried asking the manufacturer directly, but I got no answer… Is there a way to tell? And, if the answer is yes for some of them (apparently folks on the HomeSeer board claim HS2 did retrieve relative humidity from the CT100 - which they then broke somehow in HS3…), how can I actually get those values?

I’m sure this question is a mix of ignorance about ST’s device types and zwave; but I’m trying to learn more.


If you go to “My Devices” in the IDE and look at your thermostat (I have the CT100), you’ll see this raw description:
0 0 0x0806 0 0 0 f 0x20 0x81 0x87 0x72 0x31 0x40 0x42 0x44 0x45 0x43 0x86 0x70 0x80 0x85 0x60

The zwave command classes are:

Here is the SmartThings documentation that teaches you how to parse these commands and generate events from them:

As you can see, the CT100 does report the battery level (0x80). Retrieving the battery level is a bit tricky. Not sure if the CT100 is a sleepy device in the traditional sense, i.e. the zwave radio will be turned off most of the time, and the device will not respond to your battery level query unless it’s woken up coincidentally. Usually you can listen to the COMMAND_CLASS_WAKE_UP (0x84) command, then send your query, before the device goes back to sleep. You’d have to play around with the device handler for a bit to figure out whether it will send the wakeup command, or if there is a better time for querying the batter level. Maybe someone with more knowledge about writing device handlers can shed some light on this issue.

The relative humidity (along with the temperature) would come in through the multilevel report (0x31). The command has a sensorType attribute, which in the case of the humidity measurement will be set to 0x05 (see the link to the documentation above). I am pretty sure the CT100 does NOT report humidity, but hopefully I am wrong. Let me know what you can find out.

1 Like

OK, this is a lot of useful information, thanks!
Now, looking at the code for the Z-Wave thermostat, I see the parse() function does:
def map = createEvent(zwaveEvent(zwave.parse(description, [0x42:1, 0x43:2, 0x31: 3])))

So, based on what I understand now (which isn’t a lot, admittedly), that’s checking for Thermostat Operating State, Setpoint and Sensor Multilevel (temperature, basically); I suppose that the createEvent(zwaveEvent()) functions transform the command returned by zwave.parse() into an event that is then handled by callback functions like:
def zwaveEvent(physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpointReport cmd)
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv3.SensorMultilevelReport cmd)
def zwaveEvent(physicalgraph.zwave.commands.thermostatoperatingstatev1.ThermostatOperatingStateReport cmd)

Am I on the right track? Is there anything else I should read instead of trying to guess?

Anyway, assuming I’m on the right track, next questions are:

  • I see more callback functions for events (like zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport cmd), which seems related to command 0x40); but I don’t see those commands (0x40, for example) referenced anywhere; how are they triggered?
  • Is the parse() function in the device handler invoked any time data is received from the device?
  • Should I expect the battery level being reported through the parse() function? So, if I were to add 0x80:1 to the parse() parameters, should I be able to get a battery level event?
  • You talk about a “battery level query” I should submit (with all the considerations about the device “sleepy state”); but how do I do that? In the Aeon multi-sensor device handler I see:
    def configure() {

    // send all data (temperature, humidity, illuminance & battery) periodically
    zwave.configurationV1.configurationSet(parameterNumber: 101, size: 4, scaledConfigurationValue: 225).format(),

But what does that really mean? What is referring to a battery there, and what are the available parameters there?

Partial success!
[Test1] Parse returned [[name:battery, unit:%, value:78, isStateChange:true, displayed:true, linkText:Downstairs, descriptionText:Downstairs battery is 78%]]

To do this I did:
#1 added “zwave.batteryV1.batteryGet().format()” to the poll() command
#2 added “0x80:1” to the zwave.parse() parameters
#3 added a “zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd)” event handler

I’m not entirely positive #3 is needed, but I would hope it is.

So, that part seems to work well, even if I got there very tentatively.
No progress on the relative humidity front; the default zwave thermostat handler already issues a zwave.sensorMultilevelV3.sensorMultilevelGet() request in poll(); and it processes 0x31:3 in parse(); I tried changing the handler code into the code below, as used in the Aeon multi-sensor device type, but I never get humidity readings…

def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv3.SensorMultilevelReport cmd)
def map = [:]
switch (cmd.sensorType) {
case 1:
// temperature
map.value = cmd.scaledSensorValue.toString()
map.unit = cmd.scale == 1 ? “F” : "C" = "temperature"
case 5:
// humidity
map.value = cmd.scaledSensorValue.toInteger().toString()
map.unit = "%" = "humidity"

@minollo - That’s great progress, and yes you are very much on the right track. All your assumptions are true.

To answer your question: If a command class is omitted in the map given to zwave.parse, then the latest version of that command class will be used. You still have to add the correct overload to zwaveEvent, with the right command class as the argument type. Otherwise, the catch-all handler (the zwaveEvent overload with the generic command class) will handle the event and not do anything with it.

As for querying the battery level: Make sure NOT to send that query every time poll() is being called. The interval of the poll() method is in the many-times-per-hour range. The common pattern here is to store the last time you queried the battery level in the state map:

def nowTime = new Date().time
state.lastQuery = nowTime

Then, only query again, if enough time has passed:

def nowTime = new Date().time
if (nowTime - state.lastQuery > some_interval_in_milliseconds) {
   state.lastQuery = nowTime // just queried!

You should probably only query once every 24 or 48 hours. Otherwise, you’d deplete the battery just by querying for the level :wink:

Thanks; a very good hint; I’ve added that behavior.

I’ve also been spending some more time yesterday night on the relative humidity measurement, but with no success. I can only assume that if the RH measurement is returned by the CT-100, it is not returned through the “sensor multilevel” command, but through something more esoteric. Too bad the 2gig folks are entirely oblivious to any request for information…

If 2gig is non-responsive, I wonder if we can ask someone at Homeseer about how they were able to retrieve the humidity levels. In another thread, I noticed the mention that there was a specific bug fix for it: “Fixed 2GIG CT100 thermostat not displaying humidity value”

Sure, that may be worth a try.
Logically RH values should be part of the multilevel sensor command (device type 5); but ct100 always only returns values for device type 1 (temperature). This page ( lists all the ct100 command classes; I tried all of them to see if RH value was hiding somewhere… no luck.