Secure Together SRT321/SSR303 or Horstmann ASR-ZW/HRT4-ZW (Updated DTH in post 61)

To control my water heater at home, I have installed a thermostat controllable by Z-Wave:

  • the SSR303 or HRT4-ZW (they changed the name) connected to the water heater itself
  • the SRT321 or ASR-ZW that I fixed somewhere on the wall

=> the both communicate using Z-Wave and can be controller by controllers like Micasaverde

=> has anyone tried to integrate them within Smartthings?

Just received a SRT321 & it doesn’t appear on a ST device search. Researching the possibility of getting it to work with ST.

Any luck with this? Hoping that if the SRT321 works, the SRT323 will too…

Cheers,
Chris.

i have just bought the SIR321 so will be getting this working hopefully. i assume if I get this working it should be possible to get the other working. It will talk very similarly.

I can certainly get the SRT321 recognised by SmartThings.
I just used the Z-Wave Thermostat as a device handler template and edited out the fan related settings.
Then I put the SRT321 into installer mode, via the DIP switch, and turned to L and pressed with SmartThings in Connect New Device mode. The SRT321 was recognised and displayed LP and SmartThings added it as a new device. Then I switched the DIP out of installer mode.
I haven’t done much with it, as I want to add and associate a TZ88 in Group 2. If I can get this working then I can use SmartThings to replace the Vera Edge that I am currently using (which keeps going wrong).

OK I have got something working.
There is still some sort of problem after you do the “Configure New Device”, as it shows up as a Thing, so I had to change the device to SRT321 Thermostat in the IDE, but it seems to be mostly working.
I have added a helper app to select a switch to be controlled by the thermostat… sore point as I maintain that SmartThings are wrong in their design and devices should be able to control / select other devices in their preferences…

/* SRT321 Device Handler by MeavyDev */
metadata
{
definition (name: “SRT321 Thermostat”, namespace: “meavydev”, author: “MeavyDev”)
{
capability "Temperature Measurement"
capability "Thermostat"
capability "Configuration"
capability "Polling"
capability “Sensor”

	command "switchMode"
    command "quickSetHeat"
	command "setTemperature"
    
	command "setupDevice" 
    
	fingerprint deviceId: "0x0800"
	fingerprint inClusters: "0x72, 0x86, 0x80, 0x84, 0x31, 0x43, 0x85, 0x70, 0xEF, 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("default", action: "setTemperature")
        }
        
        tileAttribute("device.temperature", key: "SECONDARY_CONTROL") 
        {
        	attributeState("default", label:'${currentValue}', unit:"dC")
        }
        
        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:"dC")
		}
        
        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
{
section (“Wakeup interval…”)
{
input “userWakeUpInterval”, “number”, title: “Wake Up Interval (seconds)”, description: “Default 3600 sec (10 minutes - 7 days)”, defaultValue: ‘3600’, required: false, displayDuringSetup: true
}

// This is the "Device Network Id" displayed in the IDE
section ("Associated z-wave switch network Id...")
{
	input "userAssociatedDevice", "string", title: "Associated switch ZWave network Id (hex)", required: false, displayDuringSetup: true
}

}

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 configure()
{
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)
}

// 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))]      

}

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”
}

// Command Implementations
def poll()
{
// state.refreshNeeded = true
}

def refresh()
{
state.refreshNeeded = true
}

def quickSetHeat(degrees)
{
setHeatingSetpoint(degrees, 1000)
log.debug(“Degrees at quicksetheat: $degrees”)
}

def setTempUp()
{
def newtemp = device.currentValue(“heatingSetpoint”).toInteger() + 1
log.debug "Setting temp up: $newtemp"
sendEvent(name: ‘heatingSetpoint’, value: newtemp)
quickSetHeat(newtemp)
}

def setTempDown()
{
def newtemp = device.currentValue(“heatingSetpoint”).toInteger() - 1
log.debug "Setting temp down: $newtemp"
sendEvent(name: ‘heatingSetpoint’, value: newtemp)
quickSetHeat(newtemp)
}

def setTemperature(temp)
{
log.debug "setTemperature $temp"
sendEvent(name: ‘heatingSetpoint’, value: temp)

quickSetHeat(temp)

}

def setHeatingSetpoint(degrees, delay = 30000)
{
setHeatingSetpoint(degrees.toDouble(), delay)
log.debug(“Degrees at setheatpoint: $degrees”)
}

def setHeatingSetpoint(Double degrees, Integer delay = 30000)
{
log.trace "setHeatingSetpoint($degrees, $delay)"
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
}

/* SRT321 Helper App */
definition(
name: “SRT321 App”,
namespace: “meavydev”,
author: “MeavyDev”,
description: “Associates the SRT321 with a switch”,
category: “My Apps”,
iconUrl: “https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png”,
iconX2Url: “https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png”)

