Zigbee gas detector for kitchen

I’m just getting started with ST (have v2 on pre-order) But one of the things I want to monitor is the gas stove. Recently had a burner accidentally turned and came home to find house filled with gas, not good!

Anyway, in searching, I did find a Zigbee connected Sensor, found here: http://www.shop-wifi.com/wzb-sgs920-zigbee-wireless-natural-gaslpg-detector

Any way to know how hard it might be to get to work with ST?

I suppose another alternative might be to wire up a simple monoprice z-wave open/close switch to a non-smart detector, but that seems like it might be more work, not sure.

1 Like

From the link, specs say:

ZigBee HA profile compliant

So… that makes it, like, 98% chance it can be integrated; it just takes some poking around the clusters … there’s folks in the Community who have done this with a few Devices, some more complex than others.

It would be great if the vendor or manufacturer saw the value in ST integration and did or sponsored the effort.

Well, it doesn’t look like it initially, but it does ship over from Taiwan or somewhere over there, so not sure we could even figure out who the actual manufacturer is.

Unfortunately, they only sell in a 2 pack… number two I am not too familiar with writing code, so I might have to wait until somebody takes on the project :smile:

This would be very cool. Replying to subscribe!

Well if you do get one we will be happy to help. I have created several custom device types for SmartThings (ZigBee only). The cool thing about ZigBee devices is you can query them for their supported clusters. If they us public clusters (good chance they do) you can look up the commands to talk to that clusters and your off and running.

3 Likes

I may go ahead and order them. If we get them working, I suppose I could put one in the kitchen and one in the utility room where the gas dryer, water heater and furnace are…or sell the second one :smile:

I’m not one to shy away from a DIY solution, I just know that even though I am a PC whiz… I detest programming, Hah! Guess if I want to get the most out of SmartThings (and likely any system) I need to learn anyway.

@ColinTrost I would be happy to buy one off of you.

When you get one you will need to log into your SmartThings Dev environment and watch your hub’s log while the device connects. During the connection process the hub will query the sensor and log its supported clusters. Once we have the cluster list I will look them up and see what we need to do next. The devices documentation may also have information on supported clusters.

1 Like

Now let’s cross our fingers that the Pre-orders for the v2 Hub get here in September like they’re supposed to. Anticipation was easy enough to keep in check while I was waiting for v2 to be released, now it will be a struggle :smile:

So I did some more digging and found the manufacturer of the sensor, it’s Nietzsche (not that it means much, but it helps) I decided to send an email and inquire about the supported clusters, etc. Since they’re a Taiwanese company, there might be a bit of a language barrier, but I exchanged a couple of emails with a person, and I’m not so sure they will be much help. Once I got them to understand that I wasn’t a distributor wanting to sell them, They started talking about non-disclosure agreements, etc before discussing any of the supported clusters, etc. We’ll see what I get back, I guess.

Language barrier aside, got some info. Looks like this device uses HA IAS Zone Device Cluster ID 0x0500. Of course there are probably some more clusters dealing with generic device/mfg info, but operationally, it’s on that one, which from what I can see is fairly standard. Now we’ll see about getting my hands on one. (there’s this one and it’s newer model, but I haven’t found a retail source for the new model)

2 Likes

Well, I went ahead and bought a pair of these. I must really have wanted them, though. after item cost and shipping from China, they were just over 80…EACH. I must be nuts, lol. - and that doesn’t include the fact that I am going to have to figure out power supply… they need 12v DC. Hoping there is enough room in the case for an 123A cell. At least the v2 hub should be here before they get here from China.

1 Like

Ugh. And so goes ordering from China. After placing the order, took a week to finally get a response from the company, who apparently tried to email me and the mail got blocked by my spam filters. They cancelled the order and gave me a refund because they couldn’t reach me. So, now a second email address, a new order, to find out that it will be 2-3 weeks before they have any stock to ship to me. Hooray for waiting! :smiley:

1 Like

OK! Device(s) were delivered today… now going to need lots of help figuring out how to get them to work in ST.

I pulled some stuff from the Hub log, where do I go from here?

This is from the log:

Click on Devices in the IDE and click the gas sensor. Should bring you to a screen with info like name, label, etc(where you would manually select a devicetype if you’ve done that).

A little ways down is a line called “raw description” and it will probably have a mix of 2 and 4 character blocks. Copy that here. That will tell us the supported clusters and some other info that we can use to get started.

Isn’t that the rawDescription line in the screenshot above?

Though, it does seem a bit different:

02 0104 0402 00 02 0000 0500 02 0500 0001

That’s the one. The raw in your screen is a message, but this tells us the endpoint, profile, and clusters.

Looks like this just implements the IAS zone cluster. We’ll need to get it enrolled with the hub. A good starting device would be the Smartsense Open-Closed but we can strip it way down (no battery or temp), and I think we should implement it as a smoke detector for coordinating with other apps.

Sorry, I’m only on my iPad which makes coding tough, but I can mock up the starting point tomorrow. Maybe @JohnR can help in the meantime.

2 Likes

Guys it will be a day or two before I can spend any real time on this. I haven’t worked with cluster 0x0500 so I will need to get up to speed first. Let me do some reading and I will get back with you.

