Help with Zigbee Power Outlet (Climax PSM-29ZBSR)


(Benjamin Ortuzar) #1

Hello,

I have a Climax PSM-29ZBSR Power Outlet that I have the basics working (on/off switch), but I havent been able to read the Energy Usage . I have programmed in other languages, but its my first ST device.

The Developer Documentation for the device can be found here (Clusters, attributes, etc):
https://fccid.io/pdf.php?id=2553357

Based on this documentation and my code posted below, I have a few questions.

  • Do I have the ClusterOut set correctly in the fingerprint based on the developer documentation. It also mentions 0x0A? I
  • Is the cluster out defined in fingerprint just used to identify the device, or is it also used to read from it later in the code.
  • How can I read the different attributes from the 0x0702 cluster. There are
    three diferent sets mentioned in the documentation (Reading information attribute set, format attribute set, historical attribute set) that have an identifier that is 0x00. I dont know how to get data from the different sets. How can I read them?

I have tried:

"st rattr 0x${device.deviceNetworkId} 1 0x0702 0"

That returns:

read attr - raw: 01E00107021400000025000000000000, dni: 01E0, endpoint: 01, cluster: 0702, size: 14, attrId: 0000, result: success, encoding: 25, value: 000000000000

This doesnt seem to provide the value of power consumtion.

I also have subscribed to cluster notifications with:

 "zdo bind 0x${device.deviceNetworkId} 1 1 0x0702 {${device.zigbeeId}} {}", "delay 500",

That returns information from attribute 0400 every 10 minutes as follows:

read attr - raw: 01E00107021E00042A880200000025000000000000, dni: 01E0, endpoint: 01, cluster: 0702, size: 1E, attrId: 0400, encoding: 2A, value: 000000000000250000000288

I am not sure if the information is in that value, or how to transform it.

Here is my complete code:

    metadata {
	definition (name: "Ozom Outlet", namespace: "bortuzar", author: "bortuzar") {
	
    	capability "Actuator"
        capability "Configuration"
        capability "Refresh"
		capability "Sensor"
        capability "Switch"
		//capability "Switch Level"
        capability "Energy Meter"

		fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0702", outClusters: "0009"
	}
    
   

	// 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"
	}

	// UI tile definitions
	tiles {
		standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
			state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
			state "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
			state "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
			state "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
		}
		standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
			state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
		}
		/* controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 3, inactiveLabel: false) {
			state "level", action:"switch level.setLevel"
		} */
		valueTile("power", "device.power", inactiveLabel: false, decoration: "flat") {
			state "power", label: 'Power ${currentValue} W'
		}
		
		main(["switch"])
		details(["switch", "power", "refresh"])
	}
}

	

// Parse incoming device messages to generate events
/*
def parse(String description) {

	log.trace description
    
	if (description?.startsWith("catchall:")) {
		
        def msg = zigbee.parse(description)
		log.trace msg
		log.trace "data: $msg.data"
        
        if(description?.endsWith("0100"))
        {
        	def result = createEvent(name: "switch", value: "on")
            log.debug "Parse returned ${result?.descriptionText}"
            return result
        }
        if(description?.endsWith("0000"))
        {
        	def result = createEvent(name: "switch", value: "off")
            log.debug "Parse returned ${result?.descriptionText}"
            return result
        }
	}
    if (description?.startsWith("read attr")) {
    	log.debug "READ ATTR "
        
        def descMap = parseDescriptionAsMap(description)
		log.debug "Read attr: $description"
        
        log.debug description[-2..-1]
        
        //so it only parses numeric values.Needs to be improved
        if(description[-2..-1] != "tr")
        {
         //def i = Math.round(convertHexToInt(description[-2..-1]) / 256 * 100 )
        def i = Math.round(convertHexToInt(description[-2..-1]) * 100 )
        
		sendEvent( name: "level", value: i )
        }
       
    }
    
	
}
*/

