Compare commits

...

10 Commits

Author SHA1 Message Date
Vinay Rao
090a306939 Merge pull request #1184 from SmartThingsCommunity/staging
Rolling down staging hotfix to master
2016-08-31 16:05:08 -07:00
Vinay Rao
d0a16c10b2 Merge pull request #1183 from SmartThingsCommunity/production
Rolling down production hotfix to staging
2016-08-31 16:04:45 -07:00
Lars Finander
faa65f204d Merge pull request #1181 from larsfinander/WWST-40_Philips_Hue_device_watch_staging
WWST-40 Philips Hue: Implement device watch
2016-08-31 16:39:26 -06:00
Lars Finander
bacd335991 WWST-40 Philips Hue: Implement device watch 2016-08-31 15:00:34 -06:00
Vinay Rao
740e5e096c Merge pull request #1179 from varzac/smartsense-motion-temp-alarm2
DVCSMP-1999 Use proper IAS Zone alarm for triggering motion
2016-08-31 13:00:49 -07:00
Vinay Rao
aac2f9b177 Merge pull request #1174 from workingmonk/bug/osram_hue_name
SSVD-2632 The Device Details Recently tab does not log any
2016-08-31 13:00:24 -07:00
Vinay Rao
048eb77e64 fixes SSVD-2632 The Device Details Recently tab does not log any activity regarding OSRAM Lightify RGWB bulb color change 2016-08-31 12:47:09 -07:00
Zach Varberg
dadec937fa Use proper IAS Zone alarm for triggering motion
Previously we were incorrectly looking at the alarm1 bit when we should
be looking at the alarm2 bit.

This resolves: https://smartthings.atlassian.net/browse/DVCSMP-1999
2016-08-31 13:09:18 -05:00
Vinay Rao
78aa6691c4 Merge pull request #1173 from SmartThingsCommunity/master
Rolling up changes for next week deploy
2016-08-30 13:32:36 -07:00
Vinay Rao
a22f71bc29 Merge pull request #1171 from SmartThingsCommunity/staging
Rolling up staging to prod for deploy
2016-08-30 12:57:12 -07:00
7 changed files with 133 additions and 50 deletions

View File

@@ -16,6 +16,7 @@ metadata {
capability "Switch" capability "Switch"
capability "Refresh" capability "Refresh"
capability "Sensor" capability "Sensor"
capability "Health Check"
command "setAdjustedColor" command "setAdjustedColor"
command "reset" command "reset"
@@ -55,6 +56,10 @@ metadata {
} }
} }
void installed() {
sendEvent(name: "checkInterval", value: 60 * 30, data: [protocol: "lan"], displayed: false)
}
// parse events into attributes // parse events into attributes
def parse(description) { def parse(description) {
log.debug "parse() - $description" log.debug "parse() - $description"
@@ -166,3 +171,7 @@ def verifyPercent(percent) {
return false return false
} }
} }
def ping() {
log.debug "${parent.ping(this)}"
}

View File

@@ -17,6 +17,7 @@ metadata {
capability "Switch" capability "Switch"
capability "Refresh" capability "Refresh"
capability "Sensor" capability "Sensor"
capability "Health Check"
command "setAdjustedColor" command "setAdjustedColor"
command "reset" command "reset"
@@ -64,6 +65,10 @@ metadata {
} }
} }
void installed() {
sendEvent(name: "checkInterval", value: 60 * 30, data: [protocol: "lan"], displayed: false)
}
// parse events into attributes // parse events into attributes
def parse(description) { def parse(description) {
log.debug "parse() - $description" log.debug "parse() - $description"
@@ -182,3 +187,7 @@ def verifyPercent(percent) {
return false return false
} }
} }
def ping() {
log.trace "${parent.ping(this)}"
}

View File

