I wasn’t able to find a device that met all of my needs for a Smart Things compatible pool temperature so I built one using a Z-UNO and a custom device handler. It uses a Z-UNO, a DS18B20 Sensor and that’s about it hardware wise. I did 3D print a custom water proof enclosure but that isn’t necessary for it work. I also used heat shrink tubing around the sensor so that the chemicals inside of my pool wouldn’t corrode the sensor over time.
Arduino Code
#include <ZUNO_DS18B20.h>
// Define pin DS18B20 is connected to.
#define PIN_DS18B20 11
OneWire ow(PIN_DS18B20);
// Connect to OneWire temperature sensor.
DS18B20Sensor ds1820(&ow);
byte addr1[8];
// Temperature reading storage.
int temp;
ZUNO_SETUP_CHANNELS( ZUNO_SENSOR_MULTILEVEL(ZUNO_SENSOR_MULTILEVEL_TYPE_TEMPERATURE, 0, SENSOR_MULTILEVEL_SIZE_ONE_BYTE, SENSOR_MULTILEVEL_PRECISION_ZERO_DECIMALS, getterTemp) );
void setup() { pinMode(LED_BUILTIN, OUTPUT); }
void loop() {
ds1820.scanAloneSensor(addr1);
// Turn on status light.
digitalWrite(LED_BUILTIN, HIGH);
// Pull temperature from 18B20 sensors.
float temerature = ds1820.getTemperature(addr1);
// Set temperature in fahrenheit
temp = int(temerature*180+3200);
// Send data to hub
zunoSendReport(1);
// Turn off status light.
digitalWrite(LED_BUILTIN, LOW);
// Send every 30 seconds
delay(30000);
}
word getterTemp() {
return temp;
}
Device Handler
metadata {
definition (name: “Pool Temp”, author: “Buddy Edwards”) {
capability “Temperature Measurement”
}
simulator {
status "temperature report 70°F": zwave.sensorMultilevelV2.sensorMultilevelReport(scaledSensorValue: 70.0, precision: 1, sensorType: 1, scale: 1).incomingMessage()
}
tiles {
standardTile("refresh", "command.refresh", inactiveLabel: false,
decoration: "flat") {
state "default", label:'', action:"refresh.refresh",
icon:"st.secondary.refresh"
}
valueTile("temperature", "device.temperature", width: 3, height: 2,
canChangeIcon: true, inactiveLabel: true ) {
state("temperature", label:'${currentValue}°',
backgroundColors:[
[value: 84, color: "#00ace6"],
[value: 91, color: "#ff9900"],
[value: 96, color: "#db3d3d"]
]
)
}
main (["temperature"])
details (["temperature"])
}
}
def parse(String description) {
def result = null
def cmd = zwave.parse(description, [0x60: 3])
if (cmd) {
result = zwaveEvent(cmd)
log.debug “Parsed {cmd} to {result.inspect()}”
} else {
log.debug “Non-parsed event: ${description}”
}
result
}
// def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv1.SensorBinaryReport cmd)
//{
// // Version 1 of SensorBinary doesn’t have a sensor type
// createEvent(name:“sensor”, value: cmd.sensorValue ? “active” : “inactive”)
//}
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd)
{
def map = [ displayed: true, value: cmd.scaledSensorValue.toString() ]
switch (cmd.sensorType) {
case 1:
map.name = “temperature”
map.unit = cmd.scale == 0 ? “F” : “C”
break;
}
createEvent(map)
}
// def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd)
//{
// createEvent(name:“sensor”, value: cmd.value ? “active” : “inactive”)
//}
def zwaveEvent(physicalgraph.zwave.commands.associationv2.AssociationReport cmd) {
def result =
if (cmd.nodeId.any { it == zwaveHubNodeId }) {
result << createEvent(descriptionText: “device.displayName is associated in group {cmd.groupingIdentifier}”)
} else if (cmd.groupingIdentifier == 1) {
// We’re not associated properly to group 1, set association
result << createEvent(descriptionText: “Associating device.displayName in group {cmd.groupingIdentifier}”)
result << response(zwave.associationV1.associationSet(groupingIdentifier:cmd.groupingIdentifier, nodeId:zwaveHubNodeId))
}
result
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand([0x98: 1, 0x20: 1])
// can specify command class versions here like in zwave.parse
if (encapsulatedCommand) {
return zwaveEvent(encapsulatedCommand)
}
}
def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand([0x30: 1, 0x31: 1])
// can specify command class versions here like in zwave.parse
log.debug ("Command from endpoint ${cmd.sourceEndPoint}: ${encapsulatedCommand}")
if (encapsulatedCommand) {
return zwaveEvent(encapsulatedCommand)
}
}
def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiInstanceCmdEncap cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand([0x30: 1, 0x31: 1])
// can specify command class versions here like in zwave.parse
log.debug ("Command from instance ${cmd.instance}: ${encapsulatedCommand}")
if (encapsulatedCommand) {
return zwaveEvent(encapsulatedCommand)
}
}
def zwaveEvent(physicalgraph.zwave.Command cmd) {
createEvent(descriptionText: “{device.displayName}: {cmd}”)
}
def refresh() {
delayBetween([
zwave.sensorMultilevelV2.sensorMultilevelGet(sensorType:1, scale:1).format() // get temp in Fahrenheit
], 1200)
}
def poll() {
zwave.sensorMultilevelV2.sensorMultilevelGet(sensorType:1, scale:1).format()
}
//def configure() {
// delayBetween([
// zwave.configurationV1.configurationSet(parameterNumber:1, size:2, scaledConfigurationValue:100).format(),
// ])
//}
I figure this may help someone out as a starting point for their own pool temp monitor.