Tuya Zigbee Roller Shade port help

Just bought these blinds and the company has sent me the DH they have made for ST, could some one (ive no zigbee driver experience) give it the once over and let me know if it should work as is or needs modifications

code
/** zemismart
*  Tuya Window Shade (v.0.1.0)
 *	Copyright 2020 iquix
 *
 *	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.
 This DTH is coded based on iquix's tuya-window-shade DTH.
 https://github.com/iquix/Smartthings/blob/master/devicetypes/iquix/tuya-window-shade.src/tuya-window-shade.groovy
 */

import groovy.json.JsonOutput
import physicalgraph.zigbee.zcl.DataType

metadata {
	definition(name: "ZemiSmart Zigbee Blind", namespace: "ShinJjang", author: "ShinJjang-iquix", ocfDeviceType: "oic.d.blind", vid: "generic-shade") {
		capability "Actuator"
		capability "Configuration"
		capability "Window Shade"
		capability "Window Shade Preset"
		capability "Switch Level"

		command "pause"
        
        attribute "Direction", "enum", ["Reverse","Forward"]

		fingerprint endpointId: "0x01", profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "0019", manufacturer: "_TYST11_wmcdj3aq", model: "mcdj3aq", deviceJoinName: "Zemismart Zigbee Blind"
	}

	preferences {
		input "preset", "number", title: "Preset position", description: "Set the window shade preset position", defaultValue: 50, range: "0..100", required: false, displayDuringSetup: false
        input name: "Direction", type: "enum", title: "Direction Set", defaultValue: "00", options:["01": "Reverse", "00": "Forward"], displayDuringSetup: true
	}

	tiles(scale: 2) {
		multiAttributeTile(name:"windowShade", type: "generic", width: 6, height: 4) {
			tileAttribute("device.windowShade", key: "PRIMARY_CONTROL") {
				attributeState "open", label: 'Open', action: "close", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#00A0DC", nextState: "closing"
				attributeState "closed", label: 'Closed', action: "open", icon: "http://www.ezex.co.kr/img/st/window_close.png", backgroundColor: "#ffffff", nextState: "opening"
				attributeState "partially open", label: 'Partially open', action: "close", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#d45614", nextState: "closing"
				attributeState "opening", label: 'Opening', action: "colse", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#00A0DC", nextState: "closing"
				attributeState "closing", label: 'Closing', action: "open", icon: "http://www.ezex.co.kr/img/st/window_close.png", backgroundColor: "#ffffff", nextState: "opening"
			}
		}
		standardTile("contPause", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
			state "pause", label:"", icon:'st.sonos.pause-btn', action:'pause', backgroundColor:"#cccccc"
		}
		standardTile("presetPosition", "device.presetPosition", width: 2, height: 2, decoration: "flat") {
			state "default", label: "Preset", action:"presetPosition", icon:"st.Home.home2"
		}
		standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 1) {
			state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
		}
		valueTile("shadeLevel", "device.level", width: 4, height: 1) {
			state "level", label: 'Shade is ${currentValue}% up', defaultState: true
		}
		controlTile("levelSliderControl", "device.level", "slider", width:2, height: 1, inactiveLabel: false) {
			state "level", action:"switch level.setLevel"
		}

		main "windowShade"
		details(["windowShade", "contPause", "presetPosition", "shadeLevel", "levelSliderControl", "refresh"])
	}
}

private getCLUSTER_TUYA() { 0xEF00 }
private getSETDATA() { 0x00 }

// Parse incoming device messages to generate events
def parse(String description) {
	if (description?.startsWith('catchall:') || description?.startsWith('read attr -')) {
		Map descMap = zigbee.parseDescriptionAsMap(description)        
		if (descMap?.clusterInt==CLUSTER_TUYA) {
        	log.debug descMap
			if ( descMap?.command == "01" || descMap?.command == "02" ) {
				def dp = zigbee.convertHexToInt(descMap?.data[3]+descMap?.data[2])
                log.debug "dp = " + dp
				switch (dp) {
					case 1025: // 0x04 0x01: Confirm opening/closing/stopping (triggered from Zigbee)
                    	def data = descMap.data[6]
                    	if (descMap.data[6] == "00") {
                        	log.debug "opening"
                            levelEventMoving(100)
                        } else if (descMap.data[6] == "02") {
                        	log.debug "colsing"
                            levelEventMoving(0)
                        }
                    	break
					case 1031: // 0x04 0x07: Confirm opening/closing/stopping (triggered from remote)
                    	def data = descMap.data[6]
                    	if (descMap.data[6] == "01") {
                        	log.debug "closing"
                            levelEventMoving(0)
                        } else if (descMap.data[6] == "00") {
                        	log.debug "opening"
                            levelEventMoving(100)
                        }
                    	break
					case 514: // 0x02 0x02: Started moving to position (triggered from Zigbee)
                    	def pos = zigbee.convertHexToInt(descMap.data[9])
						log.debug "moving to position :"+pos
                        levelEventMoving(pos)
                        break
					case 515: // 0x02 0x03: Arrived at position
                    	def pos = zigbee.convertHexToInt(descMap.data[9])
                    	log.debug "arrived at position :"+pos
                    	levelEventArrived(pos)
                        break
				}
			}
		}
	}
}

private levelEventMoving(currentLevel) {
	def lastLevel = device.currentValue("level")
	log.debug "levelEventMoving - currentLevel: ${currentLevel} lastLevel: ${lastLevel}"
	if (lastLevel == "undefined" || currentLevel == lastLevel) { //Ignore invalid reports
		log.debug "Ignore invalid reports"
	} else {
		if (lastLevel < currentLevel) {
			sendEvent([name:"windowShade", value: "opening"])
		} else if (lastLevel > currentLevel) {
			sendEvent([name:"windowShade", value: "closing"])
		}
    }
}

private levelEventArrived(level) {
	if (level == 0) {
    	sendEvent(name: "windowShade", value: "closed")
    } else if (level == 100) {
    	sendEvent(name: "windowShade", value: "open")
    } else if (level > 0 && level < 100) {
		sendEvent(name: "windowShade", value: "partially open")
    } else {
    	sendEvent(name: "windowShade", value: "unknown")
        return
    }
    sendEvent(name: "level", value: (level))
}

def close() {
	log.info "close()"
	def currentLevel = device.currentValue("level")
    if (currentLevel == 0) {
    	sendEvent(name: "windowShade", value: "closed")
        return
    }
	sendTuyaCommand("0104", "00", "0102")
}

def open() {
	log.info "open()"
    def currentLevel = device.currentValue("level")
    if (currentLevel == 100) {
    	sendEvent(name: "windowShade", value: "open")
        return
    }
	sendTuyaCommand("0104", "00", "0100")
}

def pause() {
	log.info "pause()"
	sendTuyaCommand("0104", "00", "0101")
    
}

def setLevel(data, rate = null) {
	log.info "setLevel("+data+")"
    def currentLevel = device.currentValue("level")
    if (currentLevel == data) {
    	sendEvent(name: "level", value: currentLevel)
        return
    }
	sendTuyaCommand("0202", "00", "04000000"+zigbee.convertToHexString(data, 2))
}


def presetPosition() {
    setLevel(preset ?: 50)
}

def installed() {
	sendEvent(name: "supportedWindowShadeCommands", value: JsonOutput.toJson(["open", "close", "pause"]), displayed: false)
    
}

def updated() {
	def val = Direction
    sendEvent([name:"Direction", value: (val == "00" ? "Forward" : "Reverse")])    
	DirectionSet(val)
}	

def DirectionSet(Dval) {
	log.info "Dset(${Dval})"
   sendHubCommand(zigbee.command(CLUSTER_TUYA, SETDATA, "00" + zigbee.convertToHexString(rand(256), 2) + "05040001" + Dval))
}

def configure() {
	log.info "configure()"
}

private sendTuyaCommand(dp, fn, data) {
	log.info "${dp},${fn},${data}"
	zigbee.command(CLUSTER_TUYA, SETDATA, "00" + zigbee.convertToHexString(rand(256), 2) + dp + fn + data)
}

private rand(n) {
	return (new Random().nextInt(n))
}

The first one should be changed to hubitat.zigbee.zcl.DataType, the second one is because endpoint IDs need to be set without 0x prefixed, only two characters are allowed, so in this case, 0x01 should be 01.

sorted those thanks
just
Capability 'Window Shade Preset' not found. on line 26

That capability doesn't exist in HE, I think it might be only for the command presetPosition, so adding that as a command should suffice.
The below should work, but I have nothing to test it with. Keep forgetting if you can just return the zigbee.command calls like they are here. I always send my calls explicitly with sendHubCommand. If they can be returned, the below should work unless the byte-order is different on ST?

Code
/** zemismart
*  Tuya Window Shade (v.0.1.0)
 *	Copyright 2020 iquix
 *
 *	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.
 This DTH is coded based on iquix's tuya-window-shade DTH.
 https://github.com/iquix/Smartthings/blob/master/devicetypes/iquix/tuya-window-shade.src/tuya-window-shade.groovy
 */

import groovy.json.JsonOutput
import hubitat.zigbee.zcl.DataType

metadata {
	definition(name: "ZemiSmart Zigbee Blind", namespace: "ShinJjang", author: "ShinJjang-iquix", ocfDeviceType: "oic.d.blind", vid: "generic-shade") {
		//capability "Actuator"
		//capability "Configuration"
		capability "Window Shade"
		//capability "Window Shade Preset"  // Doesn't exist in HE
		//capability "Switch Level"

		command "pause"
        command "presetPosition"
        
        attribute "Direction", "enum", ["Reverse","Forward"]

		fingerprint endpointId: "01", profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "0019", manufacturer: "_TYST11_wmcdj3aq", model: "mcdj3aq", deviceJoinName: "Zemismart Zigbee Blind"
	}

	preferences {
		input "preset", "number", title: "Preset position", description: "Set the window shade preset position", defaultValue: 50, range: "0..100", required: false, displayDuringSetup: false
        input name: "Direction", type: "enum", title: "Direction Set", defaultValue: "00", options:["01": "Reverse", "00": "Forward"], displayDuringSetup: true
	}
}

private getCLUSTER_TUYA() { 0xEF00 }
private getSETDATA() { 0x00 }

// Parse incoming device messages to generate events
def parse(String description) {
	if (description?.startsWith('catchall:') || description?.startsWith('read attr -')) {
		Map descMap = zigbee.parseDescriptionAsMap(description)        
		if (descMap?.clusterInt==CLUSTER_TUYA) {
        	log.debug descMap
			if ( descMap?.command == "01" || descMap?.command == "02" ) {
				def dp = zigbee.convertHexToInt(descMap?.data[3]+descMap?.data[2])
                log.debug "dp = " + dp
				switch (dp) {
					case 1025: // 0x04 0x01: Confirm opening/closing/stopping (triggered from Zigbee)
                    	def data = descMap.data[6]
                    	if (descMap.data[6] == "00") {
                        	log.debug "opening"
                            levelEventMoving(100)
                        } else if (descMap.data[6] == "02") {
                        	log.debug "colsing"
                            levelEventMoving(0)
                        }
                    	break
					case 1031: // 0x04 0x07: Confirm opening/closing/stopping (triggered from remote)
                    	def data = descMap.data[6]
                    	if (descMap.data[6] == "01") {
                        	log.debug "closing"
                            levelEventMoving(0)
                        } else if (descMap.data[6] == "00") {
                        	log.debug "opening"
                            levelEventMoving(100)
                        }
                    	break
					case 514: // 0x02 0x02: Started moving to position (triggered from Zigbee)
                    	def pos = zigbee.convertHexToInt(descMap.data[9])
						log.debug "moving to position :"+pos
                        levelEventMoving(pos)
                        break
					case 515: // 0x02 0x03: Arrived at position
                    	def pos = zigbee.convertHexToInt(descMap.data[9])
                    	log.debug "arrived at position :"+pos
                    	levelEventArrived(pos)
                        break
				}
			}
		}
	}
}

