Compare commits

..

12 Commits

Author SHA1 Message Date
Donald Kirker
d39f5b1b6d Fix typo. Make jammed state events ALERT. Always display jammed events. 2016-03-24 16:11:13 -07:00
Donald Kirker
4ec43463d8 Add initial jammed state. 2016-03-24 03:32:12 -07:00
Lars Finander
53406ada8e Merge pull request #690 from larsfinander/DVCSMP-1615-Improve-Hue-error-handling
DVCSMP-1615 Hue setColor throws exceptions when missing parameters
2016-03-23 18:57:32 -07:00
Lars Finander
ffd0dd1545 DVCSMP-1615 Hue setColor throws exceptions when missing parameters
-DVCSMP-1637 Color picker set the light on but status not updated on the detail page
-DVCSMP-1638 Temperature control selection set the light on but status not updated on the detail page
-Added a lot of error checking for input parameters
-Fixed some data parsing exceptions
2016-03-23 16:10:11 -07:00
Vinay Rao
5c1236a21a Merge pull request #693 from SmartThingsCommunity/staging
[DVCSMP-1657] Syncing down changes from staging to master
2016-03-23 15:24:13 -07:00
Vinay Rao
0911651f71 Merge pull request #692 from workingmonk/bug/hotfix_multi
[DVCSMP-1657] Bug/hotfix multi
2016-03-23 15:19:18 -07:00
Vinay Rao
9cc92b1987 fixing issue with list of list of maps with multi parsing
adding return type to methods
2016-03-23 15:13:33 -07:00
Vinay Rao
1e27dc1824 Merge pull request #674 from workingmonk/feature/zigbee_library_refactor
[DPROT-20] ZigBee Refactor
2016-03-23 11:44:42 -07:00
Tom Manley
4bf3679942 Merge pull request #376 from tpmanley/feature/zigbee_refactor
[DPROT-20] Feature/zigbee refactor
2016-03-23 09:51:37 -05:00
Tom Manley
c714720578 Update zigbee-lock, zigbee-dimmer and zigbee-dimmer-power to use ZB lib API
https://smartthings.atlassian.net/browse/DPROT-20
2016-03-23 09:51:02 -05:00
Vinay Rao
281fc939ac refactoring code to get it inline with the new zigbee apis 2016-03-23 00:27:23 -07:00
Vinay Rao
a8357e7644 Merge pull request #652 from SmartThingsCommunity/master
Rolling up changes from master to staging
2016-03-18 13:38:31 -07:00
13 changed files with 183 additions and 501 deletions

View File

@@ -1,143 +0,0 @@
/**
* 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.
*
* Aeon Home Energy Meter
*
* Author: SmartThings
*
* Date: 2013-05-30
*/
metadata {
definition (name: "Aeon Home Energy Meter With PCode", namespace: "smartthings", author: "SmartThings") {
capability "Energy Meter"
capability "Power Meter"
capability "Configuration"
capability "Sensor"
attribute "ManufacturerCode", "string"
attribute "ProductCode", "string"
attribute "ProduceTypeCode", "string"
attribute "WirelessConfig", "string"
command "reset"
fingerprint deviceId: "0x2101", inClusters: " 0x70,0x31,0x72,0x86,0x32,0x80,0x85,0x60"
}
// simulator metadata
simulator {
for (int i = 0; i <= 10000; i += 1000) {
status "power ${i} W": new physicalgraph.zwave.Zwave().meterV1.meterReport(
scaledMeterValue: i, precision: 3, meterType: 4, scale: 2, size: 4).incomingMessage()
}
for (int i = 0; i <= 100; i += 10) {
status "energy ${i} kWh": new physicalgraph.zwave.Zwave().meterV1.meterReport(
scaledMeterValue: i, precision: 3, meterType: 0, scale: 0, size: 4).incomingMessage()
}
}
// tile definitions
tiles {
valueTile("power", "device.power", decoration: "flat") {
state "default", label:'${currentValue} W'
}
valueTile("energy", "device.energy", decoration: "flat") {
state "default", label:'${currentValue} kWh'
}
standardTile("reset", "device.energy", inactiveLabel: false, decoration: "flat") {
state "default", label:'reset kWh', action:"reset"
}
standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat") {
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
}
standardTile("configure", "device.power", inactiveLabel: false, decoration: "flat") {
state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
}
main (["power","energy"])
details(["power","energy", "reset","refresh", "configure"])
}
}
def parse(String description) {
def result = null
def cmd = zwave.parse(description, [0x31: 1, 0x32: 1, 0x60: 3])
if (cmd) {
result = createEvent(zwaveEvent(cmd))
}
log.debug "Parse returned ${result?.descriptionText}"
return result
}
def zwaveEvent(physicalgraph.zwave.commands.meterv1.MeterReport cmd) {
if (cmd.scale == 0) {
[name: "energy", value: cmd.scaledMeterValue, unit: "kWh"]
} else if (cmd.scale == 1) {
[name: "energy", value: cmd.scaledMeterValue, unit: "kVAh"]
}
else {
[name: "power", value: Math.round(cmd.scaledMeterValue), unit: "W"]
}
}
def zwaveEvent(physicalgraph.zwave.Command cmd) {
// Handles all Z-Wave commands we aren't interested in
[:]
}
def refresh() {
delayBetween([
zwave.meterV2.meterGet(scale: 0).format(),
zwave.meterV2.meterGet(scale: 2).format()
])
}
def reset() {
// No V1 available
return [
zwave.meterV2.meterReset().format(),
zwave.meterV2.meterGet(scale: 0).format()
]
}
def configure() {
def cmd = delayBetween([
zwave.manufacturerSpecificV2.manufacturerSpecificGet().format(),
zwave.configurationV1.configurationSet(parameterNumber: 101, size: 4, scaledConfigurationValue: 4).format(), // combined power in watts
zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: 900).format(), // every 5 min
zwave.configurationV1.configurationSet(parameterNumber: 102, size: 4, scaledConfigurationValue: 8).format(), // combined energy in kWh
zwave.configurationV1.configurationSet(parameterNumber: 112, size: 4, scaledConfigurationValue: 3600).format(), // every 5 min
zwave.configurationV1.configurationSet(parameterNumber: 103, size: 4, scaledConfigurationValue: 0).format(), // no third report
zwave.configurationV1.configurationSet(parameterNumber: 113, size: 4, scaledConfigurationValue: 900).format() // every 5 min
])
log.debug cmd
cmd
}
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
def result = []
def manufacturerCode = String.format("%04X", cmd.manufacturerId)
def productCode = String.format("%04X", cmd.productId)
def produceTypeCode = String.format("%04X", cmd.productTypeId)
def wirelessConfig = "ZWA"
sendEvent(name: "ManufacturerCode", value: manufacturerCode)
sendEvent(name: "ProductCode", value: productCode)
sendEvent(name: "ProduceTypeCode", value: produceTypeCode)
sendEvent(name: "WirelessConfig", value: wirelessConfig)
result << createEvent(descriptionText: "$device.displayName", isStateChange: false)
return result
}

