I am also based in the UK and used your very useful code for my heating system, now I am also having the same issue, where the thermostat will not change. I will also raise it with ST support
UK ST support have just got back to me. There wonāt look at the issue as itās an unsupported thermostat. Joy!
I got nearly the same response, but they also told me to log a ticket with the the developer contact centre and they maybe able to help us, they sent me the below link
https://support.smartthings.com/hc/en-us/requests/new?ticket_form_id=110843
Iāve just raised a ticket with them. Thanks for the pointer.
I need to find out if this affects the Danfoss TRV which also uses a V1 heating setpoint z-wave command!
Appears to have also broken the Danfoss TRV as well for the same reason.
For info, Iāve got a brand new hub running the following firmware
Firmware Version 000.016.00014
I have the same issue, ST will not update the physical device however Temperature reports to ST and local thermostat changes report back to ST.
(Still waiting the firmware update ā¦)
This might be a complete coincidence, but the timing looks to be the same:
Yes it does, so it looks like they have broken returning Z Wave commands with a response, as that is what I am doing:
// Battery powered devices can be configured to periodically wake up and
// check in. They send this command and stay awake long enough to receive
// commands, or until they get a WakeUpNoMoreInformation command that
// instructs them that there are no more commands to receive and they can
// stop listening.
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd)
{
def map = [name:āthermostatWakeUpā, value: ā${device.displayName} woke upā, isStateChange: true]
def event = createEvent(map)
def cmds = updateIfNeeded()
cmds << zwave.wakeUpV2.wakeUpNoMoreInformation().format()
log.debug "Wakeup $cmds"
[event, response(delayBetween(cmds, 1000))]
}
There is now a code discussion of the specifics of the problem that started a couple of days ago, including a possible workaround, in the following thread in the developers section of the forum:
Ah OK. The problem is that they appear to have broken the casting of HubMultiAction command list to a SendHubCommand.
I have managed to re-write the code to extract each cmd and send it with one SendHubCommand and it has finally workedā¦ but seriously poor testing by SmartThings devs
// Battery powered devices can be configured to periodically wake up and
// check in. They send this command and stay awake long enough to receive
// commands, or until they get a WakeUpNoMoreInformation command that
// instructs them that there are no more commands to receive and they can
// stop listening.
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd)
{
def map = [name:āthermostatWakeUpā, value: ā${device.displayName} woke upā, isStateChange: true]
def event = createEvent(map)
def cmds = updateIfNeeded()
cmds << zwave.wakeUpV2.wakeUpNoMoreInformation().format()
log.debug "Wakeup $cmds"
cmds.each
{ zwaveCmd ->
def hubCmd = []
hubCmd << response(zwaveCmd)
log.debug "HubCmds $hubCmd"
sendHubCommand(hubCmd, 1000)
log.debug "Sent hubcommand"
};
[event]
// [event, response(delayBetween(cmds, 1000))]
}
So if people edit the WakeUpNotifcationEvent code as above, then it should be a valid workaround
Thanks @meavydev really appreciate you sorting this.
Will you be updating your GitHub to reflect the change?
Iām not planning to update github, partly as SmartThings need to fix their code and partly because itās a pain to have to copy over to the US servers as no UK integrationā¦
After all, the code in github is correct and what SmartThings say you should do
All agreed. No arguments here.
Are you aware that GitHub integration is working for UK users? Itās been working for a while. It makes the DTH and SmartApp page load painful at times but itās working.
Thanks again for sorting. Iāve got 3 of Horstmann HRT4-ZW using your code controlling 3 heating zones. The fact the weather is fairly mild and your quick resolution have saved me some pain
GitHub integration doesnāt seem to be there for me in the IDE, but I am using Chrome on a Macbook.
I have no option to link to GitHub in the IDE and the documentation still says US only.
http://docs.smartthings.com/en/latest/tools-and-ide/github-integration.html?highlight=github
Glad I found something that helps, but it was lucky that SmartThings decided to break it in milder weather
The only positive aspect of this whole debacle is the weather!
Iām using OSX, from memory I enabled Github using Safari. It works well, like I said it just slows the load of the DTH page a little.
ST need to once again improve their release notification process. FW updates have been well publicised and handled much better than they have in the past (IMO). The fact theyāve changed something on the back end with no notification and broken what appears to be an increasing amount of devices will damage any of the good work done on the FW improvements. At the moment I donāt think theyāve even admitted thereās an issue, several people have contacted support only to be told that they wonāt support them because the device in question is not on the HCL.
It would just be nice if there was some commentary to acknowledge the fact there is an issue, known to be caused by a change implemented on what I believe was 19th April?
@Duncan are you tracking this issue and do you have any ETA for a fix?
Can any confirm that all is working as expected please?
I canāt seem to get my stat to heat?
Thanks
All I can say is itās fine for me, but my hub is still on 17.12 due to no internet when they did the last update, thanks to lightning taking out BT socket, router and 3G dongle backupā¦
Lucky old you, Iām sure thereās a few out there that would rather have no internet than FW18
Iāve just reset a couple of things and itās actually working. Apologies!
Another quick question for you if you have 5? Iām trying to get around an issue that Iām having in relation to the states that the DTH reports.
I want to create a state called:
capability.thermostatOperatingState
As documented here:
http://docs.smartthings.com/en/latest/capabilities-reference.html#thermostatoperatingstate
Would you have any idea how to create a state called operatingState in the DTH that was derived from the mode? So for:
mode=heat operatingstate=heating
mode=off operatingstate = idle
this would really help me out with ActionTiles if possible?
Cheers
P
Hi guys just got myself a ST hub and been hoping to get this all working with my Horstmann HRT4-ZW thermostat and receiver. Iāve read through all this and others post to learn how to publish handlers etcā¦ Iāve got both devices included in Hub and mostly working. The main issues I have are:
-
When setting a temp from the app it is not transferring to the device. I waited the 10mins as this is the default wakeup time. I even tried reducing this in the code for testing purposes with no joy. Temperature setting is reported back when I adjust setting on the device no problem. Used @meavydev DTH and also the his updated code to fix what a firmware update broke.
-
On the boiler receiver switch I can switch it on and off ok via the app but the status of the switch does not update unless I press the refresh button tile. Iām using @brumster modded boiler switch DTH.
Many thanks for your work on the code and any help to get this working world be much appreciated. I may be doing something wrong or missed something or is this broken again. My hub basically got the latest firmware update today so donāt know if this has broken anything again
Have you modified @meavydev 's code with the last update as shown couple of posts above? There was an ST release a couple of months ago that broke the code and the mod was required.
Mine are all working fine. My code pasted below. Let us know how you get on.
Befor you try this is the stat changing the actuator (boiler switch) from on to off? Make sure that all works before you pair the stat with ST.
Here the code (all credit goes to @meavydev).
/*
* SRT321 Thermostat by MeavyDev
* With support for an associated switch set by the SRT321 helper app
*/
metadata
{
definition (name: "SRT321 Thermostat", namespace: "meavydev", author: "MeavyDev")
{
capability "Actuator"
capability "Temperature Measurement"
capability "Thermostat"
capability "Configuration"
capability "Polling"
capability "Sensor"
command "switchMode"
command "quickSetHeat"
command "setTemperature"
command "setTempUp"
command "setTempDown"
command "setupDevice"
// fingerprint deviceId: "0x0800", inClusters: "0x25, 0x31, 0x40, 0x43, 0x70, 0x72, 0x80, 0x84, 0x85, 0x86, 0xef"
fingerprint deviceId: "0x0800"
fingerprint inClusters: "0x72,0x86,0x80,0x84,0x31,0x43,0x85,0x70,0x40,0x25"
}
// simulator metadata
simulator
{
}
tiles (scale: 2)
{
multiAttributeTile(name:"heatingSetpoint", type: "thermostat", width: 6, height: 4, canChangeIcon: true)
{
tileAttribute ("device.heatingSetpoint", key: "PRIMARY_CONTROL")
{
attributeState("default", unit:"dC", label:'${currentValue}Ā°')
}
tileAttribute("device.heatingSetpoint", key: "VALUE_CONTROL")
{
attributeState("VALUE_UP", action: "setTempUp")
attributeState("VALUE_DOWN", action: "setTempDown")
// attributeState("default", action: "setTemperature")
}
tileAttribute("device.temperature", key: "SECONDARY_CONTROL")
{
attributeState("default", label:'${currentValue} C', unit:"C")
}
tileAttribute("device.thermostatMode", key: "THERMOSTAT_MODE")
{
attributeState("off", label:'${name}')
attributeState("heat", label:'${name}')
}
tileAttribute("device.heatingSetpoint", key: "HEATING_SETPOINT")
{
attributeState("default", label:'${currentValue}', unit:"C")
}
tileAttribute("device.thermostatMode", key: "OPERATING_STATE")
{
attributeState("off", backgroundColor:"#44b621")
attributeState("heat", backgroundColor:"#ffa81e")
}
}
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2)
{
tileAttribute ("device.battery", key: "PRIMARY_CONTROL")
{
state "battery", label:'${currentValue}% battery', unit:""
}
}
standardTile("refresh", "device.thermostatMode", inactiveLabel: false, decoration: "flat", width: 2, height: 2)
{
state "default", action:"polling.poll", icon:"st.secondary.refresh"
}
standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat", width: 2, height: 2)
{
state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
}
}
main "heatingSetpoint"
details(["heatingSetpoint", "battery", "refresh", "configure", "temperature", "mode"])
preferences
{
input "userWakeUpInterval", "number", title: "Wakeup interval...", description: "Wake Up Interval (seconds)", defaultValue: 3600, required: false, displayDuringSetup: false
// This is the "Device Network Id" displayed in the IDE
input "userAssociatedDevice", "string", title:"Associated z-wave switch network Id...", description:"Associated switch ZWave network Id (hex)", required: false, displayDuringSetup: false
}
}
def parse(String description)
{
// log.debug "Parse $description"
def result = zwaveEvent(zwave.parse(description, [0x72:1, 0x86:1, 0x80:1, 0x84:2, 0x31:1, 0x43:1, 0x85:1, 0x70:1, 0xEF:1, 0x40:1, 0x25:1]))
if (!result)
{
log.warn "Parse returned null"
return null
}
// log.debug "Parse returned $result"
result
}
def updated()
{
log.debug "preferences updated"
state.configNeeded = true
}
def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev1.ThermostatModeSet cmd)
{
def map = [:]
switch (cmd.mode) {
case physicalgraph.zwave.commands.thermostatmodev1.ThermostatModeSet.MODE_OFF:
map.value = "off"
break
case physicalgraph.zwave.commands.thermostatmodev1.ThermostatModeSet.MODE_HEAT:
map.value = "heat"
}
map.name = "thermostatMode"
createEvent(map)
}
// Event Generation
def zwaveEvent(physicalgraph.zwave.commands.thermostatsetpointv1.ThermostatSetpointReport cmd)
{
def map = [:]
map.value = cmd.scaledValue.toString()
map.unit = cmd.scale == 1 ? "F" : "C"
map.displayed = false
switch (cmd.setpointType) {
case 1:
map.name = "heatingSetpoint"
break;
default:
return [:]
}
// So we can respond with same format
state.size = cmd.size
state.scale = cmd.scale
state.precision = cmd.precision
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv1.SensorMultilevelReport cmd)
{
def map = [:]
map.value = cmd.scaledSensorValue.toString()
map.unit = cmd.scale == 1 ? "F" : "C"
map.name = "temperature"
createEvent(map)
}
//OLD code - removed by PH
/*
// Battery powered devices can be configured to periodically wake up and
// check in. They send this command and stay awake long enough to receive
// commands, or until they get a WakeUpNoMoreInformation command that
// instructs them that there are no more commands to receive and they can
// stop listening.
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd)
{
def map = [name:"thermostatWakeUp", value: "${device.displayName} woke up", isStateChange: true]
def event = createEvent(map)
def cmds = updateIfNeeded()
cmds << zwave.wakeUpV2.wakeUpNoMoreInformation().format()
log.debug "Wakeup $cmds"
[event, response(delayBetween(cmds, 1000))]
}
*/
// New code added by PH
// Battery powered devices can be configured to periodically wake up and
// check in. They send this command and stay awake long enough to receive
// commands, or until they get a WakeUpNoMoreInformation command that
// instructs them that there are no more commands to receive and they can
// stop listening.
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd)
{
def map = [name:"thermostatWakeUp", value: "${device.displayName} woke up", isStateChange: true]
def event = createEvent(map)
def cmds = updateIfNeeded()
cmds << zwave.wakeUpV2.wakeUpNoMoreInformation().format()
log.debug "Wakeup $cmds"
cmds.each
{ zwaveCmd ->
def hubCmd = []
hubCmd << response(zwaveCmd)
log.debug "HubCmds $hubCmd"
sendHubCommand(hubCmd, 1000)
log.debug "Sent hubcommand"
};
[event]
// [event, response(delayBetween(cmds, 1000))]
}
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd)
{
def map = [ name: "battery", unit: "%" ]
if (cmd.batteryLevel == 0xFF)
{ // Special value for low battery alert
map.value = 1
map.descriptionText = "${device.displayName} has a low battery"
map.isStateChange = true
}
else
{
map.value = cmd.batteryLevel
log.debug ("Battery: $cmd.batteryLevel")
}
// Store time of last battery update so we don't ask every wakeup, see WakeUpNotification handler
state.lastbatt = new Date().time
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev1.ThermostatModeReport cmd)
{
def map = [:]
switch (cmd.mode) {
case physicalgraph.zwave.commands.thermostatmodev1.ThermostatModeReport.MODE_OFF:
map.value = "off"
break
case physicalgraph.zwave.commands.thermostatmodev1.ThermostatModeReport.MODE_HEAT:
map.value = "heat"
break
}
map.name = "thermostatMode"
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpIntervalCapabilitiesReport cmd)
{
def map = [ name: "defaultWakeUpInterval", unit: "seconds" ]
map.value = cmd.defaultWakeUpIntervalSeconds
map.displayed = false
state.defaultWakeUpInterval = cmd.defaultWakeUpIntervalSeconds
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpIntervalReport cmd)
{
def map = [ name: "reportedWakeUpInterval", unit: "seconds" ]
map.value = cmd.seconds
map.displayed = false
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd)
{
log.debug "Zwave event received: $cmd"
}
def zwaveEvent(physicalgraph.zwave.Command cmd)
{
log.warn "Unexpected zwave command $cmd"
delayBetween([
zwave.sensorMultilevelV1.sensorMultilevelGet().format(), // current temperature
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format(),
zwave.thermostatModeV1.thermostatModeGet().format(),
zwave.thermostatFanModeV3.thermostatFanModeGet().format(),
zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format()
], 1000)
}
// Command Implementations
def configure()
{
log.debug "configure"
state.configNeeded = true
// Normally this won't do anything as the thermostat is asleep,
// but do this in case it helps with the initial config
delayBetween([
zwave.thermostatModeV1.thermostatModeSupportedGet().format(),
zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format(),
// Set hub to get battery reports / warnings
zwave.associationV1.associationSet(groupingIdentifier:3, nodeId:[zwaveHubNodeId]).format(),
// Set hub to get set point reports
zwave.associationV1.associationSet(groupingIdentifier:4, nodeId:[zwaveHubNodeId]).format(),
// Set hub to get multi-level sensor reports (defaults to temperature changes of > 1C)
zwave.associationV1.associationSet(groupingIdentifier:5, nodeId:[zwaveHubNodeId]).format(),
// set the temperature sensor On
zwave.configurationV1.configurationSet(configurationValue: [0xff], parameterNumber: 1, size: 1).format()
], 1000)
}
def poll()
{
log.debug "poll"
// Normally this won't do anything as the thermostat is asleep,
// but do this in case it helps with the initial config
delayBetween([
zwave.sensorMultilevelV1.sensorMultilevelGet().format(), // current temperature
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: physicalgraph.zwave.commands.thermostatsetpointv1.ThermostatSetpointSet.SETPOINT_TYPE_HEATING_1).format(),
zwave.thermostatModeV1.thermostatModeGet().format(),
zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format()
], 1000)
}
def refresh()
{
log.debug "refresh"
state.refreshNeeded = true
// Normally this won't do anything as the thermostat is asleep,
// but do this in case it helps with the initial config
delayBetween([
zwave.sensorMultilevelV1.sensorMultilevelGet().format(), // current temperature
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: physicalgraph.zwave.commands.thermostatsetpointv1.ThermostatSetpointSet.SETPOINT_TYPE_HEATING_1).format(),
zwave.thermostatModeV1.thermostatModeGet().format(),
zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format()
], 1000)
}
def quickSetHeat(degrees)
{
setHeatingSetpoint(degrees)
log.debug("Degrees at quicksetheat: $degrees")
}
def setTempUp()
{
def newtemp = device.currentValue("heatingSetpoint").toInteger() + 1
log.debug "Setting temp up: $newtemp"
quickSetHeat(newtemp)
}
def setTempDown()
{
def newtemp = device.currentValue("heatingSetpoint").toInteger() - 1
log.debug "Setting temp down: $newtemp"
quickSetHeat(newtemp)
}
def setTemperature(temp)
{
log.debug "setTemperature $temp"
quickSetHeat(temp)
}
def setHeatingSetpoint(degrees)
{
setHeatingSetpoint(degrees.toDouble())
log.debug("Degrees at setheatpoint: $degrees")
}
def setHeatingSetpoint(Double degrees)
{
log.trace "setHeatingSetpoint($degrees)"
sendEvent(name: 'heatingSetpoint', value: degrees)
def deviceScale = state.scale ?: 1
def deviceScaleString = deviceScale == 2 ? "C" : "F"
def locationScale = getTemperatureScale()
def p = (state.precision == null) ? 1 : state.precision
def convertedDegrees
if (locationScale == "C" && deviceScaleString == "F")
{
convertedDegrees = celsiusToFahrenheit(degrees)
}
else if (locationScale == "F" && deviceScaleString == "C")
{
convertedDegrees = fahrenheitToCelsius(degrees)
}
else
{
convertedDegrees = degrees
}
log.trace "setHeatingSetpoint scale: $deviceScale precision: $p setpoint: $convertedDegrees"
state.deviceScale = deviceScale
state.p = p
state.convertedDegrees = convertedDegrees
state.updateNeeded = true
thermostatMode
}
private getStandardDelay()
{
1000
}
def updateIfNeeded()
{
def cmds = []
// Only ask for battery if we haven't had a BatteryReport in a while
if (!state.lastbatt || (new Date().time) - state.lastbatt > 24*60*60*1000)
{
log.debug "Getting battery state"
cmds << zwave.batteryV1.batteryGet().format()
}
if (state.refreshNeeded)
{
log.debug "Refresh"
cmds << zwave.sensorMultilevelV1.sensorMultilevelGet().format() // current temperature
cmds << zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: physicalgraph.zwave.commands.thermostatsetpointv1.ThermostatSetpointSet.SETPOINT_TYPE_HEATING_1).format()
cmds << zwave.thermostatModeV1.thermostatModeGet().format()
cmds << zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format()
cmds << zwave.thermostatModeV1.thermostatModeSupportedGet().format()
state.refreshNeeded = false
}
if (state.updateNeeded)
{
log.debug "Update"
cmds << zwave.thermostatSetpointV1.thermostatSetpointSet(setpointType: physicalgraph.zwave.commands.thermostatsetpointv1.ThermostatSetpointSet.SETPOINT_TYPE_HEATING_1, scale: state.deviceScale, precision: state.p, scaledValue: state.convertedDegrees).format()
state.updateNeeded = false
}
if (state.configNeeded)
{
log.debug "Config"
state.configNeeded = false
// Nodes controlled by Thermostat Mode Set - not sure this is needed?
cmds << zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format()
// Set hub to get battery reports / warnings
cmds << zwave.associationV1.associationSet(groupingIdentifier:3, nodeId:[zwaveHubNodeId]).format()
// Set hub to get set point reports
cmds << zwave.associationV1.associationSet(groupingIdentifier:4, nodeId:[zwaveHubNodeId]).format()
// Set hub to get multi-level sensor reports (defaults to temperature changes of > 1C)
cmds << zwave.associationV1.associationSet(groupingIdentifier:5, nodeId:[zwaveHubNodeId]).format()
// set the temperature sensor On
cmds << zwave.configurationV1.configurationSet(configurationValue: [0xff], parameterNumber: 1, size: 1).format()
log.debug "association $state.association user: $userAssociatedDevice"
int nodeID = getAssociatedId(state.association)
// If user has changed the switch association, send the new assocation to the device
if (nodeID != -1)
{
log.debug "Setting associated device $nodeID"
cmds << zwave.associationV1.associationSet(groupingIdentifier: 2, nodeId: nodeID).format()
}
def userWake = getUserWakeUp(userWakeUpInterval)
// If user has changed userWakeUpInterval, send the new interval to the device
if (state.wakeUpInterval != userWake)
{
state.wakeUpInterval = userWake
log.debug "Setting New WakeUp Interval to: " + state.wakeUpInterval
cmds << zwave.wakeUpV2.wakeUpIntervalSet(seconds:state.wakeUpInterval, nodeid:zwaveHubNodeId).format()
cmds << zwave.wakeUpV2.wakeUpIntervalGet().format()
}
cmds << zwave.thermostatModeV1.thermostatModeSupportedGet().format()
}
if (cmds.size() > 0)
{
cmds << "delay 2000"
}
cmds
}
private getUserWakeUp(userWake)
{
if (!userWake)
{
userWake = '3600' // set default 1 hr if no user preference
}
// make sure user setting is within valid range for device
if (userWake.toInteger() < 60)
{
userWake = '600' // 10 minutes - Mininum
}
if (userWake.toInteger() > 36000)
{
userWake = '36000' // 10 hours - Maximum
}
return userWake.toInteger()
}
// Get the Z-Wave Id of the binary switch the user wants the thermostat to control
// -1 if no association set
int getAssociatedId(association)
{
int associatedState = -1
int associatedUser = -1
log.debug "getAssociatedId $association"
if (association != null && association != "")
{
associatedState = association.toInteger()
log.debug "State $association $associatedState"
}
if (userAssociatedDevice != null && userAssociatedDevice != "")
{
try
{
associatedUser = Integer.parseInt(userAssociatedDevice, 16)
}
catch (Exception e)
{
}
log.debug "userDev $userAssociatedDevice $associatedUser"
}
// Use the app associated switch id if it exists, otherwise the device preference
return associatedState != -1 ? associatedState : associatedUser
}
// Called from the SRT321 App with the Z-Wave switch network ID
// How long before SmartThings realises that having device preferences
// with input "*" "capability.switch" is reasonable????
void setupDevice(value)
{
state.association = "$value"
int val = Integer.parseInt(value)
String hex = Integer.toHexString(val)
log.debug "Setting associated device Id $value Hex $hex"
settings.userAssociatedDevice = hex
state.configNeeded = true
}