private levelEventMoving(currentLevel) {
	def lastLevel = device.currentValue("position")
	log.debug "levelEventMoving - currentLevel: ${currentLevel} lastLevel: ${lastLevel}"
	if (lastLevel == "undefined" || currentLevel == lastLevel) { //Ignore invalid reports
		log.debug "Ignore invalid reports"
	} else {
		if (lastLevel < currentLevel) {
			sendEvent([name:"windowShade", value: "opening"])
		} else if (lastLevel > currentLevel) {
			sendEvent([name:"windowShade", value: "closing"])
		}
    }
}

private levelEventArrived(level) {
	if (level == 0) {
    	sendEvent(name: "windowShade", value: "closed")
    } else if (level == 100) {
    	sendEvent(name: "windowShade", value: "open")
    } else if (level > 0 && level < 100) {
		sendEvent(name: "windowShade", value: "partially open")
    } else {
    	sendEvent(name: "windowShade", value: "unknown")
        return
    }
    //sendEvent(name: "level", value: (level))
    sendEvent(name: "position", value: (level))
}

def close() {
	log.info "close()"
	def currentLevel = device.currentValue("position")
    if (currentLevel == 0) {
    	sendEvent(name: "windowShade", value: "closed")
        return
    }
	sendTuyaCommand("0104", "00", "0102")
}

def open() {
	log.info "open()"
    def currentLevel = device.currentValue("position")
    if (currentLevel == 100) {
    	sendEvent(name: "windowShade", value: "open")
        return
    }
	sendTuyaCommand("0104", "00", "0100")
}

def pause() {
	log.info "pause()"
	sendTuyaCommand("0104", "00", "0101")
    
}

def setLevel(data, rate = null) {
    log.info "setLevel("+data+")"
    setPosition(data)
}

def setPosition(data) {
    log.info "setPosition("+data+")"
    def currentLevel = device.currentValue("position")
    if (currentLevel == data) {
    	sendEvent(name: "level", value: currentLevel)
        return
    }
	sendTuyaCommand("0202", "00", "04000000"+zigbee.convertToHexString(data, 2))
}


def presetPosition() {
    setLevel(preset ?: 50)
}

def installed() {
    // This isn't used, this is an ST thing.
	//sendEvent(name: "supportedWindowShadeCommands", value: JsonOutput.toJson(["open", "close", "pause"]), displayed: false)
}

def updated() {
	def val = Direction
    sendEvent([name:"Direction", value: (val == "00" ? "Forward" : "Reverse")])    
	DirectionSet(val)
}	

def DirectionSet(Dval) {
	log.info "Dset(${Dval})"
   sendHubCommand(zigbee.command(CLUSTER_TUYA, SETDATA, "00" + zigbee.convertToHexString(rand(256), 2) + "05040001" + Dval))
}

/*def configure() {
	log.info "configure()"
}*/

private sendTuyaCommand(dp, fn, data) {
	log.info "${dp},${fn},${data}"
	sendHubCommand(zigbee.command(CLUSTER_TUYA, SETDATA, "00" + zigbee.convertToHexString(rand(256), 2) + dp + fn + data))
}

private rand(n) {
	return (new Random().nextInt(n))
}

soz apart from comment out cant see what else
ive also added this to redirect the HE command to the drives current setLevel
//mc
def setPosition(position){
log.info "setLevel("+$position+")"
setLevel(data = position, rate = null)
}
//mc

Yes, that you could just rename unless you really want setLevel as well, there is no setLevel in WindowShade. Most changes was about renaming like what you ahd already done and deleting the tiles-section.

If the commands are not sent, just add the sendHubCommand explicitly.