preferences
{
input “associatedSwitch”, “capability.switch”, title: “Switch?”, multiple: false, required: true
input “thermostatSelected”, “capability.thermostat”, title: “SRT321 Thermostat?”, multiple: false, required: true
}

def installed()
{
def networkId = " “+associatedSwitch.deviceNetworkId+” “
def devId = Integer.parseInt(networkId.replaceAll(” ", “”), 16)

log.debug "Installed - Id $devId"
state.association = devId

runIn(1, handler)

}

def updated()
{
def networkId = " “+associatedSwitch.deviceNetworkId+” “
def devId = Integer.parseInt(networkId.replaceAll(” ", “”), 16)

log.debug "Updated - Id $devId"
state.association = devId.toString()

runIn(1, handler)

}

// No real need for a handler, but this was avoiding crashes
// Send the “Device Network Id” to the SRT321 thermostat so it can control the switch
// How long before SmartThings realises that having device preferences
// with input “*” “capability.switch” is reasonable???
def handler()
{
log.debug “Setting thermostat association”

thermostatSelected.setupDevice(state.association)

}

Does it work well?

Could you put the handler on Github?

It is reporting temperature and setpoint changes; the associated switch is being turned on and off based on temp; the wakwup time iconfig change works… so pretty good.
Unfortunately github integration doesn’t seem to exist for me, as I am in the UK on the server https://graph-eu01-euwest1.api.smartthings.com , No signs of the enable github integration buttons…

Mmm looks like Github is only for US… so I have logged into the US and copied the files across… pain in the a*s… when will US companies realise there is a world outside the US.

So the files should be OK, but are copied across:

SRT321 Helper App

SRT321 Device Handler

1 Like

Great :slight_smile: Ill try these when Im back to France.

I think I have a work around for the initial configuration showing as a “Thing”.
Basically I think you have to do some z-wave commands in the configure and/or poll or it fails to configure correctly in SmartThings.
These changes are pushed to github.

I have also added a Parent/Child app to allow me to schedule multiple set point changes:

Scheduler Parent App

Schedule Child App

Great that I finally found someone with one of these.

I will be installing your code shortly after my temp probe turns up.

Can I ask a favour. Could you add a function to allow you to turn on the device for X length of time. maybe have an option to choose 1 hour, 2 hour or something like that.

Thanks

I am not sure what you mean, as the SRT-321 doesn’t have this config option as far as I am aware.
If you are doing this for another device type, then I think you need to create your own.
The SRT321 is what controls the switch, as it is via Z-Wave association.
My device driver isn’t involved at all. It is used for things like the temperature set point; updating the wakeup interval; battery status; current temperature…

My bad. rush reading. I have the SIR.

It uses both a temp and timer option. I have struggled getting it to communicate to the detailed functions. But then I’m not a programmer and I think its just me.

No worries.

Hi there,

So here’s my failings. I’m a newbie and I don’t code! I am however, keen to learn and take advantage of the ST which I’ve had for a few days now. Following the usual testing with lights etc I’m keen to try something a little more complicated. That would be the replacement of a Honeywell CM927 stat with something that can be controlled with ST. I’m looking at one of these:

Looking through this thread it looks like you’ve had some success in doing just that! Given the amount of thermostats on the ST UK compatibility list (none) that’s great news!!!

So, here’s the question. Would one of you be willing to give me an idiots guide for enabling the Helper App and the Device Handler on my ST? I’ve logged into IDE so I have the basics, I just need a steer!

Thanks in advance!

Paul

I’m afraid I’m not going to be much help with a guide, as I’m a software developer, so things I think are obvious probably aren’t to others. Perhaps someone else will be able to though.
All I can say is I followed the ST guides on installing device handlers and apps in the IDE and then I can select the app from my phone.
You might need to change the device handler in the IDE once the thermostat has been discovered, but again, this is standard ST, not something I’ve created.
The helper app is very simple, just letting you choose the switch to associate.

1 Like

Thanks. I realised last night that there must be a basic guide somewhere. Hopefully I’ll get my hands on a stat in a week or so and be able to test then.

Cheers.

@meavydev any chance of a bit if guidance? I’ve managed to get hold of one of these, i’m doing some testing with it (not wired to the heating system) but powered up and running.

If I change the physical thermostat the receiver kicks in and the app updates. All good! Battery is reporting at 100% and the temperature is being fed from the stat to the app.

Here’s my cry for help. If I change the temp in the app nothing happens on the physical thermostat. Any ideas?

The default wake up time is 30mins, so unless you changed this in the settings it will take up to 30mins for the thermostat to update after an app change.

Perfect.

So am I right in thinking that if update this to 10 seconds (for testing) I have to wait 30 minutes for the device to wake up and get the new config?