2 Likes

Here’s a first pass. You may need to reset it and re-pair after publishing this devicetype so that it can properly go through the IAS enrollment process. That process binds the device to the hub as a zone alarm. I’m not 100% confident in the parsing of the various alarm messages, so if you can test it while live logging: both via the device’s test procedure and for real somehow (safely if you can). We can double check the various messages we receive to make sure we’re identifying them properly.

/**
 *  WZB Natural Gas Sensor
 *
 *  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: "WZB Natural Gas", namespace: "smartthings", author: "SmartThings") {
		
        capability "Configuration"
        capability "Smoke Detector"
        capability "Sensor"
        
        command "enrollResponse"
 
 
		fingerprint profileID: "0104", deviceID: "0402", inClusters: "0000,0500", outClusters: "0500,0001"
        
}
 
	simulator {
 
	}

	preferences {
		input description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
		input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
	}
 
	tiles {
    	standardTile("smoke", "device.smoke", width: 2, height: 2) {
			state("clear", label:"clear", icon:"st.alarm.smoke.clear", backgroundColor:"#ffffff")
			state("detected", label:"SMOKE", icon:"st.alarm.smoke.smoke", backgroundColor:"#e86d13")
		}
        
        standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat") {
			state "default", action:"configuration.configure", icon:"st.secondary.configure"
		}
 
		main (["smoke"])
		details(["smoke","configure"])
	}
}
 
def parse(String description) {
	log.debug "description: $description"
    
	Map map = [:]
	if (description?.startsWith('catchall:')) {
		map = parseCatchAllMessage(description)
	}
	else if (description?.startsWith('read attr -')) {
		map = parseReportAttributeMessage(description)
	}
    else if (description?.startsWith('zone status')) {
    	map = parseIasMessage(description)
    }
 
	log.debug "Parse returned $map"
	def result = map ? createEvent(map) : null
    
    if (description?.startsWith('enroll request')) {
    	List cmds = enrollResponse()
        log.debug "enroll response: ${cmds}"
        result = cmds?.collect { new physicalgraph.device.HubAction(it) }
    }
    return result
}
 
private Map parseCatchAllMessage(String description) {
    Map resultMap = [:]
    def cluster = zigbee.parse(description)
    if (shouldProcessMessage(cluster)) {
        log.debug "Parse $cluster"
    }

    return resultMap
}

private boolean shouldProcessMessage(cluster) {
    // 0x0B is default response indicating message got through
    // 0x07 is bind message
    boolean ignoredMessage = cluster.profileId != 0x0104 || 
        cluster.command == 0x0B ||
        cluster.command == 0x07 ||
        (cluster.data.size() > 0 && cluster.data.first() == 0x3e)
    return !ignoredMessage
}

 
private Map parseReportAttributeMessage(String description) {
	Map descMap = (description - "read attr - ").split(",").inject([:]) { map, param ->
		def nameAndValue = param.split(":")
		map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
	}
	log.debug "Desc Map: $descMap"
 
}
 


private Map parseIasMessage(String description) {
    List parsedMsg = description.split(' ')
    String msgCode = parsedMsg[2]
    
    Map resultMap = [:]
    switch(msgCode) {
        case '0x0020': // Clear
        	resultMap = getSmokeResult('clear')
            break

        case '0x0021': // Smoke
        	resultMap = getSmokeResult('detected')
            break

        case '0x0022': // Tamper Alarm
            break

        case '0x0023': // Battery Alarm
            break

        case '0x0024': // Supervision Report
        	resultMap = getSmokeResult('clear')
            break

        case '0x0025': // Restore Report
        	resultMap = getSmokeResult('detected')
            break

        case '0x0026': // Trouble/Failure
            break

        case '0x0028': // Test Mode
            break
    }
    return resultMap
}


private Map getSmokeResult(value) {
	log.debug 'Gas Status'
	def linkText = getLinkText(device)
	def descriptionText = "${linkText} is ${value == 'detected' ? 'detected' : 'clear'}"
	return [
		name: 'smoke',
		value: value,
		descriptionText: descriptionText
	]
}


def configure() {

	String zigbeeId = swapEndianHex(device.hub.zigbeeId)
	log.debug "Confuguring Reporting, IAS CIE, and Bindings."
	def configCmds = [
		"zcl global write 0x500 0x10 0xf0 {${zigbeeId}}", "delay 200",
		"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500",
        
        //"raw 0x500 {01 23 00 00 00}", "delay 200",
        //"send 0x${device.deviceNetworkId} 1 1", "delay 1500",
        
	]
    log.debug "configure: Write IAS CIE"
    return configCmds // send refresh cmds as part of config
}

def enrollResponse() {
	log.debug "Sending enroll response"
    [	
    	
	"raw 0x500 {01 23 00 00 00}", "delay 200",
    "send 0x${device.deviceNetworkId} 1 ${endpointId}"
        
    ]
}
private hex(value) {
	new BigInteger(Math.round(value).toString()).toString(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
}
5 Likes

I’ll try it when I get home. Triggering it is pretty easy, it does react to most combustible gasses, so an unlit butane lighter works well for testing