From df27a44ea3bbb1f07efada5feb6f096fb9beae87 Mon Sep 17 00:00:00 2001 From: MGoralczykS Date: Tue, 14 Jul 2020 16:06:03 +0200 Subject: [PATCH 1/8] Add Qubino Relay 1/1D to existing DTH --- .../qubino-flush-relay.groovy | 467 ++++++++++++++++++ 1 file changed, 467 insertions(+) create mode 100644 devicetypes/qubino/qubino-flush-relay.src/qubino-flush-relay.groovy diff --git a/devicetypes/qubino/qubino-flush-relay.src/qubino-flush-relay.groovy b/devicetypes/qubino/qubino-flush-relay.src/qubino-flush-relay.groovy new file mode 100644 index 00000000000..f439a8cf12d --- /dev/null +++ b/devicetypes/qubino/qubino-flush-relay.src/qubino-flush-relay.groovy @@ -0,0 +1,467 @@ +/** + * Copyright 2020 SRPOL + * + * 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: "Qubino Flush 2 Relay", namespace: "qubino", author: "SmartThings", mnmn: "SmartThings", vid: "generic-switch-power-energy") { + capability "Switch" + capability "Power Meter" + capability "Energy Meter" + capability "Refresh" + capability "Actuator" + capability "Sensor" + capability "Health Check" + + command "reset" + + fingerprint mfr: "0159", prod: "0002", model: "0051", deviceJoinName: "Qubino Switch 1" //Qubino Flush 2 Relay + fingerprint mfr: "0159", prod: "0002", model: "0052", deviceJoinName: "Qubino Relay" //Qubino Flush 1 Relay + fingerprint mfr: "0159", prod: "0002", model: "0053", deviceJoinName: "Qubino Relay" //Qubino Flush 1D Relay + } + + tiles(scale: 2) { + multiAttributeTile(name:"switch", type: "generic", 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: "#00a0dc") + attributeState("off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff") + } + } + valueTile("power", "device.power", decoration: "flat", width: 2, height: 2) { + state "default", label:'${currentValue} W' + } + valueTile("energy", "device.energy", decoration: "flat", width: 2, height: 2) { + state "default", label:'${currentValue} kWh' + } + standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh" + } + standardTile("reset", "device.energy", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "default", label:'reset kWh', action:"reset" + } + + main(["switch"]) + details(["switch","power","energy","refresh","reset"]) + } + + preferences { + parameterMap.each { + input (title: it.name, description: it.description, type: "paragraph", element: "paragraph") + + switch(it.type) { + case "boolean": + input(type: "paragraph", element: "paragraph", description: "Option enabled: ${it.activeDescription}\n" + + "Option disabled: ${it.inactiveDescription}" + ) + input(name: it.key, type: "boolean", title: "Enable", defaultValue: it.defaultValue == it.activeOption, required: false) + break + case "enum": + input(name: it.key, title: "Select", type: "enum", options: it.values, defaultValue: it.defaultValue, required: false) + break + } + } + } +} + +def installed() { + state.numberOfSwitches = 2 + if (!childDevices) { + addChildSwitches(state.numberOfSwitches) + } + sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + // Preferences template begin + state.currentPreferencesState = [:] + parameterMap.each { + state.currentPreferencesState."$it.key" = [:] + state.currentPreferencesState."$it.key".value = getPreferenceValue(it) + def preferenceName = it.key + "Boolean" + settings."$preferenceName" = true + state.currentPreferencesState."$it.key".status = "synced" + } + // Preferences template end +} + +def updated() { + if (!childDevices) { + addChildSwitches(state.numberOfSwitches) + } + // Preferences template begin + parameterMap.each { + if (isPreferenceChanged(it) && !excludeParameterFromSync(it)) { + log.debug "Preference ${it.key} has been updated from value: ${state.currentPreferencesState."$it.key".value} to ${settings."$it.key"}" + state.currentPreferencesState."$it.key".status = "syncPending" + } else if (!state.currentPreferencesState."$it.key".value) { + log.warn "Preference ${it.key} no. ${it.parameterNumber} has no value. Please check preference declaration for errors." + } + } + syncConfiguration() + // Preferences template end +} + +def excludeParameterFromSync(preference){ + def exclude = false + if (preference.key == "outputQ2SwitchSelection") { + if (zwaveInfo?.model?.equals("0052") || zwaveInfo?.model?.equals("0053")) { + exclude = true + } + } + + if (exclude) { + log.warn "Preference no ${preference.parameterNumber} - ${preference.key} is not supported by this device" + } + return exclude +} + +private syncConfiguration() { + def commands = [] + parameterMap.each { + try { + if (state.currentPreferencesState."$it.key".status == "syncPending") { + commands += encap(zwave.configurationV2.configurationSet(scaledConfigurationValue: getCommandValue(it), parameterNumber: it.parameterNumber, size: it.size)) + commands += encap(zwave.configurationV2.configurationGet(parameterNumber: it.parameterNumber)) + } else if (state.currentPreferencesState."$it.key".status == "disablePending") { + commands += encap(zwave.configurationV2.configurationSet(scaledConfigurationValue: it.disableValue, parameterNumber: it.parameterNumber, size: it.size)) + commands += encap(zwave.configurationV2.configurationGet(parameterNumber: it.parameterNumber)) + } + } catch (e) { + log.warn "There's been an issue with preference: ${it.key}" + } + } + sendHubCommand(commands) +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd, ep = null) { + // Preferences template begin + log.debug "Configuration report: ${cmd}" + def preference = parameterMap.find( {it.parameterNumber == cmd.parameterNumber} ) + def key = preference.key + def preferenceValue = getPreferenceValue(preference, cmd.scaledConfigurationValue) + if (settings."$key" == preferenceValue) { + state.currentPreferencesState."$key".value = settings."$key" + state.currentPreferencesState."$key".status = "synced" + } else { + state.currentPreferencesState."$key"?.status = "syncPending" + runIn(5, "syncConfiguration", [overwrite: true]) + } + // Preferences template end +} + +private getPreferenceValue(preference, value = "default") { + def integerValue = value == "default" ? preference.defaultValue : value.intValue() + switch (preference.type) { + case "enum": + return String.valueOf(integerValue) + case "boolean": + return String.valueOf(preference.optionActive == integerValue) + default: + return integerValue + } +} + +private getCommandValue(preference) { + def parameterKey = preference.key + switch (preference.type) { + case "boolean": + return settings."$parameterKey" ? preference.optionActive : preference.optionInactive + default: + return Integer.parseInt(settings."$parameterKey") + } +} + +private isPreferenceChanged(preference) { + if (settings."$preference.key" != null) { + return state.currentPreferencesState."$preference.key".value != settings."$preference.key" + } else { + return false + } +} + +def parse(String description) { + def result = null + if (description.startsWith("Err")) { + result = createEvent(descriptionText:description, isStateChange:true) + } else if (description != "updated") { + def cmd = zwave.parse(description) + if (cmd) { + result = zwaveEvent(cmd) + } + } + log.debug "parsed '${description}' to ${result.inspect()}" + result +} + +def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd, ep = null) { + log.debug "Security Message Encap ${cmd}" + def encapsulatedCommand = cmd.encapsulatedCommand() + if (encapsulatedCommand) { + zwaveEvent(encapsulatedCommand, null) + } else { + log.warn "Unable to extract encapsulated cmd from $cmd" + createEvent(descriptionText: cmd.toString()) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd, ep = null) { + log.debug "Multichannel command ${cmd}" + (ep ? " from endpoint $ep" : "") + if (cmd.commandClass == 0x6C && cmd.parameter.size >= 4) { // Supervision encapsulated Message + // Supervision header is 4 bytes long, two bytes dropped here are the latter two bytes of the supervision header + cmd.parameter = cmd.parameter.drop(2) + // Updated Command Class/Command now with the remaining bytes + cmd.commandClass = cmd.parameter[0] + cmd.command = cmd.parameter[1] + cmd.parameter = cmd.parameter.drop(2) + } + def encapsulatedCommand = cmd.encapsulatedCommand() + zwaveEvent(encapsulatedCommand, cmd.sourceEndPoint as Integer) +} + +def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, ep = null) { + log.debug "Basic ${cmd}" + (ep ? " from endpoint $ep" : "") + changeSwitch(ep, cmd) +} + +def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd, ep = null) { + log.debug "Binary ${cmd}" + (ep ? " from endpoint $ep" : "") + changeSwitch(ep, cmd) +} + +private changeSwitch(endpoint, cmd) { + def value = cmd.value ? "on" : "off" + if (endpoint == 1) { + createEvent(name: "switch", value: value, isStateChange: true, descriptionText: "Switch ${endpoint} is ${value}") + } else if (endpoint) { + String childDni = "${device.deviceNetworkId}:$endpoint" + def child = childDevices.find { it.deviceNetworkId == childDni } + child?.sendEvent(name: "switch", value: value, isStateChange: true, descriptionText: "Switch ${endpoint} is ${value}") + } +} + +def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd, ep = null) { + log.debug "Meter ${cmd}" + (ep ? " from endpoint $ep" : "") + if (ep == 1) { + createEvent(createMeterEventMap(cmd)) + } else if (ep) { + String childDni = "${device.deviceNetworkId}:$ep" + def child = childDevices.find { it.deviceNetworkId == childDni } + child?.sendEvent(createMeterEventMap(cmd)) + } +} + +private createMeterEventMap(cmd) { + def eventMap = [:] + if (cmd.meterType == 1) { + if (cmd.scale == 0) { + eventMap.name = "energy" + eventMap.value = cmd.scaledMeterValue + eventMap.unit = "kWh" + } else if (cmd.scale == 2) { + eventMap.name = "power" + eventMap.value = Math.round(cmd.scaledMeterValue) + eventMap.unit = "W" + } + } + eventMap +} + +def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd, ep = null) { + log.debug "SensorMultilevelReport ${cmd}" + (ep ? " from endpoint $ep" : "") + def result = [] + + def map = [:] + switch (cmd.sensorType) { + case 1: + map.name = "temperature" + def cmdScale = cmd.scale == 1 ? "F" : "C" + map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision) + map.unit = getTemperatureScale() + break + default: + map.descriptionText = cmd.toString() + } + def child = childDevices.find { it.deviceNetworkId == state.temperatureSensorDni } + if (!child) { + child = addChildTemperatureSensor() + } + child?.sendEvent(map) + createEvent(map) +} + +def zwaveEvent(physicalgraph.zwave.Command cmd, ep) { + log.warn "Unhandled ${cmd}" + (ep ? " from endpoint $ep" : "") +} + +def on() { + onOffCmd(0xFF) +} + +def off() { + onOffCmd(0x00) +} + +def ping() { + refresh() +} + +def childOnOff(deviceNetworkId, value) { + def switchId = getSwitchId(deviceNetworkId) + if (switchId != null) sendHubCommand onOffCmd(value, switchId) +} + +private onOffCmd(value, endpoint = 1) { + delayBetween([ + encap(zwave.basicV1.basicSet(value: value), endpoint), + encap(zwave.basicV1.basicGet(), endpoint), + ]) +} + +def childRefresh(deviceNetworkId, includeMeterGet = true) { + def switchId = getSwitchId(deviceNetworkId) + if (switchId != null) { + sendHubCommand refresh([switchId],includeMeterGet) + } +} + +def refresh(endpoints = [1], includeMeterGet = true) { + + def cmds = [] + + endpoints.each { + cmds << [encap(zwave.basicV1.basicGet(), it)] + if (includeMeterGet) { + cmds << encap(zwave.meterV3.meterGet(scale: 0), it) + cmds << encap(zwave.meterV3.meterGet(scale: 2), it) + } + } + + delayBetween(cmds, 200) +} + +private resetAll() { + childDevices.each { + if (it.deviceNetworkId != state.temperatureSensorDni) { + childReset(it.deviceNetworkId) + } + } + sendHubCommand reset() +} + +def childReset(deviceNetworkId) { + def switchId = getSwitchId(deviceNetworkId) + if (switchId != null) { + log.debug "Child reset switchId: ${switchId}" + sendHubCommand reset(switchId) + } +} + +def reset(endpoint = 1) { + log.debug "Resetting endpoint: ${endpoint}" + delayBetween([ + encap(zwave.meterV3.meterReset(), endpoint), + encap(zwave.meterV3.meterGet(scale: 0), endpoint), + "delay 500" + ], 500) +} + +def getSwitchId(deviceNetworkId) { + def split = deviceNetworkId?.split(":") + return (split.length > 1) ? split[1] as Integer : null +} + +private encap(cmd, endpoint = null) { + if (cmd) { + if (endpoint) { + cmd = zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint: endpoint).encapsulate(cmd) + } + + if (zwaveInfo.zw.contains("s")) { + zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + cmd.format() + } + } +} + +private addChildSwitches(numberOfSwitches) { + for (def endpoint : 2..numberOfSwitches) { + try { + String childDni = "${device.deviceNetworkId}:$endpoint" + def componentLabel = device.displayName[0..-2] + "${endpoint}" + addChildDevice("smartthings", "Child Metering Switch", childDni, device.getHub().getId(), [ + completedSetup : true, + label : componentLabel, + isComponent : false + ]) + } catch(Exception e) { + log.warn "Exception: ${e}" + } + } +} + +private addChildTemperatureSensor() { + try { + String childDni = "${device.deviceNetworkId}:${state.numberOfSwitches + 1}" + state.temperatureSensorDni = childDni + def childDevice = addChildDevice("qubino", "Qubino Temperature Sensor", childDni, device.getHub().getId(), [ + completedSetup : true, + label : "Qubino Temperature Sensor", + isComponent : false + ]) + childDevice + } catch(Exception e) { + log.warn "Exception: ${e}" + } +} + +private getParameterMap() {[ + [ + name: "Input 1 switch type", key: "input1SwitchType", type: "enum", + parameterNumber: 1, size: 1, defaultValue: 1, + values: [ + 0: "Mono-stable switch type (push button)", + 1: "Bi-stable switch type", + ], + description: "Input 1 switch type" + ], + [ + name: "Input 2 switch type", key: "input2SwitchType", type: "enum", + parameterNumber: 2, size: 1, defaultValue: 1, + values: [ + 0: "Mono-stable switch type (push button)", + 1: "Bi-stable switch type", + ], + description: "Input 2 switch type" + ], + [ + name: "Saving the state of the relays Q1 and Q2 after a power failure", key: "savingTheStateOfTheRelaysQ1AndQ2AfterAPowerFailure", type: "boolean", + parameterNumber: 30, size: 1, defaultValue: 0, + optionInactive: 0, inactiveDescription: "State is saved and brought back after a power failure", + optionActive: 1, activeDescription: "State is not saved, outputs will be off after a power failure", + description: "Saving the state of the relays Q1 and Q2 after a power failure" + ], + [ + name: "Output Q1 Switch selection", key: "outputQ1SwitchSelection", type: "enum", + parameterNumber: 63, size: 1, defaultValue: 0, + values: [ + 0: "When system is turned off the output is 0V (NC).", + 1: "When system is turned off the output is 230V (NO).", + ], + description: "Set value means the type of the device that is connected to the Q1 output. The device type can be normally open (NO) or normally close (NC). " + ], + [ + name: "Output Q2 Switch selection", key: "outputQ2SwitchSelection", type: "enum", + parameterNumber: 64, size: 1, defaultValue: 0, + values: [ + 0: "When system is turned off the output is 0V (NC).", + 1: "When system is turned off the output is 230V (NO).", + ], + description: "Set value means the type of the device that is connected to the Q2 output. The device type can be normally open (NO) or normally close (NC). " + ] +]} \ No newline at end of file From bca7313e95002fb0dc1d717ef91de8505ed0d0b4 Mon Sep 17 00:00:00 2001 From: MGoralczykS Date: Wed, 15 Jul 2020 12:27:16 +0200 Subject: [PATCH 2/8] Change name in definition --- .../qubino/qubino-flush-relay.src/qubino-flush-relay.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/qubino/qubino-flush-relay.src/qubino-flush-relay.groovy b/devicetypes/qubino/qubino-flush-relay.src/qubino-flush-relay.groovy index f439a8cf12d..53e40b6ec2c 100644 --- a/devicetypes/qubino/qubino-flush-relay.src/qubino-flush-relay.groovy +++ b/devicetypes/qubino/qubino-flush-relay.src/qubino-flush-relay.groovy @@ -12,7 +12,7 @@ * */ metadata { - definition (name: "Qubino Flush 2 Relay", namespace: "qubino", author: "SmartThings", mnmn: "SmartThings", vid: "generic-switch-power-energy") { + definition (name: "Qubino Flush Relay", namespace: "qubino", author: "SmartThings", mnmn: "SmartThings", vid: "generic-switch-power-energy") { capability "Switch" capability "Power Meter" capability "Energy Meter" From feb14d62195138193c9509f713dc928c15601c22 Mon Sep 17 00:00:00 2001 From: MGoralczykS Date: Mon, 20 Jul 2020 14:06:59 +0200 Subject: [PATCH 3/8] Fixes --- .../qubino-flush-2-relay.groovy | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy index 431dadc8a5e..3599d0f88c2 100644 --- a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy +++ b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy @@ -12,7 +12,7 @@ * */ metadata { - definition (name: "Qubino Flush 2 Relay", namespace: "qubino", author: "SmartThings", mnmn: "SmartThings", vid: "generic-switch-power-energy") { + definition (name: "Qubino Flush Relay", namespace: "qubino", author: "SmartThings", mnmn: "SmartThings", vid: "generic-switch-power-energy") { capability "Switch" capability "Power Meter" capability "Energy Meter" @@ -24,6 +24,8 @@ metadata { command "reset" fingerprint mfr: "0159", prod: "0002", model: "0051", deviceJoinName: "Qubino Switch 1" //Qubino Flush 2 Relay + fingerprint mfr: "0159", prod: "0002", model: "0052", deviceJoinName: "Qubino Switch" //Qubino Flush 1 Relay + fingerprint mfr: "0159", prod: "0002", model: "0053", deviceJoinName: "Qubino Switch" //Qubino Flush 1D Relay } tiles(scale: 2) { @@ -70,7 +72,12 @@ metadata { } def installed() { - state.numberOfSwitches = 2 + if (zwaveInfo?.model.equals("0051")) { + state.numberOfSwitches = 2 + } else { + state.numberOfSwitches = 1 + } + if (!childDevices) { addChildSwitches(state.numberOfSwitches) } @@ -93,7 +100,7 @@ def updated() { } // Preferences template begin parameterMap.each { - if (isPreferenceChanged(it)) { + if (isPreferenceChanged(it) && !excludeParameterFromSync(it)) { log.debug "Preference ${it.key} has been updated from value: ${state.currentPreferencesState."$it.key".value} to ${settings."$it.key"}" state.currentPreferencesState."$it.key".status = "syncPending" } else if (!state.currentPreferencesState."$it.key".value) { @@ -104,6 +111,20 @@ def updated() { // Preferences template end } +def excludeParameterFromSync(preference){ + def exclude = false + if (preference.key == "outputQ2SwitchSelection") { + if (zwaveInfo?.model?.equals("0052") || zwaveInfo?.model?.equals("0053")) { + exclude = true + } + } + + if (exclude) { + log.warn "Preference no ${preference.parameterNumber} - ${preference.key} is not supported by this device" + } + return exclude +} + private syncConfiguration() { def commands = [] parameterMap.each { @@ -446,6 +467,6 @@ private getParameterMap() {[ 0: "When system is turned off the output is 0V (NC).", 1: "When system is turned off the output is 230V (NO).", ], - description: "Set value means the type of the device that is connected to the Q2 output. The device type can be normally open (NO) or normally close (NC). " + description: "(Only for Qubino Flush 2 Relay) Set value means the type of the device that is connected to the Q2 output. The device type can be normally open (NO) or normally close (NC). " ] ]} \ No newline at end of file From 7bd4dafc2e2fdcca1241cf475a412f029eeb6ef6 Mon Sep 17 00:00:00 2001 From: MGoralczykS Date: Mon, 20 Jul 2020 14:22:11 +0200 Subject: [PATCH 4/8] fixes --- .../qubino-flush-relay.groovy | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/devicetypes/qubino/qubino-flush-relay.src/qubino-flush-relay.groovy b/devicetypes/qubino/qubino-flush-relay.src/qubino-flush-relay.groovy index 53e40b6ec2c..3599d0f88c2 100644 --- a/devicetypes/qubino/qubino-flush-relay.src/qubino-flush-relay.groovy +++ b/devicetypes/qubino/qubino-flush-relay.src/qubino-flush-relay.groovy @@ -24,8 +24,8 @@ metadata { command "reset" fingerprint mfr: "0159", prod: "0002", model: "0051", deviceJoinName: "Qubino Switch 1" //Qubino Flush 2 Relay - fingerprint mfr: "0159", prod: "0002", model: "0052", deviceJoinName: "Qubino Relay" //Qubino Flush 1 Relay - fingerprint mfr: "0159", prod: "0002", model: "0053", deviceJoinName: "Qubino Relay" //Qubino Flush 1D Relay + fingerprint mfr: "0159", prod: "0002", model: "0052", deviceJoinName: "Qubino Switch" //Qubino Flush 1 Relay + fingerprint mfr: "0159", prod: "0002", model: "0053", deviceJoinName: "Qubino Switch" //Qubino Flush 1D Relay } tiles(scale: 2) { @@ -72,7 +72,12 @@ metadata { } def installed() { - state.numberOfSwitches = 2 + if (zwaveInfo?.model.equals("0051")) { + state.numberOfSwitches = 2 + } else { + state.numberOfSwitches = 1 + } + if (!childDevices) { addChildSwitches(state.numberOfSwitches) } @@ -462,6 +467,6 @@ private getParameterMap() {[ 0: "When system is turned off the output is 0V (NC).", 1: "When system is turned off the output is 230V (NO).", ], - description: "Set value means the type of the device that is connected to the Q2 output. The device type can be normally open (NO) or normally close (NC). " + description: "(Only for Qubino Flush 2 Relay) Set value means the type of the device that is connected to the Q2 output. The device type can be normally open (NO) or normally close (NC). " ] ]} \ No newline at end of file From f790faf49a82051bf2840464283e46ebf80df6b8 Mon Sep 17 00:00:00 2001 From: MGoralczykS Date: Mon, 20 Jul 2020 14:28:54 +0200 Subject: [PATCH 5/8] delete qubino-flush-relay new file --- .../qubino-flush-relay.groovy | 472 ------------------ 1 file changed, 472 deletions(-) delete mode 100644 devicetypes/qubino/qubino-flush-relay.src/qubino-flush-relay.groovy diff --git a/devicetypes/qubino/qubino-flush-relay.src/qubino-flush-relay.groovy b/devicetypes/qubino/qubino-flush-relay.src/qubino-flush-relay.groovy deleted file mode 100644 index 3599d0f88c2..00000000000 --- a/devicetypes/qubino/qubino-flush-relay.src/qubino-flush-relay.groovy +++ /dev/null @@ -1,472 +0,0 @@ -/** - * Copyright 2020 SRPOL - * - * 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: "Qubino Flush Relay", namespace: "qubino", author: "SmartThings", mnmn: "SmartThings", vid: "generic-switch-power-energy") { - capability "Switch" - capability "Power Meter" - capability "Energy Meter" - capability "Refresh" - capability "Actuator" - capability "Sensor" - capability "Health Check" - - command "reset" - - fingerprint mfr: "0159", prod: "0002", model: "0051", deviceJoinName: "Qubino Switch 1" //Qubino Flush 2 Relay - fingerprint mfr: "0159", prod: "0002", model: "0052", deviceJoinName: "Qubino Switch" //Qubino Flush 1 Relay - fingerprint mfr: "0159", prod: "0002", model: "0053", deviceJoinName: "Qubino Switch" //Qubino Flush 1D Relay - } - - tiles(scale: 2) { - multiAttributeTile(name:"switch", type: "generic", 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: "#00a0dc") - attributeState("off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff") - } - } - valueTile("power", "device.power", decoration: "flat", width: 2, height: 2) { - state "default", label:'${currentValue} W' - } - valueTile("energy", "device.energy", decoration: "flat", width: 2, height: 2) { - state "default", label:'${currentValue} kWh' - } - standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { - state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh" - } - standardTile("reset", "device.energy", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { - state "default", label:'reset kWh', action:"reset" - } - - main(["switch"]) - details(["switch","power","energy","refresh","reset"]) - } - - preferences { - parameterMap.each { - input (title: it.name, description: it.description, type: "paragraph", element: "paragraph") - - switch(it.type) { - case "boolean": - input(type: "paragraph", element: "paragraph", description: "Option enabled: ${it.activeDescription}\n" + - "Option disabled: ${it.inactiveDescription}" - ) - input(name: it.key, type: "boolean", title: "Enable", defaultValue: it.defaultValue == it.activeOption, required: false) - break - case "enum": - input(name: it.key, title: "Select", type: "enum", options: it.values, defaultValue: it.defaultValue, required: false) - break - } - } - } -} - -def installed() { - if (zwaveInfo?.model.equals("0051")) { - state.numberOfSwitches = 2 - } else { - state.numberOfSwitches = 1 - } - - if (!childDevices) { - addChildSwitches(state.numberOfSwitches) - } - sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) - // Preferences template begin - state.currentPreferencesState = [:] - parameterMap.each { - state.currentPreferencesState."$it.key" = [:] - state.currentPreferencesState."$it.key".value = getPreferenceValue(it) - def preferenceName = it.key + "Boolean" - settings."$preferenceName" = true - state.currentPreferencesState."$it.key".status = "synced" - } - // Preferences template end -} - -def updated() { - if (!childDevices) { - addChildSwitches(state.numberOfSwitches) - } - // Preferences template begin - parameterMap.each { - if (isPreferenceChanged(it) && !excludeParameterFromSync(it)) { - log.debug "Preference ${it.key} has been updated from value: ${state.currentPreferencesState."$it.key".value} to ${settings."$it.key"}" - state.currentPreferencesState."$it.key".status = "syncPending" - } else if (!state.currentPreferencesState."$it.key".value) { - log.warn "Preference ${it.key} no. ${it.parameterNumber} has no value. Please check preference declaration for errors." - } - } - syncConfiguration() - // Preferences template end -} - -def excludeParameterFromSync(preference){ - def exclude = false - if (preference.key == "outputQ2SwitchSelection") { - if (zwaveInfo?.model?.equals("0052") || zwaveInfo?.model?.equals("0053")) { - exclude = true - } - } - - if (exclude) { - log.warn "Preference no ${preference.parameterNumber} - ${preference.key} is not supported by this device" - } - return exclude -} - -private syncConfiguration() { - def commands = [] - parameterMap.each { - try { - if (state.currentPreferencesState."$it.key".status == "syncPending") { - commands += encap(zwave.configurationV2.configurationSet(scaledConfigurationValue: getCommandValue(it), parameterNumber: it.parameterNumber, size: it.size)) - commands += encap(zwave.configurationV2.configurationGet(parameterNumber: it.parameterNumber)) - } else if (state.currentPreferencesState."$it.key".status == "disablePending") { - commands += encap(zwave.configurationV2.configurationSet(scaledConfigurationValue: it.disableValue, parameterNumber: it.parameterNumber, size: it.size)) - commands += encap(zwave.configurationV2.configurationGet(parameterNumber: it.parameterNumber)) - } - } catch (e) { - log.warn "There's been an issue with preference: ${it.key}" - } - } - sendHubCommand(commands) -} - -def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd, ep = null) { - // Preferences template begin - log.debug "Configuration report: ${cmd}" - def preference = parameterMap.find( {it.parameterNumber == cmd.parameterNumber} ) - def key = preference.key - def preferenceValue = getPreferenceValue(preference, cmd.scaledConfigurationValue) - if (settings."$key" == preferenceValue) { - state.currentPreferencesState."$key".value = settings."$key" - state.currentPreferencesState."$key".status = "synced" - } else { - state.currentPreferencesState."$key"?.status = "syncPending" - runIn(5, "syncConfiguration", [overwrite: true]) - } - // Preferences template end -} - -private getPreferenceValue(preference, value = "default") { - def integerValue = value == "default" ? preference.defaultValue : value.intValue() - switch (preference.type) { - case "enum": - return String.valueOf(integerValue) - case "boolean": - return String.valueOf(preference.optionActive == integerValue) - default: - return integerValue - } -} - -private getCommandValue(preference) { - def parameterKey = preference.key - switch (preference.type) { - case "boolean": - return settings."$parameterKey" ? preference.optionActive : preference.optionInactive - default: - return Integer.parseInt(settings."$parameterKey") - } -} - -private isPreferenceChanged(preference) { - if (settings."$preference.key" != null) { - return state.currentPreferencesState."$preference.key".value != settings."$preference.key" - } else { - return false - } -} - -def parse(String description) { - def result = null - if (description.startsWith("Err")) { - result = createEvent(descriptionText:description, isStateChange:true) - } else if (description != "updated") { - def cmd = zwave.parse(description) - if (cmd) { - result = zwaveEvent(cmd) - } - } - log.debug "parsed '${description}' to ${result.inspect()}" - result -} - -def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd, ep = null) { - log.debug "Security Message Encap ${cmd}" - def encapsulatedCommand = cmd.encapsulatedCommand() - if (encapsulatedCommand) { - zwaveEvent(encapsulatedCommand, null) - } else { - log.warn "Unable to extract encapsulated cmd from $cmd" - createEvent(descriptionText: cmd.toString()) - } -} - -def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd, ep = null) { - log.debug "Multichannel command ${cmd}" + (ep ? " from endpoint $ep" : "") - if (cmd.commandClass == 0x6C && cmd.parameter.size >= 4) { // Supervision encapsulated Message - // Supervision header is 4 bytes long, two bytes dropped here are the latter two bytes of the supervision header - cmd.parameter = cmd.parameter.drop(2) - // Updated Command Class/Command now with the remaining bytes - cmd.commandClass = cmd.parameter[0] - cmd.command = cmd.parameter[1] - cmd.parameter = cmd.parameter.drop(2) - } - def encapsulatedCommand = cmd.encapsulatedCommand() - zwaveEvent(encapsulatedCommand, cmd.sourceEndPoint as Integer) -} - -def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, ep = null) { - log.debug "Basic ${cmd}" + (ep ? " from endpoint $ep" : "") - changeSwitch(ep, cmd) -} - -def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd, ep = null) { - log.debug "Binary ${cmd}" + (ep ? " from endpoint $ep" : "") - changeSwitch(ep, cmd) -} - -private changeSwitch(endpoint, cmd) { - def value = cmd.value ? "on" : "off" - if (endpoint == 1) { - createEvent(name: "switch", value: value, isStateChange: true, descriptionText: "Switch ${endpoint} is ${value}") - } else if (endpoint) { - String childDni = "${device.deviceNetworkId}:$endpoint" - def child = childDevices.find { it.deviceNetworkId == childDni } - child?.sendEvent(name: "switch", value: value, isStateChange: true, descriptionText: "Switch ${endpoint} is ${value}") - } -} - -def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd, ep = null) { - log.debug "Meter ${cmd}" + (ep ? " from endpoint $ep" : "") - if (ep == 1) { - createEvent(createMeterEventMap(cmd)) - } else if (ep) { - String childDni = "${device.deviceNetworkId}:$ep" - def child = childDevices.find { it.deviceNetworkId == childDni } - child?.sendEvent(createMeterEventMap(cmd)) - } -} - -private createMeterEventMap(cmd) { - def eventMap = [:] - if (cmd.meterType == 1) { - if (cmd.scale == 0) { - eventMap.name = "energy" - eventMap.value = cmd.scaledMeterValue - eventMap.unit = "kWh" - } else if (cmd.scale == 2) { - eventMap.name = "power" - eventMap.value = Math.round(cmd.scaledMeterValue) - eventMap.unit = "W" - } - } - eventMap -} - -def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd, ep = null) { - log.debug "SensorMultilevelReport ${cmd}" + (ep ? " from endpoint $ep" : "") - def result = [] - - def map = [:] - switch (cmd.sensorType) { - case 1: - map.name = "temperature" - def cmdScale = cmd.scale == 1 ? "F" : "C" - map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision) - map.unit = getTemperatureScale() - break - default: - map.descriptionText = cmd.toString() - } - def child = childDevices.find { it.deviceNetworkId == state.temperatureSensorDni } - if (!child) { - child = addChildTemperatureSensor() - } - child?.sendEvent(map) - createEvent(map) -} - -def zwaveEvent(physicalgraph.zwave.Command cmd, ep) { - log.warn "Unhandled ${cmd}" + (ep ? " from endpoint $ep" : "") -} - -def on() { - onOffCmd(0xFF) -} - -def off() { - onOffCmd(0x00) -} - -def ping() { - refresh() -} - -def childOnOff(deviceNetworkId, value) { - def switchId = getSwitchId(deviceNetworkId) - if (switchId != null) sendHubCommand onOffCmd(value, switchId) -} - -private onOffCmd(value, endpoint = 1) { - delayBetween([ - encap(zwave.basicV1.basicSet(value: value), endpoint), - encap(zwave.basicV1.basicGet(), endpoint), - ]) -} - -def childRefresh(deviceNetworkId, includeMeterGet = true) { - def switchId = getSwitchId(deviceNetworkId) - if (switchId != null) { - sendHubCommand refresh([switchId],includeMeterGet) - } -} - -def refresh(endpoints = [1], includeMeterGet = true) { - - def cmds = [] - - endpoints.each { - cmds << [encap(zwave.basicV1.basicGet(), it)] - if (includeMeterGet) { - cmds << encap(zwave.meterV3.meterGet(scale: 0), it) - cmds << encap(zwave.meterV3.meterGet(scale: 2), it) - } - } - - delayBetween(cmds, 200) -} - -private resetAll() { - childDevices.each { - if (it.deviceNetworkId != state.temperatureSensorDni) { - childReset(it.deviceNetworkId) - } - } - sendHubCommand reset() -} - -def childReset(deviceNetworkId) { - def switchId = getSwitchId(deviceNetworkId) - if (switchId != null) { - log.debug "Child reset switchId: ${switchId}" - sendHubCommand reset(switchId) - } -} - -def reset(endpoint = 1) { - log.debug "Resetting endpoint: ${endpoint}" - delayBetween([ - encap(zwave.meterV3.meterReset(), endpoint), - encap(zwave.meterV3.meterGet(scale: 0), endpoint), - "delay 500" - ], 500) -} - -def getSwitchId(deviceNetworkId) { - def split = deviceNetworkId?.split(":") - return (split.length > 1) ? split[1] as Integer : null -} - -private encap(cmd, endpoint = null) { - if (cmd) { - if (endpoint) { - cmd = zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint: endpoint).encapsulate(cmd) - } - - if (zwaveInfo.zw.contains("s")) { - zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() - } else { - cmd.format() - } - } -} - -private addChildSwitches(numberOfSwitches) { - for (def endpoint : 2..numberOfSwitches) { - try { - String childDni = "${device.deviceNetworkId}:$endpoint" - def componentLabel = device.displayName[0..-2] + "${endpoint}" - addChildDevice("smartthings", "Child Metering Switch", childDni, device.getHub().getId(), [ - completedSetup : true, - label : componentLabel, - isComponent : false - ]) - } catch(Exception e) { - log.warn "Exception: ${e}" - } - } -} - -private addChildTemperatureSensor() { - try { - String childDni = "${device.deviceNetworkId}:${state.numberOfSwitches + 1}" - state.temperatureSensorDni = childDni - def childDevice = addChildDevice("qubino", "Qubino Temperature Sensor", childDni, device.getHub().getId(), [ - completedSetup : true, - label : "Qubino Temperature Sensor", - isComponent : false - ]) - childDevice - } catch(Exception e) { - log.warn "Exception: ${e}" - } -} - -private getParameterMap() {[ - [ - name: "Input 1 switch type", key: "input1SwitchType", type: "enum", - parameterNumber: 1, size: 1, defaultValue: 1, - values: [ - 0: "Mono-stable switch type (push button)", - 1: "Bi-stable switch type", - ], - description: "Input 1 switch type" - ], - [ - name: "Input 2 switch type", key: "input2SwitchType", type: "enum", - parameterNumber: 2, size: 1, defaultValue: 1, - values: [ - 0: "Mono-stable switch type (push button)", - 1: "Bi-stable switch type", - ], - description: "Input 2 switch type" - ], - [ - name: "Saving the state of the relays Q1 and Q2 after a power failure", key: "savingTheStateOfTheRelaysQ1AndQ2AfterAPowerFailure", type: "boolean", - parameterNumber: 30, size: 1, defaultValue: 0, - optionInactive: 0, inactiveDescription: "State is saved and brought back after a power failure", - optionActive: 1, activeDescription: "State is not saved, outputs will be off after a power failure", - description: "Saving the state of the relays Q1 and Q2 after a power failure" - ], - [ - name: "Output Q1 Switch selection", key: "outputQ1SwitchSelection", type: "enum", - parameterNumber: 63, size: 1, defaultValue: 0, - values: [ - 0: "When system is turned off the output is 0V (NC).", - 1: "When system is turned off the output is 230V (NO).", - ], - description: "Set value means the type of the device that is connected to the Q1 output. The device type can be normally open (NO) or normally close (NC). " - ], - [ - name: "Output Q2 Switch selection", key: "outputQ2SwitchSelection", type: "enum", - parameterNumber: 64, size: 1, defaultValue: 0, - values: [ - 0: "When system is turned off the output is 0V (NC).", - 1: "When system is turned off the output is 230V (NO).", - ], - description: "(Only for Qubino Flush 2 Relay) Set value means the type of the device that is connected to the Q2 output. The device type can be normally open (NO) or normally close (NC). " - ] -]} \ No newline at end of file From 20f887b089099771d0b666343150e3d51e7c5727 Mon Sep 17 00:00:00 2001 From: MGoralczykS Date: Mon, 20 Jul 2020 14:32:46 +0200 Subject: [PATCH 6/8] rename file --- .../qubino-flush-relay.groovy} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename devicetypes/qubino/{qubino-flush-2-relay.src/qubino-flush-2-relay.groovy => qubino-flush-relay.src/qubino-flush-relay.groovy} (100%) diff --git a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy b/devicetypes/qubino/qubino-flush-relay.src/qubino-flush-relay.groovy similarity index 100% rename from devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy rename to devicetypes/qubino/qubino-flush-relay.src/qubino-flush-relay.groovy From 9a7810a173bb9910e8b2d29adfe73602f1ab4ee4 Mon Sep 17 00:00:00 2001 From: MGoralczykS Date: Tue, 21 Jul 2020 14:28:30 +0200 Subject: [PATCH 7/8] Change name of file --- .../qubino-flush-2-relay.groovy} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename devicetypes/qubino/{qubino-flush-relay.src/qubino-flush-relay.groovy => qubino-flush-2-relay.src/qubino-flush-2-relay.groovy} (100%) diff --git a/devicetypes/qubino/qubino-flush-relay.src/qubino-flush-relay.groovy b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy similarity index 100% rename from devicetypes/qubino/qubino-flush-relay.src/qubino-flush-relay.groovy rename to devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy From 6aefa1e2cebe416c01eba6cc2f653c0f48f94631 Mon Sep 17 00:00:00 2001 From: MGoralczykS Date: Wed, 22 Jul 2020 08:58:28 +0200 Subject: [PATCH 8/8] Change name in definition --- .../qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy index 3599d0f88c2..2dbeb85dd8b 100644 --- a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy +++ b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy @@ -12,7 +12,7 @@ * */ metadata { - definition (name: "Qubino Flush Relay", namespace: "qubino", author: "SmartThings", mnmn: "SmartThings", vid: "generic-switch-power-energy") { + definition (name: "Qubino Flush 2 Relay", namespace: "qubino", author: "SmartThings", mnmn: "SmartThings", vid: "generic-switch-power-energy") { capability "Switch" capability "Power Meter" capability "Energy Meter"