where would i put that?
private sendTuyaCommand(dp, fn, data) {
log.info "${dp},${fn},${data}"
zigbee.command(CLUSTER_TUYA, SETDATA, "00" + zigbee.convertToHexString(rand(256), 2) + dp + fn + data)
}
??

around any zigbee.command, in this case the one in sendTuyaCommand. Just like it is in DirectionSet

I also just saw that you will need to define 'command "pause"' as well, that is not part of the HE capability.

sendEvent(name: "level", value: (level)) should be sendEvent(name: "position", value: (level))

If you keep the Switch level capability, you will have to send both.

Ok updated version, i have a wait for them to arrive so just trying to get as much done ready

Summary
/** zemismart
*  Tuya Window Shade (v.0.1.0)
 *	Copyright 2020 iquix
 *
 *	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.
 This DTH is coded based on iquix's tuya-window-shade DTH.
 https://github.com/iquix/Smartthings/blob/master/devicetypes/iquix/tuya-window-shade.src/tuya-window-shade.groovy
 */

import groovy.json.JsonOutput
//mc // import physicalgraph.zigbee.zcl.DataType
import hubitat.zigbee.zcl.DataType

metadata {
	definition(name: "ZemiSmart Zigbee Blind", namespace: "ShinJjang", author: "ShinJjang-iquix", ocfDeviceType: "oic.d.blind", vid: "generic-shade") {
		capability "Actuator"
		capability "Configuration"
		capability "Window Shade"
// mc not supported in HE		capability "Window Shade Preset"
		capability "Switch Level"

		command "pause"
        
        attribute "Direction", "enum", ["Reverse","Forward"]

		fingerprint endpointId: "01", profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "0019", manufacturer: "_TYST11_wmcdj3aq", model: "mcdj3aq", deviceJoinName: "Zemismart Zigbee Blind"
//mc changeed endpointId from 0x01 to 01	
    }

	preferences {
//mc no tiles		input "preset", "number", title: "Preset position", description: "Set the window shade preset position", defaultValue: 50, range: "0..100", required: false, displayDuringSetup: false
        input name: "Direction", type: "enum", title: "Direction Set", defaultValue: "00", options:["01": "Reverse", "00": "Forward"], displayDuringSetup: true
	}
// removed tiles for HE
}

private getCLUSTER_TUYA() { 0xEF00 }
private getSETDATA() { 0x00 }

// Parse incoming device messages to generate events
def parse(String description) {
	if (description?.startsWith('catchall:') || description?.startsWith('read attr -')) {
		Map descMap = zigbee.parseDescriptionAsMap(description)        
		if (descMap?.clusterInt==CLUSTER_TUYA) {
        	log.debug descMap
			if ( descMap?.command == "01" || descMap?.command == "02" ) {
				def dp = zigbee.convertHexToInt(descMap?.data[3]+descMap?.data[2])
                log.debug "dp = " + dp
				switch (dp) {
					case 1025: // 0x04 0x01: Confirm opening/closing/stopping (triggered from Zigbee)
                    	def data = descMap.data[6]
                    	if (descMap.data[6] == "00") {
                        	log.debug "opening"
                            levelEventMoving(100)
                        } else if (descMap.data[6] == "02") {
                        	log.debug "colsing"
                            levelEventMoving(0)
                        }
                    	break
					case 1031: // 0x04 0x07: Confirm opening/closing/stopping (triggered from remote)
                    	def data = descMap.data[6]
                    	if (descMap.data[6] == "01") {
                        	log.debug "closing"
                            levelEventMoving(0)
                        } else if (descMap.data[6] == "00") {
                        	log.debug "opening"
                            levelEventMoving(100)
                        }
                    	break
					case 514: // 0x02 0x02: Started moving to position (triggered from Zigbee)
                    	def pos = zigbee.convertHexToInt(descMap.data[9])
						log.debug "moving to position :"+pos
                        levelEventMoving(pos)
                        break
					case 515: // 0x02 0x03: Arrived at position
                    	def pos = zigbee.convertHexToInt(descMap.data[9])
                    	log.debug "arrived at position :"+pos
                    	levelEventArrived(pos)
                        break
				}
			}
		}
	}
}

