Compare commits

..

5 Commits

Author SHA1 Message Date
Garth Williams
62a264bbac MSA-2129: A device handler for the Devolo Humidity Sensor 2017-07-25 15:00:44 -07:00
Jack Chi
47eedf5770 Merge pull request #2170 from pchomal/hc_changes
[DHF-14][DHF-15] Updated comments & rectified typo
2017-07-25 12:25:38 +05:30
Luke Ness
3d0dbc57a1 Merge pull request #2100 from gausnes/ZwaveSirenFix
BatteryGet has a format method not property
2017-07-24 16:05:36 -05:00
piyush.c
f96ae94d12 [DHF-14][DHF-15] Health Check Aeon Key Fob and Aeon Minimote 2017-07-19 14:18:32 +05:30
Luke Ness
5e787dd0e3 BatteryGet has a format method not property 2017-06-19 11:59:13 -05:00
5 changed files with 193 additions and 445 deletions

View File

@@ -1,439 +0,0 @@
/**
* FortrezZ Flow Meter Interface
*
* Copyright 2016 FortrezZ, LLC
*
* 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: "FortrezZ Flow Meter Interface", namespace: "fortrezz", author: "Daniel Kurin") {
capability "Battery"
capability "Energy Meter"
capability "Image Capture"
capability "Temperature Measurement"
capability "Sensor"
capability "Water Sensor"
attribute "gpm", "number"
attribute "cumulative", "number"
attribute "alarmState", "string"
attribute "chartMode", "string"
attribute "lastThreshhold", "number"
command "chartMode"
command "zero"
command "setHighFlowLevel", ["number"]
fingerprint deviceId: "0x2101", inClusters: "0x5E, 0x86, 0x72, 0x5A, 0x73, 0x71, 0x85, 0x59, 0x32, 0x31, 0x70, 0x80, 0x7A"
}
simulator {
// TODO: define status and reply messages here
}
preferences {
input "gallonThreshhold", "decimal", title: "High Flow Rate Threshhold", description: "Flow rate (in gpm) that will trigger a notification.", defaultValue: 5, required: false, displayDuringSetup: true
input("registerEmail", type: "email", required: false, title: "Email Address", description: "Register your device with FortrezZ", displayDuringSetup: true)
}
tiles(scale: 2) {
carouselTile("flowHistory", "device.image", width: 6, height: 3) { }
valueTile("battery", "device.battery", inactiveLabel: false, width: 2, height: 2) {
state "battery", label:'${currentValue}%\nBattery', unit:""
}
valueTile("temperature", "device.temperature", width: 2, height: 2) {
state("temperature", label:'${currentValue}°',
backgroundColors:[
[value: 31, color: "#153591"],
[value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"],
[value: 95, color: "#d04e00"],
[value: 96, color: "#bc2323"]
]
)
}
valueTile("gpm", "device.gpm", inactiveLabel: false, width: 2, height: 2) {
state "gpm", label:'${currentValue}gpm', unit:""
}
standardTile("powerState", "device.powerState", width: 2, height: 2) {
state "reconnected", icon:"http://swiftlet.technology/wp-content/uploads/2016/02/Connected-64.png", backgroundColor:"#cccccc"
state "disconnected", icon:"http://swiftlet.technology/wp-content/uploads/2016/02/Disconnected-64.png", backgroundColor:"#cc0000"
state "batteryReplaced", icon:"http://swiftlet.technology/wp-content/uploads/2016/04/Full-Battery-96.png", backgroundColor:"#cccccc"
state "noBattery", icon:"http://swiftlet.technology/wp-content/uploads/2016/04/No-Battery-96.png", backgroundColor:"#cc0000"
}
standardTile("waterState", "device.waterState", width: 2, height: 2, canChangeIcon: true) {
state "none", icon:"http://cdn.device-icons.smartthings.com/Weather/weather12-icn@2x.png", backgroundColor:"#cccccc", label: "No Flow"
state "flow", icon:"http://cdn.device-icons.smartthings.com/Weather/weather12-icn@2x.png", backgroundColor:"#53a7c0", label: "Flow"
state "overflow", icon:"http://cdn.device-icons.smartthings.com/Weather/weather12-icn@2x.png", backgroundColor:"#cc0000", label: "High Flow"
}
standardTile("heatState", "device.heatState", width: 2, height: 2) {
state "normal", label:'Normal', icon:"st.alarm.temperature.normal", backgroundColor:"#ffffff"
state "freezing", label:'Freezing', icon:"st.alarm.temperature.freeze", backgroundColor:"#2eb82e"
state "overheated", label:'Overheated', icon:"st.alarm.temperature.overheat", backgroundColor:"#F80000"
}
standardTile("take1", "device.image", width: 2, height: 2, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false, decoration: "flat") {
state "take", label: "", action: "Image Capture.take", nextState:"taking", icon: "st.secondary.refresh"
}
standardTile("chartMode", "device.chartMode", width: 2, height: 2, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
state "day", label:'24 Hours\n(press to change)', nextState: "week", action: 'chartMode'
state "week", label:'7 Days\n(press to change)', nextState: "month", action: 'chartMode'
state "month", label:'4 Weeks\n(press to change)', nextState: "day", action: 'chartMode'
}
valueTile("zeroTile", "device.zero", width: 2, height: 2, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
state "zero", label:'Zero', action: 'zero'
}
main (["waterState"])
details(["flowHistory", "chartMode", "take1", "temperature", "gpm", "waterState", "battery"])
}
}
// parse events into attributes
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) {
results << createEvent( zwaveEvent(cmd) )
}
}
//log.debug "\"$description\" parsed to ${results.inspect()}"
if(gallonThreshhold != device.currentValue("lastThreshhold"))
{
results << setThreshhold(gallonThreshhold)
}
log.debug "zwave parsed to ${results.inspect()}"
return results
}
def updated()
{
log.debug("Updated")
}
def setHighFlowLevel(level)
{
setThreshhold(level)
}
def take() {
def mode = device.currentValue("chartMode")
if(mode == "day")
{
take1()
}
else if(mode == "week")
{
take7()
}
else if(mode == "month")
{
take28()
}
}
def chartMode(string) {
log.debug("ChartMode")
def state = device.currentValue("chartMode")
def tempValue = ""
switch(state)
{
case "day":
tempValue = "week"
break
case "week":
tempValue = "month"
break
case "month":
tempValue = "day"
break
default:
tempValue = "day"
break
}
sendEvent(name: "chartMode", value: tempValue)
take()
}
def take1() {
api("24hrs", "") {
log.debug("Image captured")
if(it.headers.'Content-Type'.contains("image/png")) {
if(it.data) {
storeImage(getPictureName("24hrs"), it.data)
}
}
}
}
def take7() {
api("7days", "") {
log.debug("Image captured")
if(it.headers.'Content-Type'.contains("image/png")) {
if(it.data) {
storeImage(getPictureName("7days"), it.data)
}
}
}
}
def take28() {
api("4weeks", "") {
log.debug("Image captured")
if(it.headers.'Content-Type'.contains("image/png")) {
if(it.data) {
storeImage(getPictureName("4weeks"), it.data)
}
}
}
}
def zero()
{
delayBetween([
zwave.meterV3.meterReset().format(),
zwave.meterV3.meterGet().format(),
zwave.firmwareUpdateMdV2.firmwareMdGet().format(),
], 100)
}
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd)
{
log.debug cmd
def map = [:]
if(cmd.sensorType == 1) {
map = [name: "temperature"]
if(cmd.scale == 0) {
map.value = getTemperature(cmd.scaledSensorValue)
} else {
map.value = cmd.scaledSensorValue
}
map.unit = location.temperatureScale
} /* else if(cmd.sensorType == 2) {
map = [name: "waterState"]
if(cmd.sensorValue[0] == 0x80) {
map.value = "flow"
sendEvent(name: "water", value: "dry")
} else if(cmd.sensorValue[0] == 0x00) {
map.value = "none"
sendEvent(name: "water", value: "dry")
} else if(cmd.sensorValue[0] == 0xFF) {
map.value = "overflow"
sendEvent(name: "water", value: "wet")
sendAlarm("waterOverflow")
}
} */
return map
}
def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd)
{
def map = [:]
map.name = "gpm"
def delta = cmd.scaledMeterValue - cmd.scaledPreviousMeterValue
if (delta < 0 || delta > 10000) {
log.error(cmd)
delta = 0
}
map.value = delta
map.unit = "gpm"
sendDataToCloud(delta)
sendEvent(name: "cumulative", value: cmd.scaledMeterValue, displayed: false, unit: "gal")
return map
}
def zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd)
{
def map = [:]
if (cmd.zwaveAlarmType == 8) // Power Alarm
{
map.name = "powerState" // For Tile (shows in "Recently")
if (cmd.zwaveAlarmEvent == 2) // AC Mains Disconnected
{
map.value = "disconnected"
sendAlarm("acMainsDisconnected")
}
else if (cmd.zwaveAlarmEvent == 3) // AC Mains Reconnected
{
map.value = "reconnected"
sendAlarm("acMainsReconnected")
}
else if (cmd.zwaveAlarmEvent == 0x0B) // Replace Battery Now
{
map.value = "noBattery"
sendAlarm("replaceBatteryNow")
}
else if (cmd.zwaveAlarmEvent == 0x00) // Battery Replaced
{
map.value = "batteryReplaced"
sendAlarm("batteryReplaced")
}
}
else if (cmd.zwaveAlarmType == 4) // Heat Alarm
{
map.name = "heatState"
if (cmd.zwaveAlarmEvent == 0) // Normal
{
map.value = "normal"
}
else if (cmd.zwaveAlarmEvent == 1) // Overheat
{
map.value = "overheated"
sendAlarm("tempOverheated")
}
else if (cmd.zwaveAlarmEvent == 5) // Underheat
{
map.value = "freezing"
sendAlarm("tempFreezing")
}
}
else if (cmd.zwaveAlarmType == 5) // Water Alarm
{
map.name = "waterState"
if (cmd.zwaveAlarmEvent == 0) // Normal
{
map.value = "none"
sendEvent(name: "water", value: "dry")
}
else if (cmd.zwaveAlarmEvent == 6) // Flow Detected
{
if(cmd.eventParameter[0] == 2)
{
map.value = "flow"
sendEvent(name: "water", value: "dry")
}
else if(cmd.eventParameter[0] == 3)
{
map.value = "overflow"
sendAlarm("waterOverflow")
sendEvent(name: "water", value: "wet")
}
}
}
//log.debug "alarmV2: $cmd"
return map
}
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
def map = [:]
if(cmd.batteryLevel == 0xFF) {
map.name = "battery"
map.value = 1
map.descriptionText = "${device.displayName} has a low battery"
map.displayed = true
} else {
map.name = "battery"
map.value = cmd.batteryLevel > 0 ? cmd.batteryLevel.toString() : 1
map.unit = "%"
map.displayed = false
}
return map
}
def zwaveEvent(physicalgraph.zwave.Command cmd)
{
log.debug "COMMAND CLASS: $cmd"
}
def sendDataToCloud(double data)
{
def params = [
uri: "https://iot.swiftlet.technology",
path: "/fortrezz/post.php",
body: [
id: device.id,
value: data,
email: registerEmail
]
]
//log.debug("POST parameters: ${params}")
try {
httpPostJson(params) { resp ->
resp.headers.each {
//log.debug "${it.name} : ${it.value}"
}
log.debug "sendDataToCloud query response: ${resp.data}"
}
} catch (e) {
log.debug "something went wrong: $e"
}
}
def getTemperature(value) {
if(location.temperatureScale == "C"){
return value
} else {
return Math.round(celsiusToFahrenheit(value))
}
}
private getPictureName(category) {
//def pictureUuid = device.id.toString().replaceAll('-', '')
def pictureUuid = java.util.UUID.randomUUID().toString().replaceAll('-', '')
def name = "image" + "_$pictureUuid" + "_" + category + ".png"
return name
}
def api(method, args = [], success = {}) {
def methods = [
//"snapshot": [uri: "http://${ip}:${port}/snapshot.cgi${login()}&${args}", type: "post"],
"24hrs": [uri: "https://iot.swiftlet.technology/fortrezz/chart.php?uuid=${device.id}&tz=${location.timeZone.ID}&type=1", type: "get"],
"7days": [uri: "https://iot.swiftlet.technology/fortrezz/chart.php?uuid=${device.id}&tz=${location.timeZone.ID}&type=2", type: "get"],
"4weeks": [uri: "https://iot.swiftlet.technology/fortrezz/chart.php?uuid=${device.id}&tz=${location.timeZone.ID}&type=3", type: "get"],
]
def request = methods.getAt(method)
return doRequest(request.uri, request.type, success)
}
private doRequest(uri, type, success) {
log.debug(uri)
if(type == "post") {
httpPost(uri , "", success)
}
else if(type == "get") {
httpGet(uri, success)
}
}
def sendAlarm(text)
{
sendEvent(name: "alarmState", value: text, descriptionText: text, displayed: false)
}
def setThreshhold(rate)
{
log.debug "Setting Threshhold to ${rate}"
def event = createEvent(name: "lastThreshhold", value: rate, displayed: false)
def cmds = []
cmds << zwave.configurationV2.configurationSet(configurationValue: [(int)Math.round(rate*10)], parameterNumber: 5, size: 1).format()
sendEvent(event)
return response(cmds) // return a list containing the event and the result of response()
}

View File

@@ -0,0 +1,188 @@
/**
* Devolo Humidity Sensor
*
* Copyright 2017 Garth Williams
*
* 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.
*
*
* Based on definition (name: "Aeon Multisensor 6", namespace: "smartthings", author: "SmartThings")
*
*/
metadata {
definition (name: "Devolo Humidity Sensor", namespace: "garth", author: "Garth Williams") {
capability "Temperature Measurement"
capability "Relative Humidity Measurement"
capability "Configuration"
capability "Sensor"
capability "Battery"
capability "Health Check"
attribute "batteryStatus", "string"
fingerprint mfr: "0175", prod: "0002", model: "0020"
}
simulator {
for (int i = 0; i <= 100; i += 20) {
status "temperature ${i}F": new physicalgraph.zwave.Zwave().securityV1.securityMessageEncapsulation().encapsulate(
new physicalgraph.zwave.Zwave().sensorMultilevelV2.sensorMultilevelReport(
scaledSensorValue: i, precision: 1, sensorType: 1, scale: 1)
).incomingMessage()
}
for (int i = 0; i <= 100; i += 20) {
status "humidity ${i}%": new physicalgraph.zwave.Zwave().securityV1.securityMessageEncapsulation().encapsulate(
new physicalgraph.zwave.Zwave().sensorMultilevelV2.sensorMultilevelReport(scaledSensorValue: i, sensorType: 5)
).incomingMessage()
}
for (int i in [0, 5, 10, 15, 50, 99, 100]) {
status "battery ${i}%": new physicalgraph.zwave.Zwave().securityV1.securityMessageEncapsulation().encapsulate(
new physicalgraph.zwave.Zwave().batteryV1.batteryReport(batteryLevel: i)
).incomingMessage()
}
status "low battery alert": new physicalgraph.zwave.Zwave().securityV1.securityMessageEncapsulation().encapsulate(
new physicalgraph.zwave.Zwave().batteryV1.batteryReport(batteryLevel: 255)
).incomingMessage()
status "wake up" : "command: 8407, payload: "
}
preferences {
}
tiles(scale: 2) {
valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
state "temperature", label:'${currentValue}°',
backgroundColors:[
[value: 32, color: "#153591"],
[value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"],
[value: 92, color: "#d04e00"],
[value: 98, color: "#bc2323"]
]
}
valueTile("humidity", "device.humidity", inactiveLabel: false, width: 2, height: 2) {
state "humidity", label:'${currentValue}% humidity', unit:""
}
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "battery", label:'${currentValue}% battery', unit:""
}
valueTile("batteryStatus", "device.batteryStatus", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "batteryStatus", label:'${currentValue}', unit:""
}
main(["temperature", "humidity"])
details(["temperature", "humidity", "batteryStatus"])
}
}
def parse(String description) {
log.debug "parse() >> description: $description"
def result = null
if (description.startsWith("Err 106")) {
log.debug "parse() >> Err 106"
result = createEvent( name: "secureInclusion", value: "failed", isStateChange: true,
descriptionText: "This sensor failed to complete the network security key exchange. If you are unable to control it via SmartThings, you must remove it from your network and add it again.")
} else if (description != "updated") {
log.debug "parse() >> zwave.parse(description)"
def cmd = zwave.parse(description, [0x31: 5, 0x30: 2, 0x84: 1])
if (cmd) {
result = zwaveEvent(cmd)
}
}
log.debug "After zwaveEvent(cmd) >> Parsed '${description}' to ${result.inspect()}"
return result
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand([0x31: 5, 0x30: 2, 0x84: 1])
state.sec = 1
log.debug "encapsulated: ${encapsulatedCommand}"
if (encapsulatedCommand) {
zwaveEvent(encapsulatedCommand)
} else {
log.warn "Unable to extract encapsulated cmd from $cmd"
createEvent(descriptionText: cmd.toString())
}
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityCommandsSupportedReport cmd) {
log.info "Executing zwaveEvent 98 (SecurityV1): 03 (SecurityCommandsSupportedReport) with cmd: $cmd"
state.sec = 1
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.NetworkKeyVerify cmd) {
state.sec = 1
log.info "Executing zwaveEvent 98 (SecurityV1): 07 (NetworkKeyVerify) with cmd: $cmd (node is securely included)"
def result = [createEvent(name:"secureInclusion", value:"success", descriptionText:"Secure inclusion was successful", isStateChange: true)]
result
}
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
def result = []
def map = [ name: "battery", unit: "%" ]
if (cmd.batteryLevel == 0xFF) {
map.value = 1
map.descriptionText = "${device.displayName} battery is low"
map.isStateChange = true
} else {
map.value = cmd.batteryLevel
}
state.lastbatt = now()
result << createEvent(map)
if (device.latestValue("powerSupply") != "USB Cable"){
result << createEvent(name: "batteryStatus", value: "${map.value} % battery", displayed: false)
}
result
}
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd){
def map = [:]
switch (cmd.sensorType) {
case 1:
map.name = "temperature"
def cmdScale = cmd.scale == 1 ? "F" : "C"
map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision)
map.unit = getTemperatureScale()
break
case 5:
map.name = "humidity"
map.value = cmd.scaledSensorValue.toInteger()
map.unit = "%"
break
default:
map.descriptionText = cmd.toString()
}
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) {
log.debug "ConfigurationReport: $cmd"
def result = []
def value
if (cmd.parameterNumber == 9 && cmd.configurationValue[0] == 1) {
value = "Battery"
result << createEvent(name: "powerSupply", value: value, displayed: false)
} else if (cmd.parameterNumber == 101){
result << response(configure())
}
result
}
def zwaveEvent(physicalgraph.zwave.Command cmd) {
log.debug "General zwaveEvent cmd: ${cmd}"
createEvent(descriptionText: cmd.toString(), isStateChange: false)
}

View File

@@ -133,8 +133,8 @@ def updated() {
}
def initialize() {
// Arrival sensors only goes OFFLINE when Hub is off
sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zigbee", scheme:"untracked"]), displayed: false)
// Device only goes OFFLINE when Hub is off
sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zwave", scheme:"untracked"]), displayed: false)
def zwMap = getZwaveInfo()
def buttons = 4 // Default for Key Fob

View File

@@ -111,7 +111,6 @@ def configure() {
return cmds
}
def installed() {
initialize()
}
@@ -121,7 +120,7 @@ def updated() {
}
def initialize() {
// Arrival sensors only goes OFFLINE when Hub is off
sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zigbee", scheme:"untracked"]), displayed: false)
// Device only goes OFFLINE when Hub is off
sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zwave", scheme:"untracked"]), displayed: false)
sendEvent(name: "numberOfButtons", value: 4)
}

View File

@@ -72,7 +72,7 @@ def createEvents(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
def poll() {
if (secondsPast(state.lastbatt, 36*60*60)) {
return zwave.batteryV1.batteryGet().format
return zwave.batteryV1.batteryGet().format()
} else {
return null
}