mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-22 21:03:51 +00:00
Compare commits
1 Commits
MSA-1518-2
...
MSA-1502-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed3472f8d2 |
@@ -97,14 +97,13 @@ def refresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def healthPoll() {
|
def healthPoll() {
|
||||||
log.debug "healthPoll()"
|
|
||||||
def cmds = zigbee.onOffRefresh() + zigbee.levelRefresh()
|
def cmds = zigbee.onOffRefresh() + zigbee.levelRefresh()
|
||||||
cmds.each{ sendHubCommand(new physicalgraph.device.HubAction(it))}
|
cmds.each{ sendHubCommand(new physicalgraph.device.HubAction(it))}
|
||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
unschedule()
|
unschedule()
|
||||||
runEvery5Minutes("healthPoll")
|
schedule("0 0/5 * * * ? *", "healthPoll")
|
||||||
log.debug "Configuring Reporting and Bindings."
|
log.debug "Configuring Reporting and Bindings."
|
||||||
// Device-Watch allows 2 check-in misses from device
|
// Device-Watch allows 2 check-in misses from device
|
||||||
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||||
|
|||||||
@@ -31,13 +31,13 @@ metadata {
|
|||||||
command "switchMode"
|
command "switchMode"
|
||||||
command "switchFanMode"
|
command "switchFanMode"
|
||||||
|
|
||||||
attribute "thermostatSetpoint", "number"
|
attribute "thermostatSetpoint","number"
|
||||||
attribute "thermostatStatus", "string"
|
attribute "thermostatStatus","string"
|
||||||
attribute "maxHeatingSetpoint", "number"
|
attribute "maxHeatingSetpoint", "number"
|
||||||
attribute "minHeatingSetpoint", "number"
|
attribute "minHeatingSetpoint", "number"
|
||||||
attribute "maxCoolingSetpoint", "number"
|
attribute "maxCoolingSetpoint", "number"
|
||||||
attribute "minCoolingSetpoint", "number"
|
attribute "minCoolingSetpoint", "number"
|
||||||
attribute "deviceTemperatureUnit", "string"
|
attribute "deviceTemperatureUnit", "number"
|
||||||
}
|
}
|
||||||
|
|
||||||
tiles {
|
tiles {
|
||||||
@@ -655,60 +655,55 @@ void lowerSetpoint() {
|
|||||||
void alterSetpoint(temp) {
|
void alterSetpoint(temp) {
|
||||||
|
|
||||||
def mode = device.currentValue("thermostatMode")
|
def mode = device.currentValue("thermostatMode")
|
||||||
|
def heatingSetpoint = device.currentValue("heatingSetpoint")
|
||||||
|
def coolingSetpoint = device.currentValue("coolingSetpoint")
|
||||||
|
def deviceId = device.deviceNetworkId.split(/\./).last()
|
||||||
|
|
||||||
if (mode == "off" || mode == "auto") {
|
def targetHeatingSetpoint
|
||||||
log.warn "this mode: $mode does not allow alterSetpoint"
|
def targetCoolingSetpoint
|
||||||
} else {
|
|
||||||
def heatingSetpoint = device.currentValue("heatingSetpoint")
|
|
||||||
def coolingSetpoint = device.currentValue("coolingSetpoint")
|
|
||||||
def deviceId = device.deviceNetworkId.split(/\./).last()
|
|
||||||
|
|
||||||
def targetHeatingSetpoint
|
//step1: check thermostatMode, enforce limits before sending request to cloud
|
||||||
def targetCoolingSetpoint
|
if (mode == "heat" || mode == "auxHeatOnly"){
|
||||||
|
if (temp.value > coolingSetpoint){
|
||||||
//step1: check thermostatMode, enforce limits before sending request to cloud
|
targetHeatingSetpoint = temp.value
|
||||||
if (mode == "heat" || mode == "auxHeatOnly"){
|
targetCoolingSetpoint = temp.value
|
||||||
if (temp.value > coolingSetpoint){
|
|
||||||
targetHeatingSetpoint = temp.value
|
|
||||||
targetCoolingSetpoint = temp.value
|
|
||||||
} else {
|
|
||||||
targetHeatingSetpoint = temp.value
|
|
||||||
targetCoolingSetpoint = coolingSetpoint
|
|
||||||
}
|
|
||||||
} else if (mode == "cool") {
|
|
||||||
//enforce limits before sending request to cloud
|
|
||||||
if (temp.value < heatingSetpoint){
|
|
||||||
targetHeatingSetpoint = temp.value
|
|
||||||
targetCoolingSetpoint = temp.value
|
|
||||||
} else {
|
|
||||||
targetHeatingSetpoint = heatingSetpoint
|
|
||||||
targetCoolingSetpoint = temp.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug "alterSetpoint >> in mode ${mode} trying to change heatingSetpoint to $targetHeatingSetpoint " +
|
|
||||||
"coolingSetpoint to $targetCoolingSetpoint with holdType : ${holdType}"
|
|
||||||
|
|
||||||
def sendHoldType = holdType ? (holdType=="Temporary")? "nextTransition" : (holdType=="Permanent")? "indefinite" : "indefinite" : "indefinite"
|
|
||||||
|
|
||||||
def coolingValue = location.temperatureScale == "C"? convertCtoF(targetCoolingSetpoint) : targetCoolingSetpoint
|
|
||||||
def heatingValue = location.temperatureScale == "C"? convertCtoF(targetHeatingSetpoint) : targetHeatingSetpoint
|
|
||||||
|
|
||||||
if (parent.setHold(heatingValue, coolingValue, deviceId, sendHoldType)) {
|
|
||||||
sendEvent("name": "thermostatSetpoint", "value": temp.value, displayed: false)
|
|
||||||
sendEvent("name": "heatingSetpoint", "value": targetHeatingSetpoint, "unit": location.temperatureScale)
|
|
||||||
sendEvent("name": "coolingSetpoint", "value": targetCoolingSetpoint, "unit": location.temperatureScale)
|
|
||||||
log.debug "alterSetpoint in mode $mode succeed change setpoint to= ${temp.value}"
|
|
||||||
} else {
|
} else {
|
||||||
log.error "Error alterSetpoint()"
|
targetHeatingSetpoint = temp.value
|
||||||
if (mode == "heat" || mode == "auxHeatOnly"){
|
targetCoolingSetpoint = coolingSetpoint
|
||||||
sendEvent("name": "thermostatSetpoint", "value": heatingSetpoint.toString(), displayed: false)
|
}
|
||||||
} else if (mode == "cool") {
|
} else if (mode == "cool") {
|
||||||
sendEvent("name": "thermostatSetpoint", "value": coolingSetpoint.toString(), displayed: false)
|
//enforce limits before sending request to cloud
|
||||||
}
|
if (temp.value < heatingSetpoint){
|
||||||
|
targetHeatingSetpoint = temp.value
|
||||||
|
targetCoolingSetpoint = temp.value
|
||||||
|
} else {
|
||||||
|
targetHeatingSetpoint = heatingSetpoint
|
||||||
|
targetCoolingSetpoint = temp.value
|
||||||
}
|
}
|
||||||
generateStatusEvent()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.debug "alterSetpoint >> in mode ${mode} trying to change heatingSetpoint to $targetHeatingSetpoint " +
|
||||||
|
"coolingSetpoint to $targetCoolingSetpoint with holdType : ${holdType}"
|
||||||
|
|
||||||
|
def sendHoldType = holdType ? (holdType=="Temporary")? "nextTransition" : (holdType=="Permanent")? "indefinite" : "indefinite" : "indefinite"
|
||||||
|
|
||||||
|
def coolingValue = location.temperatureScale == "C"? convertCtoF(targetCoolingSetpoint) : targetCoolingSetpoint
|
||||||
|
def heatingValue = location.temperatureScale == "C"? convertCtoF(targetHeatingSetpoint) : targetHeatingSetpoint
|
||||||
|
|
||||||
|
if (parent.setHold(heatingValue, coolingValue, deviceId, sendHoldType)) {
|
||||||
|
sendEvent("name": "thermostatSetpoint", "value": temp.value, displayed: false)
|
||||||
|
sendEvent("name": "heatingSetpoint", "value": targetHeatingSetpoint, "unit": location.temperatureScale)
|
||||||
|
sendEvent("name": "coolingSetpoint", "value": targetCoolingSetpoint, "unit": location.temperatureScale)
|
||||||
|
log.debug "alterSetpoint in mode $mode succeed change setpoint to= ${temp.value}"
|
||||||
|
} else {
|
||||||
|
log.error "Error alterSetpoint()"
|
||||||
|
if (mode == "heat" || mode == "auxHeatOnly"){
|
||||||
|
sendEvent("name": "thermostatSetpoint", "value": heatingSetpoint.toString(), displayed: false)
|
||||||
|
} else if (mode == "cool") {
|
||||||
|
sendEvent("name": "thermostatSetpoint", "value": coolingSetpoint.toString(), displayed: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
generateStatusEvent()
|
||||||
}
|
}
|
||||||
|
|
||||||
def generateStatusEvent() {
|
def generateStatusEvent() {
|
||||||
|
|||||||
@@ -189,4 +189,4 @@ private calcDigestAuth(String method, String uri) {
|
|||||||
def response = hashMD5("${HA1}::::auth:${HA2}")
|
def response = hashMD5("${HA1}::::auth:${HA2}")
|
||||||
|
|
||||||
'Digest username="'+ getUsername() + '", realm="", nonce="", uri="'+ uri +'", qop=auth, nc=, cnonce="", response="' + response + '", opaque=""'
|
'Digest username="'+ getUsername() + '", realm="", nonce="", uri="'+ uri +'", qop=auth, nc=, cnonce="", response="' + response + '", opaque=""'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,9 @@
|
|||||||
metadata {
|
metadata {
|
||||||
// Automatically generated. Make future change here.
|
// Automatically generated. Make future change here.
|
||||||
definition (name: "Hue Bridge", namespace: "smartthings", author: "SmartThings") {
|
definition (name: "Hue Bridge", namespace: "smartthings", author: "SmartThings") {
|
||||||
capability "Health Check"
|
|
||||||
|
|
||||||
attribute "networkAddress", "string"
|
attribute "networkAddress", "string"
|
||||||
// Used to indicate if bridge is reachable or not, i.e. is the bridge connected to the network
|
// Used to indicate if bridge is reachable or not, i.e. is the bridge connected to the network
|
||||||
// Possible values "Online" or "Offline"
|
// Possible values "Online" or "Offline"
|
||||||
attribute "status", "string"
|
attribute "status", "string"
|
||||||
// Id is the number on the back of the hub, Hue uses last six digits of Mac address
|
// Id is the number on the back of the hub, Hue uses last six digits of Mac address
|
||||||
// This is also used in the Hue application as ID
|
// This is also used in the Hue application as ID
|
||||||
@@ -44,10 +42,6 @@ metadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void installed() {
|
|
||||||
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan"], displayed: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse events into attributes
|
// parse events into attributes
|
||||||
def parse(description) {
|
def parse(description) {
|
||||||
log.debug "Parsing '${description}'"
|
log.debug "Parsing '${description}'"
|
||||||
@@ -76,8 +70,13 @@ def parse(description) {
|
|||||||
def bulbs = new groovy.json.JsonSlurper().parseText(msg.body)
|
def bulbs = new groovy.json.JsonSlurper().parseText(msg.body)
|
||||||
if (bulbs.state) {
|
if (bulbs.state) {
|
||||||
log.info "Bridge response: $msg.body"
|
log.info "Bridge response: $msg.body"
|
||||||
|
} else {
|
||||||
|
// Sending Bulbs List to parent"
|
||||||
|
if (parent.isInBulbDiscovery())
|
||||||
|
log.info parent.bulbListHandler(device.hub.id, msg.body)
|
||||||
}
|
}
|
||||||
} else if (contentType?.contains("xml")) {
|
}
|
||||||
|
else if (contentType?.contains("xml")) {
|
||||||
log.debug "HUE BRIDGE ALREADY PRESENT"
|
log.debug "HUE BRIDGE ALREADY PRESENT"
|
||||||
parent.hubVerification(device.hub.id, msg.body)
|
parent.hubVerification(device.hub.id, msg.body)
|
||||||
}
|
}
|
||||||
@@ -86,7 +85,3 @@ def parse(description) {
|
|||||||
}
|
}
|
||||||
results
|
results
|
||||||
}
|
}
|
||||||
|
|
||||||
def ping() {
|
|
||||||
log.debug "${parent.ping(this)}"
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ void setColorTemperature(value) {
|
|||||||
|
|
||||||
void refresh() {
|
void refresh() {
|
||||||
log.debug "Executing 'refresh'"
|
log.debug "Executing 'refresh'"
|
||||||
parent?.manualRefresh()
|
parent.manualRefresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
def verifyPercent(percent) {
|
def verifyPercent(percent) {
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
.st-ignore
|
|
||||||
README.md
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
# Nyce Door/Window Sensor (Open/Close Sensor)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Works with:
|
|
||||||
|
|
||||||
* [NYCE Door/Window Sensor NCZ-3011](https://support.smartthings.com/hc/en-us/articles/204576764-NYCE-Door-Window-Sensor)
|
|
||||||
|
|
||||||
## Table of contents
|
|
||||||
|
|
||||||
* [Capabilities](#capabilities)
|
|
||||||
* [Health](#device-health)
|
|
||||||
* [Battery](#battery-specification)
|
|
||||||
* [Troubleshooting](#troubleshooting)
|
|
||||||
|
|
||||||
## Capabilities
|
|
||||||
|
|
||||||
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
|
|
||||||
* **Contact Sensor** - can detect contact (with possible values - open/closed)
|
|
||||||
* **Battery** - defines device uses a battery
|
|
||||||
* **Refresh** - _refresh()_ command for status updates
|
|
||||||
* **Health Check** - indicates ability to get device health notifications
|
|
||||||
|
|
||||||
## Device Health
|
|
||||||
|
|
||||||
A Category C2 Nyce Door/Window sensor that has 12min check-in interval
|
|
||||||
|
|
||||||
## Battery Specification
|
|
||||||
|
|
||||||
One 3V CR2032 battery required.
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the sensor is out of range.
|
|
||||||
Pairing needs to be tried again by placing the sensor closer to the hub.
|
|
||||||
Instructions related to pairing, resetting and removing the sensor from SmartThings can be found in the following link:
|
|
||||||
* [Nyce Door/Window Sensor](https://support.smartthings.com/hc/en-us/articles/204576764-NYCE-Door-Window-Sensor)
|
|
||||||
@@ -19,26 +19,25 @@ import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
|
|||||||
|
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "NYCE Open/Closed Sensor", namespace: "smartthings", author: "NYCE") {
|
definition (name: "NYCE Open/Closed Sensor", namespace: "smartthings", author: "NYCE") {
|
||||||
capability "Battery"
|
capability "Battery"
|
||||||
capability "Configuration"
|
capability "Configuration"
|
||||||
capability "Contact Sensor"
|
capability "Contact Sensor"
|
||||||
capability "Refresh"
|
capability "Refresh"
|
||||||
capability "Health Check"
|
|
||||||
|
command "enrollResponse"
|
||||||
command "enrollResponse"
|
|
||||||
|
|
||||||
|
fingerprint inClusters: "0000,0001,0003,0500,0020", manufacturer: "NYCE", model: "3010", deviceJoinName: "NYCE Door Hinge Sensor"
|
||||||
fingerprint inClusters: "0000,0001,0003,0500,0020", manufacturer: "NYCE", model: "3010", deviceJoinName: "NYCE Door Hinge Sensor"
|
|
||||||
fingerprint inClusters: "0000,0001,0003,0406,0500,0020", manufacturer: "NYCE", model: "3011", deviceJoinName: "NYCE Door/Window Sensor"
|
fingerprint inClusters: "0000,0001,0003,0406,0500,0020", manufacturer: "NYCE", model: "3011", deviceJoinName: "NYCE Door/Window Sensor"
|
||||||
fingerprint inClusters: "0000,0001,0003,0500,0020", manufacturer: "NYCE", model: "3011", deviceJoinName: "NYCE Door/Window Sensor"
|
fingerprint inClusters: "0000,0001,0003,0500,0020", manufacturer: "NYCE", model: "3011", deviceJoinName: "NYCE Door/Window Sensor"
|
||||||
fingerprint inClusters: "0000,0001,0003,0406,0500,0020", manufacturer: "NYCE", model: "3014", deviceJoinName: "NYCE Tilt Sensor"
|
fingerprint inClusters: "0000,0001,0003,0406,0500,0020", manufacturer: "NYCE", model: "3014", deviceJoinName: "NYCE Tilt Sensor"
|
||||||
fingerprint inClusters: "0000,0001,0003,0500,0020", manufacturer: "NYCE", model: "3014", deviceJoinName: "NYCE Tilt Sensor"
|
fingerprint inClusters: "0000,0001,0003,0500,0020", manufacturer: "NYCE", model: "3014", deviceJoinName: "NYCE Tilt Sensor"
|
||||||
}
|
}
|
||||||
|
|
||||||
simulator {
|
simulator {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tiles {
|
tiles {
|
||||||
standardTile("contact", "device.contact", width: 2, height: 2) {
|
standardTile("contact", "device.contact", width: 2, height: 2) {
|
||||||
state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
|
state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
|
||||||
@@ -274,28 +273,23 @@ private List parseIasMessage(String description) {
|
|||||||
return resultListMap
|
return resultListMap
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* PING is used by Device-Watch in attempt to reach the Device
|
|
||||||
* */
|
|
||||||
def ping() {
|
|
||||||
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
|
|
||||||
}
|
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
// Device-Watch allows 2 check-in misses from device
|
|
||||||
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
|
||||||
|
|
||||||
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
||||||
|
|
||||||
def enrollCmds = [
|
def configCmds = [
|
||||||
|
//battery reporting and heartbeat
|
||||||
|
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 1 {${device.zigbeeId}} {}", "delay 200",
|
||||||
|
"zcl global send-me-a-report 1 0x20 0x20 600 3600 {01}", "delay 200",
|
||||||
|
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500",
|
||||||
|
|
||||||
|
|
||||||
// Writes CIE attribute on end device to direct reports to the hub's EUID
|
// Writes CIE attribute on end device to direct reports to the hub's EUID
|
||||||
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
||||||
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
|
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
|
||||||
]
|
]
|
||||||
|
|
||||||
log.debug "configure: Write IAS CIE"
|
log.debug "configure: Write IAS CIE"
|
||||||
// battery minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
|
return configCmds
|
||||||
return enrollCmds + zigbee.batteryConfig(30, 300) + refresh() // send refresh cmds as part of config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def enrollResponse() {
|
def enrollResponse() {
|
||||||
@@ -340,8 +334,7 @@ Integer convertHexToInt(hex) {
|
|||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
log.debug "Refreshing Battery"
|
log.debug "Refreshing Battery"
|
||||||
def refreshCmds = [
|
[
|
||||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 1 0x20", "delay 200"
|
"st rattr 0x${device.deviceNetworkId} ${endpointId} 1 0x20", "delay 200"
|
||||||
]
|
]
|
||||||
return refreshCmds + enrollResponse()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
metadata {
|
metadata {
|
||||||
// Automatically generated. Make future change here.
|
// Automatically generated. Make future change here.
|
||||||
definition (name: "SmartPower Outlet", namespace: "smartthings", author: "SmartThings") {
|
definition (name: "SmartPower Outlet", namespace: "smartthings", author: "SmartThings", category: "C1") {
|
||||||
capability "Actuator"
|
capability "Actuator"
|
||||||
capability "Switch"
|
capability "Switch"
|
||||||
capability "Power Meter"
|
capability "Power Meter"
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
|
|||||||
|
|
||||||
|
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "SmartSense Moisture Sensor",namespace: "smartthings", author: "SmartThings") {
|
definition (name: "SmartSense Moisture Sensor",namespace: "smartthings", author: "SmartThings", category: "C2") {
|
||||||
capability "Configuration"
|
capability "Configuration"
|
||||||
capability "Battery"
|
capability "Battery"
|
||||||
capability "Refresh"
|
capability "Refresh"
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
|
|||||||
|
|
||||||
|
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "SmartSense Motion Sensor", namespace: "smartthings", author: "SmartThings") {
|
definition (name: "SmartSense Motion Sensor", namespace: "smartthings", author: "SmartThings", category: "C2") {
|
||||||
capability "Motion Sensor"
|
capability "Motion Sensor"
|
||||||
capability "Configuration"
|
capability "Configuration"
|
||||||
capability "Battery"
|
capability "Battery"
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
|
import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
|
||||||
|
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "SmartSense Multi Sensor", namespace: "smartthings", author: "SmartThings") {
|
definition (name: "SmartSense Multi Sensor", namespace: "smartthings", author: "SmartThings", category: "C2") {
|
||||||
|
|
||||||
capability "Three Axis"
|
capability "Three Axis"
|
||||||
capability "Battery"
|
capability "Battery"
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
|
import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
|
||||||
|
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "SmartSense Open/Closed Sensor", namespace: "smartthings", author: "SmartThings") {
|
definition (name: "SmartSense Open/Closed Sensor", namespace: "smartthings", author: "SmartThings", category: "C2") {
|
||||||
capability "Battery"
|
capability "Battery"
|
||||||
capability "Configuration"
|
capability "Configuration"
|
||||||
capability "Contact Sensor"
|
capability "Contact Sensor"
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "SmartSense Temp/Humidity Sensor",namespace: "smartthings", author: "SmartThings") {
|
definition (name: "SmartSense Temp/Humidity Sensor",namespace: "smartthings", author: "SmartThings", category: "C2") {
|
||||||
capability "Configuration"
|
capability "Configuration"
|
||||||
capability "Battery"
|
capability "Battery"
|
||||||
capability "Refresh"
|
capability "Refresh"
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
|
import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
|
||||||
|
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "Tyco Door/Window Sensor", namespace: "smartthings", author: "SmartThings") {
|
definition (name: "Tyco Door/Window Sensor", namespace: "smartthings", author: "SmartThings", category: "C2") {
|
||||||
capability "Battery"
|
capability "Battery"
|
||||||
capability "Configuration"
|
capability "Configuration"
|
||||||
capability "Contact Sensor"
|
capability "Contact Sensor"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "ZigBee Dimmer", namespace: "smartthings", author: "SmartThings") {
|
definition (name: "ZigBee Dimmer", namespace: "smartthings", author: "SmartThings", category: "C1") {
|
||||||
capability "Actuator"
|
capability "Actuator"
|
||||||
capability "Configuration"
|
capability "Configuration"
|
||||||
capability "Refresh"
|
capability "Refresh"
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "ZigBee RGBW Bulb", namespace: "smartthings", author: "SmartThings") {
|
definition (name: "ZigBee RGBW Bulb", namespace: "smartthings", author: "SmartThings", category: "C6") {
|
||||||
|
|
||||||
capability "Actuator"
|
capability "Actuator"
|
||||||
capability "Color Control"
|
capability "Color Control"
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "ZigBee White Color Temperature Bulb", namespace: "smartthings", author: "SmartThings") {
|
definition (name: "ZigBee White Color Temperature Bulb", namespace: "smartthings", author: "SmartThings", category: "C1") {
|
||||||
|
|
||||||
capability "Actuator"
|
capability "Actuator"
|
||||||
capability "Color Temperature"
|
capability "Color Temperature"
|
||||||
|
|||||||
@@ -0,0 +1,372 @@
|
|||||||
|
/**
|
||||||
|
* SMCDW30-Z
|
||||||
|
*
|
||||||
|
* Copyright 2016 Roy Chuang
|
||||||
|
*
|
||||||
|
* 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: "SMC Door/Window Sensor", namespace: "SMC", author: "Roy Chuang") {
|
||||||
|
capability "Contact Sensor"
|
||||||
|
capability "Configuration"
|
||||||
|
capability "Battery"
|
||||||
|
capability "Temperature Measurement"
|
||||||
|
capability "Refresh"
|
||||||
|
capability "Tamper Alert"
|
||||||
|
capability "Sensor"
|
||||||
|
|
||||||
|
command "enrollResponse"
|
||||||
|
|
||||||
|
fingerprint profileId: "0104", deviceId: "0402", deviceVersion: "00", inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "ACCTON", model: "SMCDW30-Z"
|
||||||
|
}
|
||||||
|
|
||||||
|
simulator {
|
||||||
|
status "closed": "zone status 0x0000 -- extended status 0x00"
|
||||||
|
status "open": "zone status 0x0001 -- extended status 0x00"
|
||||||
|
status "tamper alert with closed": "zone status 0x0004 -- extended status 0x00"
|
||||||
|
status "tamper alert with open": "zone status 0x0005 -- extended status 0x00"
|
||||||
|
}
|
||||||
|
|
||||||
|
preferences {
|
||||||
|
section {
|
||||||
|
image(name: 'educationalcontent', multiple: true, images: [
|
||||||
|
"http://na.smc.com/site/wp-content/uploads/2016/06/SMCDW30-Z-Sensor-_Magnet-1-170x170.png"
|
||||||
|
])
|
||||||
|
}
|
||||||
|
section {
|
||||||
|
input title: "Temperature Offset", description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter '-5'. If 3 degrees too cold, enter '+3'.", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||||
|
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tiles(scale: 2) {
|
||||||
|
multiAttributeTile(name:"contact", type: "generic", width: 6, height: 4){
|
||||||
|
tileAttribute ("device.contact", key: "PRIMARY_CONTROL") {
|
||||||
|
attributeState "open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#53a7c0"
|
||||||
|
attributeState "closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#ffffff"
|
||||||
|
attributeState "tamper alert with open", label:'tamper', icon:"st.contact.contact.open", backgroundColor:"#cc0000"
|
||||||
|
attributeState "tamper alert with closed", label:'tamper', icon:"st.contact.contact.closed", backgroundColor:"#cc0000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
valueTile("temperature", "device.temperature", width: 2, height: 2) {
|
||||||
|
state("temperature", label:'${currentValue}°', unit:"F",
|
||||||
|
backgroundColors:[
|
||||||
|
[value: 31, color: "#153591"],
|
||||||
|
[value: 44, color: "#1e9cbb"],
|
||||||
|
[value: 59, color: "#90d2a7"],
|
||||||
|
[value: 74, color: "#44b621"],
|
||||||
|
[value: 84, color: "#f1d801"],
|
||||||
|
[value: 95, color: "#d04e00"],
|
||||||
|
[value: 96, color: "#bc2323"]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {
|
||||||
|
state "battery", label:'${currentValue}% battery', unit:""
|
||||||
|
}
|
||||||
|
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||||
|
state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||||
|
}
|
||||||
|
standardTile("rssi", "device.rssi", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {
|
||||||
|
state "rssi", label:'${currentValue}% rssi', unit:""
|
||||||
|
}
|
||||||
|
|
||||||
|
main(["contact"])
|
||||||
|
details(["contact", "temperature", "battery", "refresh", "rssi"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def parse(String description) {
|
||||||
|
log.debug "description: $description"
|
||||||
|
|
||||||
|
Map map = [:]
|
||||||
|
if (description?.startsWith('catchall:')) {
|
||||||
|
map = parseCatchAllMessage(description)
|
||||||
|
}
|
||||||
|
else if (description?.startsWith('read attr -')) {
|
||||||
|
map = parseReportAttributeMessage(description)
|
||||||
|
}
|
||||||
|
else if (description?.startsWith('temperature: ')) {
|
||||||
|
map = parseCustomMessage(description)
|
||||||
|
}
|
||||||
|
else if (description?.startsWith('zone status')) {
|
||||||
|
map = parseIasMessage(description)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug "Parse returned $map"
|
||||||
|
def result = map ? createEvent(map) : null
|
||||||
|
|
||||||
|
if (description?.startsWith('enroll request')) {
|
||||||
|
List cmds = enrollResponse()
|
||||||
|
log.debug "enroll response: ${cmds}"
|
||||||
|
result = cmds?.collect { new physicalgraph.device.HubAction(it) }
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map parseCatchAllMessage(String description) {
|
||||||
|
Map resultMap = [:]
|
||||||
|
def cluster = zigbee.parse(description)
|
||||||
|
if (shouldProcessMessage(cluster)) {
|
||||||
|
switch(cluster.clusterId) {
|
||||||
|
case 0x0001:
|
||||||
|
resultMap = getBatteryResult(cluster.data.last())
|
||||||
|
break
|
||||||
|
|
||||||
|
case 0x0402:
|
||||||
|
// temp is last 2 data values. reverse to swap endian
|
||||||
|
String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
|
||||||
|
def value = getTemperature(temp)
|
||||||
|
resultMap = getTemperatureResult(value)
|
||||||
|
break
|
||||||
|
|
||||||
|
case 0x0b05:
|
||||||
|
log.debug 'Diag'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultMap
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldProcessMessage(cluster) {
|
||||||
|
// 0x0B is default response indicating message got through
|
||||||
|
// 0x07 is bind message
|
||||||
|
boolean ignoredMessage = cluster.profileId != 0x0104 ||
|
||||||
|
cluster.command == 0x0B ||
|
||||||
|
cluster.command == 0x07 ||
|
||||||
|
(cluster.data.size() > 0 && cluster.data.first() == 0x3e)
|
||||||
|
return !ignoredMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map parseReportAttributeMessage(String description) {
|
||||||
|
Map descMap = (description - "read attr - ").split(",").inject([:]) { map, param ->
|
||||||
|
def nameAndValue = param.split(":")
|
||||||
|
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
|
||||||
|
}
|
||||||
|
log.debug "Desc Map: $descMap"
|
||||||
|
|
||||||
|
Map resultMap = [:]
|
||||||
|
if (descMap.cluster == "0402" && descMap.attrId == "0000") {
|
||||||
|
def value = getTemperature(descMap.value)
|
||||||
|
resultMap = getTemperatureResult(value)
|
||||||
|
}
|
||||||
|
else if (descMap.cluster == "0001" && descMap.attrId == "0020") {
|
||||||
|
resultMap = getBatteryResult(Integer.parseInt(descMap.value, 16))
|
||||||
|
}
|
||||||
|
else if (descMap.cluster == "0B05" && descMap.attrId == "011d") {
|
||||||
|
resultMap = getRssiResult(Integer.parseInt(descMap.value, 16))
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultMap
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map parseCustomMessage(String description) {
|
||||||
|
Map resultMap = [:]
|
||||||
|
if (description?.startsWith('temperature: ')) {
|
||||||
|
def value = zigbee.parseHATemperatureValue(description, "temperature: ", getTemperatureScale())
|
||||||
|
resultMap = getTemperatureResult(value)
|
||||||
|
}
|
||||||
|
return resultMap
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map parseIasMessage(String description) {
|
||||||
|
List parsedMsg = description.split(' ')
|
||||||
|
String msgCode = parsedMsg[2]
|
||||||
|
|
||||||
|
Map resultMap = [:]
|
||||||
|
switch(msgCode) {
|
||||||
|
case '0x0000': // Closed
|
||||||
|
resultMap = getContactResult('closed')
|
||||||
|
break
|
||||||
|
|
||||||
|
case '0x0001': // Open/Alarm1
|
||||||
|
resultMap = getContactResult('open')
|
||||||
|
break
|
||||||
|
|
||||||
|
case '0x0004': // Closed & Tamper
|
||||||
|
resultMap = getContactResult('tamper alert with closed')
|
||||||
|
break
|
||||||
|
|
||||||
|
case '0x0005': // Open & Tamper
|
||||||
|
resultMap = getContactResult('tamper alert with open')
|
||||||
|
break
|
||||||
|
|
||||||
|
default :
|
||||||
|
log.warn "ZoneStatus not support!"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return resultMap
|
||||||
|
}
|
||||||
|
|
||||||
|
def getTemperature(value) {
|
||||||
|
def celsius = Integer.parseInt(value, 16).shortValue() / 100
|
||||||
|
if(getTemperatureScale() == "C"){
|
||||||
|
return celsius
|
||||||
|
} else {
|
||||||
|
return celsiusToFahrenheit(celsius) as Integer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map getBatteryResult(rawValue) {
|
||||||
|
log.info "Battery rawValue = ${rawValue}"
|
||||||
|
def linkText = getLinkText(device)
|
||||||
|
|
||||||
|
def result = [
|
||||||
|
name: 'battery',
|
||||||
|
value: '--',
|
||||||
|
translatable: true
|
||||||
|
]
|
||||||
|
|
||||||
|
def volts = rawValue / 10
|
||||||
|
|
||||||
|
if (rawValue == 0 || rawValue == 255) {}
|
||||||
|
else {
|
||||||
|
if (volts > 3.5) {
|
||||||
|
result.descriptionText = "${device.displayName} battery has too much power: (> 3.5) volts."
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
def minVolts = 2.1
|
||||||
|
def maxVolts = 3.2
|
||||||
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
|
def roundedPct = Math.round(pct * 100)
|
||||||
|
result.value = Math.min(100, roundedPct)
|
||||||
|
result.descriptionText = "${device.displayName} battery was ${result.value}%"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map getRssiResult(rawValue) {
|
||||||
|
log.info "RSSI rawValue = ${rawValue}"
|
||||||
|
def linkText = getLinkText(device)
|
||||||
|
|
||||||
|
def result = [
|
||||||
|
name: 'rssi',
|
||||||
|
value: '--',
|
||||||
|
translatable: true
|
||||||
|
]
|
||||||
|
|
||||||
|
def pct = rawValue / 255
|
||||||
|
def roundedPct = Math.round(pct * 100)
|
||||||
|
result.value = Math.min(100, roundedPct)
|
||||||
|
result.descriptionText = "${device.displayName} RSSI was ${result.value}%"
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map getTemperatureResult(value) {
|
||||||
|
if (tempOffset) {
|
||||||
|
def offset = tempOffset as int
|
||||||
|
def v = value as int
|
||||||
|
value = v + offset
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info "Temperature is ${value}"
|
||||||
|
def descriptionText
|
||||||
|
if ( temperatureScale == 'C' )
|
||||||
|
descriptionText = "${device.displayName} was ${value}°C"
|
||||||
|
else
|
||||||
|
descriptionText = "${device.displayName} was ${value}°F"
|
||||||
|
|
||||||
|
return [
|
||||||
|
name: 'temperature',
|
||||||
|
value: value,
|
||||||
|
descriptionText: descriptionText,
|
||||||
|
translatable: true
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map getContactResult(value) {
|
||||||
|
String descriptionText = "${device.displayName}"
|
||||||
|
return [
|
||||||
|
name: 'contact',
|
||||||
|
value: value,
|
||||||
|
descriptionText: descriptionText,
|
||||||
|
translatable: true
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
def refresh() {
|
||||||
|
log.debug "refresh called"
|
||||||
|
def refreshCmds = [
|
||||||
|
"st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 200",
|
||||||
|
"st rattr 0x${device.deviceNetworkId} 1 0x001 0x20", "delay 200",
|
||||||
|
"st rattr 0x${device.deviceNetworkId} 1 0xb05 0x11d", "delay 200"
|
||||||
|
]
|
||||||
|
|
||||||
|
return refreshCmds
|
||||||
|
}
|
||||||
|
|
||||||
|
def configure() {
|
||||||
|
sendEvent(name: "checkInterval", value: 7200, displayed: false)
|
||||||
|
|
||||||
|
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
||||||
|
log.debug "Configuring Reporting, IAS CIE, and Bindings."
|
||||||
|
log.debug "${device.hub.zigbeeEui}. ${zigbeeEui}"
|
||||||
|
|
||||||
|
def configCmds = [
|
||||||
|
/* Write CIE */
|
||||||
|
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
||||||
|
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 2000",
|
||||||
|
|
||||||
|
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 1 {${device.zigbeeId}} {}", "delay 500",
|
||||||
|
"zcl global send-me-a-report 0x0001 0x20 0x20 30 21600 {02}", //Battery Voltage 6 hrs
|
||||||
|
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1000",
|
||||||
|
|
||||||
|
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x402 {${device.zigbeeId}} {}", "delay 500",
|
||||||
|
"zcl global send-me-a-report 0x0402 0 0x29 30 3600 {C800}", //Temperature 1 hr
|
||||||
|
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1000",
|
||||||
|
|
||||||
|
//Set long poll interval to 5 min
|
||||||
|
"raw 0x0020 {11 00 02 b0 04 00 00}",
|
||||||
|
"send 0x${device.deviceNetworkId} 1 1", "delay 500"
|
||||||
|
]
|
||||||
|
return configCmds + refresh() // send refresh cmds as part of config
|
||||||
|
}
|
||||||
|
|
||||||
|
def enrollResponse() {
|
||||||
|
log.debug "Sending enroll response"
|
||||||
|
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
||||||
|
[
|
||||||
|
//Enroll Response
|
||||||
|
"raw 0x500 {01 23 00 00 00}",
|
||||||
|
"send 0x${device.deviceNetworkId} 1 1", "delay 200"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
private getEndpointId() {
|
||||||
|
new BigInteger(device.endpointId, 16).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
private hex(value) {
|
||||||
|
new BigInteger(Math.round(value).toString()).toString(16)
|
||||||
|
}
|
||||||
|
|
||||||
|
private String swapEndianHex(String hex) {
|
||||||
|
reverseArray(hex.decodeHex()).encodeHex()
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] reverseArray(byte[] array) {
|
||||||
|
int i = 0;
|
||||||
|
int j = array.length - 1;
|
||||||
|
byte tmp;
|
||||||
|
while (j > i) {
|
||||||
|
tmp = array[j];
|
||||||
|
array[j] = array[i];
|
||||||
|
array[i] = tmp;
|
||||||
|
j--;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return array
|
||||||
|
}
|
||||||
@@ -131,7 +131,10 @@ def bulbDiscovery() {
|
|||||||
def refreshInterval = 3
|
def refreshInterval = 3
|
||||||
state.inBulbDiscovery = true
|
state.inBulbDiscovery = true
|
||||||
def bridge = null
|
def bridge = null
|
||||||
|
if (selectedHue) {
|
||||||
|
bridge = getChildDevice(selectedHue)
|
||||||
|
subscribe(bridge, "bulbList", bulbListData)
|
||||||
|
}
|
||||||
state.bridgeRefreshCount = 0
|
state.bridgeRefreshCount = 0
|
||||||
def allLightsFound = bulbsDiscovered() ?: [:]
|
def allLightsFound = bulbsDiscovered() ?: [:]
|
||||||
|
|
||||||
@@ -256,6 +259,10 @@ Map bulbsDiscovered() {
|
|||||||
return bulbmap
|
return bulbmap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def bulbListData(evt) {
|
||||||
|
state.bulbs = evt.jsonData
|
||||||
|
}
|
||||||
|
|
||||||
Map getHueBulbs() {
|
Map getHueBulbs() {
|
||||||
state.bulbs = state.bulbs ?: [:]
|
state.bulbs = state.bulbs ?: [:]
|
||||||
}
|
}
|
||||||
@@ -309,6 +316,29 @@ def uninstalled(){
|
|||||||
state.username = null
|
state.username = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handles events to add new bulbs
|
||||||
|
def bulbListHandler(hub, data = "") {
|
||||||
|
def msg = "Bulbs list not processed. Only while in settings menu."
|
||||||
|
def bulbs = [:]
|
||||||
|
if (state.inBulbDiscovery) {
|
||||||
|
def logg = ""
|
||||||
|
log.trace "Adding bulbs to state..."
|
||||||
|
state.bridgeProcessedLightList = true
|
||||||
|
def object = new groovy.json.JsonSlurper().parseText(data)
|
||||||
|
object.each { k,v ->
|
||||||
|
if (v instanceof Map)
|
||||||
|
bulbs[k] = [id: k, name: v.name, type: v.type, modelid: v.modelid, hub:hub, online: v.state?.reachable]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def bridge = null
|
||||||
|
if (selectedHue) {
|
||||||
|
bridge = getChildDevice(selectedHue)
|
||||||
|
bridge?.sendEvent(name: "bulbList", value: hub, data: bulbs, isStateChange: true, displayed: false)
|
||||||
|
}
|
||||||
|
msg = "${bulbs.size()} bulbs found. ${bulbs}"
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
private upgradeDeviceType(device, newHueType) {
|
private upgradeDeviceType(device, newHueType) {
|
||||||
def deviceType = getDeviceType(newHueType)
|
def deviceType = getDeviceType(newHueType)
|
||||||
|
|
||||||
@@ -540,8 +570,11 @@ void lightsHandler(physicalgraph.device.HubResponse hubResponse) {
|
|||||||
if (isValidSource(hubResponse.mac)) {
|
if (isValidSource(hubResponse.mac)) {
|
||||||
def body = hubResponse.json
|
def body = hubResponse.json
|
||||||
if (!body?.state?.on) { //check if first time poll made it here by mistake
|
if (!body?.state?.on) { //check if first time poll made it here by mistake
|
||||||
|
def bulbs = getHueBulbs()
|
||||||
log.debug "Adding bulbs to state!"
|
log.debug "Adding bulbs to state!"
|
||||||
updateBulbState(body, hubResponse.hubId)
|
body.each { k, v ->
|
||||||
|
bulbs[k] = [id: k, name: v.name, type: v.type, modelid: v.modelid, hub: hubResponse.hubId]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -657,8 +690,11 @@ def locationHandler(evt) {
|
|||||||
} else {
|
} else {
|
||||||
//GET /api/${state.username}/lights response (application/json)
|
//GET /api/${state.username}/lights response (application/json)
|
||||||
if (!body?.state?.on) { //check if first time poll made it here by mistake
|
if (!body?.state?.on) { //check if first time poll made it here by mistake
|
||||||
|
def bulbs = getHueBulbs()
|
||||||
log.debug "Adding bulbs to state!"
|
log.debug "Adding bulbs to state!"
|
||||||
updateBulbState(body, parsedEvent.hub)
|
body.each { k,v ->
|
||||||
|
bulbs[k] = [id: k, name: v.name, type: v.type, modelid: v.modelid, hub:parsedEvent.hub]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -718,7 +754,7 @@ 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.value.idNumber is Offline"
|
log.warn "Bridge $it.key is Offline"
|
||||||
d.sendEvent(name: "status", value: "Offline")
|
d.sendEvent(name: "status", value: "Offline")
|
||||||
|
|
||||||
state.bulbs?.each {
|
state.bulbs?.each {
|
||||||
@@ -743,31 +779,6 @@ def isInBulbDiscovery() {
|
|||||||
return state.inBulbDiscovery
|
return state.inBulbDiscovery
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateBulbState(messageBody, hub) {
|
|
||||||
def bulbs = getHueBulbs()
|
|
||||||
|
|
||||||
// Copy of bulbs used to locate old lights in state that are no longer on bridge
|
|
||||||
def toRemove = [:]
|
|
||||||
toRemove << bulbs
|
|
||||||
|
|
||||||
messageBody.each { k,v ->
|
|
||||||
|
|
||||||
if (v instanceof Map) {
|
|
||||||
if (bulbs[k] == null) {
|
|
||||||
bulbs[k] = [:]
|
|
||||||
}
|
|
||||||
bulbs[k] << [id: k, name: v.name, type: v.type, modelid: v.modelid, hub:hub, remove: false]
|
|
||||||
toRemove.remove(k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove bulbs from state that are no longer discovered
|
|
||||||
toRemove.each { k,v ->
|
|
||||||
log.warn "${bulbs[k].name} no longer exists on bridge, removing"
|
|
||||||
bulbs.remove(k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
//CHILD DEVICE METHODS
|
//CHILD DEVICE METHODS
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
@@ -1162,14 +1173,7 @@ def setColor(childDevice, huesettings) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def ping(childDevice) {
|
def ping(childDevice) {
|
||||||
if (childDevice.device?.deviceNetworkId?.equalsIgnoreCase(selectedHue)) {
|
if (isOnline(getId(childDevice))) {
|
||||||
if (childDevice.device?.currentValue("status")?.equalsIgnoreCase("Online")) {
|
|
||||||
childDevice.sendEvent(name: "deviceWatch-ping", value: "ONLINE", description: "Hue Bridge is reachable", displayed: false, isStateChange: true)
|
|
||||||
return "Bridge is Online"
|
|
||||||
} else {
|
|
||||||
return "Bridge is Offline"
|
|
||||||
}
|
|
||||||
} else if (isOnline(getId(childDevice))) {
|
|
||||||
childDevice.sendEvent(name: "deviceWatch-ping", value: "ONLINE", description: "Hue Light is reachable", displayed: false, isStateChange: true)
|
childDevice.sendEvent(name: "deviceWatch-ping", value: "ONLINE", description: "Hue Light is reachable", displayed: false, isStateChange: true)
|
||||||
return "Device is Online"
|
return "Device is Online"
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -362,7 +362,7 @@ Map discoverDevices() {
|
|||||||
def hubname = getHubName(it.key)
|
def hubname = getHubName(it.key)
|
||||||
def hubvalue = "${hubname}"
|
def hubvalue = "${hubname}"
|
||||||
hubs["harmony-${hubkey}"] = hubvalue
|
hubs["harmony-${hubkey}"] = hubvalue
|
||||||
it.value.response.data.activities.each {
|
it.value.response.data.activities.each {
|
||||||
def value = "${it.value.name}"
|
def value = "${it.value.name}"
|
||||||
def key = "harmony-${hubkey}-${it.key}"
|
def key = "harmony-${hubkey}-${it.key}"
|
||||||
activities["${key}"] = value
|
activities["${key}"] = value
|
||||||
@@ -461,11 +461,22 @@ def activityResponse(response, data) {
|
|||||||
log.warn "Logitech Harmony - Access token has expired"
|
log.warn "Logitech Harmony - Access token has expired"
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (response.status == 200) {
|
def ResponseValues
|
||||||
log.trace "Command sent succesfully"
|
try {
|
||||||
poll()
|
// json response already parsed into JSONElement object
|
||||||
|
ResponseValues = response.json
|
||||||
|
} catch (e) {
|
||||||
|
log.error "Logitech Harmony - error parsing json from response: $e"
|
||||||
|
}
|
||||||
|
if (ResponseValues) {
|
||||||
|
if (ResponseValues.code == 200) {
|
||||||
|
log.trace "Command sent succesfully"
|
||||||
|
poll()
|
||||||
|
} else {
|
||||||
|
log.trace "Command failed. Error: $response.data.code"
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log.trace "Command failed. Error: $response.status"
|
log.debug "Logitech Harmony - did not get json results from response body: $response.data"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -473,6 +484,7 @@ def activityResponse(response, data) {
|
|||||||
def poll() {
|
def poll() {
|
||||||
// GET THE LIST OF ACTIVITIES
|
// GET THE LIST OF ACTIVITIES
|
||||||
if (state.HarmonyAccessToken) {
|
if (state.HarmonyAccessToken) {
|
||||||
|
getActivityList()
|
||||||
def tokenParam = [auth: state.HarmonyAccessToken]
|
def tokenParam = [auth: state.HarmonyAccessToken]
|
||||||
def params = [
|
def params = [
|
||||||
uri: "https://home.myharmony.com/cloudapi/state?${toQueryString(tokenParam)}",
|
uri: "https://home.myharmony.com/cloudapi/state?${toQueryString(tokenParam)}",
|
||||||
@@ -481,7 +493,7 @@ def poll() {
|
|||||||
]
|
]
|
||||||
asynchttp_v1.get('pollResponse', params)
|
asynchttp_v1.get('pollResponse', params)
|
||||||
} else {
|
} else {
|
||||||
log.warn "Logitech Harmony - Access token has expired"
|
log.warn "Logitech Harmony - Access token has expired"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,12 +522,7 @@ def pollResponse(response, data) {
|
|||||||
if (it.value.response.data.currentAvActivity == "-1") {
|
if (it.value.response.data.currentAvActivity == "-1") {
|
||||||
hub.sendEvent(name: "currentActivity", value: "--", descriptionText: "There isn't any activity running", display: false)
|
hub.sendEvent(name: "currentActivity", value: "--", descriptionText: "There isn't any activity running", display: false)
|
||||||
} else {
|
} else {
|
||||||
def currentActivity
|
def currentActivity = getChildDevice("harmony-${it.key}-${it.value.response.data.currentAvActivity}").device.displayName
|
||||||
def activityDTH = getChildDevice("harmony-${it.key}-${it.value.response.data.currentAvActivity}")
|
|
||||||
if (activityDTH)
|
|
||||||
currentActivity = activityDTH.device.displayName
|
|
||||||
else
|
|
||||||
currentActivity = getActivityName(it.value.response.data.currentAvActivity,it.key)
|
|
||||||
hub.sendEvent(name: "currentActivity", value: currentActivity, descriptionText: "Current activity is ${currentActivity}", display: false)
|
hub.sendEvent(name: "currentActivity", value: currentActivity, descriptionText: "Current activity is ${currentActivity}", display: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -550,6 +557,58 @@ def pollResponse(response, data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def getActivityList() {
|
||||||
|
if (state.HarmonyAccessToken) {
|
||||||
|
def tokenParam = [auth: state.HarmonyAccessToken]
|
||||||
|
def params = [
|
||||||
|
uri: "https://home.myharmony.com/cloudapi/activity/all?${toQueryString(tokenParam)}",
|
||||||
|
headers: ["Accept": "application/json"],
|
||||||
|
contentType: 'application/json'
|
||||||
|
]
|
||||||
|
asynchttp_v1.get('activityListResponse', params)
|
||||||
|
} else {
|
||||||
|
log.warn "Logitech Harmony - Access token has expired"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def activityListResponse(response, data) {
|
||||||
|
if (response.hasError()) {
|
||||||
|
log.error "Logitech Harmony - response has error: $response.errorMessage"
|
||||||
|
if (response.status == 401) { // token is expired
|
||||||
|
state.remove("HarmonyAccessToken")
|
||||||
|
log.warn "Logitech Harmony - Access token has expired"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
def ResponseValues
|
||||||
|
try {
|
||||||
|
// json response already parsed into JSONElement object
|
||||||
|
ResponseValues = response.json
|
||||||
|
} catch (e) {
|
||||||
|
log.error "Logitech Harmony - error parsing json from response: $e"
|
||||||
|
}
|
||||||
|
if (ResponseValues) {
|
||||||
|
ResponseValues.hubs.each {
|
||||||
|
def hub = getChildDevice("harmony-${it.key}")
|
||||||
|
if (hub) {
|
||||||
|
def hubname = getHubName("${it.key}")
|
||||||
|
def activities = []
|
||||||
|
def aux = it.value.response?.data.activities.size()
|
||||||
|
if (aux >= 1) {
|
||||||
|
activities = it.value.response.data.activities.collect {
|
||||||
|
[id: it.key, name: it.value['name'], type: it.value['type']]
|
||||||
|
}
|
||||||
|
activities += [id: "off", name: "Activity OFF", type: "0"]
|
||||||
|
log.trace activities
|
||||||
|
}
|
||||||
|
hub.sendEvent(name: "activities", value: new groovy.json.JsonBuilder(activities).toString(), descriptionText: "Activities are ${activities.collect { it.name }?.join(', ')}", display: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.debug "Logitech Harmony - did not get json results from response body: $response.data"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def getActivityName(activity,hubId) {
|
def getActivityName(activity,hubId) {
|
||||||
// GET ACTIVITY'S NAME
|
// GET ACTIVITY'S NAME
|
||||||
def actname = activity
|
def actname = activity
|
||||||
|
|||||||
Reference in New Issue
Block a user