// Parse incoming device messages to generate events
def parse(String description) {
	log.debug "PARSE METHOD Start"
	log.trace description
    /*
	if (description?.startsWith("catchall:")) {
		def msg = zigbee.parse(description)
		log.trace "data: $msg.data"

	} else if (description?.startsWith("read attr -")) {
		def descMap = parseDescriptionAsMap(description)
        log.debug descMap
		log.debug "Read attr: $description"
    } else if (descMap.cluster == "0006" && descMap.attrId == "0000") {
			name = "switch"
			value = descMap.value.endsWith("01") ? "on" : "off"
            def result = createEvent(name: name, value: value)
            log.debug "Parse returned ${result?.descriptionText}"
            return result
    } else if(descMap.cluster =="0702" && descMap.attrId == "0x00") {
        def value = convertHexToInt(descMap.value)/10 
        //Dividing by 10 as the Divisor is 10000 and unit is kW for the device. AttrId: 0302 and 0300. Simplifying to 10
        log.debug value
        def name = "power"
        def result = createEvent(name: name, value: value)
        log.debug "Parse returned ${result?.descriptionText}"
        return result //https://graph.api.smartthings.com/ide/device/editor/f64c2963-da1e-4ce3-8263-c9922e506c9c#
	
    } else {
		def name = description?.startsWith("on/off: ") ? "switch" : null
		def value = name == "switch" ? (description?.endsWith(" 1") ? "on" : "off") : null
		def result = createEvent(name: name, value: value)
		log.debug "Parse returned ${result?.descriptionText}"
		return result
	}
    
    */
}

def on() {
	log.debug "on()"
	sendEvent(name: "switch", value: "on")
	"st cmd 0x${device.deviceNetworkId} 1 6 1 {}"
}

def off() {
	log.debug "off()"
	sendEvent(name: "switch", value: "off")
	"st cmd 0x${device.deviceNetworkId} 1 6 0 {}"
}

def refresh() {

  	log.debug "Refresh Start";	
  
    [
		//"st rattr 0x${device.deviceNetworkId} 1 6 0", "delay 500",    
        //"st rattr 0x${device.deviceNetworkId} 1 0x0702 0x00", "delay 500" 
	]
    
    log.debug "Refresh End";
}

/*
def setLevel(value) {
	log.trace "setLevel($value)"
	def cmds = []

	if (value == 0) {
		sendEvent(name: "switch", value: "off")
		cmds << "st cmd 0x${device.deviceNetworkId} 1 8 0 {0000 0000}"
	}
	else if (device.latestValue("switch") == "off") {
		sendEvent(name: "switch", value: "on")
	}

	sendEvent(name: "level", value: value)
    def level = hexString(Math.round(value * 255/100))
	cmds << "st cmd 0x${device.deviceNetworkId} 1 8 4 {${level} 0000}"

	//log.debug cmds
	cmds
}
*/

def configure() {

	String zigbeeId = swapEndianHex(device.hub.zigbeeId)
	log.debug "Confuguring Reporting and Bindings."
	def configCmds = [	
  
        //Switch Reporting
        "zcl global send-me-a-report 6 0 0x10 0 3600 {01}", "delay 500",
        "send 0x${device.deviceNetworkId} 1 1", "delay 1000",
        
        //Level Control Reporting
        "zcl global send-me-a-report 8 0 0x20 5 3600 {0010}", "delay 200",
        "send 0x${device.deviceNetworkId} 1 1", "delay 1500",
        
        "zdo bind 0x${device.deviceNetworkId} 1 1 6 {${device.zigbeeId}} {}", "delay 1000",
		"zdo bind 0x${device.deviceNetworkId} 1 1 8 {${device.zigbeeId}} {}", "delay 500",
        
        //"zdo bind 0x${device.deviceNetworkId} 1 1 702 {${device.zigbeeId}} {}", "delay 500",
        
        "zdo bind 0x${device.deviceNetworkId} 1 1 0x0702 {${device.zigbeeId}} {}", "delay 500",
        
        //"st rattr cmd 0x${device.deviceNetworkId} ${endpointId} 8 0 {}"
        "st rattr 0x${device.deviceNetworkId} 1 0x0702 0"
        
	]
    return configCmds + refresh() // send refresh cmds as part of config
}