@@ -14,6 +14,7 @@ metadata {
capability "Switch" capability "Switch"
capability "Refresh" capability "Refresh"
capability "Sensor" capability "Sensor"
capability "Health Check"
command "refresh" command "refresh"
} }
@@ -48,6 +49,10 @@ metadata {
} }
} }
void installed() {
sendEvent(name: "checkInterval", value: 60 * 30, data: [protocol: "lan"], displayed: false)
}
// parse events into attributes // parse events into attributes
def parse(description) { def parse(description) {
log.debug "parse() - $description" log.debug "parse() - $description"
@@ -87,3 +92,7 @@ void refresh() {
log.debug "Executing 'refresh'" log.debug "Executing 'refresh'"
parent.manualRefresh() parent.manualRefresh()
} }
def ping() {
log.debug "${parent.ping(this)}"
}

View File

@@ -15,6 +15,7 @@ metadata {
capability "Color Temperature" capability "Color Temperature"
capability "Switch" capability "Switch"
capability "Refresh" capability "Refresh"
capability "Health Check"
command "refresh" command "refresh"
} }
@@ -53,6 +54,10 @@ metadata {
} }
} }
void installed() {
sendEvent(name: "checkInterval", value: 60 * 30, data: [protocol: "lan"], displayed: false)
}
// parse events into attributes // parse events into attributes
def parse(description) { def parse(description) {
log.debug "parse() - $description" log.debug "parse() - $description"
@@ -101,3 +106,7 @@ void refresh() {
log.debug "Executing 'refresh'" log.debug "Executing 'refresh'"
parent.manualRefresh() parent.manualRefresh()
} }
def ping() {
log.debug "${parent.ping(this)}"
}

View File

@@ -170,7 +170,7 @@ private Map parseCustomMessage(String description) {
private Map parseIasMessage(String description) { private Map parseIasMessage(String description) {
ZoneStatus zs = zigbee.parseZoneStatus(description) ZoneStatus zs = zigbee.parseZoneStatus(description)
return zs.isAlarm1Set() ? getMotionResult('active') : getMotionResult('inactive') return (zs.isAlarm1Set() || zs.isAlarm2Set()) ? getMotionResult('active') : getMotionResult('inactive')
} }
def getTemperature(value) { def getTemperature(value) {

View File

@@ -106,11 +106,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) / 255 * 360) def hueValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 360)
sendEvent(name: "hue", value: hueValue, displayed:false) 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) / 255 * 100) def saturationValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 100)
sendEvent(name: "saturation", value: saturationValue, displayed:false) sendEvent(name: "saturation", value: saturationValue, descriptionText: "Color has changed", displayed: false)
} }
} }
else { else {

View File

@@ -95,8 +95,7 @@ def bridgeDiscoveryFailed() {
} }
} }
def bridgeLinking() def bridgeLinking() {
{
int linkRefreshcount = !state.linkRefreshcount ? 0 : state.linkRefreshcount as int int linkRefreshcount = !state.linkRefreshcount ? 0 : state.linkRefreshcount as int
state.linkRefreshcount = linkRefreshcount + 1 state.linkRefreshcount = linkRefreshcount + 1
def refreshInterval = 3 def refreshInterval = 3
@@ -328,7 +327,7 @@ def bulbListHandler(hub, data = "") {
def object = new groovy.json.JsonSlurper().parseText(data) def object = new groovy.json.JsonSlurper().parseText(data)
object.each { k,v -> object.each { k,v ->
if (v instanceof Map) if (v instanceof Map)
bulbs[k] = [id: k, name: v.name, type: v.type, modelid: v.modelid, hub:hub] bulbs[k] = [id: k, name: v.name, type: v.type, modelid: v.modelid, hub:hub, online: v.state?.reachable]
} }
} }
def bridge = null def bridge = null
@@ -448,7 +447,6 @@ def addBridge() {
updateBridgeStatus(childDevice) updateBridgeStatus(childDevice)
childDevice.sendEvent(name: "idNumber", value: idNumber) childDevice.sendEvent(name: "idNumber", value: idNumber)
if (vbridge.value.ip && vbridge.value.port) { if (vbridge.value.ip && vbridge.value.port) {
if (vbridge.value.ip.contains(".")) { if (vbridge.value.ip.contains(".")) {
childDevice.sendEvent(name: "networkAddress", value: vbridge.value.ip + ":" + vbridge.value.port) childDevice.sendEvent(name: "networkAddress", value: vbridge.value.ip + ":" + vbridge.value.port)
@@ -649,8 +647,7 @@ def locationHandler(evt) {
} }
} }
} }
} } else if (parsedEvent.headers && parsedEvent.body) {
else if (parsedEvent.headers && parsedEvent.body) {
log.trace "HUE BRIDGE RESPONSES" log.trace "HUE BRIDGE RESPONSES"
def headerString = parsedEvent.headers.toString() def headerString = parsedEvent.headers.toString()
if (headerString?.contains("xml")) { if (headerString?.contains("xml")) {
@@ -733,7 +730,7 @@ private void updateBridgeStatus(childDevice) {
private void checkBridgeStatus() { private void checkBridgeStatus() {
def bridges = getHueBridges() def bridges = getHueBridges()
// Check if each bridge has been heard from within the last 16 minutes (3 poll intervals times 5 minutes plus buffer) // Check if each bridge has been heard from within the last 16 minutes (3 poll intervals times 5 minutes plus buffer)
def time = now() - (1000 * 60 * 16) def time = now() - (1000 * 60 * 30)
bridges.each { bridges.each {
def d = getChildDevice(it.value.mac) def d = getChildDevice(it.value.mac)
if(d) { if(d) {
@@ -746,6 +743,8 @@ private void checkBridgeStatus() {
if (it.value.lastActivity < time) { // it.value.lastActivity != null && if (it.value.lastActivity < time) { // it.value.lastActivity != null &&
log.warn "Bridge $it.key is Offline" log.warn "Bridge $it.key is Offline"
d.sendEvent(name: "status", value: "Offline") d.sendEvent(name: "status", value: "Offline")
// set all lights to offline since bridge is not reachable
state.bulbs?.each {it.value.online = false}
} else { } else {
d.sendEvent(name: "status", value: "Online")//setOnline(false) d.sendEvent(name: "status", value: "Online")//setOnline(false)
} }
@@ -781,8 +780,7 @@ def parse(childDevice, description) {
if (body instanceof java.util.Map) { if (body instanceof java.util.Map) {
// get (poll) reponse // get (poll) reponse
return handlePoll(body) return handlePoll(body)
} } else {
else {
//put response //put response
return handleCommandResponse(body) return handleCommandResponse(body)
} }
@@ -879,36 +877,40 @@ private handleCommandResponse(body) {
// scan entire response before sending events to make sure they are always in the same order // scan entire response before sending events to make sure they are always in the same order
def updates = [:] def updates = [:]
body.each { payload -> body.each { payload ->
log.debug $payload log.debug $payload
if (payload?.success) { if (payload?.success) {
def childDeviceNetworkId = app.id + "/" def childDeviceNetworkId = app.id + "/"
def eventType def eventType
payload.success.each { k, v -> payload.success.each { k, v ->
def data = k.split("/") def data = k.split("/")
if (data.length == 5) { if (data.length == 5) {
childDeviceNetworkId = app.id + "/" + k.split("/")[2] childDeviceNetworkId = app.id + "/" + k.split("/")[2]
if (!updates[childDeviceNetworkId]) if (!updates[childDeviceNetworkId])
updates[childDeviceNetworkId] = [:] updates[childDeviceNetworkId] = [:]
eventType = k.split("/")[4] eventType = k.split("/")[4]
updates[childDeviceNetworkId]."$eventType" = v updates[childDeviceNetworkId]."$eventType" = v
} }
} }
} else if (payload.error) { } else if (payload.error) {
log.warn "Error returned from Hue bridge error = ${body?.error}" log.warn "Error returned from Hue bridge error = ${body?.error}"
} }
} }
// send events for each update found above (order of events should be same as handlePoll()) // send events for each update found above (order of events should be same as handlePoll())
updates.each { childDeviceNetworkId, params -> updates.each { childDeviceNetworkId, params ->
def device = getChildDevice(childDeviceNetworkId) def device = getChildDevice(childDeviceNetworkId)
sendBasicEvents(device, "on", params.on) def id = getId(device)
sendBasicEvents(device, "bri", params.bri) // If device is offline, then don't send events which will update device watch
sendColorEvents(device, params.xy, params.hue, params.sat, params.ct) if (isOnline(id)) {
} sendBasicEvents(device, "on", params.on)
sendBasicEvents(device, "bri", params.bri)
sendColorEvents(device, params.xy, params.hue, params.sat, params.ct)
}
}
return [] return []
} }
/** /**
* Handles a response to a poll (GET) sent to the Hue Bridge. * Handles a response to a poll (GET) sent to the Hue Bridge.
@@ -928,26 +930,32 @@ private handleCommandResponse(body) {
* @return empty array * @return empty array
*/ */
private handlePoll(body) { private handlePoll(body) {
if (state.updating) {
// If user just executed commands, then ignore poll to not confuse the turning on/off state
return []
}
def bulbs = getChildDevices() def bulbs = getChildDevices()
for (bulb in body) { for (bulb in body) {
def device = bulbs.find{it.deviceNetworkId == "${app.id}/${bulb.key}"} def device = bulbs.find{it.deviceNetworkId == "${app.id}/${bulb.key}"}
if (device) { if (device) {
if (bulb.value.state?.reachable) { if (bulb.value.state?.reachable) {
sendBasicEvents(device, "on", bulb.value?.state?.on) if (state.bulbs[bulb.key]?.online == false) {
sendBasicEvents(device, "bri", bulb.value?.state?.bri) // light just came back online, notify device watch
sendColorEvents(device, bulb.value?.state?.xy, bulb.value?.state?.hue, bulb.value?.state?.sat, bulb.value?.state?.ct, bulb.value?.state?.colormode) def lastActivity = now()
device.sendEvent(name: "deviceWatch-status", value: "ONLINE", description: "Last Activity is on ${new Date((long) lastActivity)}", displayed: false, isStateChange: true)
}
state.bulbs[bulb.key]?.online = true
// If user just executed commands, then do not send events to avoid confusing the turning on/off state
if (!state.updating) {
sendBasicEvents(device, "on", bulb.value?.state?.on)
sendBasicEvents(device, "bri", bulb.value?.state?.bri)
sendColorEvents(device, bulb.value?.state?.xy, bulb.value?.state?.hue, bulb.value?.state?.sat, bulb.value?.state?.ct, bulb.value?.state?.colormode)
}
} else { } else {
state.bulbs[bulb.key]?.online = false
log.warn "$device is not reachable by Hue bridge" log.warn "$device is not reachable by Hue bridge"
} }
} }
}
return []
} }
return []
}
private updateInProgress() { private updateInProgress() {
state.updating = true state.updating = true
@@ -976,22 +984,34 @@ def hubVerification(bodytext) {
def on(childDevice) { def on(childDevice) {
log.debug "Executing 'on'" log.debug "Executing 'on'"
def id = getId(childDevice)
if (!isOnline(id)) {
return "Bulb is unreachable"
}
updateInProgress() updateInProgress()
createSwitchEvent(childDevice, "on") createSwitchEvent(childDevice, "on")
put("lights/${getId(childDevice)}/state", [on: true]) put("lights/$id/state", [on: true])
return "Bulb is turning On" return "Bulb is turning On"
} }
def off(childDevice) { def off(childDevice) {
log.debug "Executing 'off'" log.debug "Executing 'off'"
def id = getId(childDevice)
if (!isOnline(id)) {
return "Bulb is unreachable"
}
updateInProgress() updateInProgress()
createSwitchEvent(childDevice, "off") createSwitchEvent(childDevice, "off")
put("lights/${getId(childDevice)}/state", [on: false]) put("lights/$id/state", [on: false])
return "Bulb is turning Off" return "Bulb is turning Off"
} }
def setLevel(childDevice, percent) { def setLevel(childDevice, percent) {
log.debug "Executing 'setLevel'" log.debug "Executing 'setLevel'"
def id = getId(childDevice)
if (!isOnline(id)) {
return "Bulb is unreachable"
}
updateInProgress() updateInProgress()
// 1 - 254 // 1 - 254
def level def level
@@ -1006,48 +1026,64 @@ def setLevel(childDevice, percent) {
// that means that the light will still be on when on is called next time // that means that the light will still be on when on is called next time
// Lets emulate that here // Lets emulate that here
if (percent > 0) { if (percent > 0) {
put("lights/${getId(childDevice)}/state", [bri: level, on: true]) put("lights/$id/state", [bri: level, on: true])
} else { } else {
put("lights/${getId(childDevice)}/state", [on: false]) put("lights/$id/state", [on: false])
} }
return "Setting level to $percent" return "Setting level to $percent"
} }
def setSaturation(childDevice, percent) { def setSaturation(childDevice, percent) {
log.debug "Executing 'setSaturation($percent)'" log.debug "Executing 'setSaturation($percent)'"
updateInProgress() def id = getId(childDevice)
if (!isOnline(id)) {
return "Bulb is unreachable"
}
updateInProgress()
// 0 - 254 // 0 - 254
def level = Math.min(Math.round(percent * 254 / 100), 254) def level = Math.min(Math.round(percent * 254 / 100), 254)
// TODO should this be done by app only or should we default to on? // TODO should this be done by app only or should we default to on?
createSwitchEvent(childDevice, "on") createSwitchEvent(childDevice, "on")
put("lights/${getId(childDevice)}/state", [sat: level, on: true]) put("lights/$id/state", [sat: level, on: true])
return "Setting saturation to $percent" return "Setting saturation to $percent"
} }
def setHue(childDevice, percent) { def setHue(childDevice, percent) {
log.debug "Executing 'setHue($percent)'" log.debug "Executing 'setHue($percent)'"
def id = getId(childDevice)
if (!isOnline(id)) {
return "Bulb is unreachable"
}
updateInProgress() updateInProgress()
// 0 - 65535 // 0 - 65535
def level = Math.min(Math.round(percent * 65535 / 100), 65535) def level = Math.min(Math.round(percent * 65535 / 100), 65535)
// TODO should this be done by app only or should we default to on? // TODO should this be done by app only or should we default to on?
createSwitchEvent(childDevice, "on") createSwitchEvent(childDevice, "on")
put("lights/${getId(childDevice)}/state", [hue: level, on: true]) put("lights/$id/state", [hue: level, on: true])
return "Setting hue to $percent" return "Setting hue to $percent"
} }
def setColorTemperature(childDevice, huesettings) { def setColorTemperature(childDevice, huesettings) {
log.debug "Executing 'setColorTemperature($huesettings)'" log.debug "Executing 'setColorTemperature($huesettings)'"
def id = getId(childDevice)
if (!isOnline(id)) {
return "Bulb is unreachable"
}
updateInProgress() updateInProgress()
// 153 (6500K) to 500 (2000K) // 153 (6500K) to 500 (2000K)
def ct = hueSettings == 6500 ? 153 : Math.round(1000000/huesettings) def ct = hueSettings == 6500 ? 153 : Math.round(1000000/huesettings)
createSwitchEvent(childDevice, "on") createSwitchEvent(childDevice, "on")
put("lights/${getId(childDevice)}/state", [ct: ct, on: true]) put("lights/$id/state", [ct: ct, on: true])
return "Setting color temperature to $percent" return "Setting color temperature to $percent"
} }
def setColor(childDevice, huesettings) { def setColor(childDevice, huesettings) {
log.debug "Executing 'setColor($huesettings)'" log.debug "Executing 'setColor($huesettings)'"
def id = getId(childDevice)
if (!isOnline(id)) {
return "Bulb is unreachable"
}
updateInProgress() updateInProgress()
def value = [:] def value = [:]
@@ -1104,15 +1140,23 @@ def setColor(childDevice, huesettings) {
value.on = false value.on = false
createSwitchEvent(childDevice, value.on ? "on" : "off") createSwitchEvent(childDevice, value.on ? "on" : "off")
put("lights/${getId(childDevice)}/state", value) put("lights/$id/state", value)
return "Setting color to $value" return "Setting color to $value"
} }
def ping(childDevice) {
if (isOnline(getId(childDevice))) {
childDevice.sendEvent(name: "deviceWatch-ping", value: "ONLINE", description: "Hue Light is reachable", displayed: false, isStateChange: true)
return "Device is Online"
} else {
return "Device is Offline"
}
}
private getId(childDevice) { private getId(childDevice) {
if (childDevice.device?.deviceNetworkId?.startsWith("HUE")) { if (childDevice.device?.deviceNetworkId?.startsWith("HUE")) {
return childDevice.device?.deviceNetworkId[3..-1] return childDevice.device?.deviceNetworkId[3..-1]
} } else {
else {
return childDevice.device?.deviceNetworkId.split("/")[-1] return childDevice.device?.deviceNetworkId.split("/")[-1]
} }
} }
@@ -1123,10 +1167,13 @@ private poll() {
log.debug "GET: $host$uri" log.debug "GET: $host$uri"
sendHubCommand(new physicalgraph.device.HubAction("""GET ${uri} HTTP/1.1 sendHubCommand(new physicalgraph.device.HubAction("""GET ${uri} HTTP/1.1
HOST: ${host} HOST: ${host}
""", physicalgraph.device.Protocol.LAN, selectedHue)) """, physicalgraph.device.Protocol.LAN, selectedHue))
} }
private isOnline(id) {
return (state.bulbs[id].online != null && state.bulbs[id].online) || state.bulbs[id].online == null
}
private put(path, body) { private put(path, body) {
def host = getBridgeIP() def host = getBridgeIP()
def uri = "/api/${state.username}/$path" def uri = "/api/${state.username}/$path"
@@ -1194,7 +1241,7 @@ def convertBulbListToMap() {
if (state.bulbs instanceof java.util.List) { if (state.bulbs instanceof java.util.List) {
def map = [:] def map = [:]
state.bulbs.unique {it.id}.each { bulb -> state.bulbs.unique {it.id}.each { bulb ->
map << ["${bulb.id}":["id":bulb.id, "name":bulb.name, "type": bulb.type, "modelid": bulb.modelid, "hub":bulb.hub]] map << ["${bulb.id}":["id":bulb.id, "name":bulb.name, "type": bulb.type, "modelid": bulb.modelid, "hub":bulb.hub, "online": bulb.online]]
} }
state.bulbs = map state.bulbs = map
} }