mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-10 05:11:51 +00:00
Compare commits
22 Commits
PROD_2016.
...
MSA-1559-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bdf491becb | ||
|
|
687c64d29d | ||
|
|
2f87309fdf | ||
|
|
37524f17b2 | ||
|
|
47522facc7 | ||
|
|
330b41941a | ||
|
|
26d286e0a0 | ||
|
|
ef2323f1b1 | ||
|
|
24ea8269a3 | ||
|
|
f1309b2ee2 | ||
|
|
72b2016b7d | ||
|
|
a9aee8fd96 | ||
|
|
5c015cf678 | ||
|
|
cf1a46e309 | ||
|
|
1ff77dc608 | ||
|
|
afbec02217 | ||
|
|
434a72bd13 | ||
|
|
3fba7c9422 | ||
|
|
b63d4a9156 | ||
|
|
6eb29ad019 | ||
|
|
711cdc3ebf | ||
|
|
6a1a2b0ed9 |
@@ -0,0 +1,394 @@
|
||||
/**
|
||||
* Trend Setter - Colorful Light Group Device
|
||||
*
|
||||
* Copyright 2015 Chris Kitch
|
||||
*
|
||||
* 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: "Colorful Light Group Device", namespace: "kriskit.trendSetter", author: "Chris Kitch") {
|
||||
capability "Actuator"
|
||||
capability "Sensor"
|
||||
capability "Switch"
|
||||
capability "Switch Level"
|
||||
capability "Color Control"
|
||||
|
||||
command "adjustLevel"
|
||||
command "adjustSaturation"
|
||||
command "adjustHue"
|
||||
|
||||
attribute "onPercentage", "number"
|
||||
attribute "levelSync", "string"
|
||||
attribute "colorSync", "string"
|
||||
attribute "saturationSync", "string"
|
||||
attribute "hueSync", "string"
|
||||
}
|
||||
|
||||
simulator {
|
||||
// TODO: define status and reply messages here
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "on", label: '${name}', action: "switch.off", icon: "st.lights.multi-light-bulb-on", backgroundColor: "#79b821", nextState: "turningOff"
|
||||
attributeState "off", label: '${name}', action: "switch.on", icon: "st.lights.multi-light-bulb-off", backgroundColor: "#ffffff", nextState: "turningOn"
|
||||
attributeState "turningOn", label: '${name}', action: "switch.off", icon: "st.lights.multi-light-bulb-on", backgroundColor: "#79b821", nextState: "turningOff"
|
||||
attributeState "turningOff", label: '${name}', action: "switch.on", icon: "st.lights.multi-light-bulb-off", backgroundColor: "#ffffff", nextState: "turningOn"
|
||||
attributeState "half", label: '${name}', action: "switch.on", icon: "st.lights.multi-light-bulb-on", backgroundColor: "#a3d164", nextState: "turningOn"
|
||||
attributeState "mostlyOn", label: 'Onish', action: "switch.on", icon: "st.lights.multi-light-bulb-on", backgroundColor: "#79b821", nextState: "turningOn"
|
||||
attributeState "mostlyOff", label: 'Offish', action: "switch.off", icon: "st.lights.multi-light-bulb-off", backgroundColor: "#d1e5b5", nextState: "turninOff"
|
||||
}
|
||||
|
||||
tileAttribute ("device.color", key: "COLOR_CONTROL") {
|
||||
attributeState "color", action: "color control.setColor"
|
||||
}
|
||||
|
||||
tileAttribute ("device.onPercentage", key: "SECONDARY_CONTROL") {
|
||||
attributeState "onPercentage", label:'${currentValue}% On'
|
||||
attributeState "100", label:'All On'
|
||||
attributeState "0", label:'All Off'
|
||||
}
|
||||
|
||||
tileAttribute("device.level", key: "SLIDER_CONTROL") {
|
||||
attributeState "default", label: '', action: "switch level.setLevel"
|
||||
}
|
||||
}
|
||||
|
||||
standardTile("levelLabel", "levelLable", height:1, width:1, decoration: "flat", inactiveLabel: true) {
|
||||
state "default", label:"Level", unit:"", icon: "st.illuminance.illuminance.light"
|
||||
}
|
||||
|
||||
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 3, inactiveLabel: false) {
|
||||
state "level", action:"switch level.setLevel"
|
||||
}
|
||||
|
||||
valueTile("levelValue", "device.level", inactiveLabel: true, height:1, width:1, decoration: "flat") {
|
||||
state "default", label:'${currentValue}%', unit:""
|
||||
}
|
||||
|
||||
valueTile("levelSync", "device.levelSync", height:1, width:1) {
|
||||
state "default", label:' Sync ', unit:"", action: "adjustLevel", backgroundColor: "#ff9900"
|
||||
state "ok", label:'', unit:"", backgroundColor: "#00b509"
|
||||
}
|
||||
|
||||
standardTile("saturationLabel", "saturationLabel", height:1, width:1, decoration: "flat", inactiveLabel: true) {
|
||||
state "default", label:"Sat", unit:"", icon: "st.Kids.kids2"
|
||||
}
|
||||
|
||||
controlTile("saturationSliderControl", "device.saturation", "slider", height: 1, width: 3, inactiveLabel: false) {
|
||||
state "saturation", action:"color control.setSaturation"
|
||||
}
|
||||
|
||||
valueTile("saturationValue", "device.saturation", inactiveLabel: true, height:1, width:1, decoration: "flat") {
|
||||
state "default", label:'${currentValue}%', unit:""
|
||||
}
|
||||
|
||||
valueTile("saturationSync", "device.saturationSync", height:1, width:1) {
|
||||
state "default", label:' Sync ', unit:"", action: "adjustSaturation", backgroundColor: "#ff9900"
|
||||
state "ok", label:'', unit:"", backgroundColor: "#00b509"
|
||||
}
|
||||
|
||||
standardTile("hueLabel", "hueLabel", height:1, width:1, decoration: "flat", inactiveLabel: true) {
|
||||
state "default", label:"Hue", unit:"", icon: "st.Kids.kids2"
|
||||
}
|
||||
|
||||
controlTile("hueSliderControl", "device.hue", "slider", height: 1, width: 3, inactiveLabel: false) {
|
||||
state "hue", action:"color control.setHue"
|
||||
}
|
||||
|
||||
valueTile("hueValue", "device.hue", inactiveLabel: true, height:1, width:1, decoration: "flat") {
|
||||
state "default", label:'${currentValue}%', unit:""
|
||||
}
|
||||
|
||||
valueTile("hueSync", "device.hueSync", height:1, width:1) {
|
||||
state "default", label:' Sync ', unit:"", action: "adjustHue", backgroundColor: "#ff9900"
|
||||
state "ok", label:'', unit:"", backgroundColor: "#00b509"
|
||||
}
|
||||
}
|
||||
|
||||
main "switch"
|
||||
details([
|
||||
"switch",
|
||||
"levelLabel",
|
||||
"levelSliderControl",
|
||||
"levelValue",
|
||||
"levelSync",
|
||||
"saturationLabel",
|
||||
"saturationSliderControl",
|
||||
"saturationValue",
|
||||
"saturationSync",
|
||||
"hueLabel",
|
||||
"hueSliderControl",
|
||||
"hueValue",
|
||||
"hueSync"])
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
}
|
||||
|
||||
def groupSync(name, values) {
|
||||
try {
|
||||
"sync${name.capitalize()}"(values)
|
||||
} catch(ex) {
|
||||
log.error "Error executing 'sync${name.capitalize()}' method: $ex"
|
||||
}
|
||||
}
|
||||
|
||||
// SWITCH
|
||||
def on() {
|
||||
on(true)
|
||||
}
|
||||
|
||||
def on(triggerGroup) {
|
||||
sendEvent(name: "switch", value: "on")
|
||||
sendEvent(name: "onPercentage", value: 100, displayed: false)
|
||||
|
||||
if (triggerGroup)
|
||||
parent.performGroupCommand("on")
|
||||
}
|
||||
|
||||
def off() {
|
||||
off(true)
|
||||
}
|
||||
|
||||
def off(triggerGroup) {
|
||||
sendEvent(name: "switch", value: "off")
|
||||
sendEvent(name: "onPercentage", value: 0, displayed: false)
|
||||
|
||||
if (triggerGroup)
|
||||
parent.performGroupCommand("off")
|
||||
}
|
||||
|
||||
def syncSwitch(values) {
|
||||
log.debug "syncSwitch(): $values"
|
||||
|
||||
def onCount = values?.count { it == "on" }
|
||||
def percentOn = (int)Math.floor((onCount / values?.size()) * 100)
|
||||
|
||||
log.debug "Percent On: $percentOn"
|
||||
|
||||
if (percentOn == 0 || percentOn == 100) {
|
||||
if (percentOn == 0)
|
||||
off(false)
|
||||
else
|
||||
on(false)
|
||||
return
|
||||
}
|
||||
|
||||
def value = null
|
||||
|
||||
if (percentOn == 50)
|
||||
value = "half"
|
||||
else if (percentOn > 0 && percentOn < 50)
|
||||
value = "mostlyOff"
|
||||
else if (percentOn > 50 && percentOn < 100)
|
||||
value = "mostlyOn"
|
||||
|
||||
sendEvent(name: "switch", value: value)
|
||||
sendEvent(name: "onPercentage", value: percentOn, displayed: false)
|
||||
}
|
||||
|
||||
// LEVEL
|
||||
def setLevel(val) {
|
||||
setLevel(val, true)
|
||||
}
|
||||
|
||||
def setLevel(val, triggerGroup) {
|
||||
log.debug "Setting level to $val"
|
||||
|
||||
if (val < 0)
|
||||
val = 0
|
||||
|
||||
if( val > 100)
|
||||
val = 100
|
||||
|
||||
if (triggerGroup) {
|
||||
if (val == 0)
|
||||
off()
|
||||
else
|
||||
on()
|
||||
}
|
||||
|
||||
sendEvent(name: "level", value: val, isStateChange: true)
|
||||
sendEvent(name: "switch.setLevel", value: val, isStateChange: true)
|
||||
|
||||
if (triggerGroup)
|
||||
parent.performGroupCommand("setLevel", [val])
|
||||
}
|
||||
|
||||
def syncLevel(values) {
|
||||
log.debug "syncLevel(): $values"
|
||||
|
||||
def valueCount = values?.size()
|
||||
def valueCountBy = values?.countBy { it }
|
||||
def matchValue = "bad"
|
||||
def level = device.currentValue("level")
|
||||
|
||||
valueCountBy.each { value, count ->
|
||||
if (count == valueCount) {
|
||||
level = value
|
||||
matchValue = "ok"
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if (matchValue == "bad")
|
||||
level = getAdjustmentLevel(values)
|
||||
|
||||
setLevel(level, false)
|
||||
sendEvent(name: "levelSync", value: matchValue, displayed: false)
|
||||
}
|
||||
|
||||
def adjustLevel() {
|
||||
def values = parent.getGroupCurrentValues("level")
|
||||
|
||||
if (!values)
|
||||
return
|
||||
|
||||
def level = getAdjustmentLevel(values)
|
||||
setLevel(level)
|
||||
}
|
||||
|
||||
def getAdjustmentLevel(values) {
|
||||
if (!values)
|
||||
return
|
||||
|
||||
def valueCountBy = values?.countBy { it }
|
||||
valueCountBy = valueCountBy?.sort { a, b -> b.value <=> a.value }
|
||||
|
||||
def level = device.currentValue("level")
|
||||
|
||||
if (valueCountBy.size() > 1) {
|
||||
if (valueCountBy.size() == values.size()) {
|
||||
log.debug "Values are all different - making average"
|
||||
level = Math.round(values.sum() / values.size())
|
||||
} else {
|
||||
log.debug "Some values are the same, choosing most popular"
|
||||
def firstItem = valueCountBy.find { true }
|
||||
level = firstItem.key
|
||||
}
|
||||
}
|
||||
|
||||
return level
|
||||
}
|
||||
|
||||
// COLOR
|
||||
def setColor(value) {
|
||||
setColor(value, true)
|
||||
}
|
||||
|
||||
def setColor(value, triggerGroup) {
|
||||
value.level = null
|
||||
|
||||
def hex = value.hex
|
||||
|
||||
if (!hex && value.hue && value.saturation)
|
||||
hex = colorUtil.hslToHex(value.hue, value.saturation)
|
||||
|
||||
sendEvent(name: "color", value: value.hex, displayed:false)
|
||||
|
||||
if (triggerGroup)
|
||||
parent.performGroupCommand("setColor", [value])
|
||||
|
||||
if (value.saturation)
|
||||
setSaturation(value.saturation, triggerGroup, false)
|
||||
|
||||
if (value.hue)
|
||||
setHue(value.hue, triggerGroup, false)
|
||||
}
|
||||
|
||||
def syncColor(values) {
|
||||
log.debug "syncColor(): $values"
|
||||
}
|
||||
|
||||
// SATURATION
|
||||
def setSaturation(value) {
|
||||
setSaturation(value, true, true)
|
||||
}
|
||||
|
||||
def setSaturation(value, triggerGroup, sendColor) {
|
||||
on(triggerGroup)
|
||||
|
||||
sendEvent(name: "saturation", value: (int)value, displayed:false)
|
||||
|
||||
if (triggerGroup)
|
||||
parent.performGroupCommand("setSaturation", [value])
|
||||
|
||||
if (sendColor) {
|
||||
def hex = colorUtil.hslToHex((int)device.currentValue("hue"), value)
|
||||
sendEvent(name: "color", value: hex, displayed:false)
|
||||
}
|
||||
}
|
||||
|
||||
def syncSaturation(values) {
|
||||
log.debug "syncSaturation(): $values"
|
||||
|
||||
def valueCount = values?.size()
|
||||
def valueCountBy = values?.countBy { it }
|
||||
def matchValue = "bad"
|
||||
|
||||
valueCountBy.each { value, count ->
|
||||
if (count == valueCount) {
|
||||
matchValue = "ok"
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
sendEvent(name: "saturationSync", value: matchValue, displayed: false)
|
||||
}
|
||||
|
||||
def adjustSaturation() {
|
||||
def saturation = (int)device.currentValue("saturation")
|
||||
log.debug "adjustSaturation $saturation"
|
||||
setSaturation(saturation)
|
||||
}
|
||||
|
||||
// HUE
|
||||
def setHue(value) {
|
||||
setHue(value, true, true)
|
||||
}
|
||||
|
||||
def setHue(value, triggerGroup, sendColor) {
|
||||
on(triggerGroup)
|
||||
sendEvent(name: "hue", value: (int)value, displayed: false)
|
||||
|
||||
if (triggerGroup)
|
||||
parent.performGroupCommand("setHue", [value])
|
||||
|
||||
if (sendColor) {
|
||||
def hex = colorUtil.hslToHex(value, (int)device.currentValue("saturation"))
|
||||
sendEvent(name: "color", value: hex, displayed:false)
|
||||
}
|
||||
}
|
||||
|
||||
def syncHue(values) {
|
||||
log.debug "syncHue(): $values"
|
||||
|
||||
def valueCount = values?.size()
|
||||
def valueCountBy = values?.countBy { it }
|
||||
def matchValue = "bad"
|
||||
|
||||
valueCountBy.each { value, count ->
|
||||
if (count == valueCount) {
|
||||
matchValue = "ok"
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
sendEvent(name: "hueSync", value: matchValue, displayed: false)
|
||||
}
|
||||
|
||||
def adjustHue() {
|
||||
def hue = (int)device.currentValue("hue")
|
||||
log.debug "adjustHue: $hue"
|
||||
setHue(hue)
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
/**
|
||||
* Trend Setter - Dimmer Group Device
|
||||
*
|
||||
* Copyright 2015 Chris Kitch
|
||||
*
|
||||
* 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: "Dimmer Group Device", namespace: "kriskit.trendSetter", author: "Chris Kitch") {
|
||||
capability "Actuator"
|
||||
capability "Sensor"
|
||||
capability "Switch"
|
||||
capability "Switch Level"
|
||||
|
||||
command "adjustLevel"
|
||||
|
||||
attribute "onPercentage", "number"
|
||||
attribute "levelSync", "string"
|
||||
}
|
||||
|
||||
simulator {
|
||||
// TODO: define status and reply messages here
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "on", label: '${name}', action: "switch.off", icon: "st.lights.multi-light-bulb-on", backgroundColor: "#79b821", nextState: "turningOff"
|
||||
attributeState "off", label: '${name}', action: "switch.on", icon: "st.lights.multi-light-bulb-off", backgroundColor: "#ffffff", nextState: "turningOn"
|
||||
attributeState "turningOn", label: '${name}', action: "switch.off", icon: "st.lights.multi-light-bulb-on", backgroundColor: "#79b821", nextState: "turningOff"
|
||||
attributeState "turningOff", label: '${name}', action: "switch.on", icon: "st.lights.multi-light-bulb-off", backgroundColor: "#ffffff", nextState: "turningOn"
|
||||
attributeState "half", label: '${name}', action: "switch.on", icon: "st.lights.multi-light-bulb-on", backgroundColor: "#a3d164", nextState: "turningOn"
|
||||
attributeState "mostlyOn", label: 'Onish', action: "switch.on", icon: "st.lights.multi-light-bulb-on", backgroundColor: "#79b821", nextState: "turningOn"
|
||||
attributeState "mostlyOff", label: 'Offish', action: "switch.off", icon: "st.lights.multi-light-bulb-off", backgroundColor: "#d1e5b5", nextState: "turninOff"
|
||||
}
|
||||
|
||||
tileAttribute ("device.onPercentage", key: "SECONDARY_CONTROL") {
|
||||
attributeState "onPercentage", label:'${currentValue}% On'
|
||||
attributeState "100", label:'All On'
|
||||
attributeState "0", label:'All Off'
|
||||
}
|
||||
|
||||
tileAttribute("device.level", key: "SLIDER_CONTROL") {
|
||||
attributeState "default", label: '', action: "switch level.setLevel"
|
||||
}
|
||||
}
|
||||
|
||||
standardTile("levelLabel", "levelLable", height:1, width:1, decoration: "flat", inactiveLabel: true) {
|
||||
state "default", label:"Level", unit:"", icon: "st.illuminance.illuminance.bright"
|
||||
}
|
||||
|
||||
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 3, inactiveLabel: false) {
|
||||
state "level", action:"switch level.setLevel"
|
||||
}
|
||||
|
||||
valueTile("levelValue", "device.level", inactiveLabel: true, height:1, width:1, decoration: "flat") {
|
||||
state "default", label:'${currentValue}%', unit:""
|
||||
}
|
||||
|
||||
valueTile("levelSync", "device.levelSync", height:1, width:1) {
|
||||
state "default", label:' Sync ', unit:"", action: "adjustLevel", backgroundColor: "#ff9900"
|
||||
state "ok", label:'', unit:"", backgroundColor: "#00b509"
|
||||
}
|
||||
}
|
||||
|
||||
main "switch"
|
||||
details(["switch", "levelLabel", "levelSliderControl", "levelValue", "levelSync"])
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
}
|
||||
|
||||
def groupSync(name, values) {
|
||||
try {
|
||||
"sync${name.capitalize()}"(values)
|
||||
} catch(ex) {
|
||||
log.error "Error executing 'sync${name.capitalize()}' method: $ex"
|
||||
}
|
||||
}
|
||||
|
||||
// SWITCH
|
||||
def on() {
|
||||
on(true)
|
||||
}
|
||||
|
||||
def on(triggerGroup) {
|
||||
sendEvent(name: "switch", value: "on")
|
||||
sendEvent(name: "onPercentage", value: 100, displayed: false)
|
||||
|
||||
if (triggerGroup)
|
||||
parent.performGroupCommand("on")
|
||||
}
|
||||
|
||||
def off() {
|
||||
off(true)
|
||||
}
|
||||
|
||||
def off(triggerGroup) {
|
||||
sendEvent(name: "switch", value: "off")
|
||||
sendEvent(name: "onPercentage", value: 0, displayed: false)
|
||||
|
||||
if (triggerGroup)
|
||||
parent.performGroupCommand("off")
|
||||
}
|
||||
|
||||
def syncSwitch(values) {
|
||||
log.debug "syncSwitch(): $values"
|
||||
|
||||
def onCount = values?.count { it == "on" }
|
||||
def percentOn = (int)Math.floor((onCount / values?.size()) * 100)
|
||||
|
||||
log.debug "Percent On: $percentOn"
|
||||
|
||||
if (percentOn == 0 || percentOn == 100) {
|
||||
if (percentOn == 0)
|
||||
off(false)
|
||||
else
|
||||
on(false)
|
||||
return
|
||||
}
|
||||
|
||||
def value = null
|
||||
|
||||
if (percentOn == 50)
|
||||
value = "half"
|
||||
else if (percentOn > 0 && percentOn < 50)
|
||||
value = "mostlyOff"
|
||||
else if (percentOn > 50 && percentOn < 100)
|
||||
value = "mostlyOn"
|
||||
|
||||
sendEvent(name: "switch", value: value)
|
||||
sendEvent(name: "onPercentage", value: percentOn, displayed: false)
|
||||
}
|
||||
|
||||
// LEVEL
|
||||
def setLevel(val){
|
||||
setLevel(val, true)
|
||||
}
|
||||
|
||||
def setLevel(val, triggerGroup) {
|
||||
log.debug "Setting level to $val"
|
||||
|
||||
if (val < 0)
|
||||
val = 0
|
||||
|
||||
if( val > 100)
|
||||
val = 100
|
||||
|
||||
if (triggerGroup) {
|
||||
if (val == 0)
|
||||
off()
|
||||
else
|
||||
on()
|
||||
}
|
||||
|
||||
sendEvent(name: "level", value: val, isStateChange: true)
|
||||
sendEvent(name: "switch.setLevel", value: val, isStateChange: true)
|
||||
|
||||
if (triggerGroup)
|
||||
parent.performGroupCommand("setLevel", [val])
|
||||
}
|
||||
|
||||
def syncLevel(values) {
|
||||
log.debug "syncLevel(): $values"
|
||||
|
||||
def valueCount = values?.size()
|
||||
def valueCountBy = values?.countBy { it }
|
||||
def matchValue = "bad"
|
||||
def level = device.currentValue("level")
|
||||
|
||||
valueCountBy.each { value, count ->
|
||||
if (count == valueCount) {
|
||||
level = value
|
||||
matchValue = "ok"
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if (matchValue == "bad")
|
||||
level = getAdjustmentLevel(values)
|
||||
|
||||
setLevel(level, false)
|
||||
sendEvent(name: "levelSync", value: matchValue, displayed: false)
|
||||
}
|
||||
|
||||
def adjustLevel() {
|
||||
def values = parent.getGroupCurrentValues("level")
|
||||
|
||||
if (!values)
|
||||
return
|
||||
|
||||
def valueCountBy = values?.countBy { it }
|
||||
valueCountBy = valueCountBy?.sort { a, b -> b.value <=> a.value }
|
||||
|
||||
def level = getAdjustmentLevel(values)
|
||||
|
||||
setLevel(level)
|
||||
}
|
||||
|
||||
def getAdjustmentLevel(values) {
|
||||
if (!values)
|
||||
return
|
||||
|
||||
def valueCountBy = values?.countBy { it }
|
||||
valueCountBy = valueCountBy?.sort { a, b -> b.value <=> a.value }
|
||||
|
||||
def level = device.currentValue("level")
|
||||
|
||||
if (valueCountBy.size() > 1) {
|
||||
if (valueCountBy.size() == values.size()) {
|
||||
log.debug "Values are all different - making average"
|
||||
level = Math.round(values.sum() / values.size())
|
||||
} else {
|
||||
log.debug "Some values are the same, choosing most popular"
|
||||
def firstItem = valueCountBy.find { true }
|
||||
level = firstItem.key
|
||||
}
|
||||
}
|
||||
|
||||
return level
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/**
|
||||
* Power Meter Group Device
|
||||
*
|
||||
* Copyright 2015 Chris Kitch
|
||||
*
|
||||
* 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: "Power Meter Group Device", namespace: "kriskit.trendsetter", author: "Chris Kitch") {
|
||||
capability "Power Meter"
|
||||
capability "Sensor"
|
||||
capability "Refresh"
|
||||
|
||||
attribute "powerUsage", "string"
|
||||
}
|
||||
|
||||
simulator {
|
||||
// TODO: define status and reply messages here
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"main", type: "lighting", width: 6, height: 4) {
|
||||
tileAttribute ("device.powerUsage", key: "PRIMARY_CONTROL") {
|
||||
attributeState "off", label: '${name}', backgroundColor: "#ffffff", icon: "st.Appliances.appliances17"
|
||||
attributeState "low", label: '${name}', backgroundColor: "#5CB85C", icon: "st.Appliances.appliances17"
|
||||
attributeState "medium", label: '${name}', backgroundColor: "#ff7b00", icon: "st.Appliances.appliances17"
|
||||
attributeState "high", label: '${name}', backgroundColor: "#c90000", icon: "st.Appliances.appliances17"
|
||||
attributeState "veryHigh", label: '${name}', backgroundColor: "#ff0000", icon: "st.Appliances.appliances17"
|
||||
}
|
||||
|
||||
tileAttribute ("device.power", key: "SECONDARY_CONTROL") {
|
||||
attributeState "default", label: '${currentValue} W'
|
||||
}
|
||||
}
|
||||
|
||||
standardTile("refresh", "refresh", height:2, width:6, inactiveLabel: false, decoration: "flat") {
|
||||
state "default", action: "refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
|
||||
main("main")
|
||||
details(["main", "refresh"])
|
||||
}
|
||||
}
|
||||
|
||||
// parse events into attributes
|
||||
def parse(String description) {
|
||||
}
|
||||
|
||||
def groupSync(name, values) {
|
||||
try {
|
||||
"sync${name.capitalize()}"(values)
|
||||
} catch(ex) {
|
||||
log.error "Error executing 'sync${name.capitalize()}' method: $ex"
|
||||
}
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
def powerValues = parent.getGroupCurrentValues("power")
|
||||
syncPower(powerValues)
|
||||
}
|
||||
|
||||
// POWER
|
||||
def syncPower(values) {
|
||||
log.debug "syncPower(): $values"
|
||||
def total = values?.sum()
|
||||
|
||||
if (total == 0) {
|
||||
sendEvent(name: "power", value: 0)
|
||||
sendEvent(name: "powerUsage", value: "off")
|
||||
return
|
||||
}
|
||||
|
||||
def aggregate = state.powerAggregate ?: []
|
||||
state.powerSyncCount = state.powerSyncCount + 1
|
||||
|
||||
if (state.powerSyncCount != null && state.powerSyncCount % 5 != 0) {
|
||||
aggregate.push(total)
|
||||
state.powerAggregate = aggregate
|
||||
return
|
||||
}
|
||||
|
||||
def aggregatedAverage = getAverage(aggregate)
|
||||
log.debug "Aggregated Average Power: $aggregatedAverage"
|
||||
sendEvent(name: "power", value: aggregatedAverage)
|
||||
|
||||
def level = getPowerUsageLevel(aggregatedAverage)
|
||||
log.debug "Power usage level: $level"
|
||||
sendEvent(name: "powerUsage", value: level)
|
||||
|
||||
state.powerAggregate = []
|
||||
}
|
||||
|
||||
def getPowerUsageLevel(value) {
|
||||
if (value == 0)
|
||||
return "off"
|
||||
|
||||
def boundaries = getPowerUsageBoundaries()
|
||||
|
||||
if (!boundaries)
|
||||
return "low"
|
||||
|
||||
log.debug "Determining power usage level with boundaries: $boundaries for value $value"
|
||||
|
||||
if (value > 0 && value <= boundaries.bottom)
|
||||
return "low"
|
||||
|
||||
if (value > boundaries.bottom && value < boundaries.top)
|
||||
return "medium"
|
||||
|
||||
if (value >= boundaries.top && value <= boundaries.max)
|
||||
return "high"
|
||||
|
||||
if (value > boundaries.max) {
|
||||
state.powerUsageBoundaries = null
|
||||
return "veryHigh"
|
||||
}
|
||||
}
|
||||
|
||||
def getPowerUsageBoundaries() {
|
||||
if (state.powerUsageBoundaries && state.powerSyncCount < 100)
|
||||
return state.powerUsageBoundaries
|
||||
|
||||
def events = device.events([max: 500])
|
||||
def powerEvents = events?.findAll {
|
||||
it.name == "power" && it.doubleValue > 0
|
||||
}
|
||||
|
||||
def powerValues = powerEvents*.doubleValue
|
||||
powerValues.sort()
|
||||
|
||||
def eventCount = powerValues?.size()
|
||||
def eventChunkSize = (int)Math.round(eventCount / 2)
|
||||
def chunkedEvents = powerValues.collate(eventChunkSize)
|
||||
|
||||
if (chunkedEvents.size() < 2)
|
||||
return null
|
||||
|
||||
def boundaries = [
|
||||
top: getAverage(chunkedEvents[1]),
|
||||
bottom: getAverage(chunkedEvents[0]),
|
||||
max: powerValues.max()
|
||||
]
|
||||
|
||||
state.powerSyncCount = 0
|
||||
state.powerUsageBoundaries = boundaries
|
||||
|
||||
log.debug "New boundaries: $boundaries"
|
||||
|
||||
return boundaries
|
||||
}
|
||||
|
||||
def getAverage(values) {
|
||||
return Math.round((values.sum() / values.size()) * 100) / 100
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* Trend Setter - Switch Group Device
|
||||
*
|
||||
* Copyright 2015 Chris Kitch
|
||||
*
|
||||
* 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: "Switch Group Device", namespace: "kriskit.trendSetter", author: "Chris Kitch") {
|
||||
capability "Actuator"
|
||||
capability "Sensor"
|
||||
capability "Switch"
|
||||
|
||||
attribute "onPercentage", "number"
|
||||
}
|
||||
|
||||
simulator {
|
||||
// TODO: define status and reply messages here
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821", nextState: "turningOff"
|
||||
attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn"
|
||||
attributeState "turningOn", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821", nextState: "turningOff"
|
||||
attributeState "turningOff", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn"
|
||||
attributeState "half", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#a3d164", nextState: "turningOn"
|
||||
attributeState "mostlyOn", label: 'Onish', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#79b821", nextState: "turningOn"
|
||||
attributeState "mostlyOff", label: 'Offish', action: "switch.off", icon: "st.switches.switch.off", backgroundColor: "#d1e5b5", nextState: "turninOff"
|
||||
}
|
||||
|
||||
tileAttribute ("device.onPercentage", key: "SECONDARY_CONTROL") {
|
||||
attributeState "onPercentage", label:'${currentValue}% On'
|
||||
attributeState "100", label:'All On'
|
||||
attributeState "0", label:'All Off'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main "switch"
|
||||
details(["switch"])
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
}
|
||||
|
||||
def groupSync(name, values) {
|
||||
try {
|
||||
"sync${name.capitalize()}"(values)
|
||||
} catch(ex) {
|
||||
log.error "Error executing 'sync${name.capitalize()}' method: $ex"
|
||||
}
|
||||
}
|
||||
|
||||
// SWITCH
|
||||
def on() {
|
||||
on(true)
|
||||
}
|
||||
|
||||
def on(triggerGroup) {
|
||||
sendEvent(name: "switch", value: "on")
|
||||
sendEvent(name: "onPercentage", value: 100, displayed: false)
|
||||
|
||||
if (triggerGroup)
|
||||
parent.performGroupCommand("on")
|
||||
}
|
||||
|
||||
def off() {
|
||||
off(true)
|
||||
}
|
||||
|
||||
def off(triggerGroup) {
|
||||
sendEvent(name: "switch", value: "off")
|
||||
sendEvent(name: "onPercentage", value: 0, displayed: false)
|
||||
|
||||
if (triggerGroup)
|
||||
parent.performGroupCommand("off")
|
||||
}
|
||||
|
||||
def syncSwitch(values) {
|
||||
log.debug "syncSwitch(): $values"
|
||||
|
||||
def onCount = values?.count { it == "on" }
|
||||
def percentOn = (int)Math.floor((onCount / values?.size()) * 100)
|
||||
|
||||
log.debug "Percent On: $percentOn"
|
||||
|
||||
if (percentOn == 0 || percentOn == 100) {
|
||||
if (percentOn == 0)
|
||||
off(false)
|
||||
else
|
||||
on(false)
|
||||
return
|
||||
}
|
||||
|
||||
def value = null
|
||||
|
||||
if (percentOn == 50)
|
||||
value = "half"
|
||||
else if (percentOn > 0 && percentOn < 50)
|
||||
value = "mostlyOff"
|
||||
else if (percentOn > 50 && percentOn < 100)
|
||||
value = "mostlyOn"
|
||||
|
||||
sendEvent(name: "switch", value: value)
|
||||
sendEvent(name: "onPercentage", value: percentOn, displayed: false)
|
||||
}
|
||||
@@ -59,6 +59,15 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.debug "${device} installed"
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "${device} updated"
|
||||
configureHealthCheck()
|
||||
}
|
||||
|
||||
// Parse incoming device messages to generate events
|
||||
def parse(String description) {
|
||||
log.debug "description is $description"
|
||||
@@ -96,6 +105,14 @@ def refresh() {
|
||||
zigbee.onOffRefresh() + zigbee.levelRefresh()
|
||||
}
|
||||
|
||||
def configureHealthCheck() {
|
||||
log.debug "configureHealthCheck"
|
||||
unschedule("healthPoll")
|
||||
runEvery5Minutes("healthPoll")
|
||||
// Device-Watch allows 2 check-in misses from device
|
||||
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
def healthPoll() {
|
||||
log.debug "healthPoll()"
|
||||
def cmds = zigbee.onOffRefresh() + zigbee.levelRefresh()
|
||||
@@ -103,9 +120,5 @@ def healthPoll() {
|
||||
}
|
||||
|
||||
def configure() {
|
||||
unschedule()
|
||||
runEvery5Minutes("healthPoll")
|
||||
// Device-Watch allows 2 check-in misses from device
|
||||
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
zigbee.onOffRefresh() + zigbee.levelRefresh()
|
||||
refresh()
|
||||
}
|
||||
|
||||
2
devicetypes/smartthings/dimmer-switch.src/.st-ignore
Normal file
2
devicetypes/smartthings/dimmer-switch.src/.st-ignore
Normal file
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
45
devicetypes/smartthings/dimmer-switch.src/README.md
Normal file
45
devicetypes/smartthings/dimmer-switch.src/README.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Z-wave Dimmer Switch
|
||||
|
||||
|
||||
|
||||
Works with:
|
||||
|
||||
* [GE Z-Wave In-Wall Smart Dimmer (GE 12724)](http://products.z-wavealliance.org/products/1197)
|
||||
* [GE Z-Wave In-Wall Smart Dimmer (Toggle) (GE 12729)](http://products.z-wavealliance.org/products/1201)
|
||||
* [GE Z-Wave Plug-in Smart Dimmer (GE 12718)](http://products.z-wavealliance.org/products/1191)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#Troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Switch Level** - it's defined to accept two parameters, the level and the rate of dimming
|
||||
* **Actuator** - represents that a Device has commands
|
||||
* **Indicator** - gives you the ability to set the indicator LED light on a Z-Wave switch
|
||||
* **Switch** - can detect state (possible values: on/off)
|
||||
* **Polling** - represents that poll() can be implemented for the device
|
||||
* **Refresh** - _refresh()_ command for status updates
|
||||
* **Sensor** - detects sensor events
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
## Device Health
|
||||
|
||||
Z-Wave Smart Dimmers (In-Wall, In-Wall(Toggle), Plug-In) are polled by the hub.
|
||||
As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed.
|
||||
Check-in interval = 32 mins.
|
||||
Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for
|
||||
the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row,
|
||||
it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
||||
Pairing needs to be tried again by placing the device closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
|
||||
* [General Z-Wave Dimmer/Switch Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/200955890-Troubleshooting-GE-in-wall-switch-or-dimmer-won-t-respond-to-commands-or-automations-Z-Wave-)
|
||||
* [GE Z-Wave In-Wall Smart Dimmer (GE 12724) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/200902600-GE-In-Wall-Paddle-Dimmer-Switch-GE-12724-Z-Wave-)
|
||||
* [GE Z-Wave In-Wall Smart Dimmer (Toggle) (GE 12729) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/207568463-GE-In-Wall-Smart-Toggle-Dimmer-GE-12729-Z-Wave-)
|
||||
* [GE Z-Wave Plug-in Smart Dimmer (GE 12718) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/202088474-GE-Plug-In-Smart-Dimmer-GE-12718-Z-Wave-)
|
||||
@@ -20,6 +20,7 @@ metadata {
|
||||
capability "Polling"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
|
||||
fingerprint mfr:"0063", prod:"4457", deviceJoinName: "Z-Wave Wall Dimmer"
|
||||
fingerprint mfr:"0063", prod:"4944", deviceJoinName: "Z-Wave Wall Dimmer"
|
||||
@@ -82,6 +83,8 @@ metadata {
|
||||
}
|
||||
|
||||
def updated(){
|
||||
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
||||
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
switch (ledIndicator) {
|
||||
case "on":
|
||||
indicatorWhenOn()
|
||||
@@ -215,6 +218,13 @@ def poll() {
|
||||
zwave.switchMultilevelV1.switchMultilevelGet().format()
|
||||
}
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
refresh()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
log.debug "refresh() is called"
|
||||
def commands = []
|
||||
|
||||
@@ -11,9 +11,9 @@ metadata {
|
||||
capability "Color Temperature"
|
||||
capability "Switch"
|
||||
capability "Switch Level" // brightness
|
||||
capability "Polling"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
}
|
||||
|
||||
simulator {
|
||||
@@ -23,7 +23,6 @@ metadata {
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "unreachable", label: "?", action:"refresh.refresh", icon:"http://hosted.lifx.co/smartthings/v1/196xUnreachable.png", backgroundColor:"#666666"
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", icon:"http://hosted.lifx.co/smartthings/v1/196xOff.png", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'Turning on', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
@@ -64,12 +63,8 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
// parse events into attributes
|
||||
def parse(String description) {
|
||||
if (description == 'updated') {
|
||||
return // don't poll when config settings is being updated as it may time out
|
||||
}
|
||||
poll()
|
||||
void installed() {
|
||||
sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"cloud\", \"scheme\":\"untracked\", \"hubHardwareId\": \"${device?.hub?.hardwareID}\"}")
|
||||
}
|
||||
|
||||
// handle commands
|
||||
@@ -192,14 +187,17 @@ def off() {
|
||||
return []
|
||||
}
|
||||
|
||||
def poll() {
|
||||
log.debug "Executing 'poll' for ${device} ${this} ${device.deviceNetworkId}"
|
||||
def refresh() {
|
||||
log.debug "Executing 'refresh'"
|
||||
|
||||
def resp = parent.apiGET("/lights/${selector()}")
|
||||
if (resp.status == 404) {
|
||||
sendEvent(name: "switch", value: "unreachable")
|
||||
state.online = false
|
||||
sendEvent(name: "DeviceWatch-DeviceStatusUpdate", value: "offline", displayed: false)
|
||||
log.warn "$device is Offline"
|
||||
return []
|
||||
} else if (resp.status != 200) {
|
||||
log.error("Unexpected result in poll(): [${resp.status}] ${resp.data}")
|
||||
log.error("Unexpected result in refresh(): [${resp.status}] ${resp.data}")
|
||||
return []
|
||||
}
|
||||
def data = resp.data[0]
|
||||
@@ -208,19 +206,20 @@ def poll() {
|
||||
sendEvent(name: "label", value: data.label)
|
||||
sendEvent(name: "level", value: Math.round((data.brightness ?: 1) * 100))
|
||||
sendEvent(name: "switch.setLevel", value: Math.round((data.brightness ?: 1) * 100))
|
||||
sendEvent(name: "switch", value: data.connected ? data.power : "unreachable")
|
||||
sendEvent(name: "switch", value: data.power)
|
||||
sendEvent(name: "color", value: colorUtil.hslToHex((data.color.hue / 3.6) as int, (data.color.saturation * 100) as int))
|
||||
sendEvent(name: "hue", value: data.color.hue / 3.6)
|
||||
sendEvent(name: "saturation", value: data.color.saturation * 100)
|
||||
sendEvent(name: "colorTemperature", value: data.color.kelvin)
|
||||
sendEvent(name: "model", value: "${data.product.company} ${data.product.name}")
|
||||
sendEvent(name: "model", value: data.product.name)
|
||||
|
||||
return []
|
||||
if (data.connected) {
|
||||
sendEvent(name: "DeviceWatch-DeviceStatus", value: "online", displayed: false)
|
||||
log.debug "$device is Online"
|
||||
} else {
|
||||
sendEvent(name: "DeviceWatch-DeviceStatus", value: "offline", displayed: false)
|
||||
log.warn "$device is Offline"
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
log.debug "Executing 'refresh'"
|
||||
poll()
|
||||
}
|
||||
|
||||
def selector() {
|
||||
|
||||
@@ -10,9 +10,9 @@ metadata {
|
||||
capability "Color Temperature"
|
||||
capability "Switch"
|
||||
capability "Switch Level" // brightness
|
||||
capability "Polling"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
}
|
||||
|
||||
simulator {
|
||||
@@ -22,13 +22,12 @@ metadata {
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "unreachable", label: "?", action:"refresh.refresh", icon:"http://hosted.lifx.co/smartthings/v1/196xUnreachable.png", backgroundColor:"#666666"
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", icon:"http://hosted.lifx.co/smartthings/v1/196xOff.png", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'Turning on', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'Turning off', action:"switch.on", icon:"http://hosted.lifx.co/smartthings/v1/196xOff.png", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
|
||||
}
|
||||
|
||||
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
|
||||
attributeState "level", action:"switch level.setLevel"
|
||||
}
|
||||
@@ -53,15 +52,10 @@ metadata {
|
||||
main "switch"
|
||||
details(["switch", "colorTempSliderControl", "colorTemp", "refresh"])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// parse events into attributes
|
||||
def parse(String description) {
|
||||
if (description == 'updated') {
|
||||
return // don't poll when config settings is being updated as it may time out
|
||||
}
|
||||
poll()
|
||||
void installed() {
|
||||
sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"cloud\", \"scheme\":\"untracked\", \"hubHardwareId\": \"${device?.hub?.hardwareID}\"}")
|
||||
}
|
||||
|
||||
// handle commands
|
||||
@@ -122,14 +116,17 @@ def off() {
|
||||
return []
|
||||
}
|
||||
|
||||
def poll() {
|
||||
log.debug "Executing 'poll' for ${device} ${this} ${device.deviceNetworkId}"
|
||||
def refresh() {
|
||||
log.debug "Executing 'refresh'"
|
||||
|
||||
def resp = parent.apiGET("/lights/${selector()}")
|
||||
if (resp.status == 404) {
|
||||
sendEvent(name: "switch", value: "unreachable")
|
||||
state.online = false
|
||||
sendEvent(name: "DeviceWatch-DeviceStatusUpdate", value: "offline", displayed: false)
|
||||
log.warn "$device is Offline"
|
||||
return []
|
||||
} else if (resp.status != 200) {
|
||||
log.error("Unexpected result in poll(): [${resp.status}] ${resp.data}")
|
||||
log.error("Unexpected result in refresh(): [${resp.status}] ${resp.data}")
|
||||
return []
|
||||
}
|
||||
def data = resp.data[0]
|
||||
@@ -137,16 +134,17 @@ def poll() {
|
||||
sendEvent(name: "label", value: data.label)
|
||||
sendEvent(name: "level", value: Math.round((data.brightness ?: 1) * 100))
|
||||
sendEvent(name: "switch.setLevel", value: Math.round((data.brightness ?: 1) * 100))
|
||||
sendEvent(name: "switch", value: data.connected ? data.power : "unreachable")
|
||||
sendEvent(name: "switch", value: data.power)
|
||||
sendEvent(name: "colorTemperature", value: data.color.kelvin)
|
||||
sendEvent(name: "model", value: data.product.name)
|
||||
|
||||
return []
|
||||
if (data.connected) {
|
||||
sendEvent(name: "DeviceWatch-DeviceStatus", value: "online", displayed: false)
|
||||
log.debug "$device is Online"
|
||||
} else {
|
||||
sendEvent(name: "DeviceWatch-DeviceStatus", value: "offline", displayed: false)
|
||||
log.warn "$device is Offline"
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
log.debug "Executing 'refresh'"
|
||||
poll()
|
||||
}
|
||||
|
||||
def selector() {
|
||||
|
||||
@@ -74,6 +74,15 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.debug "${device} installed"
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "${device} updated"
|
||||
configureHealthCheck()
|
||||
}
|
||||
|
||||
// Parse incoming device messages to generate events
|
||||
def parse(String description) {
|
||||
log.debug "description is $description"
|
||||
@@ -140,10 +149,14 @@ def refresh() {
|
||||
zigbee.onOffRefresh() + zigbee.electricMeasurementPowerRefresh()
|
||||
}
|
||||
|
||||
def configure() {
|
||||
def configureHealthCheck() {
|
||||
// Device-Watch allows 3 check-in misses from device (plus 1 min lag time)
|
||||
// enrolls with default periodic reporting until newer 5 min interval is confirmed
|
||||
sendEvent(name: "checkInterval", value: 3 * 10 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
def configure() {
|
||||
|
||||
|
||||
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
|
||||
refresh() + zigbee.onOffConfig(0, 300) + powerConfig()
|
||||
|
||||
@@ -84,6 +84,15 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.debug "${device} installed"
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "${device} updated"
|
||||
configureHealthCheck()
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
log.debug "description: $description"
|
||||
|
||||
@@ -303,11 +312,13 @@ def refresh() {
|
||||
return refreshCmds + enrollResponse()
|
||||
}
|
||||
|
||||
def configure() {
|
||||
def configureHealthCheck() {
|
||||
// Device-Watch allows 3 check-in misses from device (plus 1 min lag time)
|
||||
// enrolls with default periodic reporting until newer 5 min interval is confirmed
|
||||
sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
def configure() {
|
||||
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
|
||||
// battery minReport 30 seconds, maxReportTime 6 hrs by default
|
||||
return refresh() + zigbee.batteryConfig() + zigbee.temperatureConfig(30, 300) // send refresh cmds as part of config
|
||||
|
||||
@@ -88,6 +88,15 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.debug "${device} installed"
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "${device} updated"
|
||||
configureHealthCheck()
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
log.debug "description: $description"
|
||||
|
||||
@@ -318,11 +327,13 @@ def refresh() {
|
||||
return refreshCmds + enrollResponse()
|
||||
}
|
||||
|
||||
def configure() {
|
||||
def configureHealthCheck() {
|
||||
// Device-Watch allows 3 check-in misses from device (plus 1 min lag time)
|
||||
// enrolls with default periodic reporting until newer 5 min interval is confirmed
|
||||
sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
def configure() {
|
||||
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
|
||||
// battery minReport 30 seconds, maxReportTime 6 hrs by default
|
||||
return refresh() + zigbee.batteryConfig() + zigbee.temperatureConfig(30, 300) // send refresh cmds as part of config
|
||||
|
||||
@@ -115,6 +115,34 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.debug "${device} installed"
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "updated called"
|
||||
log.info "garage value : $garageSensor"
|
||||
if (garageSensor == "Yes") {
|
||||
def descriptionText = "Updating device to garage sensor"
|
||||
if (device.latestValue("status") == "open") {
|
||||
sendEvent(name: 'status', value: 'garage-open', descriptionText: descriptionText, translatable: true)
|
||||
}
|
||||
else if (device.latestValue("status") == "closed") {
|
||||
sendEvent(name: 'status', value: 'garage-closed', descriptionText: descriptionText, translatable: true)
|
||||
}
|
||||
}
|
||||
else {
|
||||
def descriptionText = "Updating device to open/close sensor"
|
||||
if (device.latestValue("status") == "garage-open") {
|
||||
sendEvent(name: 'status', value: 'open', descriptionText: descriptionText, translatable: true)
|
||||
}
|
||||
else if (device.latestValue("status") == "garage-closed") {
|
||||
sendEvent(name: 'status', value: 'closed', descriptionText: descriptionText, translatable: true)
|
||||
}
|
||||
}
|
||||
configureHealthCheck()
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
Map map = [:]
|
||||
if (description?.startsWith('catchall:')) {
|
||||
@@ -246,29 +274,6 @@ private Map parseIasMessage(String description) {
|
||||
return resultMap
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "updated called"
|
||||
log.info "garage value : $garageSensor"
|
||||
if (garageSensor == "Yes") {
|
||||
def descriptionText = "Updating device to garage sensor"
|
||||
if (device.latestValue("status") == "open") {
|
||||
sendEvent(name: 'status', value: 'garage-open', descriptionText: descriptionText, translatable: true)
|
||||
}
|
||||
else if (device.latestValue("status") == "closed") {
|
||||
sendEvent(name: 'status', value: 'garage-closed', descriptionText: descriptionText, translatable: true)
|
||||
}
|
||||
}
|
||||
else {
|
||||
def descriptionText = "Updating device to open/close sensor"
|
||||
if (device.latestValue("status") == "garage-open") {
|
||||
sendEvent(name: 'status', value: 'open', descriptionText: descriptionText, translatable: true)
|
||||
}
|
||||
else if (device.latestValue("status") == "garage-closed") {
|
||||
sendEvent(name: 'status', value: 'closed', descriptionText: descriptionText, translatable: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def getTemperature(value) {
|
||||
def celsius = Integer.parseInt(value, 16).shortValue() / 100
|
||||
if(getTemperatureScale() == "C"){
|
||||
@@ -411,11 +416,13 @@ def refresh() {
|
||||
return refreshCmds + enrollResponse()
|
||||
}
|
||||
|
||||
def configure() {
|
||||
def configureHealthCheck(){
|
||||
// Device-Watch allows 3 check-in misses from device (plus 1 min lag time)
|
||||
// enrolls with default periodic reporting until newer 5 min interval is confirmed
|
||||
sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
def configure() {
|
||||
log.debug "Configuring Reporting"
|
||||
|
||||
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
|
||||
|
||||
@@ -75,6 +75,15 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.debug "${device} installed"
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "${device} updated"
|
||||
configureHealthCheck()
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
log.debug "description: $description"
|
||||
|
||||
@@ -265,11 +274,12 @@ def refresh() {
|
||||
return refreshCmds + enrollResponse()
|
||||
}
|
||||
|
||||
def configure() {
|
||||
def configureHealthCheck() {
|
||||
// Device-Watch allows 3 check-in misses from device (plus 1 min lag time)
|
||||
// enrolls with default periodic reporting until newer 5 min interval is confirmed
|
||||
sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
|
||||
}
|
||||
def configure() {
|
||||
log.debug "Configuring Reporting, IAS CIE, and Bindings."
|
||||
|
||||
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
|
||||
|
||||
@@ -69,6 +69,15 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.debug "${device} installed"
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "${device} updated"
|
||||
configureHealthCheck()
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
log.debug "description: $description"
|
||||
|
||||
@@ -268,11 +277,13 @@ def refresh()
|
||||
zigbee.readAttribute(0x0001, 0x0020)
|
||||
}
|
||||
|
||||
def configure() {
|
||||
def configureHealthCheck() {
|
||||
// Device-Watch allows 3 check-in misses from device (plus 1 min lag time)
|
||||
// enrolls with default periodic reporting until newer 5 min interval is confirmed
|
||||
sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
def configure() {
|
||||
log.debug "Configuring Reporting and Bindings."
|
||||
def humidityConfigCmds = [
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 1 0xFC45 {${device.zigbeeId}} {}", "delay 500",
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
41
devicetypes/smartthings/zigbee-dimmer-power.src/README.md
Normal file
41
devicetypes/smartthings/zigbee-dimmer-power.src/README.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# GE Plug-In/In-Wall Smart Dimmer (ZigBee)
|
||||
|
||||
|
||||
|
||||
Works with:
|
||||
|
||||
* [GE In-Wall Smart Dimmer (ZigBee)](https://shop.smartthings.com/#!/products/ge-in-wall-smart-dimmer-switch)
|
||||
* [GE Plug-In Smart Dimmer (ZigBee)](https://www.smartthings.com/works-with-smartthings/ge/ge-plug-in-smart-dimmer-zigbee)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#Troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Actuator** - represents that a Device has commands
|
||||
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
|
||||
* **Refresh** - _refresh()_ command for status updates
|
||||
* **Power Meter** - ability to check the power meter(energy consumption) of device
|
||||
* **Sensor** - represents the device sensor capability
|
||||
* **Switch** - can detect state (possible values: on/off)
|
||||
* **Switch Level** - represents current light level, usually 0-100 in percent
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
## Device Health
|
||||
|
||||
A Zigbee dimmer with maxReportTime of 5 mins.
|
||||
Check-in interval is double the value of maxReportTime.
|
||||
This gives the device twice the amount of time to respond before it is marked as offline.
|
||||
Enrolls with default periodic reporting until newer 5 min interval is confirmed
|
||||
It then enrolls the device with updated checkInterval i.e. 12 mins
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
||||
Pairing needs to be tried again by placing the device closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
|
||||
* [GE Z-Wave In-Wall Smart Dimmer (GE 45857) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/204988564-GE-In-Wall-Smart-Dimmer-45857GE-ZigBee-)
|
||||
* [GE Zigbee Plug-in Smart Dimmer (GE 45852) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/205239280-GE-Plug-In-Smart-Dimmer-45852GE-ZigBee-)
|
||||
@@ -21,6 +21,7 @@ metadata {
|
||||
capability "Sensor"
|
||||
capability "Switch"
|
||||
capability "Switch Level"
|
||||
capability "Health Check"
|
||||
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B04"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0702"
|
||||
@@ -70,8 +71,20 @@ def parse(String description) {
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.warn "DID NOT PARSE MESSAGE for description : $description"
|
||||
log.debug zigbee.parseDescriptionAsMap(description)
|
||||
def cluster = zigbee.parse(description)
|
||||
if (cluster && cluster.clusterId == 0x0006 && cluster.command == 0x07) {
|
||||
if (cluster.data[0] == 0x00){
|
||||
log.debug "ON/OFF REPORTING CONFIG RESPONSE: " + cluster
|
||||
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
else {
|
||||
log.warn "ON/OFF REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.warn "DID NOT PARSE MESSAGE for description : $description"
|
||||
log.debug zigbee.parseDescriptionAsMap(description)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,11 +100,22 @@ def setLevel(value) {
|
||||
zigbee.setLevel(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
return zigbee.onOffRefresh()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.simpleMeteringPowerRefresh() + zigbee.electricMeasurementPowerRefresh() + zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.simpleMeteringPowerConfig() + zigbee.electricMeasurementPowerConfig()
|
||||
zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.simpleMeteringPowerRefresh() + zigbee.electricMeasurementPowerRefresh() + zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.simpleMeteringPowerConfig() + zigbee.electricMeasurementPowerConfig()
|
||||
}
|
||||
|
||||
def configure() {
|
||||
log.debug "Configuring Reporting and Bindings."
|
||||
|
||||
// Device-Watch allows 3 check-in misses from device (plus 1 min lag time)
|
||||
// enrolls with default periodic reporting until newer 5 min interval is confirmed
|
||||
sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
refresh()
|
||||
}
|
||||
|
||||
@@ -48,6 +48,15 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.debug "${device} installed"
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "${device} updated"
|
||||
configureHealthCheck()
|
||||
}
|
||||
|
||||
// Parse incoming device messages to generate events
|
||||
def parse(String description) {
|
||||
log.debug "description is $description"
|
||||
@@ -100,12 +109,13 @@ def refresh() {
|
||||
zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.onOffConfig(0, 300) + zigbee.levelConfig()
|
||||
}
|
||||
|
||||
def configure() {
|
||||
log.debug "Configuring Reporting and Bindings."
|
||||
def configureHealthCheck() {
|
||||
// Device-Watch allows 3 check-in misses from device (plus 1 min lag time)
|
||||
// enrolls with default periodic reporting until newer 5 min interval is confirmed
|
||||
sendEvent(name: "checkInterval", value: 3 * 10 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
def configure() {
|
||||
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
|
||||
zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.onOffConfig(0, 300) + zigbee.levelConfig()
|
||||
}
|
||||
|
||||
@@ -70,6 +70,16 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def installed() {
|
||||
log.debug "${device} installed"
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "${device} updated"
|
||||
configureHealthCheck()
|
||||
}
|
||||
|
||||
//Globals
|
||||
private getATTRIBUTE_HUE() { 0x0000 }
|
||||
private getATTRIBUTE_SATURATION() { 0x0001 }
|
||||
@@ -138,16 +148,17 @@ def ping() {
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
|
||||
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, 0x20, 1, 3600, 0x01) + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION, 0x20, 1, 3600, 0x01)
|
||||
}
|
||||
|
||||
def configure() {
|
||||
log.debug "Configuring Reporting and Bindings."
|
||||
def configureHealthCheck() {
|
||||
// Device-Watch allows 3 check-in misses from device (plus 1 min lag time)
|
||||
// enrolls with default periodic reporting until newer 5 min interval is confirmed
|
||||
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
|
||||
def configure() {
|
||||
refresh()
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ metadata {
|
||||
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0006", outClusters: "0003, 0006, 0019, 0406", manufacturer: "Leviton", model: "ZSS-10", deviceJoinName: "Leviton Switch"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0006", outClusters: "000A", manufacturer: "HAI", model: "65A21-1", deviceJoinName: "Leviton Wireless Load Control Module-30amp"
|
||||
}
|
||||
|
||||
// simulator metadata
|
||||
|
||||
@@ -69,6 +69,15 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.debug "${device} installed"
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "${device} updated"
|
||||
configureHealthCheck()
|
||||
}
|
||||
|
||||
// Parse incoming device messages to generate events
|
||||
def parse(String description) {
|
||||
log.debug "description is $description"
|
||||
@@ -121,16 +130,17 @@ def ping() {
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
|
||||
zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh() + zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.colorTemperatureConfig()
|
||||
}
|
||||
|
||||
def configure() {
|
||||
log.debug "Configuring Reporting and Bindings."
|
||||
def configureHealthCheck() {
|
||||
// Device-Watch allows 3 check-in misses from device (plus 1 min lag time)
|
||||
// enrolls with default periodic reporting until newer 5 min interval is confirmed
|
||||
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
|
||||
def configure() {
|
||||
refresh()
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
* for the specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
import groovy.transform.Field
|
||||
|
||||
@Field Boolean hasConfiguredHealthCheck = false
|
||||
|
||||
metadata {
|
||||
definition (name: "ZLL Dimmer Bulb", namespace: "smartthings", author: "SmartThings") {
|
||||
@@ -21,6 +24,7 @@ metadata {
|
||||
capability "Refresh"
|
||||
capability "Switch"
|
||||
capability "Switch Level"
|
||||
capability "Health Check"
|
||||
|
||||
//fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 1000", outClusters: "0000,0019"
|
||||
fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 1000", outClusters: "0019"
|
||||
@@ -96,7 +100,38 @@ def poll() {
|
||||
refresh()
|
||||
}
|
||||
|
||||
def configure() {
|
||||
log.debug "Configuring Reporting and Bindings."
|
||||
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
return zigbee.levelRefresh()
|
||||
}
|
||||
|
||||
def healthPoll() {
|
||||
log.debug "healthPoll()"
|
||||
def cmds = refresh()
|
||||
cmds.each{ sendHubCommand(new physicalgraph.device.HubAction(it))}
|
||||
}
|
||||
|
||||
def configureHealthCheck() {
|
||||
Integer hcIntervalMinutes = 12
|
||||
if (!hasConfiguredHealthCheck) {
|
||||
log.debug "Configuring Health Check, Reporting"
|
||||
unschedule("healthPoll")
|
||||
runEvery5Minutes("healthPoll")
|
||||
// Device-Watch allows 2 check-in misses from device
|
||||
sendEvent(name: "checkInterval", value: hcIntervalMinutes * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
hasConfiguredHealthCheck = true
|
||||
}
|
||||
}
|
||||
|
||||
def configure() {
|
||||
log.debug "configure()"
|
||||
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
|
||||
configureHealthCheck()
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "updated()"
|
||||
configureHealthCheck()
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
* for the specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
import groovy.transform.Field
|
||||
|
||||
@Field Boolean hasConfiguredHealthCheck = false
|
||||
|
||||
metadata {
|
||||
definition (name: "ZLL White Color Temperature Bulb", namespace: "smartthings", author: "SmartThings") {
|
||||
@@ -22,6 +25,7 @@ metadata {
|
||||
capability "Refresh"
|
||||
capability "Switch"
|
||||
capability "Switch Level"
|
||||
capability "Health Check"
|
||||
|
||||
attribute "colorName", "string"
|
||||
command "setGenericName"
|
||||
@@ -96,9 +100,41 @@ def poll() {
|
||||
zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh()
|
||||
}
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
return zigbee.levelRefresh()
|
||||
}
|
||||
|
||||
def healthPoll() {
|
||||
log.debug "healthPoll()"
|
||||
def cmds = zigbee.onOffRefresh() + zigbee.levelRefresh()
|
||||
cmds.each{ sendHubCommand(new physicalgraph.device.HubAction(it))}
|
||||
}
|
||||
|
||||
def configureHealthCheck() {
|
||||
Integer hcIntervalMinutes = 12
|
||||
if (!hasConfiguredHealthCheck) {
|
||||
log.debug "Configuring Health Check, Reporting"
|
||||
unschedule("healthPoll")
|
||||
runEvery5Minutes("healthPoll")
|
||||
// Device-Watch allows 2 check-in misses from device
|
||||
sendEvent(name: "checkInterval", value: hcIntervalMinutes * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
hasConfiguredHealthCheck = true
|
||||
}
|
||||
}
|
||||
|
||||
def configure() {
|
||||
log.debug "Configuring Reporting and Bindings."
|
||||
log.debug "configure()"
|
||||
configureHealthCheck()
|
||||
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh()
|
||||
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "updated()"
|
||||
configureHealthCheck()
|
||||
}
|
||||
|
||||
def setColorTemperature(value) {
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
@@ -0,0 +1,39 @@
|
||||
# Z-wave Dimmer
|
||||
|
||||
|
||||
|
||||
Works with:
|
||||
|
||||
* [Leviton Plug-in Lamp Dimmer Module (DZPD3-1LW)](http://www.leviton.com/OA_HTML/ProductDetail.jsp?partnumber=DZPD3-1LW)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Switch Level** - it's defined to accept two parameters, the level and the rate of dimming
|
||||
* **Actuator** - represents that a Device has commands
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
* **Switch** - can detect state (possible values: on/off)
|
||||
* **Polling** - represents that poll() can be implemented for the device
|
||||
* **Refresh** - _refresh()_ command for status updates
|
||||
* **Sensor** - detects sensor events
|
||||
|
||||
## Device Health
|
||||
|
||||
A Category C5 Leviton Plug-in Lamp Dimmer Module (DZPA1-1LW) (Z-Wave) polled by the hub.
|
||||
As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed.
|
||||
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins.
|
||||
Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for
|
||||
the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row,
|
||||
it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
||||
Pairing needs to be tried again by placing the device closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
|
||||
* [Leviton Plug-in Lamp Dimmer Module (DZPD3-1LW) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/206171053-How-to-connect-Leviton-Z-Wave-devices)
|
||||
@@ -15,12 +15,14 @@ metadata {
|
||||
definition (name: "Z-Wave Dimmer Switch Generic", namespace: "smartthings", author: "SmartThings") {
|
||||
capability "Switch Level"
|
||||
capability "Actuator"
|
||||
capability "Health Check"
|
||||
capability "Switch"
|
||||
capability "Polling"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
|
||||
fingerprint inClusters: "0x26", deviceJoinName: "Z-Wave Dimmer"
|
||||
fingerprint mfr:"001D", prod:"1902", deviceJoinName: "Z-Wave Dimmer"
|
||||
}
|
||||
|
||||
simulator {
|
||||
@@ -68,6 +70,11 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
def updated(){
|
||||
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
||||
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
def result = null
|
||||
if (description != "updated") {
|
||||
@@ -185,6 +192,13 @@ def poll() {
|
||||
zwave.switchMultilevelV1.switchMultilevelGet().format()
|
||||
}
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
refresh()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
log.debug "refresh() is called"
|
||||
def commands = []
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
39
devicetypes/smartthings/zwave-switch-generic.src/README.md
Normal file
39
devicetypes/smartthings/zwave-switch-generic.src/README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Z-wave Switch
|
||||
|
||||
|
||||
|
||||
Works with:
|
||||
|
||||
* [Leviton Appliance Module (DZPA1-1LW)](https://support.smartthings.com/hc/en-us/articles/205881176-Leviton-Appliance-Module-DZPA1-1LW-)
|
||||
* [GE Plug-In Outdoor Smart Switch (GE 12720) (Z-Wave)](https://support.smartthings.com/hc/en-us/articles/200903080-GE-Plug-In-Outdoor-Smart-Switch-GE-12720-Z-Wave-)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Actuator** - represents that a Device has commands
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
* **Switch** - can detect state (possible values: on/off)
|
||||
* **Polling** - represents that poll() can be implemented for the device
|
||||
* **Refresh** - _refresh()_ command for status updates
|
||||
* **Sensor** - detects sensor events
|
||||
|
||||
## Device Health
|
||||
|
||||
A Category C5 Leviton Appliance Module (DZPA1-1LW) and GE Plug-In Outdoor Smart Switch (GE 12720) (Z-Wave) polled by the hub.
|
||||
As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed.
|
||||
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins.
|
||||
Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for
|
||||
the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row,
|
||||
it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
||||
Pairing needs to be tried again by placing the device closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
|
||||
* [Leviton Appliance Module (DZPA1-1LW) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/205881176-Leviton-Appliance-Module-DZPA1-1LW-)
|
||||
* [GE Plug-In Outdoor Smart Switch (GE 12720) (Z-Wave) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/200903080-GE-Plug-In-Outdoor-Smart-Switch-GE-12720-Z-Wave-)
|
||||
@@ -14,12 +14,15 @@
|
||||
metadata {
|
||||
definition (name: "Z-Wave Switch Generic", namespace: "smartthings", author: "SmartThings") {
|
||||
capability "Actuator"
|
||||
capability "Health Check"
|
||||
capability "Switch"
|
||||
capability "Polling"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
|
||||
fingerprint inClusters: "0x25", deviceJoinName: "Z-Wave Switch"
|
||||
fingerprint mfr:"001D", prod:"1A02", deviceJoinName: "Z-Wave Switch"
|
||||
fingerprint mfr:"0063", prod:"4F50", deviceJoinName: "Z-Wave Switch"
|
||||
}
|
||||
|
||||
// simulator metadata
|
||||
@@ -50,6 +53,11 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
def updated(){
|
||||
// Device-Watch simply pings if no device events received for checkInterval duration of 32min = 2 * 15min + 2min lag time
|
||||
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
def result = null
|
||||
def cmd = zwave.parse(description, [0x20: 1, 0x70: 1])
|
||||
@@ -126,6 +134,13 @@ def poll() {
|
||||
])
|
||||
}
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
refresh()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
delayBetween([
|
||||
zwave.switchBinaryV1.switchBinaryGet().format(),
|
||||
|
||||
2
devicetypes/smartthings/zwave-switch.src/.st-ignore
Normal file
2
devicetypes/smartthings/zwave-switch.src/.st-ignore
Normal file
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
49
devicetypes/smartthings/zwave-switch.src/README.md
Normal file
49
devicetypes/smartthings/zwave-switch.src/README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Z-Wave Switch
|
||||
|
||||
|
||||
Works with:
|
||||
|
||||
* [GE Z-Wave Plug-In Smart Switch (12719)](http://products.z-wavealliance.org/products/1193)
|
||||
* [GE Z-Wave In-Wall Smart Outlet (12721)](http://products.z-wavealliance.org/products/1195)
|
||||
* [GE Z-Wave In-Wall Smart Switch (12722)](http://products.z-wavealliance.org/products/1196)
|
||||
* [GE Z-Wave In-Wall Smart Toggle Switch (12727)](http://products.z-wavealliance.org/products/1200)
|
||||
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#Troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Actuator** - represents that a Device has commands
|
||||
* **Indicator** - gives you the ability to set the indicator LED light on a Z-Wave switch
|
||||
* **Switch** - can detect state (possible values: on/off)
|
||||
* **Polling** - represents that poll() can be implemented for the device
|
||||
* **Refresh** - _refresh()_ command for status updates
|
||||
* **Sensor** - detects sensor events
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
## Device Health
|
||||
|
||||
Z-Wave Switches (Plug-In, In-Wall(Toggle Switch, Switch, Outlet)) are polled by the hub.
|
||||
As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed.
|
||||
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins.
|
||||
Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for
|
||||
the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row,
|
||||
it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time.
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
||||
Pairing needs to be tried again by placing the device closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
|
||||
* [General Z-Wave Dimmer/Switch Troubleshooting](https://support.smartthings.com/hc/en-us/articles/200955890-Troubleshooting-GE-in-wall-switch-or-dimmer-won-t-respond-to-commands-or-automations-Z-Wave-)
|
||||
* [GE Z-Wave Plug-In Smart Switch (12719) Troubleshooting](https://support.smartthings.com/hc/en-us/articles/200903070-GE-Plug-In-Smart-Switch-GE-12719-Z-Wave)
|
||||
* [GE Z-Wave In-Wall Smart Outlet (12721) Troubleshooting](https://support.smartthings.com/hc/en-us/articles/200903020-GE-In-Wall-Smart-Outlet-GE-12721-Z-Wave)
|
||||
* [GE Z-Wave In-Wall Smart Switch (12722) Troubleshooting](https://support.smartthings.com/hc/en-us/articles/200902540-GE-In-Wall-Smart-Switch-GE-12722-Z-Wave)
|
||||
* [GE Z-Wave In-Wall Smart Toggle Switch (12727) Troubleshooting](https://support.smartthings.com/hc/en-us/articles/207568933-GE-In-Wall-Smart-Toggle-Switch-GE-12727-Z-Wave)
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ metadata {
|
||||
capability "Polling"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
|
||||
fingerprint mfr:"0063", prod:"4952", deviceJoinName: "Z-Wave Wall Switch"
|
||||
fingerprint mfr:"0063", prod:"5257", deviceJoinName: "Z-Wave Wall Switch"
|
||||
@@ -64,6 +65,8 @@ metadata {
|
||||
}
|
||||
|
||||
def updated(){
|
||||
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
||||
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
switch (ledIndicator) {
|
||||
case "on":
|
||||
indicatorWhenOn()
|
||||
@@ -156,6 +159,13 @@ def poll() {
|
||||
])
|
||||
}
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
**/
|
||||
def ping() {
|
||||
refresh()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
delayBetween([
|
||||
zwave.switchBinaryV1.switchBinaryGet().format(),
|
||||
|
||||
317
smartapps/kriskit-trendsetter/group.src/group.groovy
Normal file
317
smartapps/kriskit-trendsetter/group.src/group.groovy
Normal file
@@ -0,0 +1,317 @@
|
||||
/**
|
||||
* Trend Setter - Group
|
||||
*
|
||||
* Copyright 2015 Chris Kitch
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
definition(
|
||||
name: "Group",
|
||||
namespace: "kriskit.trendSetter",
|
||||
author: "Chris Kitch",
|
||||
description: "A child SmartApp for Trend Setter for handling a group of devices.",
|
||||
category: "My Apps",
|
||||
iconUrl: "https://cdn.rawgit.com/Kriskit/SmartThingsPublic/master/smartapps/kriskit/trendsetter/icon.png",
|
||||
iconX2Url: "https://cdn.rawgit.com/Kriskit/SmartThingsPublic/master/smartapps/kriskit/trendsetter/icon@2x.png",
|
||||
iconX3Url: "https://cdn.rawgit.com/Kriskit/SmartThingsPublic/master/smartapps/kriskit/trendsetter/icon@3x.png",
|
||||
parent: "kriskit.trendsetter:Trend Setter")
|
||||
|
||||
def version() {
|
||||
return "1.0"
|
||||
}
|
||||
|
||||
def typeDefinitions() {
|
||||
return [
|
||||
[
|
||||
type: "switch",
|
||||
singular: "Switch",
|
||||
plural: "Switches",
|
||||
deviceType: "Switch Group Device",
|
||||
attributes: [
|
||||
[name: "switch"]
|
||||
]
|
||||
],
|
||||
[
|
||||
type: "switchLevel",
|
||||
singular: "Dimmer",
|
||||
plural: "Dimmers",
|
||||
deviceType: "Dimmer Group Device",
|
||||
inherits: "switch",
|
||||
attributes: [
|
||||
[name: "level"]
|
||||
]
|
||||
],
|
||||
[
|
||||
type: "colorControl",
|
||||
singular: "Colorful Light",
|
||||
plural: "Colorful Lights",
|
||||
deviceType: "Colorful Light Group Device",
|
||||
inherits: "switchLevel",
|
||||
attributes: [
|
||||
[name: "hue"],
|
||||
[name: "saturation"],
|
||||
[name: "color"]
|
||||
]
|
||||
],
|
||||
[
|
||||
type: "powerMeter",
|
||||
singular: "Power Meter",
|
||||
plural: "Power Meters",
|
||||
deviceType: "Power Meter Group Device",
|
||||
attributes: [
|
||||
[name: "power"]
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
// Setup
|
||||
preferences {
|
||||
page(name: "configure")
|
||||
}
|
||||
|
||||
def configure() {
|
||||
atomicState.typeDefinitions = null
|
||||
def controller = getControllerDevice();
|
||||
|
||||
dynamicPage(name: "configure", uninstall: controller != null, install: true) {
|
||||
if (!controller) {
|
||||
section {
|
||||
input "deviceType", "enum", title: "Device Type", required: true, submitOnChange: true, options: getDeviceTypeOptions()
|
||||
paragraph "This cannot be changed once the group is created.", color: "#ffcc00"
|
||||
}
|
||||
}
|
||||
|
||||
if (deviceType) {
|
||||
def definition = getTypeDefinition(deviceType)
|
||||
|
||||
section(title: controller == null ? "Grouping" : null) {
|
||||
label title: "Group Name", required: true
|
||||
|
||||
input "devices", "capability.${deviceType}", title: "${definition.plural}", multiple: true, required: true, submitOnChange: controller != null
|
||||
|
||||
if (selectedDevicesContainsController()) {
|
||||
paragraph "WARNING: You have selected the controller ${definition.singular.toLowerCase()} for this group. This will likely cause unexpected behaviour.\n\nPlease uncheck the '${controller.displayName}' from the selected ${definition.plural.toLowerCase()}.",
|
||||
image: "https://cdn2.iconfinder.com/data/icons/freecns-cumulus/32/519791-101_Warning-512.png"
|
||||
}
|
||||
}
|
||||
|
||||
if (controller == null) {
|
||||
section(title: "Controller") {
|
||||
input "deviceName", "text", title: "${definition.singular} Name", required: true, description: "For the controlling virtual ${definition.singular.toLowerCase()} to be created"
|
||||
}
|
||||
}
|
||||
|
||||
if (definition.advanced) {
|
||||
section(title: "Advanced", hidden: true, hideable: true) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.debug "Installed with settings: ${settings}"
|
||||
|
||||
installControllerDevice()
|
||||
initialize()
|
||||
}
|
||||
|
||||
def uninstalled() {
|
||||
log.debug "Uninstalled"
|
||||
}
|
||||
|
||||
def installControllerDevice() {
|
||||
def definition = getTypeDefinition()
|
||||
|
||||
log.debug "Installing switch group controller device..."
|
||||
addChildDevice("kriskit.trendSetter", definition.deviceType, UUID.randomUUID().toString(), null, ["name": deviceName, "label": deviceName, completedSetup: true])
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "Updated with settings: ${settings}"
|
||||
|
||||
unsubscribe()
|
||||
initialize()
|
||||
}
|
||||
|
||||
def initialize() {
|
||||
def definition = getTypeDefinition()
|
||||
addSubscriptions(definition)
|
||||
def namesToCheck = definition.attributes?.collect { it.name }
|
||||
updateControllerState(namesToCheck)
|
||||
}
|
||||
|
||||
def addSubscriptions(definition) {
|
||||
def controller = getControllerDevice()
|
||||
|
||||
definition.attributes?.each {
|
||||
log.debug "Subscribing to ${it.name}..."
|
||||
subscribe(devices, it.name, onDeviceAttributeChange)
|
||||
}
|
||||
}
|
||||
|
||||
// Subscription Handlers
|
||||
def onDeviceAttributeChange(evt) {
|
||||
def namesToCheck = atomicState.namesToCheck ?: []
|
||||
|
||||
log.debug "Device state change: ${evt.device.displayName} -> ${evt.name} = ${evt.value}"
|
||||
|
||||
if (!namesToCheck.any { it == evt.name })
|
||||
namesToCheck.push(evt.name)
|
||||
|
||||
atomicState.namesToCheck = namesToCheck
|
||||
runIn(1, "updateControllerState")
|
||||
}
|
||||
|
||||
def updateControllerState() {
|
||||
def namesToCheck = atomicState.namesToCheck
|
||||
updateControllerState(namesToCheck)
|
||||
atomicState.namesToCheck = null
|
||||
}
|
||||
|
||||
def updateControllerState(namesToCheck) {
|
||||
if (!namesToCheck)
|
||||
return
|
||||
|
||||
def controller = getControllerDevice()
|
||||
namesToCheck?.each { name ->
|
||||
def values = devices?.currentValue(name)
|
||||
values?.removeAll([null])
|
||||
log.debug "Updating Controller State: $name -> $values"
|
||||
controller.groupSync(name, values)
|
||||
}
|
||||
}
|
||||
|
||||
def performGroupCommand(command, arguments = null) {
|
||||
runCommand(devices, command, arguments ?: [])
|
||||
}
|
||||
|
||||
def runCommand(target, command, args) {
|
||||
log.debug "Running command '${command}' with arguments ${args} on ${target}..."
|
||||
$performCommand(target, command, args)
|
||||
}
|
||||
|
||||
def getGroupCurrentValues(name) {
|
||||
return devices?.currentValue(name)
|
||||
}
|
||||
|
||||
// Utilities
|
||||
def getTypeDefinitions() {
|
||||
if (atomicState.version != version()) {
|
||||
atomicState.typeDefinitions = null
|
||||
atomicState.version = version()
|
||||
}
|
||||
|
||||
if (atomicState.typeDefinitions)
|
||||
return atomicState.typeDefinitions
|
||||
|
||||
log.debug "Building type definitions..."
|
||||
|
||||
def result = []
|
||||
def definitions = typeDefinitions()
|
||||
|
||||
definitions?.each { definition ->
|
||||
if (definition.inherits)
|
||||
definition = mergeAttributes(definition, definitions.find { it.type == definition.inherits })
|
||||
|
||||
result.push(definition)
|
||||
}
|
||||
|
||||
atomicState.typeDefinitions = result
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
def mergeAttributes(definition, inheritedDefinition) {
|
||||
inheritedDefinition.attributes?.each { attr ->
|
||||
if (!definition.attributes?.any { it.name == attr.name })
|
||||
definition.attributes.push(attr)
|
||||
}
|
||||
|
||||
if (inheritedDefinition.inherits) {
|
||||
def definitions = typeDefinitions()
|
||||
definition = mergeAttributes(definition, definitions.find { it.type == inheritedDefinition.inherits })
|
||||
}
|
||||
|
||||
return definition
|
||||
}
|
||||
|
||||
def getControllerDevice() {
|
||||
return getChildDevices()?.find { true }
|
||||
}
|
||||
|
||||
def getTypeDefinition() {
|
||||
return getTypeDefinition(deviceType)
|
||||
}
|
||||
|
||||
def getTypeDefinition(type) {
|
||||
return getTypeDefinitions().find {
|
||||
it.type == type
|
||||
}
|
||||
}
|
||||
|
||||
def getDeviceTypeOptions() {
|
||||
return getTypeDefinitions().collect {
|
||||
["${it.type}": it.singular]
|
||||
}
|
||||
}
|
||||
|
||||
def selectedDevicesContainsController() {
|
||||
def controller = getControllerDevice()
|
||||
return devices?.any {
|
||||
it?.deviceNetworkId == controller?.deviceNetworkId
|
||||
}
|
||||
}
|
||||
|
||||
private $performCommand(target, command, args) {
|
||||
switch(args?.size()) {
|
||||
default:
|
||||
target?."$command"()
|
||||
break
|
||||
|
||||
case 1:
|
||||
target?."$command"(args[0])
|
||||
break
|
||||
|
||||
case 2:
|
||||
target?."$command"(args[0], args[1])
|
||||
break
|
||||
|
||||
case 3:
|
||||
target?."$command"(args[0], args[1], args[2])
|
||||
break
|
||||
|
||||
case 4:
|
||||
target?."$command"(args[0], args[1], args[2], args[3])
|
||||
break
|
||||
|
||||
case 5:
|
||||
target?."$command"(args[0], args[1], args[2], args[3], args[4], args[5])
|
||||
break
|
||||
|
||||
case 6:
|
||||
target?."$command"(args[0], args[1], args[2], args[3], args[4], args[5], args[6])
|
||||
break
|
||||
|
||||
case 7:
|
||||
target?."$command"(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7])
|
||||
break
|
||||
|
||||
case 8:
|
||||
target?."$command"(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8])
|
||||
break
|
||||
|
||||
case 9:
|
||||
target?."$command"(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9])
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Trend Setter
|
||||
*
|
||||
* Copyright 2015 Chris Kitch
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
definition(
|
||||
name: "Trend Setter",
|
||||
namespace: "kriskit.trendSetter",
|
||||
author: "Chris Kitch",
|
||||
description: "Uses virtual child devices to group other devices together and perform commands and aggregate data from the group.",
|
||||
category: "My Apps",
|
||||
iconUrl: "https://cdn.rawgit.com/Kriskit/SmartThingsPublic/master/smartapps/kriskit/trendsetter/icon.png",
|
||||
iconX2Url: "https://cdn.rawgit.com/Kriskit/SmartThingsPublic/master/smartapps/kriskit/trendsetter/icon@2x.png",
|
||||
iconX3Url: "https://cdn.rawgit.com/Kriskit/SmartThingsPublic/master/smartapps/kriskit/trendsetter/icon@3x.png",
|
||||
singleInstance: true)
|
||||
|
||||
|
||||
preferences {
|
||||
page(name: "mainPage")
|
||||
}
|
||||
|
||||
def mainPage() {
|
||||
dynamicPage(name: "mainPage", title: "Your Groups", install: true, uninstall: true, submitOnChange: true) {
|
||||
section {
|
||||
app(name: "groups", appName: "Group", namespace: "kriskit.trendSetter", title: "Create Group...", multiple: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.debug "Installed with settings: ${settings}"
|
||||
|
||||
initialize()
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "Updated with settings: ${settings}"
|
||||
|
||||
unsubscribe()
|
||||
initialize()
|
||||
}
|
||||
|
||||
def initialize() {
|
||||
// TODO: subscribe to attributes, devices, locations, etc.
|
||||
}
|
||||
|
||||
def childUninstalled() {
|
||||
}
|
||||
@@ -242,8 +242,6 @@ def installed() {
|
||||
} else {
|
||||
initialize()
|
||||
}
|
||||
// Check for new devices and remove old ones every 3 hours
|
||||
runEvery3Hours('updateDevices')
|
||||
}
|
||||
|
||||
// called after settings are changed
|
||||
@@ -271,9 +269,19 @@ private removeChildDevices(devices) {
|
||||
def initialize() {
|
||||
log.debug "initialize"
|
||||
updateDevices()
|
||||
// Check for new devices and remove old ones every 3 hours
|
||||
runEvery5Minutes('updateDevices')
|
||||
setupDeviceWatch()
|
||||
}
|
||||
|
||||
// Misc
|
||||
private setupDeviceWatch() {
|
||||
def hub = location.hubs[0]
|
||||
// Make sure that all child devices are enrolled in device watch
|
||||
getChildDevices().each {
|
||||
it.sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"LAN\", \"scheme\":\"untracked\", \"hubHardwareId\": \"${hub?.hub?.hardwareID}\"}")
|
||||
}
|
||||
}
|
||||
|
||||
Map apiRequestHeaders() {
|
||||
return ["Authorization": "Bearer ${state.lifxAccessToken}",
|
||||
@@ -376,7 +384,7 @@ def updateDevices() {
|
||||
def data = [
|
||||
label: device.label,
|
||||
level: Math.round((device.brightness ?: 1) * 100),
|
||||
switch: device.connected ? device.power : "unreachable",
|
||||
switch: device.power,
|
||||
colorTemperature: device.color.kelvin
|
||||
]
|
||||
if (device.product.capabilities.has_color) {
|
||||
@@ -387,18 +395,42 @@ def updateDevices() {
|
||||
} else {
|
||||
childDevice = addChildDevice(app.namespace, "LIFX White Bulb", device.id, null, data)
|
||||
}
|
||||
childDevice?.completedSetup = true
|
||||
} else {
|
||||
if (device.product.capabilities.has_color) {
|
||||
sendEvent(name: "color", value: colorUtil.hslToHex((device.color.hue / 3.6) as int, (device.color.saturation * 100) as int))
|
||||
sendEvent(name: "hue", value: device.color.hue / 3.6)
|
||||
sendEvent(name: "saturation", value: device.color.saturation * 100)
|
||||
}
|
||||
childDevice.sendEvent(name: "label", value: device.label)
|
||||
childDevice.sendEvent(name: "level", value: Math.round((device.brightness ?: 1) * 100))
|
||||
childDevice.sendEvent(name: "switch.setLevel", value: Math.round((device.brightness ?: 1) * 100))
|
||||
childDevice.sendEvent(name: "switch", value: device.power)
|
||||
childDevice.sendEvent(name: "colorTemperature", value: device.color.kelvin)
|
||||
childDevice.sendEvent(name: "model", value: device.product.name)
|
||||
}
|
||||
|
||||
if (state.devices[device.id] == null) {
|
||||
// State missing, add it and set it to opposite status as current status to provoke event below
|
||||
state.devices[device.id] = [online : !device.connected]
|
||||
}
|
||||
|
||||
if (!state.devices[device.id]?.online && device.connected) {
|
||||
// Device came online after being offline
|
||||
childDevice?.sendEvent(name: "DeviceWatch-DeviceStatus", value: "online", displayed: false)
|
||||
log.debug "$device is back Online"
|
||||
} else if (state.devices[device.id]?.online && !device.connected) {
|
||||
// Device went offline after being online
|
||||
childDevice?.sendEvent(name: "DeviceWatch-DeviceStatus", value: "offline", displayed: false)
|
||||
log.debug "$device went Offline"
|
||||
}
|
||||
state.devices[device.id] = [online: device.connected]
|
||||
}
|
||||
getChildDevices().findAll { !selectors.contains("${it.deviceNetworkId}") }.each {
|
||||
log.info("Deleting ${it.deviceNetworkId}")
|
||||
state.devices[it.deviceNetworkId] = null
|
||||
deleteChildDevice(it.deviceNetworkId)
|
||||
}
|
||||
runIn(1, 'refreshDevices') // Asynchronously refresh devices so we don't block
|
||||
}
|
||||
|
||||
def refreshDevices() {
|
||||
log.info("Refreshing all devices...")
|
||||
getChildDevices().each { device ->
|
||||
device.refresh()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user