def parseDescriptionAsMap(description) {
	(description - "read attr - ").split(",").inject([:]) { map, param ->
		def nameAndValue = param.split(":")
		map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
	}
}

private hex(value, width=2) {
	def s = new BigInteger(Math.round(value).toString()).toString(16)
	while (s.size() < width) {
		s = "0" + s
	}
	s
}

private Integer convertHexToInt(hex) {
	Integer.parseInt(hex,16)
}


private String swapEndianHex(String hex) {
    reverseArray(hex.decodeHex()).encodeHex()
}



private byte[] reverseArray(byte[] array) {
    int i = 0;
    int j = array.length - 1;
    byte tmp;
    while (j > i) {
        tmp = array[j];
        array[j] = array[i];
        array[i] = tmp;
        j--;
        i++;
    }
    return array
}

Any help woud be appreciated on how to get this working.

Thanks,
Benjamin


SmartThings Hub with Ozom products?
Connecting Climax Power Outlets PSM-VS and PSD-29ZBS
(ActionTiles.com co-founder Terry @ActionTiles; GitHub: @cosmicpuppy) #2

Does the code for the CentraLite “SmartPower Outlet” (in the IDE SmartDevice Type Templates) help at all? It has powe measurement, so it may even use same clusters…

/**
 *	CentraLite Switch
 *
 *	Author: SmartThings
 *	Date: 2013-12-02
 */
metadata {
	// Automatically generated. Make future change here.
	definition (name: "SmartPower Outlet", namespace: "smartthings", author: "SmartThings") {
		capability "Actuator"
		capability "Switch"
		capability "Power Meter"
		capability "Configuration"
		capability "Refresh"
		capability "Sensor"

		fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019"
	}

	// 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"
	}

	// UI tile definitions
	tiles {
		standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
			state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
			state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821"
		}
		valueTile("power", "device.power", decoration: "flat") {
			state "power", label:'${currentValue} W'
		}
		standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat") {
			state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
		}

		main "switch"
		details(["switch","power","refresh"])
	}
}

Zigbee Energy Meter
(Benjamin Ortuzar) #3

I think I got it all working:

It recognizes the fingerprint.
Runs the configure command when adding the device.
Gets notifications on power changes

Here is the code:

    /**
 *  Climax PSM-29ZBSR - Zigbee - Power Outlet Switch and Energy Meter
 *
 *  Copyright 2015 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.
 *
 *  Thanks to SmartThings and its community!
 *
 */
metadata {
	definition (name: "Climax Power Outlet", namespace: "bortuzar", author: "bortuzar") {
	
    	capability "Actuator"
        capability "Configuration"
        capability "Refresh"
		capability "Sensor"
        capability "Switch"
        capability "Power Meter"

		fingerprint endpointId: "0x0A", profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0702", outClusters: "0000"
	}
    

	// 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"
	}

	// UI tile definitions
	tiles {
		standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
			state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
			state "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
			state "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
			state "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
		}
		standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
			state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
		}
		valueTile("power", "device.power", inactiveLabel: false, decoration: "flat") {
			state "power", label: '${currentValue} W'
		}
        
        //standardTile("configure", "device.power", inactiveLabel: false, decoration: "flat") {
		//	state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
		//}

		
		main(["switch", "power"])
		details(["switch", "power", "refresh"])
	}
}


