mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-08 05:31:56 +00:00
Compare commits
1 Commits
PROD_2017.
...
MSA-1559-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bdf491becb |
@@ -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)
|
||||
}
|
||||
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() {
|
||||
}
|
||||
Reference in New Issue
Block a user