private levelEventMoving(currentLevel) {
	def lastLevel = device.currentValue("level")
	log.debug "levelEventMoving - currentLevel: ${currentLevel} lastLevel: ${lastLevel}"
	if (lastLevel == "undefined" || currentLevel == lastLevel) { //Ignore invalid reports
		log.debug "Ignore invalid reports"
	} else {
		if (lastLevel < currentLevel) {
			sendEvent([name:"windowShade", value: "opening"])
		} else if (lastLevel > currentLevel) {
			sendEvent([name:"windowShade", value: "closing"])
		}
    }
}

private levelEventArrived(level) {
	if (level == 0) {
    	sendEvent(name: "windowShade", value: "closed")
    } else if (level == 100) {
    	sendEvent(name: "windowShade", value: "open")
    } else if (level > 0 && level < 100) {
		sendEvent(name: "windowShade", value: "partially open")
    } else {
    	sendEvent(name: "windowShade", value: "unknown")
        return
    }
    sendEvent(name: "level", value: (level))
    //mc added
    sendEvent(name: "position", value: (level))
    //mc
}

def close() {
	log.info "close()"
	def currentLevel = device.currentValue("level")
    if (currentLevel == 0) {
    	sendEvent(name: "windowShade", value: "closed")
        return
    }
	sendTuyaCommand("0104", "00", "0102")
}

def open() {
	log.info "open()"
    def currentLevel = device.currentValue("level")
    if (currentLevel == 100) {
    	sendEvent(name: "windowShade", value: "open")
        return
    }
	sendTuyaCommand("0104", "00", "0100")
}

def pause() {
	log.info "pause()"
	sendTuyaCommand("0104", "00", "0101")
    
}

def setLevel(data, rate = null) {
	log.info "setLevel("+data+")"
    def currentLevel = device.currentValue("level")
    if (currentLevel == data) {
    	sendEvent(name: "level", value: currentLevel)
        //mc added
        sendEvent(name: "position", value: (level))
        //mc
        return
    }
	sendTuyaCommand("0202", "00", "04000000"+zigbee.convertToHexString(data, 2))
}

//mc new for HE Commands
def setPosition(position){
    log.info "setLevel("+$position+")"
    setLevel(data = position, rate = null)
}
//mc

/* mc not used as no preset tile/button
def presetPosition() {
    setLevel(preset ?: 50)
}
*/
def installed() {
	sendEvent(name: "supportedWindowShadeCommands", value: JsonOutput.toJson(["open", "close", "pause"]), displayed: false)
    
}

def updated() {
	def val = Direction
    sendEvent([name:"Direction", value: (val == "00" ? "Forward" : "Reverse")])    
	DirectionSet(val)
}	

def DirectionSet(Dval) {
	log.info "Dset(${Dval})"
   sendHubCommand(zigbee.command(CLUSTER_TUYA, SETDATA, "00" + zigbee.convertToHexString(rand(256), 2) + "05040001" + Dval))
}

def configure() {
	log.info "configure()"
}

private sendTuyaCommand(dp, fn, data) {
	log.info "${dp},${fn},${data}"
	zigbee.command(CLUSTER_TUYA, SETDATA, "00" + zigbee.convertToHexString(rand(256), 2) + dp + fn + data)
    /* if issue use this
    sendHubCommand(zigbee.command(CLUSTER_TUYA, SETDATA, "00" + zigbee.convertToHexString(rand(256), 2) + dp + fn + data))
    */
}

private rand(n) {
	return (new Random().nextInt(n))
}

The "preset" Preference you could use, I actually kind of like the idea, might add that to my curtain driver.

No, it is part of the Switch Level Capability, to sync these with a dimmer for example, that is something which is usually added for that pupose.

By adding the command presetPosition to the definition section. That way, you could call the command from either a voice command or from a button press which send that command using RM or some other app.