View File

@@ -1,200 +0,0 @@
/**
* 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: "Aeon Smart Switch GEN5", namespace: "smartthings", author: "SmartThings") {
capability "Power Meter"
capability "Energy Meter"
capability "Actuator"
capability "Switch"
capability "Configuration"
capability "Polling"
capability "Refresh"
capability "Sensor"
attribute "ManufacturerCode", "string"
attribute "ProductCode", "string"
attribute "ProduceTypeCode", "string"
attribute "WirelessConfig", "string"
command "reset"
fingerprint deviceId: "0x1001", inClusters: "0x25,0x32,0x27,0x2C,0x2B,0x70,0x85,0x56,0x72,0x86,0x5E,0x59,0x7A,0x73,0x98,0xEF,0x5A,0x82", outClusters: "0x82"
}
// simulator metadata
simulator {
status "on": "command: 2003, payload: FF"
status "off": "command: 2003, payload: 00"
for (int i = 0; i <= 100; i += 10) {
status "energy ${i} kWh": new physicalgraph.zwave.Zwave().meterV2.meterReport(
scaledMeterValue: i, precision: 3, meterType: 0, scale: 0, size: 4).incomingMessage()
}
// reply messages
reply "2001FF,delay 100,2502": "command: 2503, payload: FF"
reply "200100,delay 100,2502": "command: 2503, payload: 00"
}
// tile definitions
tiles {
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821"
state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
}
valueTile("power", "device.power", decoration: "flat") {
state "default", label:'${currentValue} W'
}
valueTile("energy", "device.energy", decoration: "flat") {
state "default", label:'${currentValue} kWh'
}
standardTile("configure", "device.power", inactiveLabel: false, decoration: "flat") {
state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
}
standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat") {
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
}
main "switch"
details(["switch","power","energy","refresh","configure"])
}
}
def parse(String description) {
def result = null
def cmd = zwave.parse(description, [0x20: 1, 0x32: 2])
if (cmd) {
log.debug cmd
result = createEvent(zwaveEvent(cmd))
}
return result
}
def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd) {
log.debug "MeterReport ${cmd}"
if (cmd.scale == 0) {
[name: "energy", value: cmd.scaledMeterValue, unit: "kWh"]
} else if (cmd.scale == 1) {
[name: "energy", value: cmd.scaledMeterValue, unit: "kVAh"]
}
else {
[name: "power", value: Math.round(cmd.scaledMeterValue), unit: "W"]
}
}
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd)
{
[
name: "switch", value: cmd.value ? "on" : "off", type: "physical"
]
}
def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd)
{
[
name: "switch", value: cmd.value ? "on" : "off", type: "digital"
]
}
def zwaveEvent(physicalgraph.zwave.Command cmd) {
// Handles all Z-Wave commands we aren't interested in
return createEvent(descriptionText: "$device.displayName: $cmd", displayed: false)
[:]
}
def on() {
delayBetween([
secure(zwave.basicV1.basicSet(value: 0xFF)),
secure(zwave.switchBinaryV1.switchBinaryGet())
])
}
def off() {
delayBetween([
secure(zwave.basicV1.basicSet(value: 0x00)),
secure(zwave.switchBinaryV1.switchBinaryGet())
])
}
def poll() {
delayBetween([
secure(zwave.switchBinaryV1.switchBinaryGet()),
secure(zwave.meterV2.meterGet())
])
}
def refresh() {
secure(zwave.switchBinaryV1.switchBinaryGet())
}
def reset() {
return [
secure(zwave.meterV2.meterReset()),
secure(zwave.meterV2.meterGet())
]
}
def configure() {
delayBetween([
zwave.manufacturerSpecificV2.manufacturerSpecificGet().format(),
secure(zwave.configurationV1.configurationSet(parameterNumber: 101, size: 4, scaledConfigurationValue: 8)), // energy in kWh
secure(zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: 300)), // every 1 min
secure(zwave.configurationV1.configurationSet(parameterNumber: 102, size: 4, scaledConfigurationValue: 4)),
secure(zwave.configurationV1.configurationSet(parameterNumber: 112, size: 4, scaledConfigurationValue: 900)),
secure(zwave.configurationV1.configurationSet(parameterNumber: 103, size: 4, scaledConfigurationValue: 0))
])
}
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
def result = []
def manufacturerCode = String.format("%04X", cmd.manufacturerId)
def productCode = String.format("%04X", cmd.productId)
def produceTypeCode = String.format("%04X", cmd.productTypeId)
def wirelessConfig = "ZWP"
sendEvent(name: "ManufacturerCode", value: manufacturerCode)
sendEvent(name: "ProductCode", value: productCode)
sendEvent(name: "ProduceTypeCode", value: produceTypeCode)
sendEvent(name: "WirelessConfig", value: wirelessConfig)
result << createEvent(descriptionText: "$device.displayName", isStateChange: false)
return result
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand([0x20: 1, 0x85: 2, 0x70: 1])
log.debug "encapsulated: $encapsulatedCommand"
if (encapsulatedCommand) {
state.sec = 1
zwaveEvent(encapsulatedCommand)
}
}
private secure(physicalgraph.zwave.Command cmd) {
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
}
private secureSequence(commands, delay=200) {
delayBetween(commands.collect{ secure(it) }, delay)
}

View File

@@ -103,62 +103,104 @@ void nextLevel() {
} }
void setLevel(percent) { void setLevel(percent) {
log.debug "Executing 'setLevel'" log.debug "Executing 'setLevel'"
parent.setLevel(this, percent) if (verifyPercent(percent)) {
sendEvent(name: "level", value: percent, descriptionText: "Level has changed to ${percent}%") parent.setLevel(this, percent)
sendEvent(name: "level", value: percent, descriptionText: "Level has changed to ${percent}%")
sendEvent(name: "switch", value: "on")
}
} }
void setSaturation(percent) { void setSaturation(percent) {
log.debug "Executing 'setSaturation'" log.debug "Executing 'setSaturation'"
parent.setSaturation(this, percent) if (verifyPercent(percent)) {
sendEvent(name: "saturation", value: percent, displayed: false) parent.setSaturation(this, percent)
sendEvent(name: "saturation", value: percent, displayed: false)
}
} }
void setHue(percent) { void setHue(percent) {
log.debug "Executing 'setHue'" log.debug "Executing 'setHue'"
parent.setHue(this, percent) if (verifyPercent(percent)) {
sendEvent(name: "hue", value: percent, displayed: false) parent.setHue(this, percent)
sendEvent(name: "hue", value: percent, displayed: false)
}
} }
void setColor(value) { void setColor(value) {
log.debug "setColor: ${value}, $this" log.debug "setColor: ${value}, $this"
parent.setColor(this, value) def events = []
if (value.hue) { sendEvent(name: "hue", value: value.hue, displayed: false)} def validValues = [:]
if (value.saturation) { sendEvent(name: "saturation", value: value.saturation, displayed: false)}
if (value.hex) { sendEvent(name: "color", value: value.hex)} if (verifyPercent(value.hue)) {
if (value.level) { sendEvent(name: "level", value: value.level, descriptionText: "Level has changed to ${value.level}%")} events << createEvent(name: "hue", value: value.hue, displayed: false)
sendEvent(name: "switch", value: "on") validValues.hue = value.hue
}
if (verifyPercent(value.saturation)) {
events << createEvent(name: "saturation", value: value.saturation, displayed: false)
validValues.saturation = value.saturation
}
if (value.hex != null) {
if (value.hex ==~ /^\#([A-Fa-f0-9]){6}$/) {
events << createEvent(name: "color", value: value.hex)
validValues.hex = value.hex
} else {
log.warn "$value.hex is not a valid color"
}
}
if (verifyPercent(value.level)) {
events << createEvent(name: "level", value: value.level, descriptionText: "Level has changed to ${value.level}%")
validValues.level = value.level
}
if (value.switch == "off" || (value.level != null && value.level <= 0)) {
events << createEvent(name: "switch", value: "off")
validValues.switch = "off"
} else {
events << createEvent(name: "switch", value: "on")
validValues.switch = "on"
}
if (!events.isEmpty()) {
parent.setColor(this, validValues)
}
events.each {
sendEvent(it)
}
} }
void reset() { void reset() {
log.debug "Executing 'reset'" log.debug "Executing 'reset'"
def value = [level:100, saturation:56, hue:23] def value = [level:100, saturation:56, hue:23]
setAdjustedColor(value) setAdjustedColor(value)
parent.poll() parent.poll()
} }
void setAdjustedColor(value) { void setAdjustedColor(value) {
if (value) { if (value) {
log.trace "setAdjustedColor: ${value}" log.trace "setAdjustedColor: ${value}"
def adjusted = value + [:] def adjusted = value + [:]
adjusted.hue = adjustOutgoingHue(value.hue) adjusted.hue = adjustOutgoingHue(value.hue)
// Needed because color picker always sends 100 // Needed because color picker always sends 100
adjusted.level = null adjusted.level = null
setColor(adjusted) setColor(adjusted)
} else {
log.warn "Invalid color input"
} }
} }
void setColorTemperature(value) { void setColorTemperature(value) {
if (value) { if (value) {
log.trace "setColorTemperature: ${value}k" log.trace "setColorTemperature: ${value}k"
parent.setColorTemperature(this, value) parent.setColorTemperature(this, value)
sendEvent(name: "colorTemperature", value: value) sendEvent(name: "colorTemperature", value: value)
} sendEvent(name: "switch", value: "on")
} else {
log.warn "Invalid color temperature"
}
} }
void refresh() { void refresh() {
log.debug "Executing 'refresh'" log.debug "Executing 'refresh'"
parent.manualRefresh() parent.manualRefresh()
} }
def adjustOutgoingHue(percent) { def adjustOutgoingHue(percent) {
@@ -177,3 +219,14 @@ def adjustOutgoingHue(percent) {
log.info "percent: $percent, adjusted: $adjusted" log.info "percent: $percent, adjusted: $adjusted"
adjusted adjusted
} }
def verifyPercent(percent) {
if (percent == null)
return false
else if (percent >= 0 && percent <= 100) {
return true
} else {
log.warn "$percent is not 0-100"
return false
}
}

View File

@@ -79,8 +79,12 @@ void off() {
void setLevel(percent) { void setLevel(percent) {
log.debug "Executing 'setLevel'" log.debug "Executing 'setLevel'"
parent.setLevel(this, percent) if (percent != null && percent >= 0 && percent <= 100) {
sendEvent(name: "level", value: percent) parent.setLevel(this, percent)
sendEvent(name: "level", value: percent)
} else {
log.warn "$percent is not 0-100"
}
} }
void refresh() { void refresh() {

View File

@@ -203,7 +203,7 @@ private List parseContactMessage(String description) {
parts.each { part -> parts.each { part ->
part = part.trim() part = part.trim()
if (part.startsWith('contactState:')) { if (part.startsWith('contactState:')) {
results << getContactResult(part, description) results.addAll(getContactResult(part, description))
} }
else if (part.startsWith('accelerationState:')) { else if (part.startsWith('accelerationState:')) {
results << getAccelerationResult(part, description) results << getAccelerationResult(part, description)
@@ -316,7 +316,7 @@ private List getContactResult(part, description) {
results results
} }
private getAccelerationResult(part, description) { private Map getAccelerationResult(part, description) {
def name = "acceleration" def name = "acceleration"
def value = part.endsWith("1") ? "active" : "inactive" def value = part.endsWith("1") ? "active" : "inactive"
def linkText = getLinkText(device) def linkText = getLinkText(device)
@@ -335,7 +335,7 @@ private getAccelerationResult(part, description) {
] ]
} }
private getTempResult(part, description) { private Map getTempResult(part, description) {
def name = "temperature" def name = "temperature"
def temperatureScale = getTemperatureScale() def temperatureScale = getTemperatureScale()
def value = zigbee.parseSmartThingsTemperatureValue(part, "temp: ", temperatureScale) def value = zigbee.parseSmartThingsTemperatureValue(part, "temp: ", temperatureScale)
@@ -360,7 +360,7 @@ private getTempResult(part, description) {
] ]
} }
private getXyzResult(results, description) { private Map getXyzResult(results, description) {
def name = "threeAxis" def name = "threeAxis"
def value = "${results.x},${results.y},${results.z}" def value = "${results.x},${results.y},${results.z}"
def linkText = getLinkText(device) def linkText = getLinkText(device)
@@ -379,7 +379,7 @@ private getXyzResult(results, description) {
] ]
} }
private getBatteryResult(part, description) { private Map getBatteryResult(part, description) {
def batteryDivisor = description.split(",").find {it.split(":")[0].trim() == "batteryDivisor"} ? description.split(",").find {it.split(":")[0].trim() == "batteryDivisor"}.split(":")[1].trim() : null def batteryDivisor = description.split(",").find {it.split(":")[0].trim() == "batteryDivisor"} ? description.split(",").find {it.split(":")[0].trim() == "batteryDivisor"}.split(":")[1].trim() : null
def name = "battery" def name = "battery"
def value = zigbee.parseSmartThingsBatteryValue(part, batteryDivisor) def value = zigbee.parseSmartThingsBatteryValue(part, batteryDivisor)
@@ -400,7 +400,7 @@ private getBatteryResult(part, description) {
] ]
} }
private getRssiResult(part, description, lastHop=false) { private Map getRssiResult(part, description, lastHop=false) {
def name = lastHop ? "lastHopRssi" : "rssi" def name = lastHop ? "lastHopRssi" : "rssi"
def valueString = part.split(":")[1].trim() def valueString = part.split(":")[1].trim()
def value = (Integer.parseInt(valueString) - 128).toString() def value = (Integer.parseInt(valueString) - 128).toString()
@@ -431,7 +431,7 @@ private getRssiResult(part, description, lastHop=false) {
* Note: To make the signal strength indicator more accurate, we could combine * Note: To make the signal strength indicator more accurate, we could combine
* LQI with RSSI. * LQI with RSSI.
*/ */
private getLqiResult(part, description, lastHop=false) { private Map getLqiResult(part, description, lastHop=false) {
def name = lastHop ? "lastHopLqi" : "lqi" def name = lastHop ? "lastHopLqi" : "lqi"
def valueString = part.split(":")[1].trim() def valueString = part.split(":")[1].trim()
def percentageOf = 255 def percentageOf = 255

View File

@@ -56,21 +56,17 @@ metadata {
def parse(String description) { def parse(String description) {
log.debug "description is $description" log.debug "description is $description"
def resultMap = zigbee.getKnownDescription(description) def event = zigbee.getEvent(description)
if (resultMap) { if (event) {
log.info resultMap log.info event
if (resultMap.type == "update") { if (event.name == "power") {
log.info "$device updates: ${resultMap.value}"
}
else if (resultMap.type == "power") {
def powerValue
if (device.getDataValue("manufacturer") != "OSRAM") { //OSRAM devices do not reliably update power if (device.getDataValue("manufacturer") != "OSRAM") { //OSRAM devices do not reliably update power
powerValue = (resultMap.value as Integer)/10 //TODO: The divisor value needs to be set as part of configuration event.value = (event.value as Integer) / 10 //TODO: The divisor value needs to be set as part of configuration
sendEvent(name: "power", value: powerValue) sendEvent(event)
} }
} }
else { else {
sendEvent(name: resultMap.type, value: resultMap.value) sendEvent(event)
} }
} }
else { else {

View File

@@ -51,15 +51,9 @@ metadata {
def parse(String description) { def parse(String description) {
log.debug "description is $description" log.debug "description is $description"
def resultMap = zigbee.getKnownDescription(description) def event = zigbee.getEvent(description)
if (resultMap) { if (event) {
log.info resultMap sendEvent(event)
if (resultMap.type == "update") {
log.info "$device updates: ${resultMap.value}"
}
else {
sendEvent(name: resultMap.type, value: resultMap.value)
}
} }
else { else {
log.warn "DID NOT PARSE MESSAGE for description : $description" log.warn "DID NOT PARSE MESSAGE for description : $description"

View File

@@ -83,32 +83,19 @@ def uninstalled() {
} }
def configure() { def configure() {
/*
def cmds = def cmds =
zigbee.configSetup("${CLUSTER_DOORLOCK}", "${DOORLOCK_ATTR_LOCKSTATE}", zigbee.configureReporting(CLUSTER_DOORLOCK, DOORLOCK_ATTR_LOCKSTATE,
"${TYPE_ENUM8}", 0, 3600, "{01}") + TYPE_ENUM8, 0, 3600, null) +
zigbee.configSetup("${CLUSTER_POWER}", "${POWER_ATTR_BATTERY_PERCENTAGE_REMAINING}", zigbee.configureReporting(CLUSTER_POWER, POWER_ATTR_BATTERY_PERCENTAGE_REMAINING,
"${TYPE_U8}", 600, 21600, "{01}") TYPE_U8, 600, 21600, 0x01)
*/
def zigbeeId = device.zigbeeId
def cmds =
[
"zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 1 ${CLUSTER_DOORLOCK} {$zigbeeId} {}", "delay 200",
"zcl global send-me-a-report ${CLUSTER_DOORLOCK} ${DOORLOCK_ATTR_LOCKSTATE} ${TYPE_ENUM8} 0 3600 {01}", "delay 200",
"send 0x${device.deviceNetworkId} 1 0x${device.endpointId}", "delay 200",
"zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 1 ${CLUSTER_POWER} {$zigbeeId} {}", "delay 200",
"zcl global send-me-a-report ${CLUSTER_POWER} ${POWER_ATTR_BATTERY_PERCENTAGE_REMAINING} ${TYPE_U8} 600 21600 {01}", "delay 200",
"send 0x${device.deviceNetworkId} 1 0x${device.endpointId}", "delay 200",
]
log.info "configure() --- cmds: $cmds" log.info "configure() --- cmds: $cmds"
return cmds + refresh() // send refresh cmds as part of config return cmds + refresh() // send refresh cmds as part of config
} }
def refresh() { def refresh() {
def cmds = def cmds =
zigbee.refreshData("${CLUSTER_DOORLOCK}", "${DOORLOCK_ATTR_LOCKSTATE}") + zigbee.readAttribute(CLUSTER_DOORLOCK, DOORLOCK_ATTR_LOCKSTATE) +
zigbee.refreshData("${CLUSTER_POWER}", "${POWER_ATTR_BATTERY_PERCENTAGE_REMAINING}") zigbee.readAttribute(CLUSTER_POWER, POWER_ATTR_BATTERY_PERCENTAGE_REMAINING)
log.info "refresh() --- cmds: $cmds" log.info "refresh() --- cmds: $cmds"
return cmds return cmds
} }
@@ -121,34 +108,27 @@ def parse(String description) {
map = parseReportAttributeMessage(description) map = parseReportAttributeMessage(description)
} }
log.debug "parse() --- Parse returned $map"
def result = map ? createEvent(map) : null def result = map ? createEvent(map) : null
log.debug "parse() --- returned: $result"
return result return result
} }
// Lock capability commands // Lock capability commands
def lock() { def lock() {
//def cmds = zigbee.zigbeeCommand("${CLUSTER_DOORLOCK}", "${DOORLOCK_CMD_LOCK_DOOR}", "{}") def cmds = zigbee.command(CLUSTER_DOORLOCK, DOORLOCK_CMD_LOCK_DOOR)
//log.info "lock() -- cmds: $cmds" log.info "lock() -- cmds: $cmds"
//return cmds return cmds
"st cmd 0x${device.deviceNetworkId} 0x${device.endpointId} ${CLUSTER_DOORLOCK} ${DOORLOCK_CMD_LOCK_DOOR} {}"
} }
def unlock() { def unlock() {
//def cmds = zigbee.zigbeeCommand("${CLUSTER_DOORLOCK}", "${DOORLOCK_CMD_UNLOCK_DOOR}", "{}") def cmds = zigbee.command(CLUSTER_DOORLOCK, DOORLOCK_CMD_UNLOCK_DOOR)
//log.info "unlock() -- cmds: $cmds" log.info "unlock() -- cmds: $cmds"
//return cmds return cmds
"st cmd 0x${device.deviceNetworkId} 0x${device.endpointId} ${CLUSTER_DOORLOCK} ${DOORLOCK_CMD_UNLOCK_DOOR} {}"
} }
// Private methods // Private methods
private Map parseReportAttributeMessage(String description) { private Map parseReportAttributeMessage(String description) {
log.trace "parseReportAttributeMessage() --- description: $description"
Map descMap = zigbee.parseDescriptionAsMap(description) Map descMap = zigbee.parseDescriptionAsMap(description)
log.debug "parseReportAttributeMessage() --- descMap: $descMap"
Map resultMap = [:] Map resultMap = [:]
if (descMap.clusterInt == CLUSTER_POWER && descMap.attrInt == POWER_ATTR_BATTERY_PERCENTAGE_REMAINING) { if (descMap.clusterInt == CLUSTER_POWER && descMap.attrInt == POWER_ATTR_BATTERY_PERCENTAGE_REMAINING) {
resultMap.name = "battery" resultMap.name = "battery"
@@ -156,18 +136,24 @@ private Map parseReportAttributeMessage(String description) {
if (device.getDataValue("manufacturer") == "Yale") { //Handling issue with Yale locks incorrect battery reporting if (device.getDataValue("manufacturer") == "Yale") { //Handling issue with Yale locks incorrect battery reporting
resultMap.value = Integer.parseInt(descMap.value, 16) resultMap.value = Integer.parseInt(descMap.value, 16)
} }
log.info "parseReportAttributeMessage() --- battery: ${resultMap.value}"
} }
else if (descMap.clusterInt == CLUSTER_DOORLOCK && descMap.attrInt == DOORLOCK_ATTR_LOCKSTATE) { else if (descMap.clusterInt == CLUSTER_DOORLOCK && descMap.attrInt == DOORLOCK_ATTR_LOCKSTATE) {
def value = Integer.parseInt(descMap.value, 16) def value = Integer.parseInt(descMap.value, 16)
def linkText = getLinkText(device)
resultMap.name = "lock" resultMap.name = "lock"
resultMap.putAll([0:["value":"unknown", if (value == 0) {
"descriptionText":"Not fully locked"], resultMap.value = "unknown"
1:["value":"locked"], resultMap.descriptionText = "${linkText} is not fully locked"
2:["value":"unlocked"]].get(value, } else if (value == 1) {
["value":"unknown", resultMap.value = "locked"
"descriptionText":"Unknown lock state"])) resultMap.descriptionText = "${linkText} is locked"
log.info "parseReportAttributeMessage() --- lock: ${resultMap.value}" } else if (value == 2) {
resultMap.value = "unlocked"
resultMap.descriptionText = "${linkText} is unlocked"
} else {
resultMap.value = "unknown"
resultMap.descriptionText = "${linkText} is in unknown lock state"
}
} }
else { else {
log.debug "parseReportAttributeMessage() --- ignoring attribute" log.debug "parseReportAttributeMessage() --- ignoring attribute"

View File

@@ -51,22 +51,15 @@ metadata {
// Parse incoming device messages to generate events // Parse incoming device messages to generate events
def parse(String description) { def parse(String description) {
log.debug "description is $description" log.debug "description is $description"
def event = zigbee.getEvent(description)
def resultMap = zigbee.getKnownDescription(description) if (event) {
if (resultMap) { if (event.name == "power") {
log.info resultMap
if (resultMap.type == "update") {
log.info "$device updates: ${resultMap.value}"
}
else if (resultMap.type == "power") {
def powerValue def powerValue
if (device.getDataValue("manufacturer") != "OSRAM") { //OSRAM devices do not reliably update power powerValue = (event.value as Integer)/10 //TODO: The divisor value needs to be set as part of configuration
powerValue = (resultMap.value as Integer)/10 //TODO: The divisor value needs to be set as part of configuration sendEvent(name: "power", value: powerValue)
sendEvent(name: "power", value: powerValue)
}
} }
else { else {
sendEvent(name: resultMap.type, value: resultMap.value) sendEvent(event)
} }
} }
else { else {

View File

@@ -53,16 +53,9 @@ metadata {
// Parse incoming device messages to generate events // Parse incoming device messages to generate events
def parse(String description) { def parse(String description) {
log.debug "description is $description" log.debug "description is $description"
def event = zigbee.getEvent(description)
def resultMap = zigbee.getKnownDescription(description) if (event) {
if (resultMap) { sendEvent(event)
log.info resultMap
if (resultMap.type == "update") {
log.info "$device updates: ${resultMap.value}"
}
else {
sendEvent(name: resultMap.type, value: resultMap.value)
}
} }
else { else {
log.warn "DID NOT PARSE MESSAGE for description : $description" log.warn "DID NOT PARSE MESSAGE for description : $description"

View File

@@ -73,16 +73,9 @@ metadata {
// Parse incoming device messages to generate events // Parse incoming device messages to generate events
def parse(String description) { def parse(String description) {
log.debug "description is $description" log.debug "description is $description"
def event = zigbee.getEvent(description)
def finalResult = zigbee.getKnownDescription(description) if (event) {
if (finalResult) { sendEvent(event)
log.info finalResult
if (finalResult.type == "update") {
log.info "$device updates: ${finalResult.value}"
}
else {
sendEvent(name: finalResult.type, value: finalResult.value)
}
} }
else { else {
log.warn "DID NOT PARSE MESSAGE for description : $description" log.warn "DID NOT PARSE MESSAGE for description : $description"

View File

@@ -41,6 +41,7 @@ metadata {
attributeState "locked", label:'locked', action:"lock.unlock", icon:"st.locks.lock.locked", backgroundColor:"#79b821", nextState:"unlocking" attributeState "locked", label:'locked', action:"lock.unlock", icon:"st.locks.lock.locked", backgroundColor:"#79b821", nextState:"unlocking"
attributeState "unlocked", label:'unlocked', action:"lock.lock", icon:"st.locks.lock.unlocked", backgroundColor:"#ffffff", nextState:"locking" attributeState "unlocked", label:'unlocked', action:"lock.lock", icon:"st.locks.lock.unlocked", backgroundColor:"#ffffff", nextState:"locking"
attributeState "unknown", label:"unknown", action:"lock.lock", icon:"st.locks.lock.unknown", backgroundColor:"#ffffff", nextState:"locking" attributeState "unknown", label:"unknown", action:"lock.lock", icon:"st.locks.lock.unknown", backgroundColor:"#ffffff", nextState:"locking"
attributeState "jammed", label:"jammed", action:"lock.lock", icon:"st.locks.lock.unknown", backgroundColor:"#ffffff", nextState:"locking"
attributeState "locking", label:'locking', icon:"st.locks.lock.locked", backgroundColor:"#79b821" attributeState "locking", label:'locking', icon:"st.locks.lock.locked", backgroundColor:"#79b821"
attributeState "unlocking", label:'unlocking', icon:"st.locks.lock.unlocked", backgroundColor:"#ffffff" attributeState "unlocking", label:'unlocking', icon:"st.locks.lock.unlocked", backgroundColor:"#ffffff"
} }
@@ -130,7 +131,7 @@ def zwaveEvent(DoorLockOperationReport cmd) {
if (cmd.doorLockMode == 0xFF) { if (cmd.doorLockMode == 0xFF) {
map.value = "locked" map.value = "locked"
} else if (cmd.doorLockMode >= 0x40) { } else if (cmd.doorLockMode >= 0x40) {
map.value = "unknown" map.value = "unknown" // XXX: Jammed?
} else if (cmd.doorLockMode & 1) { } else if (cmd.doorLockMode & 1) {
map.value = "unlocked with timeout" map.value = "unlocked with timeout"
} else { } else {
@@ -180,7 +181,7 @@ def zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd) {
map = [ name: "lock", value: "unknown", descriptionText: "$device.displayName was not locked fully" ] map = [ name: "lock", value: "unknown", descriptionText: "$device.displayName was not locked fully" ]
break break
case 0xB: case 0xB:
map = [ name: "lock", value: "unknown", descriptionText: "$device.displayName is jammed" ] map = [ name: "lock", value: "jammed", descriptionText: "$device.displayName is jammed", displayed: true, eventType: "ALERT" ]
break break
case 0xC: case 0xC:
map = [ name: "codeChanged", value: "all", descriptionText: "$device.displayName: all user codes deleted", isStateChange: true ] map = [ name: "codeChanged", value: "all", descriptionText: "$device.displayName: all user codes deleted", isStateChange: true ]
@@ -266,7 +267,7 @@ def zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd) {
case 17: case 17:
case 23: case 23:
case 26: case 26:
map = [ name: "lock", value: "unknown", descriptionText: "$device.displayName bolt is jammed" ] map = [ name: "lock", value: "jammed", descriptionText: "$device.displayName bolt is jammed", displayed: true, eventType: "ALERT" ]
break break
case 13: case 13:
map = [ name: "codeChanged", value: cmd.alarmLevel, descriptionText: "$device.displayName code $cmd.alarmLevel was added", isStateChange: true ] map = [ name: "codeChanged", value: cmd.alarmLevel, descriptionText: "$device.displayName code $cmd.alarmLevel was added", isStateChange: true ]

View File

@@ -657,39 +657,53 @@ def setColorTemperature(childDevice, huesettings) {
} }
def setColor(childDevice, huesettings) { def setColor(childDevice, huesettings) {
log.debug "Executing 'setColor($huesettings)'" log.debug "Executing 'setColor($huesettings)'"
def value = [:]
def hue = null def hue = null
def sat = null def sat = null
def xy = null def xy = null
if (huesettings.hex) {
xy = getHextoXY(huesettings.hex) if (huesettings.hex != null) {
} else if (huesettings.hue && huesettings.saturation) { value.xy = getHextoXY(huesettings.hex)
hue = Math.min(Math.round(huesettings.hue * 65535 / 100), 65535) } else {
sat = Math.min(Math.round(huesettings.saturation * 255 / 100), 255) if (huesettings.hue != null)
value.hue = Math.min(Math.round(huesettings.hue * 65535 / 100), 65535)
if (huesettings.saturation != null)
value.sat = Math.min(Math.round(huesettings.saturation * 255 / 100), 255)
} }
def alert = huesettings.alert ? huesettings.alert : "none"
def transition = huesettings.transition ? huesettings.transition : 4 // Default behavior is to turn light on
value.on = true
def value = [xy: xy, sat: sat, hue: hue, alert: alert, transitiontime: transition, on: true] if (huesettings.level != null) {
if (huesettings.level <= 0)
value.on = false
else if (huesettings.level == 1)
value.bri = 1
else
value.bri = Math.min(Math.round(huesettings.level * 255 / 100), 255)
}
value.alert = huesettings.alert ? huesettings.alert : "none"
value.transition = huesettings.transition ? huesettings.transition : 4
if (huesettings.level != null) { // Make sure to turn off light if requested
if (huesettings.level == 1) value.bri = 1 else value.bri = Math.min(Math.round(huesettings.level * 255 / 100), 255) if (huesettings.switch == "off")
value.on = value.bri > 0 value.on = false
}
log.debug "sending command $value" log.debug "sending command $value"
put("lights/${getId(childDevice)}/state", value) put("lights/${getId(childDevice)}/state", value)
return "Color set to $value"
} }
def nextLevel(childDevice) { def nextLevel(childDevice) {
def level = device.latestValue("level") as Integer ?: 0 def level = device.latestValue("level") as Integer ?: 0
if (level < 100) { if (level < 100) {
level = Math.min(25 * (Math.round(level / 25) + 1), 100) as Integer level = Math.min(25 * (Math.round(level / 25) + 1), 100) as Integer
} } else {
else { level = 25
level = 25 }
} setLevel(childDevice,level)
setLevel(childDevice,level)
} }
private getId(childDevice) { private getId(childDevice) {
@@ -788,16 +802,14 @@ private getHextoXY(String colorStr) {
// Make green more vivid // Make green more vivid
if (normalizedToOne[1] > 0.04045) { if (normalizedToOne[1] > 0.04045) {
green = (float) Math.pow((normalizedToOne[1] + 0.055) green = (float) Math.pow((normalizedToOne[1] + 0.055) / (1.0 + 0.055), 2.4);
/ (1.0 + 0.055), 2.4);
} else { } else {
green = (float) (normalizedToOne[1] / 12.92); green = (float) (normalizedToOne[1] / 12.92);
} }
// Make blue more vivid // Make blue more vivid
if (normalizedToOne[2] > 0.04045) { if (normalizedToOne[2] > 0.04045) {
blue = (float) Math.pow((normalizedToOne[2] + 0.055) blue = (float) Math.pow((normalizedToOne[2] + 0.055) / (1.0 + 0.055), 2.4);
/ (1.0 + 0.055), 2.4);
} else { } else {
blue = (float) (normalizedToOne[2] / 12.92); blue = (float) (normalizedToOne[2] / 12.92);
} }
@@ -806,8 +818,8 @@ private getHextoXY(String colorStr) {
float Y = (float) (red * 0.234327 + green * 0.743075 + blue * 0.022598); float Y = (float) (red * 0.234327 + green * 0.743075 + blue * 0.022598);
float Z = (float) (red * 0.0000000 + green * 0.053077 + blue * 1.035763); float Z = (float) (red * 0.0000000 + green * 0.053077 + blue * 1.035763);
float x = X / (X + Y + Z); float x = (X != 0 ? X / (X + Y + Z) : 0);
float y = Y / (X + Y + Z); float y = (Y != 0 ? Y / (X + Y + Z) : 0);
double[] xy = new double[2]; double[] xy = new double[2];
xy[0] = x; xy[0] = x;