--- /dev/null
+/**
+ * Smart Bulb Notifier/Flasher
+ *
+ * Copyright 2015 Scott Gibson
+ *
+ * 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.
+ *
+ * Many thanks to Eric Roberts for his virtual switch creator, which served as the template for creating child switch devices!
+ *
+ */
+definition(
+ name: "Smart Bulb Alert (Blink/Flash)",
+ namespace: "sticks18",
+ author: "Scott Gibson",
+ description: "Creates a virtual switch button that when turned on will trigger selected smart bulbs to blink or flash",
+ category: "My Apps",
+ iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
+ iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience%402x.png"
+)
+
+preferences {
+ page(name: "basicInfo")
+ page(name: "configureBulbs")
+}
+
+def basicInfo(){
+ dynamicPage(name: "basicInfo", title: "First, name your trigger and select smart bulb types", nextPage: "configureBulbs", uninstall: true){
+ section("Create Virtual Momentary Button as Trigger") {
+ input "switchLabel", "text", title: "Switch Button Label", multiple: false, required: true
+ }
+ section("Choose your Smart Bulbs") {
+ paragraph "This SmartApp will work with most zigbee bulbs by directly calling the Identify function built into the hardware. This function is generally run when you first pair a bulb to the hub to let you know it was successful. Different bulbs have different capabilities and/or endpoints to address, so they must be selected separately in this SmartApp. If expanded functionality exists for a particular bulb type, you will be given additional options to select. Caveats: Non-Hue bulbs connected to the Hue Bridge, such as GE Link or Cree Connected, will not work because the Hue API is not designed for them.",
+ title: "How to select your bulbs...", required: true
+ input "geLinks", "capability.switch level", title: "Select GE Link Bulbs", required: false, multiple: true
+ //input "creeCons", "capability.switch level", title: "Select Cree Connected Bulbs", required: false, multiple: true
+ //input "hueHubs", "capability.switch level", title: "Select Hue Bulbs connected to Hue Hub", required: false, multiple: true
+ //input "hueDirs", "capability.switch level", title: "Select Hue Bulbs directly connected to ST Hub", required: false, multiple: true
+ //input "osramLs", "capability.switch level", title: "Select Osram Lightify Bulbs", required: false, multiple: true
+ }
+ }
+}
+
+def configureBulbs(){
+ dynamicPage(name: "configureBulbs", title: "Additional Configuration Options by Bulb Type", install: true, uninstall: true) {
+ section(title: "Auto-off for bulbs directly connected to SmartThings Hub") {
+ input "autoOff", "bool", title: "Automatically stop the alert", required: false, submitOnChange: true
+ input "offDelay", "number", title: "Turn off alert after X seconds (default = 5, max = 10)", required: false, submitOnChange: true
+ }
+ /*section(title: "Options for Hue Bulbs connected to Hue Bridge", hidden: hideHueHubBulbs(), hideable: true) {
+ input "hBlink", "enum", title: "Select Blink for single flash or Breathe for 15 seconds continuous (default = Breathe)", multiple: false, required: false,
+ options: ["Blink", "Breathe"]
+ }
+ section(title: "Options for Hue Bulbs connected directly to SmartThings Hub", hidden: hideHueDirBulbs(), hideable: true) {
+ input "hdStyle", "enum", title: "Select alert style: Normal = Bulb will blink until stopped via auto-off or trigger, Blink = Single flash, Breathe = Mult Flash for 15 seconds, Okay = Bulb turn green for 2 seconds",
+ multiple: false, required: false, options: ["Normal", "Blink", "Breathe", "Okay"]
+ }
+ section(title: "Options for Osram Lightify Bulbs", hidden: hideOsramBulbs(), hideable: true) {
+ input "osStyle", "enum", title: "Select alert style: Normal = Bulb will blink until stopped via auto-off or trigger",
+ multiple: false, required: false, options: ["Normal"]
+ }*/
+ }
+}
+
+
+
+def installed() {
+ log.debug "Installed with settings: ${settings}"
+
+ initialize()
+}
+
+def updated() {
+ log.debug "Updated with settings: ${settings}"
+
+ unsubscribe()
+ initialize()
+}
+
+def initialize() {
+
+ state.autoOff = offDelay
+ // state.hueHubOption = getHueHubOption()
+ // state.hueDirOption = getHueDirOption()
+ // state.osramOption = getOsramOption()
+ // state.onlyHueHub = getHueHubOnly()
+
+ def deviceId = app.id + "SimulatedSwitch"
+ log.debug(deviceId)
+ def existing = getChildDevice(deviceId)
+ log.debug existing
+ if (!existing) {
+ log.debug "Add child"
+ def childDevice = addChildDevice("sticks18", "Smart Bulb Alert Switch", deviceId, null, [label: switchLabel])
+ }
+
+}
+
+def uninstalled() {
+ removeChildDevices(getChildDevices())
+}
+
+private removeChildDevices(delete) {
+ delete.each {
+ deleteChildDevice(it.deviceNetworkId)
+ }
+}
+
+/* private getOffDelay() {
+ def result = 5
+ log.debug "autoOff is ${autoOff}"
+ if (autoOff) {
+ result = 5
+ log.debug "offDelay is ${offDelay}"
+ if(offDelay) {
+ def offSec = Math.Min(offDelay, 10)
+ offSec = Math.Max(offSec, 1)
+ result = offSec
+ }
+ }
+ log.debug "off delay: ${result}"
+ return result
+}
+
+private getHueDirOption() {
+ def result = null
+ log.debug hueDirs
+ if (hueDirs) {
+ switch(hdStyle) {
+ case "Normal":
+ result = ""
+ break
+ case "Blink":
+ result = "00"
+ break
+ case "Breathe":
+ result = "01"
+ break
+ case "Okay":
+ result = "02"
+ break
+ default:
+ result = ""
+ break
+ }
+ }
+ log.debug "hue dir option: ${result}"
+ return result
+}
+
+private getHueHubOnly() {
+ (creeCons || geLinks || osramLs || hueDirs) ? false : true
+}
+
+private getHueHubOption() {
+ def result = null
+ if (hueHubs) {
+ switch(hBlink) {
+ case "Blink":
+ result = "select"
+ break
+ case "Breathe":
+ result = "lselect"
+ break
+ default:
+ result = "lselect"
+ break
+ }
+ }
+ log.debug "hue hub option: ${result}"
+ return result
+}
+
+private getOsramOption() {
+ def result = null
+ if (osramLs) {
+ switch(osStyle) {
+ case "Normal":
+ result = ""
+ break
+ case "Blink":
+ result = "00"
+ break
+ case "Breathe":
+ result = "01"
+ break
+ case "Okay":
+ result = "02"
+ break
+ default:
+ result = ""
+ break
+ }
+ }
+ log.debug "osram option: ${result}"
+ return result
+}
+*/
+private hideHueHubBulbs() {
+ (hueHubs) ? false : true
+}
+
+private hideHueDirBulbs() {
+ (hueDirs) ? false : true
+}
+
+private hideOsramBulbs() {
+ (osramLs) ? false : true
+}
+
+private hex(value, width=4) {
+ def s = new BigInteger(Math.round(value).toString()).toString(16)
+ while (s.size() < width) {
+ s = "0" + s
+ }
+ s
+}
+
+private Integer convertHexToInt(hex) {
+ Integer.parseInt(hex,16)
+}
+
+private String swapEndianHex(String hex) {
+ reverseArray(hex.decodeHex()).encodeHex()
+}
+
+private byte[] reverseArray(byte[] array) {
+ int i = 0;
+ int j = array.length - 1;
+ byte tmp;
+ while (j > i) {
+ tmp = array[j];
+ array[j] = array[i];
+ array[i] = tmp;
+ j--;
+ i++;
+ }
+ return array
+}
+
+// Child device methods after this point. Instead of subscribing to child, have child directly call parent
+
+def on(childDevice) {
+ log.debug "Start alert"
+ if(state.autoOff != null) {
+ log.debug "Only alert for ${state.autoOff} seconds"
+ if(geLinks != null) {
+ geLinks.each { bulb ->
+ // bulb.setLevel(99, 1800)
+ bulb.alert(state.autoOff)
+ // childDevice.attWrite(bulb.deviceNetworkId, 1, 3, 0, "0x21", state.autoOff)
+ // childDevice.zigbeeCmd(bulb.deviceNetworkId, 1, 3, 0, "")
+ }
+ }
+ /*if(creeCons != null) {
+ creeCons.each { bulb ->
+ childDevice.attWrite(bulb.deviceNetworkId, "10", "3", "0", "0x21", state.autoOff)
+ }
+ }
+ if(state.hueDirOption != null) {
+ if(state.hueDirOption == "") {
+ hueDirs.each { bulb ->
+ childDevice.attWrite(bulb.deviceNetworkId, "0B", "3", "0", "0x21", state.autoOff)
+ }
+ }
+ else {
+ hueDirs.each { bulb ->
+ def payload = state.hueDirOption + " 00"
+ childDevice.zigbeeCmd(bulb.deviceNetworkId, "0B", "3", "0x40", payload)
+ }
+ }
+ }
+ if(state.osramOption != null) {
+ if(state.osramOption == "") {
+ osramLs.each { bulb ->
+ childDevice.attWrite(bulb.deviceNetworkId, "03", "3", "0", "0x21", state.autoOff)
+ }
+ }
+ else {
+ osramLs.each { bulb ->
+ def payload = state.osramOption + " 00"
+ childDevice.zigbeeCmd(bulb.deviceNetworkId, "03", "3", "0x40", payload)
+ }
+ }
+ } */
+ childDevice.justOff()
+
+ } /*
+ else {
+ log.debug "Starting continous alert"
+ if(geLinks != null) {
+ geLinks.each { bulb ->
+ childDevice.zigbeeCmd(bulb.deviceNetworkId, "1", "3", "0", "")
+ }
+ }
+ if(creeCons != null) {
+ creeCons.each { bulb ->
+ childDevice.zigbeeCmd(bulb.deviceNetworkId, "10", "3", "0", "")
+ }
+ }
+ if(state.hueDirOption != null) {
+ if(state.hueDirOption == "") {
+ hueDirs.each { bulb ->
+ childDevice.zigbeeCmd(bulb.deviceNetworkId, "0B", "3", "0", "")
+ }
+ }
+ else {
+ hueDirs.each { bulb ->
+ def payload = state.hueDirOption + " 00"
+ childDevice.zigbeeCmd(bulb.deviceNetworkId, "0B", "3", "0x40", payload)
+ }
+ }
+ }
+ if(state.osramOption != null) {
+ if(state.osramOption == "") {
+ osramLs.each { bulb ->
+ childDevice.zigbeeCmd(bulb.deviceNetworkId, "03", "3", "0", "")
+ }
+ }
+ else {
+ osramLs.each { bulb ->
+ def payload = state.osramOption + " 00"
+ childDevice.zigbeeCmd(bulb.deviceNetworkId, "03", "3", "0x40", payload)
+ }
+ }
+ }
+ }
+ if(state.hueHubOption != null) {
+ log.debug "Send planned alert to Hue Bulbs via Hue Bridge"
+ hueHubs.each { bulb ->
+ def hue = bulb.currentValue("hue")
+ def sat = bulb.currentValue("saturation")
+ def alert = state.hueHubOption
+ def value = [hue: hue, saturation: sat, alert: alert]
+ bulb.setColor(value)
+ }
+ if(state.hueHubOnly) { childDevice.justOff() }
+ }*/
+
+}
+
+def off(childDevice) {
+ log.debug "Stop Alert" /*
+ if(state.autoOff != null) {
+ childDevice.justOff()
+ }
+ else {
+ log.debug "Stopping continous alert"
+ if(geLinks != null) {
+ geLinks.each { bulb ->
+ childDevice.zigbeeCmd(bulb.deviceNetworkId, "1", "3", "0", "")
+ }
+ }
+ if(creeCons != null) {
+ creeCons.each { bulb ->
+ childDevice.zigbeeCmd(bulb.deviceNetworkId, "10", "3", "0", "")
+ }
+ }
+ if(state.hueDirOption != null) {
+ if(state.hueDirOption == "") {
+ hueDirs.each { bulb ->
+ childDevice.zigbeeCmd(bulb.deviceNetworkId, "0B", "3", "0", "")
+ }
+ }
+ }
+ if(state.osramOption != null) {
+ if(state.osramOption == "") {
+ osramLs.each { bulb ->
+ childDevice.zigbeeCmd(bulb.deviceNetworkId, "03", "3", "0", "")
+ }
+ }
+ }
+ }
+ */
+}
+
+def allOff(childDevice) {
+ log.debug "Stopping alerts" /*
+ if(geLinks != null) {
+ geLinks.each { bulb ->
+ childDevice.attWrite(bulb.deviceNetworkId, "1", "3", "0", "0x21", "0100")
+ }
+ }
+ if(creeCons != null) {
+ creeCons.each { bulb ->
+ childDevice.attWrite(bulb.deviceNetworkId, "10", "3", "0", "0x21", "0100")
+ }
+ }
+ if(hueDirs != null) {
+ hueDirs.each { bulb ->
+ childDevice.attWrite(bulb.deviceNetworkId, "0B", "3", "0", "0x21", "0100")
+ }
+ }
+ if(osramLs != null) {
+ osramLs.each { bulb ->
+ childDevice.attWrite(bulb.deviceNetworkId, "03", "3", "0", "0x21", "0100")
+ }
+ } */
+}