// Parse incoming device messages to generate events
def parse(String description) {

	log.debug "Parse Method Called"
	log.trace description
    
	if (description?.startsWith("catchall:")) {
		def msg = zigbee.parse(description)
		log.trace "data: $msg.data"

	} else if (description?.startsWith("read attr -")) {
		def descMap = parseDescriptionAsMap(description)
        log.debug "Desc Map:"
        log.debug descMap
		
        if (descMap.cluster == "0006" && descMap.attrId == "0000") {
                name = "switch"
                value = descMap.value.endsWith("01") ? "on" : "off"
                def result = createEvent(name: name, value: value)
                log.debug "Parse returned ${result?.descriptionText}"
                return result
        } else if(descMap.cluster =="0702" && descMap.attrId == "0400") {
            
            def value = convertHexToInt(descMap.value[-4..-1])/10 
            // Reading the last 4 characters of the string...Maybe 4 are needed. Needs further test.
            // Dividing by 10 as the Divisor is 10000 and unit is kW for the device. AttrId: 0302 and 0300. Simplifying to 10
            log.debug value
            def name = "power"
            def result = createEvent(name: name, value: value)
            log.debug "Parse returned ${result?.descriptionText}"
            return result 
        }
    } else {
		def name = description?.startsWith("on/off: ") ? "switch" : null
		def value = name == "switch" ? (description?.endsWith(" 1") ? "on" : "off") : null
		def result = createEvent(name: name, value: value)
		log.debug "Parse returned ${result?.descriptionText}"
		return result
	}
}

def on() {
	log.debug "On Method called"
	sendEvent(name: "switch", value: "on")
	"st cmd 0x${device.deviceNetworkId} 1 6 1 {}"
}

def off() {
	log.debug "Off Method called"
	sendEvent(name: "switch", value: "off")
	"st cmd 0x${device.deviceNetworkId} 1 6 0 {}"
}

def refresh() {
  	log.debug "Refresh Method called";	
    [
		"st rattr 0x${device.deviceNetworkId} 1 6 0", "delay 500",    
        "st rattr 0x${device.deviceNetworkId} 1 0x0702 1024", , "delay 500"
	]
}


def configure() {

	log.debug "Configure Method called"

	String zigbeeId = swapEndianHex(device.hub.zigbeeId)
	
	def configCmds = [	
  
        //Switch Reporting
        "zcl global send-me-a-report 6 0 0x10 0 3600 {01}", "delay 500",
        "send 0x${device.deviceNetworkId} 1 1", "delay 1000",
        
        //bing to cluster 0x006. Switch On-Off
        "zdo bind 0x${device.deviceNetworkId} 1 1 6 {${device.zigbeeId}} {}", "delay 1000",
        
        //bind to cluster 0x702. Power Consumption
        "zdo bind 0x${device.deviceNetworkId} 1 1 0x0702 {${device.zigbeeId}} {}", "delay 500",
        
        //read attribute 1024Dec/400Hex.
        //"st rattr 0x${device.deviceNetworkId} 1 0x0702 1024"
        
	]
    return configCmds + refresh() // send refresh cmds as part of config
}


def parseDescriptionAsMap(description) {
	(description - "read attr - ").split(",").inject([:]) { map, param ->
		def nameAndValue = param.split(":")
		map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
	}
}

private hex(value, width=2) {
	def s = new BigInteger(Math.round(value).toString()).toString(16)
	while (s.size() < width) {
		s = "0" + s
	}
	s
}

private Integer convertHexToInt(hex) {
	Integer.parseInt(hex,16)
}


private String swapEndianHex(String hex) {
    reverseArray(hex.decodeHex()).encodeHex()
}



private byte[] reverseArray(byte[] array) {
    int i = 0;
    int j = array.length - 1;
    byte tmp;
    while (j > i) {
        tmp = array[j];
        array[j] = array[i];
        array[i] = tmp;
        j--;
        i++;
    }
    return array
}

If someone can answer my original questions it would be great. Even if I got this fully working I still would like to know about this:

  • Do I have the ClusterOut set correctly in the fingerprint based on the developer documentation? I think its not 100% right.
  • Is the cluster out defined in fingerprint just used to identify the device, or is it also used to read from it later in the code.
  • How can I read the different attributes from the 0x0702 cluster. There are
    three diferent sets mentioned in the documentation (Reading information attribute set, format attribute set, historical attribute set) that have an identifier that is 0x00. I dont know how to get data from the different sets. How can I read them?

