Compare commits

..

1 Commits

Author SHA1 Message Date
Kadeem Coleman
cbd11e8ad7 MSA-1938: Smart bed 2017-04-30 05:05:35 -07:00
9 changed files with 1051 additions and 328 deletions

View File

@@ -0,0 +1,900 @@
/**
* kad
*
* Copyright 2017 Kadeem Coleman
*
* 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: "kad", namespace: "happy new ", author: "Kadeem Coleman") {
capability "Acceleration Sensor"
capability "Actuator"
capability "Alarm"
capability "Alarm System"
capability "Alarm System Arm Only"
capability "Astronomical Data"
capability "Audio Notification"
capability "Battery"
capability "Beacon"
capability "Bridge"
capability "Buffered Video Capture"
capability "Bulb"
capability "Button"
capability "Carbon Dioxide Measurement"
capability "Carbon Monoxide Detector"
capability "Color Control"
capability "Color Temperature"
capability "Configuration"
capability "Consumable"
capability "Contact Sensor"
capability "Door Control"
capability "Energy Meter"
capability "Estimated Time Of Arrival"
capability "Garage Door Control"
capability "Health Check"
capability "Image Capture"
capability "Indicator"
capability "Infrared Level"
capability "Light"
capability "Light"
capability "Lock"
capability "Lock Codes"
capability "Lock Only"
capability "Media Controller"
capability "Momentary"
capability "Motion Sensor"
capability "Music Player"
capability "Ocf"
capability "Outlet"
capability "pH Measurement"
capability "Polling"
capability "Power"
capability "Power Meter"
capability "Power Source"
capability "Presence Sensor"
capability "Refresh"
capability "Relay Switch"
capability "Samsung TV"
capability "Sensor"
capability "Shock Sensor"
capability "Signal Strength"
capability "Sleep Sensor"
capability "Smoke Detector"
capability "Sound Sensor"
capability "Speech Recognition"
capability "Speech Synthesis"
capability "Switch"
capability "Switch Level"
capability "Tamper Alert"
capability "Temperature Measurement"
capability "Test Capability"
capability "Thermostat"
capability "Thermostat Cooling Setpoint"
capability "Thermostat Fan Mode"
capability "Thermostat Heating Setpoint"
capability "Thermostat Mode"
capability "Thermostat Operating State"
capability "Thermostat Setpoint"
capability "Three Axis"
capability "Timed Session"
capability "Tone"
capability "Touch Sensor"
capability "Ultraviolet Index"
capability "Valve"
capability "Video Camera"
capability "Video Capture"
capability "Voltage Measurement"
capability "Water Sensor"
capability "Window Shade"
capability "Zw Multichannel"
}
simulator {
// TODO: define status and reply messages here
}
tiles {
// TODO: define your main and details tiles here
}
}
// parse events into attributes
def parse(String description) {
log.debug "Parsing '${description}'"
// TODO: handle 'acceleration' attribute
// TODO: handle 'alarm' attribute
// TODO: handle 'alarmSystemStatus' attribute
// TODO: handle 'alarmSystemStatus' attribute
// TODO: handle 'sunrise' attribute
// TODO: handle 'sunset' attribute
// TODO: handle 'sunriseTime' attribute
// TODO: handle 'sunsetTime' attribute
// TODO: handle 'battery' attribute
// TODO: handle 'presence' attribute
// TODO: handle 'clip' attribute
// TODO: handle 'switch' attribute
// TODO: handle 'button' attribute
// TODO: handle 'numberOfButtons' attribute
// TODO: handle 'carbonDioxide' attribute
// TODO: handle 'carbonMonoxide' attribute
// TODO: handle 'hue' attribute
// TODO: handle 'saturation' attribute
// TODO: handle 'color' attribute
// TODO: handle 'colorTemperature' attribute
// TODO: handle 'consumableStatus' attribute
// TODO: handle 'contact' attribute
// TODO: handle 'door' attribute
// TODO: handle 'energy' attribute
// TODO: handle 'eta' attribute
// TODO: handle 'door' attribute
// TODO: handle 'checkInterval' attribute
// TODO: handle 'image' attribute
// TODO: handle 'indicatorStatus' attribute
// TODO: handle 'infraredLevel' attribute
// TODO: handle 'switch' attribute
// TODO: handle 'switch' attribute
// TODO: handle 'lock' attribute
// TODO: handle 'lock' attribute
// TODO: handle 'codeReport' attribute
// TODO: handle 'codeChanged' attribute
// TODO: handle 'lock' attribute
// TODO: handle 'activities' attribute
// TODO: handle 'currentActivity' attribute
// TODO: handle 'motion' attribute
// TODO: handle 'status' attribute
// TODO: handle 'level' attribute
// TODO: handle 'trackDescription' attribute
// TODO: handle 'trackData' attribute
// TODO: handle 'mute' attribute
// TODO: handle 'switch' attribute
// TODO: handle 'pH' attribute
// TODO: handle 'powerSource' attribute
// TODO: handle 'power' attribute
// TODO: handle 'powerSource' attribute
// TODO: handle 'presence' attribute
// TODO: handle 'switch' attribute
// TODO: handle 'volume' attribute
// TODO: handle 'mute' attribute
// TODO: handle 'pictureMode' attribute
// TODO: handle 'soundMode' attribute
// TODO: handle 'switch' attribute
// TODO: handle 'messageButton' attribute
// TODO: handle 'shock' attribute
// TODO: handle 'lqi' attribute
// TODO: handle 'rssi' attribute
// TODO: handle 'sleeping' attribute
// TODO: handle 'smoke' attribute
// TODO: handle 'carbonMonoxide' attribute
// TODO: handle 'sound' attribute
// TODO: handle 'phraseSpoken' attribute
// TODO: handle 'switch' attribute
// TODO: handle 'level' attribute
// TODO: handle 'tamper' attribute
// TODO: handle 'temperature' attribute
// TODO: handle 'temperature' attribute
// TODO: handle 'heatingSetpoint' attribute
// TODO: handle 'coolingSetpoint' attribute
// TODO: handle 'thermostatSetpoint' attribute
// TODO: handle 'thermostatMode' attribute
// TODO: handle 'thermostatFanMode' attribute
// TODO: handle 'thermostatOperatingState' attribute
// TODO: handle 'schedule' attribute
// TODO: handle 'coolingSetpointMin' attribute
// TODO: handle 'coolingSetpointMax' attribute
// TODO: handle 'heatingSetpointMin' attribute
// TODO: handle 'heatingSetpointMax' attribute
// TODO: handle 'thermostatSetpointMin' attribute
// TODO: handle 'thermostatSetpointMax' attribute
// TODO: handle 'coolingSetpointMin' attribute
// TODO: handle 'coolingSetpointMax' attribute
// TODO: handle 'heatingSetpointMin' attribute
// TODO: handle 'heatingSetpointMax' attribute
// TODO: handle 'thermostatSetpointMin' attribute
// TODO: handle 'thermostatSetpointMax' attribute
// TODO: handle 'coolingSetpoint' attribute
// TODO: handle 'coolingSetpointMin' attribute
// TODO: handle 'coolingSetpointMax' attribute
// TODO: handle 'thermostatFanMode' attribute
// TODO: handle 'heatingSetpoint' attribute
// TODO: handle 'heatingSetpointMin' attribute
// TODO: handle 'heatingSetpointMax' attribute
// TODO: handle 'thermostatMode' attribute
// TODO: handle 'thermostatOperatingState' attribute
// TODO: handle 'thermostatSetpoint' attribute
// TODO: handle 'thermostatSetpointMin' attribute
// TODO: handle 'thermostatSetpointMax' attribute
// TODO: handle 'threeAxis' attribute
// TODO: handle 'sessionStatus' attribute
// TODO: handle 'timeRemaining' attribute
// TODO: handle 'touch' attribute
// TODO: handle 'ultravioletIndex' attribute
// TODO: handle 'contact' attribute
// TODO: handle 'valve' attribute
// TODO: handle 'camera' attribute
// TODO: handle 'statusMessage' attribute
// TODO: handle 'mute' attribute
// TODO: handle 'settings' attribute
// TODO: handle 'clip' attribute
// TODO: handle 'stream' attribute
// TODO: handle 'voltage' attribute
// TODO: handle 'water' attribute
// TODO: handle 'windowShade' attribute
// TODO: handle 'epEvent' attribute
// TODO: handle 'epInfo' attribute
}
// handle commands
def off() {
log.debug "Executing 'off'"
// TODO: handle 'off' command
}
def strobe() {
log.debug "Executing 'strobe'"
// TODO: handle 'strobe' command
}
def siren() {
log.debug "Executing 'siren'"
// TODO: handle 'siren' command
}
def both() {
log.debug "Executing 'both'"
// TODO: handle 'both' command
}
def sendEvent(alarmSystemStatus,off)() {
log.debug "Executing 'sendEvent(alarmSystemStatus,off)'"
// TODO: handle 'sendEvent(alarmSystemStatus,off)' command
}
def sendEvent(alarmSystemStatus,stay)() {
log.debug "Executing 'sendEvent(alarmSystemStatus,stay)'"
// TODO: handle 'sendEvent(alarmSystemStatus,stay)' command
}
def sendEvent(alarmSystemStatus,away)() {
log.debug "Executing 'sendEvent(alarmSystemStatus,away)'"
// TODO: handle 'sendEvent(alarmSystemStatus,away)' command
}
def sendEvent(alarmSystemStatus,stay)() {
log.debug "Executing 'sendEvent(alarmSystemStatus,stay)'"
// TODO: handle 'sendEvent(alarmSystemStatus,stay)' command
}
def sendEvent(alarmSystemStatus,away)() {
log.debug "Executing 'sendEvent(alarmSystemStatus,away)'"
// TODO: handle 'sendEvent(alarmSystemStatus,away)' command
}
def sendEvent(sunrise)() {
log.debug "Executing 'sendEvent(sunrise)'"
// TODO: handle 'sendEvent(sunrise)' command
}
def sendEvent(sunset)() {
log.debug "Executing 'sendEvent(sunset)'"
// TODO: handle 'sendEvent(sunset)' command
}
def sendEvent(sunriseTime)() {
log.debug "Executing 'sendEvent(sunriseTime)'"
// TODO: handle 'sendEvent(sunriseTime)' command
}
def sendEvent(sunsetTime)() {
log.debug "Executing 'sendEvent(sunsetTime)'"
// TODO: handle 'sendEvent(sunsetTime)' command
}
def playText() {
log.debug "Executing 'playText'"
// TODO: handle 'playText' command
}
def playTextAndResume() {
log.debug "Executing 'playTextAndResume'"
// TODO: handle 'playTextAndResume' command
}
def playTextAndRestore() {
log.debug "Executing 'playTextAndRestore'"
// TODO: handle 'playTextAndRestore' command
}
def playTrack() {
log.debug "Executing 'playTrack'"
// TODO: handle 'playTrack' command
}
def playTrackAndResume() {
log.debug "Executing 'playTrackAndResume'"
// TODO: handle 'playTrackAndResume' command
}
def playTrackAndRestore() {
log.debug "Executing 'playTrackAndRestore'"
// TODO: handle 'playTrackAndRestore' command
}
def capture() {
log.debug "Executing 'capture'"
// TODO: handle 'capture' command
}
def off() {
log.debug "Executing 'off'"
// TODO: handle 'off' command
}
def on() {
log.debug "Executing 'on'"
// TODO: handle 'on' command
}
def setHue() {
log.debug "Executing 'setHue'"
// TODO: handle 'setHue' command
}
def setSaturation() {
log.debug "Executing 'setSaturation'"
// TODO: handle 'setSaturation' command
}
def setColor() {
log.debug "Executing 'setColor'"
// TODO: handle 'setColor' command
}
def setColorTemperature() {
log.debug "Executing 'setColorTemperature'"
// TODO: handle 'setColorTemperature' command
}
def configure() {
log.debug "Executing 'configure'"
// TODO: handle 'configure' command
}
def setConsumableStatus() {
log.debug "Executing 'setConsumableStatus'"
// TODO: handle 'setConsumableStatus' command
}
def open() {
log.debug "Executing 'open'"
// TODO: handle 'open' command
}
def close() {
log.debug "Executing 'close'"
// TODO: handle 'close' command
}
def open() {
log.debug "Executing 'open'"
// TODO: handle 'open' command
}
def close() {
log.debug "Executing 'close'"
// TODO: handle 'close' command
}
def ping() {
log.debug "Executing 'ping'"
// TODO: handle 'ping' command
}
def take() {
log.debug "Executing 'take'"
// TODO: handle 'take' command
}
def indicatorWhenOn() {
log.debug "Executing 'indicatorWhenOn'"
// TODO: handle 'indicatorWhenOn' command
}
def indicatorWhenOff() {
log.debug "Executing 'indicatorWhenOff'"
// TODO: handle 'indicatorWhenOff' command
}
def indicatorNever() {
log.debug "Executing 'indicatorNever'"
// TODO: handle 'indicatorNever' command
}
def setInfraredLevel() {
log.debug "Executing 'setInfraredLevel'"
// TODO: handle 'setInfraredLevel' command
}
def off() {
log.debug "Executing 'off'"
// TODO: handle 'off' command
}
def on() {
log.debug "Executing 'on'"
// TODO: handle 'on' command
}
def off() {
log.debug "Executing 'off'"
// TODO: handle 'off' command
}
def on() {
log.debug "Executing 'on'"
// TODO: handle 'on' command
}
def lock() {
log.debug "Executing 'lock'"
// TODO: handle 'lock' command
}
def unlock() {
log.debug "Executing 'unlock'"
// TODO: handle 'unlock' command
}
def lock() {
log.debug "Executing 'lock'"
// TODO: handle 'lock' command
}
def unlock() {
log.debug "Executing 'unlock'"
// TODO: handle 'unlock' command
}
def updateCodes() {
log.debug "Executing 'updateCodes'"
// TODO: handle 'updateCodes' command
}
def setCode() {
log.debug "Executing 'setCode'"
// TODO: handle 'setCode' command
}
def deleteCode() {
log.debug "Executing 'deleteCode'"
// TODO: handle 'deleteCode' command
}
def requestCode() {
log.debug "Executing 'requestCode'"
// TODO: handle 'requestCode' command
}
def reloadAllCodes() {
log.debug "Executing 'reloadAllCodes'"
// TODO: handle 'reloadAllCodes' command
}
def lock() {
log.debug "Executing 'lock'"
// TODO: handle 'lock' command
}
def startActivity() {
log.debug "Executing 'startActivity'"
// TODO: handle 'startActivity' command
}
def getAllActivities() {
log.debug "Executing 'getAllActivities'"
// TODO: handle 'getAllActivities' command
}
def getCurrentActivity() {
log.debug "Executing 'getCurrentActivity'"
// TODO: handle 'getCurrentActivity' command
}
def push() {
log.debug "Executing 'push'"
// TODO: handle 'push' command
}
def play() {
log.debug "Executing 'play'"
// TODO: handle 'play' command
}
def pause() {
log.debug "Executing 'pause'"
// TODO: handle 'pause' command
}
def stop() {
log.debug "Executing 'stop'"
// TODO: handle 'stop' command
}
def nextTrack() {
log.debug "Executing 'nextTrack'"
// TODO: handle 'nextTrack' command
}
def playTrack() {
log.debug "Executing 'playTrack'"
// TODO: handle 'playTrack' command
}
def setLevel() {
log.debug "Executing 'setLevel'"
// TODO: handle 'setLevel' command
}
def mute() {
log.debug "Executing 'mute'"
// TODO: handle 'mute' command
}
def previousTrack() {
log.debug "Executing 'previousTrack'"
// TODO: handle 'previousTrack' command
}
def unmute() {
log.debug "Executing 'unmute'"
// TODO: handle 'unmute' command
}
def setTrack() {
log.debug "Executing 'setTrack'"
// TODO: handle 'setTrack' command
}
def resumeTrack() {
log.debug "Executing 'resumeTrack'"
// TODO: handle 'resumeTrack' command
}
def restoreTrack() {
log.debug "Executing 'restoreTrack'"
// TODO: handle 'restoreTrack' command
}
def postOcfCommand() {
log.debug "Executing 'postOcfCommand'"
// TODO: handle 'postOcfCommand' command
}
def off() {
log.debug "Executing 'off'"
// TODO: handle 'off' command
}
def on() {
log.debug "Executing 'on'"
// TODO: handle 'on' command
}
def poll() {
log.debug "Executing 'poll'"
// TODO: handle 'poll' command
}
def refresh() {
log.debug "Executing 'refresh'"
// TODO: handle 'refresh' command
}
def on() {
log.debug "Executing 'on'"
// TODO: handle 'on' command
}
def off() {
log.debug "Executing 'off'"
// TODO: handle 'off' command
}
def volumeUp() {
log.debug "Executing 'volumeUp'"
// TODO: handle 'volumeUp' command
}
def volumeDown() {
log.debug "Executing 'volumeDown'"
// TODO: handle 'volumeDown' command
}
def setVolume() {
log.debug "Executing 'setVolume'"
// TODO: handle 'setVolume' command
}
def mute() {
log.debug "Executing 'mute'"
// TODO: handle 'mute' command
}
def unmute() {
log.debug "Executing 'unmute'"
// TODO: handle 'unmute' command
}
def setPictureMode() {
log.debug "Executing 'setPictureMode'"
// TODO: handle 'setPictureMode' command
}
def setSoundMode() {
log.debug "Executing 'setSoundMode'"
// TODO: handle 'setSoundMode' command
}
def on() {
log.debug "Executing 'on'"
// TODO: handle 'on' command
}
def off() {
log.debug "Executing 'off'"
// TODO: handle 'off' command
}
def showMessage() {
log.debug "Executing 'showMessage'"
// TODO: handle 'showMessage' command
}
def speak() {
log.debug "Executing 'speak'"
// TODO: handle 'speak' command
}
def on() {
log.debug "Executing 'on'"
// TODO: handle 'on' command
}
def off() {
log.debug "Executing 'off'"
// TODO: handle 'off' command
}
def setLevel() {
log.debug "Executing 'setLevel'"
// TODO: handle 'setLevel' command
}
def setHeatingSetpoint() {
log.debug "Executing 'setHeatingSetpoint'"
// TODO: handle 'setHeatingSetpoint' command
}
def setCoolingSetpoint() {
log.debug "Executing 'setCoolingSetpoint'"
// TODO: handle 'setCoolingSetpoint' command
}
def off() {
log.debug "Executing 'off'"
// TODO: handle 'off' command
}
def heat() {
log.debug "Executing 'heat'"
// TODO: handle 'heat' command
}
def emergencyHeat() {
log.debug "Executing 'emergencyHeat'"
// TODO: handle 'emergencyHeat' command
}
def cool() {
log.debug "Executing 'cool'"
// TODO: handle 'cool' command
}
def setThermostatMode() {
log.debug "Executing 'setThermostatMode'"
// TODO: handle 'setThermostatMode' command
}
def fanOn() {
log.debug "Executing 'fanOn'"
// TODO: handle 'fanOn' command
}
def fanAuto() {
log.debug "Executing 'fanAuto'"
// TODO: handle 'fanAuto' command
}
def fanCirculate() {
log.debug "Executing 'fanCirculate'"
// TODO: handle 'fanCirculate' command
}
def setThermostatFanMode() {
log.debug "Executing 'setThermostatFanMode'"
// TODO: handle 'setThermostatFanMode' command
}
def auto() {
log.debug "Executing 'auto'"
// TODO: handle 'auto' command
}
def setSchedule() {
log.debug "Executing 'setSchedule'"
// TODO: handle 'setSchedule' command
}
def setCoolingSetpoint() {
log.debug "Executing 'setCoolingSetpoint'"
// TODO: handle 'setCoolingSetpoint' command
}
def fanOn() {
log.debug "Executing 'fanOn'"
// TODO: handle 'fanOn' command
}
def fanAuto() {
log.debug "Executing 'fanAuto'"
// TODO: handle 'fanAuto' command
}
def fanCirculate() {
log.debug "Executing 'fanCirculate'"
// TODO: handle 'fanCirculate' command
}
def setThermostatFanMode() {
log.debug "Executing 'setThermostatFanMode'"
// TODO: handle 'setThermostatFanMode' command
}
def setHeatingSetpoint() {
log.debug "Executing 'setHeatingSetpoint'"
// TODO: handle 'setHeatingSetpoint' command
}
def off() {
log.debug "Executing 'off'"
// TODO: handle 'off' command
}
def heat() {
log.debug "Executing 'heat'"
// TODO: handle 'heat' command
}
def emergencyHeat() {
log.debug "Executing 'emergencyHeat'"
// TODO: handle 'emergencyHeat' command
}
def cool() {
log.debug "Executing 'cool'"
// TODO: handle 'cool' command
}
def auto() {
log.debug "Executing 'auto'"
// TODO: handle 'auto' command
}
def setThermostatMode() {
log.debug "Executing 'setThermostatMode'"
// TODO: handle 'setThermostatMode' command
}
def setTimeRemaining() {
log.debug "Executing 'setTimeRemaining'"
// TODO: handle 'setTimeRemaining' command
}
def start() {
log.debug "Executing 'start'"
// TODO: handle 'start' command
}
def stop() {
log.debug "Executing 'stop'"
// TODO: handle 'stop' command
}
def pause() {
log.debug "Executing 'pause'"
// TODO: handle 'pause' command
}
def cancel() {
log.debug "Executing 'cancel'"
// TODO: handle 'cancel' command
}
def beep() {
log.debug "Executing 'beep'"
// TODO: handle 'beep' command
}
def open() {
log.debug "Executing 'open'"
// TODO: handle 'open' command
}
def close() {
log.debug "Executing 'close'"
// TODO: handle 'close' command
}
def on() {
log.debug "Executing 'on'"
// TODO: handle 'on' command
}
def off() {
log.debug "Executing 'off'"
// TODO: handle 'off' command
}
def mute() {
log.debug "Executing 'mute'"
// TODO: handle 'mute' command
}
def unmute() {
log.debug "Executing 'unmute'"
// TODO: handle 'unmute' command
}
def flip() {
log.debug "Executing 'flip'"
// TODO: handle 'flip' command
}
def capture() {
log.debug "Executing 'capture'"
// TODO: handle 'capture' command
}
def open() {
log.debug "Executing 'open'"
// TODO: handle 'open' command
}
def close() {
log.debug "Executing 'close'"
// TODO: handle 'close' command
}
def presetPosition() {
log.debug "Executing 'presetPosition'"
// TODO: handle 'presetPosition' command
}
def enableEpEvents() {
log.debug "Executing 'enableEpEvents'"
// TODO: handle 'enableEpEvents' command
}
def epCmd() {
log.debug "Executing 'epCmd'"
// TODO: handle 'epCmd' command
}

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright 2017 SmartThings * Copyright 2016 SmartThings
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * 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: * in compliance with the License. You may obtain a copy of the License at:
@@ -64,7 +64,6 @@ private getATTRIBUTE_HUE() { 0x0000 }
private getATTRIBUTE_SATURATION() { 0x0001 } private getATTRIBUTE_SATURATION() { 0x0001 }
private getHUE_COMMAND() { 0x00 } private getHUE_COMMAND() { 0x00 }
private getSATURATION_COMMAND() { 0x03 } private getSATURATION_COMMAND() { 0x03 }
private getMOVE_TO_HUE_AND_SATURATION_COMMAND() { 0x06 }
private getCOLOR_CONTROL_CLUSTER() { 0x0300 } private getCOLOR_CONTROL_CLUSTER() { 0x0300 }
// Parse incoming device messages to generate events // Parse incoming device messages to generate events
@@ -85,11 +84,11 @@ def parse(String description) {
if (zigbeeMap?.clusterInt == COLOR_CONTROL_CLUSTER) { if (zigbeeMap?.clusterInt == COLOR_CONTROL_CLUSTER) {
if(zigbeeMap.attrInt == ATTRIBUTE_HUE){ //Hue Attribute if(zigbeeMap.attrInt == ATTRIBUTE_HUE){ //Hue Attribute
def hueValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 0xfe * 100) def hueValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 100)
sendEvent(name: "hue", value: hueValue, descriptionText: "Color has changed") sendEvent(name: "hue", value: hueValue, descriptionText: "Color has changed")
} }
else if(zigbeeMap.attrInt == ATTRIBUTE_SATURATION){ //Saturation Attribute else if(zigbeeMap.attrInt == ATTRIBUTE_SATURATION){ //Saturation Attribute
def saturationValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 0xfe * 100) def saturationValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 100)
sendEvent(name: "saturation", value: saturationValue, descriptionText: "Color has changed", displayed: false) sendEvent(name: "saturation", value: saturationValue, descriptionText: "Color has changed", displayed: false)
} }
} }
@@ -124,12 +123,7 @@ def ping() {
} }
def refresh() { def refresh() {
zigbee.onOffRefresh() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION) + zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE, DataType.UINT8, 1, 3600, 0x01) + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION, DataType.UINT8, 1, 3600, 0x01)
zigbee.levelRefresh() +
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) +
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION) +
zigbee.onOffConfig(0, 300) +
zigbee.levelConfig()
} }
def configure() { def configure() {
@@ -139,38 +133,26 @@ def configure() {
sendEvent(name: "checkInterval", value: 3 * 10 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) sendEvent(name: "checkInterval", value: 3 * 10 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity // OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
refresh() zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE, DataType.UINT8, 1, 3600, 0x01) + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION, DataType.UINT8, 1, 3600, 0x01) + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
} }
def setLevel(value) { def setLevel(value) {
zigbee.setLevel(value) zigbee.setLevel(value)
} }
private getScaledHue(value) {
zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
}
private getScaledSaturation(value) {
zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
}
def setColor(value){ def setColor(value){
log.trace "setColor($value)" log.trace "setColor($value)"
zigbee.on() + zigbee.on() + setHue(value.hue) + "delay 500" + setSaturation(value.saturation)
zigbee.command(COLOR_CONTROL_CLUSTER, MOVE_TO_HUE_AND_SATURATION_COMMAND,
getScaledHue(value.hue), getScaledSaturation(value.saturation), "0000") +
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) +
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
} }
def setHue(value) { def setHue(value) {
zigbee.command(COLOR_CONTROL_CLUSTER, HUE_COMMAND, getScaledHue(value), "00", "0000") + def scaledHueValue = zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) zigbee.command(COLOR_CONTROL_CLUSTER, HUE_COMMAND, scaledHueValue, "00", "0500") //payload-> hue value, direction (00-> shortest distance), transition time (1/10th second) (0500 in U16 reads 5)
} }
def setSaturation(value) { def setSaturation(value) {
zigbee.command(COLOR_CONTROL_CLUSTER, SATURATION_COMMAND, getScaledSaturation(value), "0000") + def scaledSatValue = zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION) zigbee.command(COLOR_CONTROL_CLUSTER, SATURATION_COMMAND, scaledSatValue, "0500") + "delay 1000" + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
} }
def installed() { def installed() {
@@ -179,4 +161,4 @@ def installed() {
sendEvent(name: "level", value: 100) sendEvent(name: "level", value: 100)
} }
} }
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright 2017 SmartThings * Copyright 2016 SmartThings
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * 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: * in compliance with the License. You may obtain a copy of the License at:
@@ -78,7 +78,6 @@ private getATTRIBUTE_HUE() { 0x0000 }
private getATTRIBUTE_SATURATION() { 0x0001 } private getATTRIBUTE_SATURATION() { 0x0001 }
private getHUE_COMMAND() { 0x00 } private getHUE_COMMAND() { 0x00 }
private getSATURATION_COMMAND() { 0x03 } private getSATURATION_COMMAND() { 0x03 }
private getMOVE_TO_HUE_AND_SATURATION_COMMAND() { 0x06 }
private getCOLOR_CONTROL_CLUSTER() { 0x0300 } private getCOLOR_CONTROL_CLUSTER() { 0x0300 }
private getATTRIBUTE_COLOR_TEMPERATURE() { 0x0007 } private getATTRIBUTE_COLOR_TEMPERATURE() { 0x0007 }
@@ -103,11 +102,11 @@ def parse(String description) {
if (zigbeeMap?.clusterInt == COLOR_CONTROL_CLUSTER) { if (zigbeeMap?.clusterInt == COLOR_CONTROL_CLUSTER) {
if(zigbeeMap.attrInt == ATTRIBUTE_HUE){ //Hue Attribute if(zigbeeMap.attrInt == ATTRIBUTE_HUE){ //Hue Attribute
def hueValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 0xfe * 100) def hueValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 100)
sendEvent(name: "hue", value: hueValue, descriptionText: "Color has changed") sendEvent(name: "hue", value: hueValue, descriptionText: "Color has changed")
} }
else if(zigbeeMap.attrInt == ATTRIBUTE_SATURATION){ //Saturation Attribute else if(zigbeeMap.attrInt == ATTRIBUTE_SATURATION){ //Saturation Attribute
def saturationValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 0xfe * 100) def saturationValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 100)
sendEvent(name: "saturation", value: saturationValue, descriptionText: "Color has changed", displayed: false) sendEvent(name: "saturation", value: saturationValue, descriptionText: "Color has changed", displayed: false)
} }
} }
@@ -142,13 +141,7 @@ def ping() {
} }
def refresh() { def refresh() {
zigbee.onOffRefresh() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_COLOR_TEMPERATURE) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION) + zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE, DataType.UINT8, 1, 3600, 0x01) + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION, DataType.UINT8, 1, 3600, 0x01)
zigbee.levelRefresh() +
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_COLOR_TEMPERATURE) +
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) +
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION) +
zigbee.onOffConfig(0, 300) +
zigbee.levelConfig()
} }
def configure() { def configure() {
@@ -163,12 +156,7 @@ def configure() {
def setColorTemperature(value) { def setColorTemperature(value) {
setGenericName(value) setGenericName(value)
value = value as Integer zigbee.setColorTemperature(value)
def tempInMired = (1000000 / value) as Integer
def finalHex = zigbee.swapEndianHex(zigbee.convertToHexString(tempInMired, 4))
zigbee.command(COLOR_CONTROL_CLUSTER, 0x0A, "$finalHex 0000") +
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_COLOR_TEMPERATURE)
} }
//Naming based on the wiki article here: http://en.wikipedia.org/wiki/Color_temperature //Naming based on the wiki article here: http://en.wikipedia.org/wiki/Color_temperature
@@ -192,31 +180,19 @@ def setLevel(value) {
zigbee.setLevel(value) zigbee.setLevel(value)
} }
private getScaledHue(value) {
zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
}
private getScaledSaturation(value) {
zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
}
def setColor(value){ def setColor(value){
log.trace "setColor($value)" log.trace "setColor($value)"
zigbee.on() + zigbee.on() + setHue(value.hue) + "delay 300" + setSaturation(value.saturation)
zigbee.command(COLOR_CONTROL_CLUSTER, MOVE_TO_HUE_AND_SATURATION_COMMAND,
getScaledHue(value.hue), getScaledSaturation(value.saturation), "0000") +
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) +
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
} }
def setHue(value) { def setHue(value) {
zigbee.command(COLOR_CONTROL_CLUSTER, HUE_COMMAND, getScaledHue(value), "00", "0000") + def scaledHueValue = zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) zigbee.command(COLOR_CONTROL_CLUSTER, HUE_COMMAND, scaledHueValue, "00", "0500") //payload-> hue value, direction (00-> shortest distance), transition time (1/10th second) (0500 in U16 reads 5)
} }
def setSaturation(value) { def setSaturation(value) {
zigbee.command(COLOR_CONTROL_CLUSTER, SATURATION_COMMAND, getScaledSaturation(value), "0000") + def scaledSatValue = zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION) zigbee.command(COLOR_CONTROL_CLUSTER, SATURATION_COMMAND, scaledSatValue, "0500") + "delay 1000" + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
} }
def installed() { def installed() {
@@ -225,4 +201,4 @@ def installed() {
sendEvent(name: "level", value: 100) sendEvent(name: "level", value: 100)
} }
} }
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright 2017 SmartThings * Copyright 2015 SmartThings
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * 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: * in compliance with the License. You may obtain a copy of the License at:
@@ -71,11 +71,6 @@ metadata {
} }
} }
// Globals
private getMOVE_TO_COLOR_TEMPERATURE_COMMAND() { 0x0A }
private getCOLOR_CONTROL_CLUSTER() { 0x0300 }
private getATTRIBUTE_COLOR_TEMPERATURE() { 0x0007 }
// 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"
@@ -128,11 +123,7 @@ def ping() {
} }
def refresh() { def refresh() {
zigbee.onOffRefresh() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh() + zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.colorTemperatureConfig()
zigbee.levelRefresh() +
zigbee.colorTemperatureRefresh() +
zigbee.onOffConfig(0, 300) +
zigbee.levelConfig()
} }
def configure() { def configure() {
@@ -147,12 +138,7 @@ def configure() {
def setColorTemperature(value) { def setColorTemperature(value) {
setGenericName(value) setGenericName(value)
value = value as Integer zigbee.setColorTemperature(value)
def tempInMired = (1000000 / value) as Integer
def finalHex = zigbee.swapEndianHex(zigbee.convertToHexString(tempInMired, 4))
zigbee.command(COLOR_CONTROL_CLUSTER, MOVE_TO_COLOR_TEMPERATURE_COMMAND, "$finalHex 0000") +
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_COLOR_TEMPERATURE)
} }
//Naming based on the wiki article here: http://en.wikipedia.org/wiki/Color_temperature //Naming based on the wiki article here: http://en.wikipedia.org/wiki/Color_temperature

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright 2017 SmartThings * Copyright 2016 SmartThings
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * 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: * in compliance with the License. You may obtain a copy of the License at:
@@ -55,7 +55,6 @@ private getATTRIBUTE_HUE() { 0x0000 }
private getATTRIBUTE_SATURATION() { 0x0001 } private getATTRIBUTE_SATURATION() { 0x0001 }
private getHUE_COMMAND() { 0x00 } private getHUE_COMMAND() { 0x00 }
private getSATURATION_COMMAND() { 0x03 } private getSATURATION_COMMAND() { 0x03 }
private getMOVE_TO_HUE_AND_SATURATION_COMMAND() { 0x06 }
private getCOLOR_CONTROL_CLUSTER() { 0x0300 } private getCOLOR_CONTROL_CLUSTER() { 0x0300 }
// Parse incoming device messages to generate events // Parse incoming device messages to generate events
@@ -73,11 +72,11 @@ def parse(String description) {
if (zigbeeMap?.clusterInt == COLOR_CONTROL_CLUSTER) { if (zigbeeMap?.clusterInt == COLOR_CONTROL_CLUSTER) {
if(zigbeeMap.attrInt == ATTRIBUTE_HUE){ //Hue Attribute if(zigbeeMap.attrInt == ATTRIBUTE_HUE){ //Hue Attribute
def hueValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 0xfe * 100) def hueValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 360)
sendEvent(name: "hue", value: hueValue, displayed:false) sendEvent(name: "hue", value: hueValue, displayed:false)
} }
else if(zigbeeMap.attrInt == ATTRIBUTE_SATURATION){ //Saturation Attribute else if(zigbeeMap.attrInt == ATTRIBUTE_SATURATION){ //Saturation Attribute
def saturationValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 0xfe * 100) def saturationValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 100)
sendEvent(name: "saturation", value: saturationValue, displayed:false) sendEvent(name: "saturation", value: saturationValue, displayed:false)
} }
} }
@@ -109,46 +108,28 @@ def configure() {
} }
def configureAttributes() { def configureAttributes() {
zigbee.onOffConfig() + zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE, DataType.UINT8, 1, 3600, 0x01) + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION, DataType.UINT8, 1, 3600, 0x01)
zigbee.levelConfig()
} }
def refreshAttributes() { def refreshAttributes() {
zigbee.onOffRefresh() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
zigbee.levelRefresh() +
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) +
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
} }
def setLevel(value) { def setLevel(value) {
zigbee.setLevel(value) + zigbee.onOffRefresh() + zigbee.levelRefresh() //adding refresh because of ZLL bulb not conforming to send-me-a-report zigbee.setLevel(value) + zigbee.onOffRefresh() + zigbee.levelRefresh() //adding refresh because of ZLL bulb not conforming to send-me-a-report
} }
private getScaledHue(value) {
zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
}
private getScaledSaturation(value) {
zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
}
def setColor(value){ def setColor(value){
log.trace "setColor($value)" log.trace "setColor($value)"
zigbee.on() + zigbee.on() + setHue(value.hue) + ["delay 300"] + setSaturation(value.saturation) + ["delay 2000"] + refreshAttributes()
zigbee.command(COLOR_CONTROL_CLUSTER, MOVE_TO_HUE_AND_SATURATION_COMMAND,
getScaledHue(value.hue), getScaledSaturation(value.saturation), "0000") +
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) +
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
} }
def setHue(value) { def setHue(value) {
//payload-> hue value, direction (00-> shortest distance), transition time (1/10th second) def scaledHueValue = zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
zigbee.command(COLOR_CONTROL_CLUSTER, HUE_COMMAND, getScaledHue(value), "00", "0000") + zigbee.command(COLOR_CONTROL_CLUSTER, HUE_COMMAND, scaledHueValue, "00", "0500") + ["delay 1500"] + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) //payload-> hue value, direction (00-> shortest distance), transition time (1/10th second) (0500 in U16 reads 5)
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE)
} }
def setSaturation(value) { def setSaturation(value) {
//payload-> sat value, transition time def scaledSatValue = zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
zigbee.command(COLOR_CONTROL_CLUSTER, SATURATION_COMMAND, getScaledSaturation(value), "0000") + zigbee.command(COLOR_CONTROL_CLUSTER, SATURATION_COMMAND, scaledSatValue, "0500") + ["delay 1500"] + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION) //payload-> sat value, transition time
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright 2017 SmartThings * Copyright 2016 SmartThings
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * 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: * in compliance with the License. You may obtain a copy of the License at:
@@ -70,7 +70,6 @@ private getATTRIBUTE_HUE() { 0x0000 }
private getATTRIBUTE_SATURATION() { 0x0001 } private getATTRIBUTE_SATURATION() { 0x0001 }
private getHUE_COMMAND() { 0x00 } private getHUE_COMMAND() { 0x00 }
private getSATURATION_COMMAND() { 0x03 } private getSATURATION_COMMAND() { 0x03 }
private getMOVE_TO_HUE_AND_SATURATION_COMMAND() { 0x06 }
private getCOLOR_CONTROL_CLUSTER() { 0x0300 } private getCOLOR_CONTROL_CLUSTER() { 0x0300 }
private getATTRIBUTE_COLOR_TEMPERATURE() { 0x0007 } private getATTRIBUTE_COLOR_TEMPERATURE() { 0x0007 }
@@ -89,11 +88,11 @@ def parse(String description) {
if (zigbeeMap?.clusterInt == COLOR_CONTROL_CLUSTER) { if (zigbeeMap?.clusterInt == COLOR_CONTROL_CLUSTER) {
if(zigbeeMap.attrInt == ATTRIBUTE_HUE){ //Hue Attribute if(zigbeeMap.attrInt == ATTRIBUTE_HUE){ //Hue Attribute
def hueValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 0xfe * 100) def hueValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 360)
sendEvent(name: "hue", value: hueValue, displayed:false) sendEvent(name: "hue", value: hueValue, displayed:false)
} }
else if(zigbeeMap.attrInt == ATTRIBUTE_SATURATION){ //Saturation Attribute else if(zigbeeMap.attrInt == ATTRIBUTE_SATURATION){ //Saturation Attribute
def saturationValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 0xfe * 100) def saturationValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 100)
sendEvent(name: "saturation", value: saturationValue, displayed:false) sendEvent(name: "saturation", value: saturationValue, displayed:false)
} }
} }
@@ -125,16 +124,11 @@ def configure() {
} }
def configureAttributes() { def configureAttributes() {
zigbee.onOffConfig() + zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE, DataType.UINT8, 1, 3600, 0x01) + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION, DataType.UINT8, 1, 3600, 0x01)
zigbee.levelConfig()
} }
def refreshAttributes() { def refreshAttributes() {
zigbee.onOffRefresh() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh() + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
zigbee.levelRefresh() +
zigbee.colorTemperatureRefresh() +
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) +
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
} }
def setColorTemperature(value) { def setColorTemperature(value) {
@@ -145,32 +139,17 @@ def setLevel(value) {
zigbee.setLevel(value) + zigbee.onOffRefresh() + zigbee.levelRefresh() //adding refresh because of ZLL bulb not conforming to send-me-a-report zigbee.setLevel(value) + zigbee.onOffRefresh() + zigbee.levelRefresh() //adding refresh because of ZLL bulb not conforming to send-me-a-report
} }
private getScaledHue(value) {
zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
}
private getScaledSaturation(value) {
zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
}
def setColor(value){ def setColor(value){
log.trace "setColor($value)" log.trace "setColor($value)"
zigbee.on() + zigbee.on() + setHue(value.hue) + ["delay 300"] + setSaturation(value.saturation) + ["delay 2000"] + refreshAttributes()
zigbee.command(COLOR_CONTROL_CLUSTER, MOVE_TO_HUE_AND_SATURATION_COMMAND,
getScaledHue(value.hue), getScaledSaturation(value.saturation), "0000") +
zigbee.onOffRefresh() +
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) +
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
} }
def setHue(value) { def setHue(value) {
//payload-> hue value, direction (00-> shortest distance), transition time (1/10th second) def scaledHueValue = zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
zigbee.command(COLOR_CONTROL_CLUSTER, HUE_COMMAND, getScaledHue(value), "00", "0000") + zigbee.command(COLOR_CONTROL_CLUSTER, HUE_COMMAND, scaledHueValue, "00", "0500") + ["delay 1500"] + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) //payload-> hue value, direction (00-> shortest distance), transition time (1/10th second) (0500 in U16 reads 5)
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE)
} }
def setSaturation(value) { def setSaturation(value) {
//payload-> sat value, transition time def scaledSatValue = zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
zigbee.command(COLOR_CONTROL_CLUSTER, SATURATION_COMMAND, getScaledSaturation(value), "0000") + zigbee.command(COLOR_CONTROL_CLUSTER, SATURATION_COMMAND, scaledSatValue, "0500") + ["delay 1500"] + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION) //payload-> sat value, transition time
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright 2017 SmartThings * Copyright 2016 SmartThings
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * 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: * in compliance with the License. You may obtain a copy of the License at:
@@ -66,11 +66,6 @@ metadata {
} }
} }
// Globals
private getMOVE_TO_COLOR_TEMPERATURE_COMMAND() { 0x0A }
private getCOLOR_CONTROL_CLUSTER() { 0x0300 }
private getATTRIBUTE_COLOR_TEMPERATURE() { 0x0007 }
// 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"
@@ -100,14 +95,14 @@ def setLevel(value) {
} }
def refresh() { def refresh() {
def cmds = zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh() def cmds = zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh()
// Do NOT config if the device is the Eaton Halo_LT01, it responds with "switch:off" to onOffConfig, and maybe other weird things with the others // Do NOT config if the device is the Eaton Halo_LT01, it responds with "switch:off" to onOffConfig, and maybe other weird things with the others
if (!((device.getDataValue("manufacturer") == "Eaton") && (device.getDataValue("model") == "Halo_LT01"))) { if (!((device.getDataValue("manufacturer") == "Eaton") && (device.getDataValue("model") == "Halo_LT01"))) {
cmds += zigbee.onOffConfig() + zigbee.levelConfig() cmds = cmds + zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig()
} }
cmds cmds
} }
def poll() { def poll() {
@@ -143,7 +138,7 @@ def configure() {
log.debug "configure()" log.debug "configure()"
configureHealthCheck() configureHealthCheck()
// Implementation note: for the Eaton Halo_LT01, it responds with "switch:off" to onOffConfig, so be sure this is before the call to onOffRefresh // Implementation note: for the Eaton Halo_LT01, it responds with "switch:off" to onOffConfig, so be sure this is before the call to onOffRefresh
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh() zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh()
} }
def updated() { def updated() {
@@ -153,12 +148,7 @@ def updated() {
def setColorTemperature(value) { def setColorTemperature(value) {
setGenericName(value) setGenericName(value)
value = value as Integer zigbee.setColorTemperature(value) + ["delay 1500"] + zigbee.colorTemperatureRefresh()
def tempInMired = (1000000 / value) as Integer
def finalHex = zigbee.swapEndianHex(zigbee.convertToHexString(tempInMired, 4))
zigbee.command(COLOR_CONTROL_CLUSTER, MOVE_TO_COLOR_TEMPERATURE_COMMAND, "$finalHex 0000") +
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_COLOR_TEMPERATURE)
} }
//Naming based on the wiki article here: http://en.wikipedia.org/wiki/Color_temperature //Naming based on the wiki article here: http://en.wikipedia.org/wiki/Color_temperature

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright 2017 SmartThings * Copyright 2016 SmartThings
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * 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: * in compliance with the License. You may obtain a copy of the License at:
@@ -68,11 +68,6 @@ metadata {
} }
} }
// Globals
private getMOVE_TO_COLOR_TEMPERATURE_COMMAND() { 0x0A }
private getCOLOR_CONTROL_CLUSTER() { 0x0300 }
private getATTRIBUTE_COLOR_TEMPERATURE() { 0x0007 }
// 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"
@@ -99,11 +94,7 @@ def setLevel(value) {
} }
def refresh() { def refresh() {
zigbee.onOffRefresh() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh() + zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig()
zigbee.levelRefresh() +
zigbee.colorTemperatureRefresh() +
zigbee.onOffConfig() +
zigbee.levelConfig()
} }
def poll() { def poll() {
@@ -138,7 +129,8 @@ def configureHealthCheck() {
def configure() { def configure() {
log.debug "configure()" log.debug "configure()"
configureHealthCheck() configureHealthCheck()
refresh() zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh()
} }
def updated() { def updated() {
@@ -148,12 +140,7 @@ def updated() {
def setColorTemperature(value) { def setColorTemperature(value) {
setGenericName(value) setGenericName(value)
value = value as Integer zigbee.setColorTemperature(value) + ["delay 1500"] + zigbee.colorTemperatureRefresh()
def tempInMired = (1000000 / value) as Integer
def finalHex = zigbee.swapEndianHex(zigbee.convertToHexString(tempInMired, 4))
zigbee.command(COLOR_CONTROL_CLUSTER, MOVE_TO_COLOR_TEMPERATURE_COMMAND, "$finalHex 0000") +
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_COLOR_TEMPERATURE)
} }
//Naming based on the wiki article here: http://en.wikipedia.org/wiki/Color_temperature //Naming based on the wiki article here: http://en.wikipedia.org/wiki/Color_temperature

View File

@@ -1,7 +1,3 @@
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
/** /**
* OpenT2T SmartApp Test * OpenT2T SmartApp Test
* *
@@ -43,7 +39,7 @@ definition(
* garageDoors | door | open, close | unknown, closed, open, closing, opening * garageDoors | door | open, close | unknown, closed, open, closing, opening
* cameras | image | take | <String> * cameras | image | take | <String>
* thermostats | thermostat | setHeatingSetpoint, | temperature, heatingSetpoint, coolingSetpoint, * thermostats | thermostat | setHeatingSetpoint, | temperature, heatingSetpoint, coolingSetpoint,
* | | setCoolingSetpoint, | thermostatSetpoint, thermostatMode, * | | setCoolingSetpoint, | thermostatSetpoint, thermostatMode,
* | | off, heat, cool, auto,| thermostatFanMode, thermostatOperatingState * | | off, heat, cool, auto,| thermostatFanMode, thermostatOperatingState
* | | emergencyHeat, | * | | emergencyHeat, |
* | | setThermostatMode, | * | | setThermostatMode, |
@@ -59,7 +55,7 @@ preferences {
input "contactSensors", "capability.contactSensor", title: "Which Contact Sensors", multiple: true, required: false, hideWhenEmpty: true input "contactSensors", "capability.contactSensor", title: "Which Contact Sensors", multiple: true, required: false, hideWhenEmpty: true
input "garageDoors", "capability.garageDoorControl", title: "Which Garage Doors?", multiple: true, required: false, hideWhenEmpty: true input "garageDoors", "capability.garageDoorControl", title: "Which Garage Doors?", multiple: true, required: false, hideWhenEmpty: true
input "locks", "capability.lock", title: "Which Locks?", multiple: true, required: false, hideWhenEmpty: true input "locks", "capability.lock", title: "Which Locks?", multiple: true, required: false, hideWhenEmpty: true
input "cameras", "capability.videoCapture", title: "Which Cameras?", multiple: true, required: false, hideWhenEmpty: true input "cameras", "capability.videoCapture", title: "Which Cameras?", multiple: true, required: false, hideWhenEmpty: true
input "motionSensors", "capability.motionSensor", title: "Which Motion Sensors?", multiple: true, required: false, hideWhenEmpty: true input "motionSensors", "capability.motionSensor", title: "Which Motion Sensors?", multiple: true, required: false, hideWhenEmpty: true
input "presenceSensors", "capability.presenceSensor", title: "Which Presence Sensors", multiple: true, required: false, hideWhenEmpty: true input "presenceSensors", "capability.presenceSensor", title: "Which Presence Sensors", multiple: true, required: false, hideWhenEmpty: true
input "switches", "capability.switch", title: "Which Switches and Lights?", multiple: true, required: false, hideWhenEmpty: true input "switches", "capability.switch", title: "Which Switches and Lights?", multiple: true, required: false, hideWhenEmpty: true
@@ -70,49 +66,44 @@ preferences {
def getInputs() { def getInputs() {
def inputList = [] def inputList = []
inputList += contactSensors ?: [] inputList += contactSensors?: []
inputList += garageDoors ?: [] inputList += garageDoors?: []
inputList += locks ?: [] inputList += locks?: []
inputList += cameras ?: [] inputList += cameras?: []
inputList += motionSensors ?: [] inputList += motionSensors?: []
inputList += presenceSensors ?: [] inputList += presenceSensors?: []
inputList += switches ?: [] inputList += switches?: []
inputList += thermostats ?: [] inputList += thermostats?: []
inputList += waterSensors ?: [] inputList += waterSensors?: []
return inputList return inputList
} }
//API external Endpoints //API external Endpoints
mappings { mappings {
path("/devices") { path("/devices") {
action: action: [
[
GET: "getDevices" GET: "getDevices"
] ]
} }
path("/devices/:id") { path("/devices/:id") {
action: action: [
[
GET: "getDevice" GET: "getDevice"
] ]
} }
path("/update/:id") { path("/update/:id") {
action: action: [
[
PUT: "updateDevice" PUT: "updateDevice"
] ]
} }
path("/deviceSubscription") { path("/deviceSubscription") {
action: action: [
[ POST: "registerDeviceChange",
POST : "registerDeviceChange",
DELETE: "unregisterDeviceChange" DELETE: "unregisterDeviceChange"
] ]
} }
path("/locationSubscription") { path("/locationSubscription") {
action: action: [
[ POST: "registerDeviceGraph",
POST : "registerDeviceGraph",
DELETE: "unregisterDeviceGraph" DELETE: "unregisterDeviceGraph"
] ]
} }
@@ -125,21 +116,14 @@ def installed() {
def updated() { def updated() {
log.debug "Updating with settings: ${settings}" log.debug "Updating with settings: ${settings}"
if(state.deviceSubscriptionMap == null){
//Initialize state variables if didn't exist.
if (state.deviceSubscriptionMap == null) {
state.deviceSubscriptionMap = [:] state.deviceSubscriptionMap = [:]
log.debug "deviceSubscriptionMap created." log.debug "deviceSubscriptionMap created."
} }
if (state.locationSubscriptionMap == null) { if( state.locationSubscriptionMap == null){
state.locationSubscriptionMap = [:] state.locationSubscriptionMap = [:]
log.debug "locationSubscriptionMap created." log.debug "locationSubscriptionMap created."
} }
if (state.verificationKeyMap == null) {
state.verificationKeyMap = [:]
log.debug "verificationKeyMap created."
}
unsubscribe() unsubscribe()
registerAllDeviceSubscriptions() registerAllDeviceSubscriptions()
} }
@@ -148,11 +132,9 @@ def initialize() {
log.debug "Initializing with settings: ${settings}" log.debug "Initializing with settings: ${settings}"
state.deviceSubscriptionMap = [:] state.deviceSubscriptionMap = [:]
log.debug "deviceSubscriptionMap created." log.debug "deviceSubscriptionMap created."
registerAllDeviceSubscriptions()
state.locationSubscriptionMap = [:] state.locationSubscriptionMap = [:]
log.debug "locationSubscriptionMap created." log.debug "locationSubscriptionMap created."
state.verificationKeyMap = [:]
log.debug "verificationKeyMap created."
registerAllDeviceSubscriptions()
} }
/*** Subscription Functions ***/ /*** Subscription Functions ***/
@@ -162,43 +144,47 @@ def registerAllDeviceSubscriptions() {
registerChangeHandler(inputs) registerChangeHandler(inputs)
} }
//Subscribe to events from a list of devices
def registerChangeHandler(myList) {
myList.each { myDevice ->
def theAtts = myDevice.supportedAttributes
theAtts.each {att ->
subscribe(myDevice, att.name, deviceEventHandler)
log.info "Registering for ${myDevice.displayName}.${att.name}"
}
}
}
//Endpoints function: Subscribe to events from a specific device //Endpoints function: Subscribe to events from a specific device
def registerDeviceChange() { def registerDeviceChange() {
def subscriptionEndpt = params.subscriptionURL def subscriptionEndpt = params.subscriptionURL
def deviceId = params.deviceId def deviceId = params.deviceId
def myDevice = findDevice(deviceId) def myDevice = findDevice(deviceId)
if( myDevice == null ){
if (myDevice == null) {
httpError(404, "Cannot find device with device ID ${deviceId}.") httpError(404, "Cannot find device with device ID ${deviceId}.")
} }
def theAtts = myDevice.supportedAttributes def theAtts = myDevice.supportedAttributes
try { try {
theAtts.each { att -> theAtts.each {att ->
subscribe(myDevice, att.name, deviceEventHandler) subscribe(myDevice, att.name, deviceEventHandler)
} }
log.info "Subscribing for ${myDevice.displayName}" log.info "Subscribing for ${myDevice.displayName}"
if (subscriptionEndpt != null) { if(subscriptionEndpt != null){
if (state.deviceSubscriptionMap[deviceId] == null) { if(state.deviceSubscriptionMap[deviceId] == null){
state.deviceSubscriptionMap.put(deviceId, [subscriptionEndpt]) state.deviceSubscriptionMap.put(deviceId, [subscriptionEndpt])
log.info "Added subscription URL: ${subscriptionEndpt} for ${myDevice.displayName}" log.info "Added subscription URL: ${subscriptionEndpt} for ${myDevice.displayName}"
} else if (!state.deviceSubscriptionMap[deviceId].contains(subscriptionEndpt)) { } else if (!state.deviceSubscriptionMap[deviceId].contains(subscriptionEndpt)){
state.deviceSubscriptionMap[deviceId] << subscriptionEndpt state.deviceSubscriptionMap[deviceId] << subscriptionEndpt
log.info "Added subscription URL: ${subscriptionEndpt} for ${myDevice.displayName}" log.info "Added subscription URL: ${subscriptionEndpt} for ${myDevice.displayName}"
} }
if (params.key != null) {
state.verificationKeyMap[subscriptionEndpt] = params.key
log.info "Added verification key: ${params.key} for ${subscriptionEndpt}"
}
} }
} catch (e) { } catch (e) {
httpError(500, "something went wrong: $e") httpError(500, "something went wrong: $e")
} }
log.info "Current subscription map is ${state.deviceSubscriptionMap}" log.info "Current subscription map is ${state.deviceSubscriptionMap}"
log.info "Current verification key map is ${state.verificationKeyMap}"
return ["succeed"] return ["succeed"]
} }
@@ -208,19 +194,18 @@ def unregisterDeviceChange() {
def deviceId = params.deviceId def deviceId = params.deviceId
def myDevice = findDevice(deviceId) def myDevice = findDevice(deviceId)
if (myDevice == null) { if( myDevice == null ){
httpError(404, "Cannot find device with device ID ${deviceId}.") httpError(404, "Cannot find device with device ID ${deviceId}.")
} }
try { try {
if (subscriptionEndpt != null && subscriptionEndpt != "undefined") { if(subscriptionEndpt != null && subscriptionEndpt != "undefined"){
if (state.deviceSubscriptionMap[deviceId]?.contains(subscriptionEndpt)) { if (state.deviceSubscriptionMap[deviceId]?.contains(subscriptionEndpt)){
if (state.deviceSubscriptionMap[deviceId].size() == 1) { if(state.deviceSubscriptionMap[deviceId].size() == 1){
state.deviceSubscriptionMap.remove(deviceId) state.deviceSubscriptionMap.remove(deviceId)
} else { } else {
state.deviceSubscriptionMap[deviceId].remove(subscriptionEndpt) state.deviceSubscriptionMap[deviceId].remove(subscriptionEndpt)
} }
state.verificationKeyMap.remove(subscriptionEndpt)
log.info "Removed subscription URL: ${subscriptionEndpt} for ${myDevice.displayName}" log.info "Removed subscription URL: ${subscriptionEndpt} for ${myDevice.displayName}"
} }
} else { } else {
@@ -232,33 +217,25 @@ def unregisterDeviceChange() {
} }
log.info "Current subscription map is ${state.deviceSubscriptionMap}" log.info "Current subscription map is ${state.deviceSubscriptionMap}"
log.info "Current verification key map is ${state.verificationKeyMap}"
} }
//Endpoints function: Subscribe to device additiona/removal updated in a location //Endpoints function: Subscribe to device additiona/removal updated in a location
def registerDeviceGraph() { def registerDeviceGraph() {
def subscriptionEndpt = params.subscriptionURL def subscriptionEndpt = params.subscriptionURL
if (subscriptionEndpt != null && subscriptionEndpt != "undefined") { if (subscriptionEndpt != null && subscriptionEndpt != "undefined"){
subscribe(location, "DeviceCreated", locationEventHandler, [filterEvents: false]) subscribe(location, "DeviceCreated", locationEventHandler, [filterEvents: false])
subscribe(location, "DeviceUpdated", locationEventHandler, [filterEvents: false]) subscribe(location, "DeviceUpdated", locationEventHandler, [filterEvents: false])
subscribe(location, "DeviceDeleted", locationEventHandler, [filterEvents: false]) subscribe(location, "DeviceDeleted", locationEventHandler, [filterEvents: false])
if (state.locationSubscriptionMap[location.id] == null) { if(state.locationSubscriptionMap[location.id] == null){
state.locationSubscriptionMap.put(location.id, [subscriptionEndpt]) state.locationSubscriptionMap.put(location.id, [subscriptionEndpt])
log.info "Added subscription URL: ${subscriptionEndpt} for Location ${location.name}" log.info "Added subscription URL: ${subscriptionEndpt} for Location ${location.name}"
} else if (!state.locationSubscriptionMap[location.id].contains(subscriptionEndpt)) { } else if (!state.locationSubscriptionMap[location.id].contains(subscriptionEndpt)){
state.locationSubscriptionMap[location.id] << subscriptionEndpt state.locationSubscriptionMap[location.id] << subscriptionEndpt
log.info "Added subscription URL: ${subscriptionEndpt} for Location ${location.name}" log.info "Added subscription URL: ${subscriptionEndpt} for Location ${location.name}"
} }
if (params.key != null) {
state.verificationKeyMap[subscriptionEndpt] = params.key
log.info "Added verification key: ${params.key} for ${subscriptionEndpt}"
}
log.info "Current location subscription map is ${state.locationSubscriptionMap}" log.info "Current location subscription map is ${state.locationSubscriptionMap}"
log.info "Current verification key map is ${state.verificationKeyMap}"
return ["succeed"] return ["succeed"]
} else { } else {
httpError(400, "missing input parameter: subscriptionURL") httpError(400, "missing input parameter: subscriptionURL")
@@ -270,17 +247,16 @@ def unregisterDeviceGraph() {
def subscriptionEndpt = params.subscriptionURL def subscriptionEndpt = params.subscriptionURL
try { try {
if (subscriptionEndpt != null && subscriptionEndpt != "undefined") { if(subscriptionEndpt != null && subscriptionEndpt != "undefined"){
if (state.locationSubscriptionMap[location.id]?.contains(subscriptionEndpt)) { if (state.locationSubscriptionMap[location.id]?.contains(subscriptionEndpt)){
if (state.locationSubscriptionMap[location.id].size() == 1) { if(state.locationSubscriptionMap[location.id].size() == 1){
state.locationSubscriptionMap.remove(location.id) state.locationSubscriptionMap.remove(location.id)
} else { } else {
state.locationSubscriptionMap[location.id].remove(subscriptionEndpt) state.locationSubscriptionMap[location.id].remove(subscriptionEndpt)
} }
state.verificationKeyMap.remove(subscriptionEndpt)
log.info "Removed subscription URL: ${subscriptionEndpt} for Location ${location.name}" log.info "Removed subscription URL: ${subscriptionEndpt} for Location ${location.name}"
} }
} else { }else{
httpError(400, "missing input parameter: subscriptionURL") httpError(400, "missing input parameter: subscriptionURL")
} }
} catch (e) { } catch (e) {
@@ -288,40 +264,28 @@ def unregisterDeviceGraph() {
} }
log.info "Current location subscription map is ${state.locationSubscriptionMap}" log.info "Current location subscription map is ${state.locationSubscriptionMap}"
log.info "Current verification key map is ${state.verificationKeyMap}"
} }
//When events are triggered, send HTTP post to web socket servers //When events are triggered, send HTTP post to web socket servers
def deviceEventHandler(evt) { def deviceEventHandler(evt) {
def evtDevice = evt.device def evt_device = evt.device
def evtDeviceType = getDeviceType(evtDevice) def evt_deviceType = getDeviceType(evt_device)
def deviceData = []; def deviceInfo
if (evt.data != null) { def params = [ body: [deviceName: evt_device.displayName, deviceId: evt_device.id, locationId: location.id] ]
if(evt.data != null){
def evtData = parseJson(evt.data) def evtData = parseJson(evt.data)
log.info "Received event for ${evtDevice.displayName}, data: ${evtData}, description: ${evt.descriptionText}" log.info "Received event for ${evt_device.displayName}, data: ${evtData}, description: ${evt.descriptionText}"
} }
if (evtDeviceType == "thermostat") {
deviceData = [name: evtDevice.displayName, id: evtDevice.id, status: evtDevice.status, deviceType: evtDeviceType, manufacturer: evtDevice.manufacturerName, model: evtDevice.modelName, attributes: deviceAttributeList(evtDevice, evtDeviceType), locationMode: getLocationModeInfo(), locationId: location.id]
} else {
deviceData = [name: evtDevice.displayName, id: evtDevice.id, status: evtDevice.status, deviceType: evtDeviceType, manufacturer: evtDevice.manufacturerName, model: evtDevice.modelName, attributes: deviceAttributeList(evtDevice, evtDeviceType), locationId: location.id]
}
def params = [body: deviceData]
//send event to all subscriptions urls //send event to all subscriptions urls
log.debug "Current subscription urls for ${evtDevice.displayName} is ${state.deviceSubscriptionMap[evtDevice.id]}" log.debug "Current subscription urls for ${evt_device.displayName} is ${state.deviceSubscriptionMap[evt_device.id]}"
state.deviceSubscriptionMap[evtDevice.id].each { state.deviceSubscriptionMap[evt_device.id].each {
params.uri = "${it}" params.uri = "${it}"
if (state.verificationKeyMap[it] != null) {
def key = state.verificationKeyMap[it]
params.header = [Signature: ComputHMACValue(key, groovy.json.JsonOutput.toJson(params.body))]
}
log.trace "POST URI: ${params.uri}" log.trace "POST URI: ${params.uri}"
log.trace "Header: ${params.header}"
log.trace "Payload: ${params.body}" log.trace "Payload: ${params.body}"
try { try{
httpPostJson(params) { resp -> httpPostJson(params) { resp ->
log.trace "response status code: ${resp.status}" log.trace "response status code: ${resp.status}"
log.trace "response data: ${resp.data}" log.trace "response data: ${resp.data}"
@@ -334,27 +298,20 @@ def deviceEventHandler(evt) {
def locationEventHandler(evt) { def locationEventHandler(evt) {
log.info "Received event for location ${location.name}/${location.id}, Event: ${evt.name}, description: ${evt.descriptionText}, apiServerUrl: ${apiServerUrl("")}" log.info "Received event for location ${location.name}/${location.id}, Event: ${evt.name}, description: ${evt.descriptionText}, apiServerUrl: ${apiServerUrl("")}"
switch (evt.name) { switch(evt.name){
case "DeviceCreated": case "DeviceCreated":
case "DeviceDeleted": case "DeviceDeleted":
def evtDevice = evt.device def evt_device = evt.device
def evtDeviceType = getDeviceType(evtDevice) def evt_deviceType = getDeviceType(evt_device)
def params = [body: [eventType: evt.name, deviceId: evtDevice.id, locationId: location.id]] log.info "DeviceName: ${evt_device.displayName}, DeviceID: ${evt_device.id}, deviceType: ${evt_deviceType}"
if (evt.name == "DeviceDeleted" && state.deviceSubscriptionMap[deviceId] != null) { def params = [ body: [ eventType:evt.name, deviceId: evt_device.id, locationId: location.id ] ]
state.deviceSubscriptionMap.remove(evtDevice.id)
}
state.locationSubscriptionMap[location.id].each { state.locationSubscriptionMap[location.id].each {
params.uri = "${it}" params.uri = "${it}"
if (state.verificationKeyMap[it] != null) {
def key = state.verificationKeyMap[it]
params.header = [Signature: ComputHMACValue(key, groovy.json.JsonOutput.toJson(params.body))]
}
log.trace "POST URI: ${params.uri}" log.trace "POST URI: ${params.uri}"
log.trace "Header: ${params.header}"
log.trace "Payload: ${params.body}" log.trace "Payload: ${params.body}"
try { try{
httpPostJson(params) { resp -> httpPostJson(params) { resp ->
log.trace "response status code: ${resp.status}" log.trace "response status code: ${resp.status}"
log.trace "response data: ${resp.data}" log.trace "response data: ${resp.data}"
@@ -369,23 +326,6 @@ def locationEventHandler(evt) {
} }
} }
private ComputHMACValue(key, data) {
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA1")
Mac mac = Mac.getInstance("HmacSHA1")
mac.init(secretKeySpec)
byte[] digest = mac.doFinal(data.getBytes("UTF-8"))
return byteArrayToString(digest)
} catch (InvalidKeyException e) {
log.error "Invalid key exception while converting to HMac SHA1"
}
}
private def byteArrayToString(byte[] data) {
BigInteger bigInteger = new BigInteger(1, data)
String hash = bigInteger.toString(16)
return hash
}
/*** Device Query/Update Functions ***/ /*** Device Query/Update Functions ***/
@@ -394,10 +334,10 @@ def getDevices() {
def deviceData = [] def deviceData = []
inputs?.each { inputs?.each {
def deviceType = getDeviceType(it) def deviceType = getDeviceType(it)
if (deviceType == "thermostat") { if(deviceType == "thermostat") {
deviceData << [name: it.displayName, id: it.id, status: it.status, deviceType: deviceType, manufacturer: it.manufacturerName, model: it.modelName, attributes: deviceAttributeList(it, deviceType), locationMode: getLocationModeInfo()] deviceData << [name: it.displayName, id: it.id, status:it.status, deviceType:deviceType, manufacturer:it.manufacturerName, model:it.modelName, attributes: deviceAttributeList(it, deviceType), locationMode: getLocationModeInfo()]
} else { } else {
deviceData << [name: it.displayName, id: it.id, status: it.status, deviceType: deviceType, manufacturer: it.manufacturerName, model: it.modelName, attributes: deviceAttributeList(it, deviceType)] deviceData << [name: it.displayName, id: it.id, status:it.status, deviceType:deviceType, manufacturer:it.manufacturerName, model:it.modelName, attributes: deviceAttributeList(it, deviceType)]
} }
} }
@@ -410,10 +350,10 @@ def getDevice() {
def it = findDevice(params.id) def it = findDevice(params.id)
def deviceType = getDeviceType(it) def deviceType = getDeviceType(it)
def device def device
if (deviceType == "thermostat") { if(deviceType == "thermostat") {
device = [name: it.displayName, id: it.id, status: it.status, deviceType: deviceType, manufacturer: it.manufacturerName, model: it.modelName, attributes: deviceAttributeList(it, deviceType), locationMode: getLocationModeInfo()] device = [name: it.displayName, id: it.id, status:it.status, deviceType:deviceType, manufacturer:it.manufacturerName, model:it.modelName, attributes: deviceAttributeList(it,deviceType), locationMode: getLocationModeInfo()]
} else { } else {
device = [name: it.displayName, id: it.id, status: it.status, deviceType: deviceType, manufacturer: it.manufacturerName, model: it.modelName, attributes: deviceAttributeList(it, deviceType)] device = [name: it.displayName, id: it.id, status:it.status, deviceType:deviceType, manufacturer:it.manufacturerName, model:it.modelName, attributes: deviceAttributeList(it, deviceType)]
} }
log.debug "getDevice, return: ${device}" log.debug "getDevice, return: ${device}"
@@ -426,18 +366,18 @@ void updateDevice() {
request.JSON.each { request.JSON.each {
def command = it.key def command = it.key
def value = it.value def value = it.value
if (command) { if (command){
def commandList = mapDeviceCommands(command, value) def commandList = mapDeviceCommands(command, value)
command = commandList[0] command = commandList[0]
value = commandList[1] value = commandList[1]
if (command == "setAwayMode") { if (command == "setAwayMode") {
log.info "Setting away mode to ${value}" log.info "Setting away mode to ${value}"
if (location.modes?.find { it.name == value }) { if (location.modes?.find {it.name == value}) {
location.setMode(value) location.setMode(value)
} }
} else if (command == "thermostatSetpoint") { }else if (command == "thermostatSetpoint"){
switch (device.currentThermostatMode) { switch(device.currentThermostatMode){
case "cool": case "cool":
log.info "Update: ${device.displayName}, [${command}, ${value}]" log.info "Update: ${device.displayName}, [${command}, ${value}]"
device.setCoolingSetpoint(value) device.setCoolingSetpoint(value)
@@ -451,7 +391,7 @@ void updateDevice() {
httpError(501, "this mode: ${device.currentThermostatMode} does not allow changing thermostat setpoint.") httpError(501, "this mode: ${device.currentThermostatMode} does not allow changing thermostat setpoint.")
break break
} }
} else if (!device) { }else if (!device) {
log.error "updateDevice, Device not found" log.error "updateDevice, Device not found"
httpError(404, "Device not found") httpError(404, "Device not found")
} else if (!device.hasCommand(command)) { } else if (!device.hasCommand(command)) {
@@ -461,11 +401,11 @@ void updateDevice() {
if (command == "setColor") { if (command == "setColor") {
log.info "Update: ${device.displayName}, [${command}, ${value}]" log.info "Update: ${device.displayName}, [${command}, ${value}]"
device."$command"(hex: value) device."$command"(hex: value)
} else if (value.isNumber()) { } else if(value.isNumber()) {
def intValue = value as Integer def intValue = value as Integer
log.info "Update: ${device.displayName}, [${command}, ${intValue}(int)]" log.info "Update: ${device.displayName}, [${command}, ${intValue}(int)]"
device."$command"(intValue) device."$command"(intValue)
} else if (value) { } else if (value){
log.info "Update: ${device.displayName}, [${command}, ${value}]" log.info "Update: ${device.displayName}, [${command}, ${value}]"
device."$command"(value) device."$command"(value)
} else { } else {
@@ -492,16 +432,17 @@ private getDeviceType(device) {
log.debug "supported commands: [${device}, ${device.supportedCommands}]" log.debug "supported commands: [${device}, ${device.supportedCommands}]"
//Loop through the device capability list to determine the device type. //Loop through the device capability list to determine the device type.
capabilities.each { capability -> capabilities.each {capability ->
switch (capability.name.toLowerCase()) { switch(capability.name.toLowerCase())
{
case "switch": case "switch":
deviceType = "switch" deviceType = "switch"
//If the device also contains "Switch Level" capability, identify it as a "light" device. //If the device also contains "Switch Level" capability, identify it as a "light" device.
if (capabilities.any { it.name.toLowerCase() == "switch level" }) { if (capabilities.any{it.name.toLowerCase() == "switch level"}){
//If the device also contains "Power Meter" capability, identify it as a "dimmerSwitch" device. //If the device also contains "Power Meter" capability, identify it as a "dimmerSwitch" device.
if (capabilities.any { it.name.toLowerCase() == "power meter" }) { if (capabilities.any{it.name.toLowerCase() == "power meter"}){
deviceType = "dimmerSwitch" deviceType = "dimmerSwitch"
return deviceType return deviceType
} else { } else {
@@ -548,24 +489,24 @@ private deviceAttributeList(device, deviceType) {
allAttributes.each { attribute -> allAttributes.each { attribute ->
try { try {
def currentState = device.currentState(attribute.name) def currentState = device.currentState(attribute.name)
if (currentState != null) { if(currentState != null ){
switch (attribute.name) { switch(attribute.name){
case 'temperature': case 'temperature':
attributeList.putAll([(attribute.name): currentState.value, 'temperatureScale': location.temperatureScale]) attributeList.putAll([ (attribute.name): currentState.value, 'temperatureScale':location.temperatureScale ])
break; break;
default: default:
attributeList.putAll([(attribute.name): currentState.value]) attributeList.putAll([(attribute.name): currentState.value ])
break; break;
} }
if (deviceType == "genericSensor") { if( deviceType == "genericSensor" ){
def key = attribute.name + "_lastUpdated" def key = attribute.name + "_lastUpdated"
attributeList.putAll([(key): currentState.isoDate]) attributeList.putAll([ (key): currentState.isoDate ])
} }
} else { } else {
attributeList.putAll([(attribute.name): null]); attributeList.putAll([ (attribute.name): null ]);
} }
} catch (e) { } catch(e) {
attributeList.putAll([(attribute.name): null]); attributeList.putAll([ (attribute.name): null ]);
} }
} }
return attributeList return attributeList
@@ -638,7 +579,8 @@ private mapDeviceCommands(command, value) {
if (value == 1 || value == "1" || value == "lock") { if (value == 1 || value == "1" || value == "lock") {
resultCommand = "lock" resultCommand = "lock"
resultValue = "" resultValue = ""
} else if (value == 0 || value == "0" || value == "unlock") { }
else if (value == 0 || value == "0" || value == "unlock") {
resultCommand = "unlock" resultCommand = "unlock"
resultValue = "" resultValue = ""
} }
@@ -647,5 +589,5 @@ private mapDeviceCommands(command, value) {
break break
} }
return [resultCommand, resultValue] return [resultCommand,resultValue]
} }