I have commands like "pause" and "presetPosition" linked to either a physical or virtual button. Voice commands would also work, of course. I don't use dashboards for my curtains, yet. I think it is just a slider for the position.

Yes, just confirmed, it is a slider for the position.

so no pause/stop button? that not a short coming of the dashboard?

No, no pause/stop, you set the position, that is all. I only have pause as a button on my wall controllers for my curtains. But there I don't have a slider, which I think I would have preferred if I could have as a wall controller, doubt that is doable. I have a dimmer "button" that I can turn (it is round) which I have planned to use with one of my curtains.

ok thanks @markus , cleaned some of my posts to bring it down

latest
/** zemismart
*  Tuya Window Shade (v.0.1.0) Hubitat v1
 *	Copyright 2020 iquix
 *
 *	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.
 This DTH is coded based on iquix's tuya-window-shade DTH.
 https://github.com/iquix/Smartthings/blob/master/devicetypes/iquix/tuya-window-shade.src/tuya-window-shade.groovy
 */

import groovy.json.JsonOutput
//mc // import physicalgraph.zigbee.zcl.DataType
import hubitat.zigbee.zcl.DataType

metadata {
	definition(name: "ZemiSmart Zigbee Blind", namespace: "ShinJjang", author: "ShinJjang-iquix", ocfDeviceType: "oic.d.blind", vid: "generic-shade") {
		capability "Actuator"
		capability "Configuration"
		capability "Window Shade"
// mc not supported in HE		capability "Window Shade Preset"
		capability "Switch Level"

		command "pause"
        command "presetPosition"
        
        attribute "Direction", "enum", ["Reverse","Forward"]

		fingerprint endpointId: "01", profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "0019", manufacturer: "_TYST11_wmcdj3aq", model: "mcdj3aq", deviceJoinName: "Zemismart Zigbee Blind"
//mc changeed endpointId from 0x01 to 01	
    }

	preferences {
        input "preset", "number", title: "Preset position", description: "Set the window shade preset position", defaultValue: 50, range: "0..100", required: false, displayDuringSetup: false
        input name: "Direction", type: "enum", title: "Direction Set", defaultValue: "00", options:["01": "Reverse", "00": "Forward"], displayDuringSetup: true
	}
// removed tiles section as not used in Hubitat
}

private getCLUSTER_TUYA() { 0xEF00 }
private getSETDATA() { 0x00 }

// Parse incoming device messages to generate events
def parse(String description) {
	if (description?.startsWith('catchall:') || description?.startsWith('read attr -')) {
		Map descMap = zigbee.parseDescriptionAsMap(description)        
		if (descMap?.clusterInt==CLUSTER_TUYA) {
        	log.debug descMap
			if ( descMap?.command == "01" || descMap?.command == "02" ) {
				def dp = zigbee.convertHexToInt(descMap?.data[3]+descMap?.data[2])
                log.debug "dp = " + dp
				switch (dp) {
					case 1025: // 0x04 0x01: Confirm opening/closing/stopping (triggered from Zigbee)
                    	def data = descMap.data[6]
                    	if (descMap.data[6] == "00") {
                        	log.debug "opening"
                            levelEventMoving(100)
                        } else if (descMap.data[6] == "02") {
                        	log.debug "colsing"
                            levelEventMoving(0)
                        }
                    	break
					case 1031: // 0x04 0x07: Confirm opening/closing/stopping (triggered from remote)
                    	def data = descMap.data[6]
                    	if (descMap.data[6] == "01") {
                        	log.debug "closing"
                            levelEventMoving(0)
                        } else if (descMap.data[6] == "00") {
                        	log.debug "opening"
                            levelEventMoving(100)
                        }
                    	break
					case 514: // 0x02 0x02: Started moving to position (triggered from Zigbee)
                    	def pos = zigbee.convertHexToInt(descMap.data[9])
						log.debug "moving to position :"+pos
                        levelEventMoving(pos)
                        break
					case 515: // 0x02 0x03: Arrived at position
                    	def pos = zigbee.convertHexToInt(descMap.data[9])
                    	log.debug "arrived at position :"+pos
                    	levelEventArrived(pos)
                        break
				}
			}
		}
	}
}