Thanks,
Benjamin


(John Rucker) #4

Your footprint looks right according to the documentation. At least that is how I would set it up. A “in cluster” is a server cluster and your device is a server for 6 clusters and according to the documentation no “out clusters” or client clusters. The best thing to do is watch your hub’s log when the device connects. The log will show you what clusters the device responds with when queried by the SmartThings hub. The foot printing process is only used to identify the device and match it to a custom device type. You don’t have to have all the clusters your going to use listed out in the footprint.

In regards to the 0x702 cluster I think their documentation is misleading. Cluster attribute numbers are 16 bit values often listed in hex format 0x0001. They list the correct attribute numbers for all the clusters on the top half of the page but the bottom half (Metering cluster) they are only listing them as 8 bit values. I think they did this because the metering cluster’s attributes are divide into groups and they use the first byte of the attribute number to represent the grouping. You can get more information by reading the ZigBee Smart Energy Profile Specification Document 075356r16ZB on page 160. https://docs.zigbee.org/zigbee-docs/dcn/08/docs-08-4956-05-0zse-smart-energy-specification-package.pdf

I bet the attribute you will want to read is the InstantaneousDemand attribute which is 0x0400 they list is as 0x00 because they left the grouping number 0x04 off. The other attributes tell you how to interpret the the value of the instantaneous Demand attribute it is all laid-out in the documentation I referenced above. Those attributes will tell you if their is a multiplier, reporting in kilowatts or watts etc…


(Benjamin Ortuzar) #5

John,

Thanks very much for your reply. It is much more clear for me now.
Is there any website where I can learn a bit more about 8, 16 bit values, hex formats like 0x0001 and such things. Its not something I am familiar with as a programmer.

Many thanks,
Benjamin


(Morten) #6

Hi

It looks very interesting :slight_smile:

Where can I buy the Climax PSM-29ZBSR modul?

Best regard


(Benjamin Ortuzar) #7

I bought it in Chile. Its sold under the Ozom brand.


(Johan) #8

Hi Benjamin
I have just started out with SmartThings and I have some Climax Power outlets PSM-VS (2,4G) and some PSD-29ZBS (2,4G) cant get them connect to my Samsung SmartThing Hub (UK version) do you have ideas what I shall look for
best regards Johan


(Benjamin Ortuzar) #9

Hi Johan,

Can yo describe me the steps you are following, so I know if you skipped
something.

Are you adding my code in the IDE?
Did you manually change the thing device type to the device type created
with my code?

Where did you buy the outlets?

Thanks,
Benjamín


(Johan) #10

Hi Benjamin
i just copied your code to my smartThing account “create new device handler” and after that just published it "for me"
I have not added it to IDE
No I have not done any changes
My outlets was supplied by a Compony in Sweden


(Benjamin Ortuzar) #11

Ok. I think you are missing the following:

In the SmartThings web portal, go to “my devices”, click on your device
link, press the edit button, and change the “type” to the device handler
you created with the custom code.

Good luck!


(Johan) #12

Hi again Benjamin
My problem is that a cant see it my divice list so I cant change anyting
I Think ist some connecting problem


(Benjamin Ortuzar) #13

Do you see it listed as “Thing”? You would need to see it listed as "thing"
first.


(Johan) #14

No its not listed in my device/thing list
My PS is labeled ZB 2,4G so I Think they are OK could there be somthing with the ID signal.
It seems my Samsung SmarthThing Hub UK version don’t recognize them


(Benjamin Ortuzar) #15

I dont have experience on that. I am sorry. Maybe someone else can assist
that specific issue, before you continue configuring the device. All the
best.


(Luis Molina) #16

Hi Benja,

Could you explain in detail how you have connected Climax outlet to smartthigs hub, i mean have you pressed the outlet button?? how many seconds? in smartthings app how you add this device as “thing”? looking for other zigbee device??

Thanks in advance!

BR