Compare commits

..

1 Commits

6 changed files with 230 additions and 55 deletions

View File

@@ -0,0 +1,167 @@
/**
* Copyright 2015 SmartThings
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
metadata {
definition (name: "Sump Pump Controller", namespace: "fortrezz", author: "FortrezZ, LLC") {
capability "Sensor"
attribute "alarmState", "string"
attribute "powerAlert", "number"
attribute "sumpAlert", "number"
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x86,0x72,0x5A,0x85,0x59,0x73,0x20,0x7A,0x71"
}
simulator {
status "replace battery now": "command: 7105, payload: 00 00 00 FF 08 0B 00 00"
status "battery fully charged": "command: 7105, payload: 00 00 00 FF 08 0D 00 00"
status "AC mains disconnected": "command: 7105, payload: 00 00 00 FF 08 02 00 00"
status "AC mains reconnected": "command: 7105, payload: 00 00 00 FF 08 03 00 00"
status "Excess Pumping": "command: 7105, payload: 00 00 00 FF 09 03 01 01 00"
status "Float Error": "command: 7105, payload: 00 00 00 FF 09 03 01 02 00"
status "General Error": "command: 7105, payload: 00 00 00 FF 09 01 00 00"
}
tiles (scale: 2){
multiAttributeTile(name:"alert", type: "lighting", width: 6, height: 4){
tileAttribute ("device.alarm", key: "PRIMARY_CONTROL") {
attributeState("clear", label:"clear", icon:"http://swiftlet.technology/wp-content/uploads/2016/04/Ok-96.png", backgroundColor:"#ffffff")
attributeState("alert", label:"Alert!", icon:"http://swiftlet.technology/wp-content/uploads/2016/04/Error-96.png", backgroundColor:"#ff5b5b")
}
}
main "alert"
details(["alert"])
}
}
def parse(String description) {
def results = []
if (description.startsWith("Err")) {
results << createEvent(descriptionText:description, displayed:true)
} else {
def cmd = zwave.parse(description, [ 0x80: 1, 0x84: 1, 0x71: 2, 0x72: 1 ])
if (cmd) {
zwaveEvent(cmd, results)
}
}
log.debug "\"$description\" parsed to ${results.inspect()}"
return results
}
// Notification Report from SPM
def zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd, results) {
def powerAlert = device.currentValue("powerAlert")
if (powerAlert == null)
{
powerAlert = 0
}
def sumpAlert = device.currentValue("sumpAlert")
if (sumpAlert == null)
{
sumpAlert = 0
}
if (cmd.zwaveAlarmType == 8) {
if (cmd.zwaveAlarmEvent == 2) {
results << createEvent(name: "alarmState", value: "acMainsDisconnected", descriptionText: "AC Mains Disconnected")
powerAlert = powerAlert + 1
} else if (cmd.zwaveAlarmEvent == 3) {
results << createEvent(name: "alarmState", value: "acMainsReconnected", descriptionText: "AC Mains Reconnected")
powerAlert = powerAlert - 1
} else if (cmd.zwaveAlarmEvent == 11) {
results << createEvent(name: "alarmState", value: "replaceBatteryNow", descriptionText: "Replace AA batteries in Z-wave module")
powerAlert = powerAlert + 1
} else if (cmd.zwaveAlarmEvent == 13) {
results << createEvent(name: "alarmState", value: "batteryFullyCharged", descriptionText: "AA Batteries Replaced")
powerAlert = powerAlert - 1
}
if (powerAlert < 0)
{
powerAlert = 0
}
} else if (cmd.zwaveAlarmType == 9) {
if (cmd.zwaveAlarmEvent == 1) {
results << createEvent(name: "alarmState", value: "systemHardwareFailure", descriptionText: "Sump Pump Error, refer to Sump Pump Controller Unit")
sumpAlert = sumpAlert + 1
} else if (cmd.zwaveAlarmEvent == 3) {
if (cmd.eventParameter[0] == 0) {
results << createEvent(name: "alarmState", value: "alarmClear", descriptionText: "Alarm has been cleared")
sumpAlert = 0
} else if (cmd.eventParameter[0] == 1) {
results << createEvent(name: "alarmState", value: "floatError", descriptionText: "Pump or float problem")
sumpAlert = sumpAlert + 1
} else if (cmd.eventParameter[0] == 2) {
results << createEvent(name: "alarmState", value: "backupPumpActivated", descriptionText: "Backup pump was activated")
sumpAlert = sumpAlert + 1
} else if (cmd.eventParameter[0] == 3) {
results << createEvent(name: "alarmState", value: "highWater", descriptionText: "High water alarm")
sumpAlert = sumpAlert + 1
} else if (cmd.eventParameter[0] == 4) {
results << createEvent(name: "alarmState", value: "addWater", descriptionText: "Add distilled water to the battery")
sumpAlert = sumpAlert + 1
} else if (cmd.eventParameter[0] == 5) {
results << createEvent(name: "alarmState", value: "backupPumpError", descriptionText: "Backup pump is defective or not connected")
sumpAlert = sumpAlert + 1
} else if (cmd.eventParameter[0] == 6) {
results << createEvent(name: "alarmState", value: "9VBatteryLow", descriptionText: "9 volt battery is low or slide switch is OFF")
sumpAlert = sumpAlert + 1
} else if (cmd.eventParameter[0] == 7) {
results << createEvent(name: "alarmState", value: "12VBatteryError", descriptionText: "Problem with backup sump pump battery")
sumpAlert = sumpAlert + 1
} else if (cmd.eventParameter[0] == 8) {
results << createEvent(name: "alarmState", value: "checkCable", descriptionText: "Check your cable connection (to Z-wave module)")
sumpAlert = sumpAlert + 1
}
}
if (sumpAlert < 0)
{
sumpAlert = 0
}
}
if ((sumpAlert > 0) || (powerAlert > 0))
{
sendEvent(name: "alarm", value: "alert", displayed: false)
}
else
{
sendEvent(name: "alarm", value: "clear", displayed: false)
}
results
}
def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd, results) {
results << new physicalgraph.device.HubAction(zwave.wakeUpV1.wakeUpNoMoreInformation().format())
results << createEvent(descriptionText: "$device.displayName woke up", isStateChange: false)
}
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd, results) {
def map = [ name: "battery", unit: "%" ]
if (cmd.batteryLevel == 0xFF) {
map.value = 1
map.descriptionText = "$device.displayName battery is low!"
} else {
map.value = cmd.batteryLevel
}
results << createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.Command cmd, results) {
def event = [ displayed: false ]
event.linkText = device.label ?: device.name
event.descriptionText = "$event.linkText: $cmd"
results << createEvent(event)
}

View File

@@ -27,9 +27,13 @@ Works with:
## Device Health ## Device Health
Aeon Labs MultiSensor 6 is polled by the hub. Aeon Labs MultiSensor 6 is polled by the hub.
Aeon MultiSensor reports in once every hour. As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed.
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins.
Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for
the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row,
it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time.
* __122min__ checkInterval * __32min__ checkInterval
## Troubleshooting ## Troubleshooting

View File

@@ -130,13 +130,13 @@ metadata {
} }
def installed(){ def installed(){
// Device-Watch simply pings if no device events received for 122min(checkInterval) // Device-Watch simply pings if no device events received for 32min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
} }
def updated() { def updated() {
// Device-Watch simply pings if no device events received for 122min(checkInterval) // Device-Watch simply pings if no device events received for 32min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
log.debug "Updated with settings: ${settings}" log.debug "Updated with settings: ${settings}"
log.debug "${device.displayName} is now ${device.latestValue("powerSupply")}" log.debug "${device.displayName} is now ${device.latestValue("powerSupply")}"

View File

@@ -171,8 +171,8 @@ private Map getBatteryResult(rawValue) {
def pct = batteryMap[volts] def pct = batteryMap[volts]
result.value = pct result.value = pct
} else { } else {
def minVolts = 2.4 def minVolts = 2.1
def maxVolts = 2.7 def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts) def pct = (volts - minVolts) / (maxVolts - minVolts)
def roundedPct = Math.round(pct * 100) def roundedPct = Math.round(pct * 100)
if (roundedPct <= 0) if (roundedPct <= 0)

View File

@@ -274,7 +274,7 @@ private Map getBatteryResult(rawValue) {
result.value = pct result.value = pct
} else { } else {
def minVolts = 2.1 def minVolts = 2.1
def maxVolts = 2.7 def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts) def pct = (volts - minVolts) / (maxVolts - minVolts)
def roundedPct = Math.round(pct * 100) def roundedPct = Math.round(pct * 100)
if (roundedPct <= 0) if (roundedPct <= 0)

View File

@@ -15,9 +15,10 @@ metadata {
definition (name: "Z-Wave Thermostat", namespace: "smartthings", author: "SmartThings") { definition (name: "Z-Wave Thermostat", namespace: "smartthings", author: "SmartThings") {
capability "Actuator" capability "Actuator"
capability "Temperature Measurement" capability "Temperature Measurement"
capability "Relative Humidity Measurement"
capability "Thermostat" capability "Thermostat"
capability "Configuration" capability "Configuration"
capability "Refresh" capability "Polling"
capability "Sensor" capability "Sensor"
capability "Health Check" capability "Health Check"
@@ -116,7 +117,7 @@ metadata {
state "cool", label:'${currentValue}° cool', backgroundColor:"#ffffff" state "cool", label:'${currentValue}° cool', backgroundColor:"#ffffff"
} }
standardTile("refresh", "device.thermostatMode", inactiveLabel: false, decoration: "flat") { standardTile("refresh", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
state "default", action:"refresh.refresh", icon:"st.secondary.refresh" state "default", action:"polling.poll", icon:"st.secondary.refresh"
} }
main "temperature" main "temperature"
details(["temperature", "mode", "fanMode", "heatSliderControl", "heatingSetpoint", "coolSliderControl", "coolingSetpoint", "refresh"]) details(["temperature", "mode", "fanMode", "heatSliderControl", "heatingSetpoint", "coolSliderControl", "coolingSetpoint", "refresh"])
@@ -124,20 +125,13 @@ metadata {
} }
def installed(){ def installed(){
sendHubCommand(new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSupportedGet().format())) // Device-Watch simply pings if no device events received for 32min(checkInterval)
initialize() sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
} }
def updated(){ def updated(){
initialize()
}
def initialize() {
// Device-Watch simply pings if no device events received for 32min(checkInterval) // Device-Watch simply pings if no device events received for 32min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
unschedule()
runEvery5Minutes("refresh")
refresh()
} }
def parse(String description) def parse(String description)
@@ -155,7 +149,6 @@ def parse(String description)
] ]
if (map.name == "thermostatMode") { if (map.name == "thermostatMode") {
state.lastTriedMode = map.value state.lastTriedMode = map.value
map.data = [supportedThermostatModes:state.supportedThermostatModes]
if (map.value == "cool") { if (map.value == "cool") {
map2.value = device.latestValue("coolingSetpoint") map2.value = device.latestValue("coolingSetpoint")
log.info "THERMOSTAT, latest cooling setpoint = ${map2.value}" log.info "THERMOSTAT, latest cooling setpoint = ${map2.value}"
@@ -179,7 +172,6 @@ def parse(String description)
} }
} else if (map.name == "thermostatFanMode" && map.isStateChange) { } else if (map.name == "thermostatFanMode" && map.isStateChange) {
state.lastTriedFanMode = map.value state.lastTriedFanMode = map.value
map.data = [supportedThermostatFanModes: state.supportedThermostatFanModes]
} }
log.debug "Parse returned $result" log.debug "Parse returned $result"
result result
@@ -313,26 +305,26 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanMod
} }
def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSupportedReport cmd) { def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSupportedReport cmd) {
def supportedModes = [] def supportedModes = ""
if(cmd.off) { supportedModes << "off" } if(cmd.off) { supportedModes += "off " }
if(cmd.heat) { supportedModes << "heat" } if(cmd.heat) { supportedModes += "heat " }
if(cmd.cool) { supportedModes << "cool" } if(cmd.auxiliaryemergencyHeat) { supportedModes += "emergency heat " }
if(cmd.auto) { supportedModes << "auto" } if(cmd.cool) { supportedModes += "cool " }
if(cmd.auxiliaryemergencyHeat) { supportedModes << "emergency heat" } if(cmd.auto) { supportedModes += "auto " }
state.supportedThermostatModes = supportedModes state.supportedModes = supportedModes
sendEvent(name: "supportedThermostatModes", value: supportedModes, displayed: false) // No events to be generated, return empty map
return [:] return [:]
} }
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeSupportedReport cmd) { def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeSupportedReport cmd) {
def supportedFanModes = [] def supportedFanModes = ""
if(cmd.auto) { supportedFanModes << "auto" } // "fanAuto " if(cmd.auto) { supportedFanModes += "auto " } // "fanAuto "
if(cmd.circulation) { supportedFanModes << "circulate" } // "fanCirculate" if(cmd.low) { supportedFanModes += "on " } // "fanOn"
if(cmd.low) { supportedFanModes << "on" } // "fanOn" if(cmd.circulation) { supportedFanModes += "circulate " } // "fanCirculate"
state.supportedThermostatFanModes = supportedFanModes state.supportedFanModes = supportedFanModes
sendEvent(name: "supportedThermostatFanModes", value: supportedFanModes, displayed: false) // No events to be generated, return empty map
return [:] return [:]
} }
@@ -345,17 +337,15 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) {
} }
// Command Implementations // Command Implementations
def refresh() { def poll() {
def cmds = [] delayBetween([
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSupportedGet().format()) zwave.sensorMultilevelV3.sensorMultilevelGet().format(), // current temperature
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format()) zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format(),
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeGet().format()) zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format(),
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeGet().format()) zwave.thermostatModeV2.thermostatModeGet().format(),
cmds << new physicalgraph.device.HubAction(zwave.sensorMultilevelV2.sensorMultilevelGet().format()) // current temperature zwave.thermostatFanModeV3.thermostatFanModeGet().format(),
cmds << new physicalgraph.device.HubAction(zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format()) zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format()
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format()) ], 2300)
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format())
sendHubCommand(cmds)
} }
def quickSetHeat(degrees) { def quickSetHeat(degrees) {
@@ -426,14 +416,28 @@ def ping() {
poll() poll()
} }
def configure() {
delayBetween([
zwave.thermostatModeV2.thermostatModeSupportedGet().format(),
zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format(),
zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format(),
zwave.sensorMultilevelV3.sensorMultilevelGet().format(), // current temperature
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format(),
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format(),
zwave.thermostatModeV2.thermostatModeGet().format(),
zwave.thermostatFanModeV3.thermostatFanModeGet().format(),
zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format()
], 2300)
}
def modes() { def modes() {
return state.supportedThermostatModes ["off", "heat", "cool", "auto", "emergency heat"]
} }
def switchMode() { def switchMode() {
def currentMode = device.currentState("thermostatMode")?.value def currentMode = device.currentState("thermostatMode")?.value
def lastTriedMode = state.lastTriedMode ?: currentMode ?: ["off"] def lastTriedMode = state.lastTriedMode ?: currentMode ?: "off"
def supportedModes = getDataByName("supportedThermostatModes") def supportedModes = getDataByName("supportedModes")
def modeOrder = modes() def modeOrder = modes()
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] } def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
def nextMode = next(lastTriedMode) def nextMode = next(lastTriedMode)
@@ -450,7 +454,7 @@ def switchMode() {
} }
def switchToMode(nextMode) { def switchToMode(nextMode) {
def supportedModes = getDataByName("supportedThermostatModes") def supportedModes = getDataByName("supportedModes")
if(supportedModes && !supportedModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported" if(supportedModes && !supportedModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
if (nextMode in modes()) { if (nextMode in modes()) {
state.lastTriedMode = nextMode state.lastTriedMode = nextMode
@@ -462,9 +466,9 @@ def switchToMode(nextMode) {
def switchFanMode() { def switchFanMode() {
def currentMode = device.currentState("thermostatFanMode")?.value def currentMode = device.currentState("thermostatFanMode")?.value
def lastTriedMode = state.lastTriedFanMode ?: currentMode ?: ["off"] def lastTriedMode = state.lastTriedFanMode ?: currentMode ?: "off"
def supportedModes = getDataByName("supportedThermostatFanModes") ?: ["auto", "on"] def supportedModes = getDataByName("supportedFanModes") ?: "auto on" // "fanAuto fanOn"
def modeOrder = state.supportedThermostatFanModes def modeOrder = ["auto", "circulate", "on"] // "fanAuto", "fanCirculate", "fanOn"
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] } def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
def nextMode = next(lastTriedMode) def nextMode = next(lastTriedMode)
while (!supportedModes?.contains(nextMode) && nextMode != "auto") { // "fanAuto" while (!supportedModes?.contains(nextMode) && nextMode != "auto") { // "fanAuto"
@@ -474,7 +478,7 @@ def switchFanMode() {
} }
def switchToFanMode(nextMode) { def switchToFanMode(nextMode) {
def supportedFanModes = getDataByName("supportedThermostatFanModes") def supportedFanModes = getDataByName("supportedFanModes")
if(supportedFanModes && !supportedFanModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported" if(supportedFanModes && !supportedFanModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
def returnCommand def returnCommand