private levelEventMoving(currentLevel) {
	def lastLevel = device.currentValue("level")
	log.debug "levelEventMoving - currentLevel: ${currentLevel} lastLevel: ${lastLevel}"
	if (lastLevel == "undefined" || currentLevel == lastLevel) { //Ignore invalid reports
		log.debug "Ignore invalid reports"
	} else {
		if (lastLevel < currentLevel) {
			sendEvent([name:"windowShade", value: "opening"])
		} else if (lastLevel > currentLevel) {
			sendEvent([name:"windowShade", value: "closing"])
		}
    }
}

private levelEventArrived(level) {
	if (level == 0) {
    	sendEvent(name: "windowShade", value: "closed")
    } else if (level == 100) {
    	sendEvent(name: "windowShade", value: "open")
    } else if (level > 0 && level < 100) {
		sendEvent(name: "windowShade", value: "partially open")
    } else {
    	sendEvent(name: "windowShade", value: "unknown")
        return
    }
    sendEvent(name: "level", value: (level))
    //mc added
    sendEvent(name: "position", value: (level))
    //mc
}

def close() {
	log.info "close()"
	def currentLevel = device.currentValue("level")
    if (currentLevel == 0) {
    	sendEvent(name: "windowShade", value: "closed")
        return
    }
	sendTuyaCommand("0104", "00", "0102")
}

def open() {
	log.info "open()"
    def currentLevel = device.currentValue("level")
    if (currentLevel == 100) {
    	sendEvent(name: "windowShade", value: "open")
        return
    }
	sendTuyaCommand("0104", "00", "0100")
}

def pause() {
	log.info "pause()"
	sendTuyaCommand("0104", "00", "0101")
    
}

def setLevel(data, rate = null) {
	log.info "setLevel("+data+")"
    def currentLevel = device.currentValue("level")
    if (currentLevel == data) {
    	sendEvent(name: "level", value: currentLevel)
        //mc added
        sendEvent(name: "position", value: (level))
        //mc
        return
    }
	sendTuyaCommand("0202", "00", "04000000"+zigbee.convertToHexString(data, 2))
}

//mc new for HE Commands
def setPosition(position){
    log.info "setLevel("+$position+")"
    setLevel(data = position, rate = null)
}
//mc

//custom command
def presetPosition() {
    setLevel(preset ?: 50)
}

def installed() {
	sendEvent(name: "supportedWindowShadeCommands", value: JsonOutput.toJson(["open", "close", "pause"]), displayed: false)
    
}

def updated() {
	def val = Direction
    sendEvent([name:"Direction", value: (val == "00" ? "Forward" : "Reverse")])    
	DirectionSet(val)
}	

def DirectionSet(Dval) {
	log.info "Dset(${Dval})"
   sendHubCommand(zigbee.command(CLUSTER_TUYA, SETDATA, "00" + zigbee.convertToHexString(rand(256), 2) + "05040001" + Dval))
}

def configure() {
	log.info "configure()"
}

private sendTuyaCommand(dp, fn, data) {
	log.info "${dp},${fn},${data}"
	zigbee.command(CLUSTER_TUYA, SETDATA, "00" + zigbee.convertToHexString(rand(256), 2) + dp + fn + data)
    /* if issue use this
    sendHubCommand(zigbee.command(CLUSTER_TUYA, SETDATA, "00" + zigbee.convertToHexString(rand(256), 2) + dp + fn + data))
    */
}

private rand(n) {
	return (new Random().nextInt(n))
}
1 Like

Looks good, as long as there are no differences with byte-order between ST and HE (I don't have an ST), this looks like it will work.

bite order??? guess it wait and see on that

Yep, it is an easy fix if that is the case, you will know very quickly, no commands will work and status updates will not be correct/detected. It does look like it shouldn't be the case, so probably nothing to worry about.

Cool, thanks for all the help

1 Like