/* * * AlertMe Lamp Driver * */ @Field String driverVersion = "v1.28 (25th August 2023)" #include BirdsLikeWires.alertme #include BirdsLikeWires.library import groovy.transform.Field @Field boolean debugMode = false @Field int reportIntervalMinutes = 6 @Field int checkEveryMinutes = 1 @Field int rangeEveryHours = 6 metadata { definition (name: "AlertMe Lamp", namespace: "BirdsLikeWires", author: "Andrew Davison", importUrl: "https://raw.githubusercontent.com/birdslikewires/hubitat/master/alertme/drivers/alertme_lamp.groovy") { capability "Actuator" capability "Battery" capability "ColorControl" capability "ColorMode" capability "Configuration" capability "Light" capability "Refresh" capability "SignalStrength" capability "Switch" capability "SwitchLevel" capability "VoltageMeasurement" command "normalMode" command "rangingMode" //command "quietMode" command "lampSeqRGB" command "lampSeqSleepy" command "pause" attribute "batteryState", "string" attribute "healthStatus", "enum", ["offline", "online"] if (debugMode) { command "testCommand" } fingerprint profileId: "C216", inClusters: "00F0,00F3,00F5", outClusters: "", manufacturer: "AlertMe.com", model: "Beacon", deviceJoinName: "AlertMe Lamp" fingerprint profileId: "C216", inClusters: "00F0,00F3,00F5", outClusters: "", manufacturer: "AlertMe.com", model: "Lamp Device", deviceJoinName: "AlertMe Lamp" } } preferences { input name: "infoLogging", type: "bool", title: "Enable logging", defaultValue: true input name: "debugLogging", type: "bool", title: "Enable debug logging", defaultValue: false input name: "traceLogging", type: "bool", title: "Enable trace logging", defaultValue: false } void testCommand() { logging("${device} : Test Command", "info") sendZigbeeCommands(["he raw ${device.deviceNetworkId} 0 ${device.endpointId} 0x00F6 {11 00 FC 01} {0xC216}"]) // version information request } void configureSpecifics() { // Called by main configure() method in BirdsLikeWires.alertme device.name = "AlertMe Lamp" // Lamp test cycles red, green, blue, white and off. def cmds = new ArrayList() cmds.add("he raw ${device.deviceNetworkId} 0 2 0x00F5 {11 00 04 01 01} {0xC216}") cmds.add("he raw ${device.deviceNetworkId} 0 2 0x00F5 {11 00 01 01 00 01 00 FF 00 00 00 01} {0xC216}") cmds.add("he raw ${device.deviceNetworkId} 0 2 0x00F5 {11 00 01 01 00 01 00 00 FF 00 01 01} {0xC216}") cmds.add("he raw ${device.deviceNetworkId} 0 2 0x00F5 {11 00 01 01 00 01 00 00 00 FF 01 01} {0xC216}") cmds.add("he raw ${device.deviceNetworkId} 0 2 0x00F5 {11 00 01 01 00 01 00 FF FF FF 01 01} {0xC216}") cmds.add("he raw ${device.deviceNetworkId} 0 2 0x00F5 {11 00 01 01 00 01 00 00 00 00 01 01} {0xC216}") cmds.add("he raw ${device.deviceNetworkId} 0 2 0x00F5 {11 00 05 01 01} {0xC216}") sendZigbeeCommands(cmds) state.operatingMode = "normal" // Schedule ranging report. randomSixty = Math.abs(new Random().nextInt() % 60) randomTwentyFour = Math.abs(new Random().nextInt() % 24) schedule("${randomSixty} ${randomSixty} ${randomTwentyFour}/${rangeEveryHours} * * ? *", rangingMode) } void updateSpecifics() { // Called by library updated() method in BirdsLikeWires.library rangingMode() } // I can't believe this finally works! Incomplete, but working! // // The lamp operates with sequences, up to 10 steps in a sequence. // The lamp must be put into 'on' mode (done during configuration) and then a sequence uploaded and run. // Single colours are a one-step sequence. // // Lamp Cluster ID = 0x00F5 // // {11 00 01 01 00 01 00 FF FF FF 00 01} // AA AA BB CC CC DD DD EE FF GG HH II // // AA - Unknown preamble, same for all AlertMe devices. // BB - Cluster command; add state (01). // CC - Cluster control; transition time in multiples of 0.2s, 0x0001 through 0xFFFE (little endian in command) // DD - Cluster control; dwell time in multiples of 0.2s, 0x0001 through 0xFFFE (little endian in command) // EE - Red level as 8-bit hex value // FF - Green level as 8-bit hex value // GG - Blue level as 8-bit hex value // HH - Sequence behaviour; clear all previous (00), append (01) or append with confirmation (02). New sequences must commence with clear. // II - Unknown postamble, also the same for all AlertMe devices. // // {11 00 04 01 01} // AA AA BB CC DD // // AA - Unknown preamble, same for all AlertMe devices. // BB - Cluster command; set operating mode (04). // CC - Cluster control; halt sequence (00), enable sequence (01) or single state (02). // DD - Unknown postamble, also the same for all AlertMe devices. // // {11 00 05 FD 01} // AA AA BB CC DD // // AA - Unknown preamble, same for all AlertMe devices. // BB - Cluster command; set play mode (05). // CC - Cluster control; loop count, values between (00) and (FD) are valid, where (00) is load and hold and (FD) is loop indefinitely. // DD - Unknown postamble, also the same for all AlertMe devices. void off() { // Convert seconds to multiple of 0.2s, or use 0.2s if duration is zero. BigInteger dur = state.lastDuration * 5 String[] durHex = dur > 0 ? dur.toString(16).toUpperCase().padLeft(4,'0') : [0,0,0,0] def cmds = new ArrayList() // clear sequence, set RGB to 00 00 00, dwell indefinitely, transition as configured, add to sequence cmds.add("he raw ${device.deviceNetworkId} 0 2 0x00F5 {11 00 01 ${durHex[2..3].join()} ${durHex[0..1].join()} 01 00 00 00 00 00 01} {0xC216}") cmds.add("he raw ${device.deviceNetworkId} 0 2 0x00F5 {11 00 04 02 01} {0xC216}") // single state play mode cmds.add("he raw ${device.deviceNetworkId} 0 2 0x00F5 {11 00 05 FD 01} {0xC216}") // indefintely loop and play sendZigbeeCommands(cmds) sendEvent(name: "switch", value: "off") logging("${device} : Lamp : Off", "info") } void on() { // The RGB LEDs in these lamps are not calibrated so you will get colour variations and potentially some flickering at certain values. def hsl = [state.lastHue,state.lastSaturation,state.lastLevel] def rgb = hubitat.helper.ColorUtils.hsvToRGB(hsl) // Limit maximum values for code safety. BigInteger red = rgb[0] <= 255 ? rgb[0] : 255 BigInteger grn = rgb[1] <= 255 ? rgb[1] : 255 BigInteger blu = rgb[2] <= 255 ? rgb[2] : 255 // No settings? No problem! if (red + grn + blu == 0) { (red, grn, blu) = [255,255,255] } // Convert to RGB to hex values. String redHex = red.toString(16).toUpperCase().padLeft(2,'0') String grnHex = grn.toString(16).toUpperCase().padLeft(2,'0') String bluHex = blu.toString(16).toUpperCase().padLeft(2,'0') // Convert seconds to multiple of 0.2s, or use 0.2s if duration is zero. BigDecimal dur = state.lastDuration / 0.2 String[] durHex = dur > 0 ? dur.toBigInteger().toString(16).toUpperCase().padLeft(4,'0') : [0,0,0,0] def cmds = new ArrayList() // clear sequence, set RGB, dwell indefinitely, transition as configured, add to sequence cmds.add("he raw ${device.deviceNetworkId} 0 2 0x00F5 {11 00 01 ${durHex[2..3].join()} ${durHex[0..1].join()} 01 00 ${redHex} ${grnHex} ${bluHex} 00 01} {0xC216}") cmds.add("he raw ${device.deviceNetworkId} 0 2 0x00F5 {11 00 04 02 01} {0xC216}") // single state play mode cmds.add("he raw ${device.deviceNetworkId} 0 2 0x00F5 {11 00 05 FD 01} {0xC216}") // indefintely loop and play sendZigbeeCommands(cmds) logging("${device} : on : Stored HSL values convert to RGB as ${rgb} (${redHex}, ${grnHex}, ${bluHex})", "debug") sendEvent(name: "switch", value: "on") logging("${device} : Lamp : On", "info") } void pause() { def cmds = new ArrayList() cmds.add("he raw ${device.deviceNetworkId} 0 2 0x00F5 {11 00 04 00 01} {0xC216}") // disable sequencing sendZigbeeCommands(cmds) logging("${device} : Lamp : Paused", "info") } void setColor(Map colormap) { if (colormap.containsValue("NaN")) { logging("${device} : Set Colour : Invalid value, it's probably that you've not moved the hue slider.", "info") } else { state.lastHue = colormap.hue state.lastSaturation = colormap.saturation state.lastLevel = colormap.level logging("${device} : Set Colour : Saved hue (${colormap.hue}), saturation (${colormap.saturation}) and level (${colormap.level}).", "info") } } void setHue(BigDecimal hue) { state.lastHue = hue <= 100 ? hue : 100 logging("${device} : Set Hue : Saved hue (${hueSafe}).", "info") } void setSaturation(BigDecimal sat) { state.lastSaturation = sat <= 100 ? sat : 100 logging("${device} : Set Saturation : Saved saturation (${satSafe}).", "info") } void setLevel(BigDecimal level, BigDecimal duration) { state.lastLevel = level <= 100 ? level : 100 state.lastDuration = duration < 13107 ? duration : 13106 String pluralisor = duration == 1 ? "" : "s" logging("${device} : setLevel : Saved level (${state.lastLevel}%) reached over ${state.lastDuration} second${pluralisor}.", "info") } // A couple of presets to illustrate. :) void lampSeqRGB() { def cmds = new ArrayList() cmds.add("he raw ${device.deviceNetworkId} 0 2 0x00F5 {11 00 01 1A 00 08 00 FF 00 00 00 01} {0xC216}") cmds.add("he raw ${device.deviceNetworkId} 0 2 0x00F5 {11 00 01 1A 00 08 00 00 FF 00 01 01} {0xC216}") cmds.add("he raw ${device.deviceNetworkId} 0 2 0x00F5 {11 00 01 1A 00 08 00 00 00 FF 01 01} {0xC216}") cmds.add("he raw ${device.deviceNetworkId} 0 2 0x00F5 {11 00 05 FD 01} {0xC216}") sendZigbeeCommands(cmds) logging("${device} : Lamp : RGB Sequence", "info") } void lampSeqSleepy() { def cmds = new ArrayList() cmds.add("he raw ${device.deviceNetworkId} 0 2 0x00F5 {11 00 01 14 00 06 00 06 0A 0A 00 01} {0xC216}") cmds.add("he raw ${device.deviceNetworkId} 0 2 0x00F5 {11 00 01 0F 00 01 00 58 7F 7F 01 01} {0xC216}") cmds.add("he raw ${device.deviceNetworkId} 0 2 0x00F5 {11 00 05 FD 01} {0xC216}") sendZigbeeCommands(cmds) logging("${device} : Lamp : Sleepy", "info") }