Skip to content

Instantly share code, notes, and snippets.

@b16b
Last active August 4, 2021 20:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save b16b/a7f4fee9f3ad818df019963efa59c708 to your computer and use it in GitHub Desktop.
Save b16b/a7f4fee9f3ad818df019963efa59c708 to your computer and use it in GitHub Desktop.
Moes zigbee multi swich create switch for every gang but still only the first work
/*
* Moes ZigBee Switch
*
* Copyright 2020 SmartThings
*
* 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.
*
*/
public static String version() { return "v0.0.3.20210804" }
private getMODEL_MAP() {
[
//'TS0601' : 3
]
}
metadata {
definition(name: "Moes ZigBee Switch", namespace: "Moes", author: "Kotsos", ocfDeviceType: "oic.d.switch", vid: "generic-switch") {
capability "Actuator"
capability "Configuration"
capability "Refresh"
capability "Health Check"
capability "Switch"
capability "Switch Level"
command "childOn", ["string"]
command "childOff", ["string"]
//Moeshouse Switch
// 1 gang
fingerprint profileId: "0104", model: "TS0601", manufacturer: "_TZE200_amp6tsvy", endpointId: "01", inClusters: "0000,0004,0005,EF00", outClusters: "0019,000A", application: "42", deviceJoinName: "Moes Multi Switch 1"
// 2 gang
fingerprint profileId: "0104", model: "TS0601", manufacturer: "_TZE200_g1ib5ldv", endpointId: "01", inClusters: "0000,0004,0005,EF00", outClusters: "0019,000A", application: "42", deviceJoinName: "Moes Multi Switch 1"
// 3 gang
fingerprint profileId: "0104", model: "TS0601", manufacturer: "_TZE200_tz32mtza", endpointId: "01", inClusters: "0000,0004,0005,EF00", outClusters: "0019,000A", application: "42", deviceJoinName: "Moes Multi Switch 1"
}
preferences {
/*
input(name: "switchLightMode", type: "enum", title: ("Switch Backlight Mode"), description: ("- select type of backlight indicator (default: Position)"), options: ["OFF", "ON", "Position"], defaultValue: "Position", submitOnChange: true)
input(name: "relayMode", type: "enum", title: ("Switch Relay Mode"), description: ("- select relay renew state after AC failed (default: OFF)"), options: ["OFF", "ON", "Last state"], defaultValue: "OFF", submitOnChange: true)
input(name: "debugLogging", type: "bool", title: ("Enable debug logging"), description: "", defaultValue: true, submitOnChange: true, displayDuringSetup: true)
input(name: "infoLogging", type: "bool", title: ("Enable info logging"), description: "", defaultValue: true, submitOnChange: true, displayDuringSetup: true)
*/
input name: "isAutoCreateChildDevice", type: "bool", title: "Auto detecting and create device", description: "default: true", defaultValue: true, required: true
input name: "isCreateAllControllerSwitch", type: "bool", title: "Create All switch", description: "default: false", defaultValue: false, required: true
input type: "paragraph", element: "paragraph", title: "Version", description: version(), displayDuringSetup: false
}
// simulator metadata
simulator {
// status messages
status "on": "on/off: 1"
status "off": "on/off: 0"
// reply messages
reply "zcl on-off on": "on/off: 1"
reply "zcl on-off off": "on/off: 0"
}
}
def installed() {
log.debug "installed()"
def endpointCount = getEndpointCount()
def model = device.getDataValue("model")
if (endpointCount == 1) {
// for 1 gang switch - ST Official local dth
if (model == 'lumi.switch.b1naus01') {
setDeviceType("ZigBee Switch Power")
} else {
setDeviceType("ZigBee Switch")
}
} else if (endpointCount > 1){
if (model == 'FB56+ZSW1HKJ2.5' || model == 'FB56+ZSW1IKJ2.7') {
device.updateDataValue("endpointId", "10")
}
// for multi switch, cloud device
createChildDevices()
}
updateDataValue("onOff", "catchall")
state.hasConfiguredHealthCheck = false
refresh()
}
def updated() {
log.debug "updated()"
updateDataValue("onOff", "catchall")
state.hasConfiguredHealthCheck = false
refresh()
}
// Parse incoming device messages to generate events
def parse(String description) {
Map map = [:]
//def event = zigbee.getEvent(description)
if (description?.startsWith('catchall:')) {
log.debug description
// call parseCatchAllMessage to parse the catchall message received
map = parseCatchAllMessage(description)
if (map != [:]) {
log.debug "ok send event: $map.name $map.value"
sendEvent(name: map.name, value: map.value)
}
}
else {
log.warn "DID NOT PARSE MESSAGE for description : $description"
}
}
private getCLUSTER_TUYA() { 0xEF00 }
private Map parseCatchAllMessage(String description) {
// Create a map from the raw zigbee message to make parsing more intuitive
def msg = zigbee.parse(description)
Map result = [:]
switch(msg.clusterId) {
case 0xEF00:
def attribute = getAttribute(msg.data)
def value = getAttributeValue(msg.data)
switch (attribute) {
case "switch":
switch(value) {
case 0:
result = [
name: 'switch',
value: 'off',
data: [buttonNumber: 1],
descriptionText: "$device.displayName button was pressed",
isStateChange: true
]
break;
case 1:
result = [
name: 'switch',
value: 'on',
data: [buttonNumber: 1],
descriptionText: "$device.displayName button was pressed",
isStateChange: true
]
break;
}
break;
case "level":
int levelValue = value / 10
result = [
name: 'level',
value: levelValue + "%",
data: [buttonNumber: 1],
descriptionText: "$device.displayName level was modified",
isStateChange: true
]
break;
}
break;
}
return result
Map eventMap = zigbee.getEvent(description)
Map eventDescMap = zigbee.parseDescriptionAsMap(description)
if (!eventMap && eventDescMap) {
eventMap = [:]
if (eventDescMap?.clusterId == zigbee.CLUSTER_TUYA()) {
eventMap[name] = "switch"
eventMap[value] = eventDescMap?.value
}
}
if (eventMap && eventDescMap) {
if (eventDescMap?.attrId == "0000" || eventDescMap?.attId == null) {
def endpointId = device.getDataValue("endpointId")
log.debug "eventMap $eventMap | eventDescMap $eventDescMap"
eventMap[displayed] = true
if (eventDescMap?.sourceEndpoint == endpointId) {
log.debug "parse - sendEvent parent $eventDescMap.sourceEndpoint"
sendEvent(eventMap)
} else {
def childDevice = childDevices.find {
it.deviceNetworkId == "$device.deviceNetworkId:${eventDescMap.sourceEndpoint}"
}
if (childDevice) {
log.debug "parse - sendEvent child $eventDescMap.sourceEndpoint"
childDevice.sendEvent(eventMap)
} else if (isAutoCreateChildDevice != false || getEndpointCount() == 0){
def model = device.getDataValue("model")
log.debug "Child device: $device.deviceNetworkId:${eventDescMap?.sourceEndpoint} was not found"
def parentEndpointInt = zigbee.convertHexToInt(endpointId)
if (eventDescMap?.sourceEndpoint != null) {
def childEndpointInt = zigbee.convertHexToInt(eventDescMap?.sourceEndpoint)
def childEndpointHexString = zigbee.convertToHexString(childEndpointInt, 2).toUpperCase()
def deviceLabel = "${device.displayName[0..-2]}"
def deviceIndex = Math.abs(childEndpointInt - parentEndpointInt) + 1
def childByEndpointId = childDevices.find {
it.deviceNetworkId.endsWith(":${eventDescMap.sourceEndpoint}")
}
if (childByEndpointId) {
log.debug "FOUND CHILD!!!!! Change dni to $device.deviceNetworkId:$childEndpointHexString"
childByEndpointId.setDeviceNetworkId("$device.deviceNetworkId:$childEndpointHexString")
} else {
log.debug "NOT FOUND CHILD!!!!! Create to $deviceLabel$deviceIndex"
createChildDevice("$deviceLabel$deviceIndex", childEndpointHexString)
}
}
}
if (isCreateAllControllerSwitch) {
def allControlChildDevice = childDevices.find {
it.deviceNetworkId == "$device.deviceNetworkId:ALL"
}
if (!allControlChildDevice) {
def deviceLabel = "${device.displayName[0..-2]}"
createChildDevice("${deviceLabel}ALL", "ALL")
}
}
}
checkAllSwtichValue()
}
}
}
private String getAttribute(ArrayList _data) {
String retValue = ""
if (_data.size() >= 5) {
if (_data[2] == 1 && _data[3] == 1 && _data[4] == 0) {
retValue = "switch"
}
else if (_data[2] == 2 && _data[3] == 2 && _data[4] == 0) {
retValue = "level"
}
}
return retValue
}
private checkAllSwtichValue() {
def parentSwitchValue = device.currentState("switch").value
log.debug("checkAllSwtichValue ${device.label} : ${parentSwitchValue}")
def allChildDeviceValue = parentSwitchValue
def allChildDevice = null
childDevices?.each {
if (it.deviceNetworkId == "$device.deviceNetworkId:ALL") {
allChildDevice = it
} else {
if (it.currentState("switch")?.value == "on") {
allChildDeviceValue = "on"
}
}
}
if (allChildDevice?.currentState("switch") != allChildDeviceValue) {
allChildDevice?.sendEvent(name: "switch", value : allChildDeviceValue)
}
}
private getEndpointCount() {
def model = device.getDataValue("model")
def count = MODEL_MAP[model] ?: 0
def manufacturer = device.getDataValue("manufacturer")
if ( model == 'TS0601' && manufacturer == '_TZE200_amp6tsvy') {
count = 1
}
if ( model == 'TS0601' && manufacturer == '_TZE200_g1ib5ldv') {
count = 2
}
if ( model == 'TS0601' && manufacturer == '_TZE200_tz32mtza') {
count = 3
}
log.debug("getEndpointCount[$model] : $count")
return count
}
private void createChildDevices() {
log.debug("=============createChildDevices of $device.deviceNetworkId")
if (!state.isCreateChildDone || isAutoCreateChildDevice != false) {
def endpointCount = getEndpointCount()
def endpointId = device.getDataValue("endpointId")
def endpointInt = zigbee.convertHexToInt(endpointId)
def deviceLabel = "${device.displayName[0..-2]}"
for (i in 1..endpointCount - 1) {
def endpointHexString = zigbee.convertToHexString(endpointInt + i, 2).toUpperCase()
createChildDevice("$deviceLabel${i + 1}", endpointHexString)
}
def model = device.getDataValue("model")
def manufacturer = device.getDataValue("manufacturer")
state.isCreateChildDone = true
}
}
private void createChildDevice(String deviceLabel, String endpointHexString) {
def childDevice = childDevices.find {
it.deviceNetworkId == "$device.deviceNetworkId:$endpointHexString"
}
if (!childDevice) {
log.debug("===========Need to createChildDevice: $device.deviceNetworkId:$endpointHexString")
addChildDevice("smartthings", "Child Switch", "$device.deviceNetworkId:$endpointHexString", device.hubId,
[completedSetup: true, label: deviceLabel, isComponent: false])
} else {
log.debug("createChildDevice: SKIP - $device.deviceNetworkId:${endpointHexString}")
}
}
private getChildEndpoint(String dni) {
dni.split(":")[-1] as String
}
private allChildOn() {
log.debug "Executing 'on all' for 0x${device.deviceNetworkId}"
childDevices.each {
if (it.deviceNetworkId == "$device.deviceNetworkId:ALL") {
it.sendEvent(name: "switch", value: "on")
} else {
if (it.currentState("switch").value != "on") {
it.on()
}
}
}
}
private allChildOff() {
log.debug "Executing 'off all' for 0x${device.deviceNetworkId}"
childDevices.each {
if (it.deviceNetworkId == "$device.deviceNetworkId:ALL") {
it.sendEvent(name: "switch", value: "off")
} else {
if (it.currentState("switch").value != "off") {
it.off()
}
}
}
}
def off() {
log.debug "off"
zigbee.command(0xEF00, 0x0, "00010101000100")
}
def on() {
log.debug "off"
zigbee.command(0xEF00, 0x0, "00010101000101")
}
def setLevel(value) {
log.debug "called setLevel with value $value"
if (value >= 0 && value <= 100) {
//String commandValue = "0001020200040000" + zigbee.convertToHexString((value * 10) as Integer, 4)
Map commandParams = [:]
String commandPayload = "0001020200040000" + zigbee.convertToHexString((value * 10) as Integer, 4)
zigbee.command(CLUSTER_TUYA(), 0x0, commandPayload)
}
}
def childOn(String dni) {
log.debug("child on ${dni}")
def childEndpoint = getChildEndpoint(dni)
if(childEndpoint == "ALL") {
allChildOn()
if (device.currentState("switch") != "on") {
zigbee.command(0xEF00, 0x01, "01000101")
}
} else {
def endpointInt = zigbee.convertHexToInt(childEndpoint)
zigbee.command(CLUSTER_TUYA(), 0x01, "01000101", [destEndpoint: endpointInt])
}
}
def childOff(String dni) {
log.debug("child off ${dni}")
def childEndpoint = getChildEndpoint(dni)
if(childEndpoint == "ALL") {
allChildOff()
if (device.currentState("switch") != "off") {
zigbee.command(0xEF00, 0x00, "01000100")
}
}else {
def endpointInt = zigbee.convertHexToInt(childEndpoint)
zigbee.command(CLUSTER_TUYA(), 0x00, "01000100", [destEndpoint: endpointInt])
}
}
/**
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
return refresh()
}
def refresh() {
def cmds = zigbee.onOffRefresh()
def endpointCount = getEndpointCount()
og.debug "called refresh"
zigbee.command(0xEF00, 0x0, "00020100")
if (endpointCount > 1) {
childDevices.each {
log.debug "$it"
def childEndpoint = getChildEndpoint(it.deviceNetworkId)
if (childEndpoint.isNumber()) {
log.debug "refresh $childEndpoint"
def endpointInt = zigbee.convertHexToInt(childEndpoint)
cmds += zigbee.readAttribute(zigbee.CLUSTER_TUYA(), 0x0000, [destEndpoint: endpointInt])
}
}
} else {
cmds += zigbee.readAttribute(zigbee.CLUSTER_TUYA(), 0x0000, [destEndpoint: 0xFF])
}
return cmds
}
def poll() {
refresh()
}
def healthPoll() {
log.debug "healthPoll()"
def cmds = refresh()
cmds.each { sendHubCommand(new physicalgraph.device.HubAction(it)) }
}
def configureHealthCheck() {
Integer hcIntervalMinutes = 12
if (!state.hasConfiguredHealthCheck) {
log.debug "Configuring Health Check, Reporting"
unschedule("healthPoll")
runEvery5Minutes("healthPoll")
def healthEvent = [name: "checkInterval", value: hcIntervalMinutes * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]]
// Device-Watch allows 2 check-in misses from device
sendEvent(healthEvent)
childDevices.each {
def childEndpoint = getChildEndpoint(it.deviceNetworkId)
if (childEndpoint.isNumber()) {
it.sendEvent(healthEvent)
} else if (childEndpoint == "ALL") {
it.sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zigbee", scheme:"untracked"]), displayed: false)
}
}
state.hasConfiguredHealthCheck = true
}
}
def configure() {
log.debug "configure()"
configureHealthCheck()
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
//other devices supported by this DTH in the future
def cmds = zigbee.onOffConfig(0, 120)
def endpointCount = getEndpointCount()
if (endpointCount > 1) {
childDevices.each {
def childEndpoint = getChildEndpoint(it.deviceNetworkId)
if (childEndpoint.isNumber()) {
log.debug "configure(): $childEndpoint"
def endpointInt = zigbee.convertHexToInt(childEndpoint)
cmds += zigbee.configureReporting(zigbee.CLUSTER_TUYA(), 0x0000, 0x10, 0, 120, null, [destEndpoint: endpointInt])
}
}
} else {
cmds += zigbee.configureReporting(zigbee.CLUSTER_TUYA(), 0x0000, 0x10, 0, 120, null, [destEndpoint: 0xFF])
}
cmds += refresh()
return cmds
}
def initialize() {
if (infoLogging) log.info "Initializing..."
log.warn "Debug logging will be automatically disabled after 30 minutes!"
setupChildDevices()
device.updateSetting("switchLightMode",[type:"enum",value:"Position"])
device.updateSetting("relayMode",[type:"enum",value:"OFF"])
device.updateSetting("debugLogging",[type:"bool",value:"true"])
device.updateSetting("infoLogging",[type:"bool",value:"true"])
if (debugLogging) runIn(1800,logsOff)
refresh()
}
/*
def switchLightModeConfig(){
def cmds = []
switch(switchLightMode) {
case "OFF":
if (infoLogging) log.info "Backlight - OFF"
zigbee.command(0xEF00, 0x0, "00010f04000100")
break
case "ON":
if (infoLogging) log.info "Backlight - ON"
zigbee.command(0xEF00, 0x0, "00010f04000101")
break
case "Position":
if (infoLogging) log.info "Backlight - position"
zigbee.command(0xEF00, 0x0, "00010f04000102")
break
}
}
def relayModeConfig(){
def cmds = []
switch(relayMode) {
case "OFF":
if (infoLogging) log.info "Relay state - OFF"
zigbee.command(0xEF00, 0x0, "00010e04000100")
break
case "ON":
if (infoLogging) log.info "Relay state - ON"
zigbee.command(0xEF00, 0x0, "00010e04000101")
break
case "Last state":
if (infoLogging) log.info "Relay state - last state"
zigbee.command(0xEF00, 0x0, "00010e04000102")
break
}
}*/
private int getAttributeValue(ArrayList _data) {
int retValue = 0
if (_data.size() >= 6) {
int dataLength = _data[5] as Integer
int power = 1;
for (i in dataLength..1) {
retValue = retValue + power * _data[i+5]
power = power * 256
}
}
return retValue
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment