Driver request: Ecolink Firefighter (zigbee)

Driver gurus... looking for a driver for the zigbee version of the Ecolink Firefighter. Model: FFZB1-ECO @ Amazon



I found this in the ST repo... this will be my first try at messing with a driver lol...

 *  Zigbee Sound Sensor
 *  Copyright 2018 Samsung 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:
 *  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.

import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
import physicalgraph.zigbee.zcl.DataType

metadata {
	definition(name: "ZigBee Sound Sensor", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "") {
		capability "Battery"
		capability "Configuration"
		capability "Health Check"
		capability "Refresh"
		capability "Sensor"
		capability "Sound Sensor"
		capability "Temperature Measurement"

		fingerprint profileId: "0104", inClusters: "0000,0001,0003,0020,0402,0500,0B05", outClusters: "0019", manufacturer: "Ecolink", model: "FFZB1-SM-ECO", deviceJoinName: "Ecolink Sound Sensor" //Ecolink Firefighter

	tiles(scale: 2) {
		multiAttributeTile(name:"sound", type: "lighting", width: 6, height: 4) {
			tileAttribute ("device.sound", key: "PRIMARY_CONTROL") {
				attributeState("not detected", label:'${name}', icon:"st.alarm.smoke.clear", backgroundColor:"#ffffff")
				attributeState("detected", label:'${name}', icon:"st.alarm.smoke.smoke", backgroundColor:"#e86d13")
		valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
			state "battery", label:'${currentValue}% battery', unit:""
		valueTile("temperature", "device.temperature", width: 2, height: 2) {
			state("temperature", label: '${currentValue}°',
					backgroundColors: [
							[value: 31, color: "#153591"],
							[value: 44, color: "#1e9cbb"],
							[value: 59, color: "#90d2a7"],
							[value: 74, color: "#44b621"],
							[value: 84, color: "#f1d801"],
							[value: 95, color: "#d04e00"],
							[value: 96, color: "#bc2323"]

		main "sound"
		details(["sound", "battery", "temperature"])

private getPOLL_CONTROL_CLUSTER() { 0x0020 }
private getFAST_POLL_TIMEOUT_ATTR() { 0x0003 }
private getCHECK_IN_INTERVAL_ATTR() { 0x0000 }
private getBATTERY_VOLTAGE_VALUE() { 0x0020 }
private getTEMPERATURE_MEASURE_VALUE() { 0x0000 }
private getSET_LONG_POLL_INTERVAL_CMD() { 0x02 }
private getSET_SHORT_POLL_INTERVAL_CMD() { 0x03 }
private getCHECK_IN_INTERVAL_CMD() { 0x00 }

def installed() {
	sendEvent(name: "sound", value: "not detected", displayed: false)

def parse(String description) {
	def map = zigbee.getEvent(description)

	if(!map) {
		if(isZoneMessage(description)) {
			map = parseIasMessage(description)
		} else {
			map = parseAttrMessage(description)
	} else if ( == "temperature") {
		if (tempOffset) {
			map.value = new BigDecimal((map.value as float) + (tempOffset as float)).setScale(1, BigDecimal.ROUND_HALF_UP)
		map.descriptionText = temperatureScale == 'C' ? "${device.displayName} was ${map.value}°C" : "${device.displayName} was ${map.value}°F"
		map.translatable = true

	def result = map ? createEvent(map) : [:]

	if (description?.startsWith('enroll request')) {
		def cmds = zigbee.enrollResponse()
		log.debug "enroll response: ${cmds}"
		result = cmds?.collect { new physicalgraph.device.HubAction(it)}
	return result

private Map parseIasMessage(String description) {
	ZoneStatus zs = zigbee.parseZoneStatus(description)
	def result = [:]
	if(zs.isAlarm1Set() || zs.isAlarm2Set()) {
		result = getSoundDetectionResult("detected")
	} else if(!zs.isTamperSet()) {
		result = getSoundDetectionResult("not detected")
	} else {
		result = [displayed: true, descriptionText: "${device.displayName}'s case is opened"]
		sendHubCommand zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, BATTERY_VOLTAGE_VALUE)

	return result

private Map parseAttrMessage(description) {
	def descMap = zigbee.parseDescriptionAsMap(description)
	def map = [:]
	if(descMap?.clusterInt == zigbee.POWER_CONFIGURATION_CLUSTER && descMap.commandInt != 0x07 && descMap?.value) {
		map = getBatteryPercentageResult(Integer.parseInt(descMap.value, 16))
	} else if(descMap?.clusterInt == zigbee.TEMPERATURE_MEASUREMENT_CLUSTER && descMap.commandInt == 0x07) {
		if ([0] == "00") {
		} else {
			log.warn "TEMP REPORTING CONFIG FAILED - error code: ${[0]}"
	} else if(descMap.clusterInt == POLL_CONTROL_CLUSTER && descMap.commandInt == CHECK_IN_INTERVAL_CMD) {

	return map

private Map getBatteryPercentageResult(rawValue) {
	def result = [:]
	def volts = rawValue / 10
	if (!(rawValue == 0 || rawValue == 255)) {
		def minVolts = 2.2
		def maxVolts = 3.0
		def pct = (volts - minVolts) / (maxVolts - minVolts)
		def roundedPct = Math.round(pct * 100)
		if (roundedPct <= 0) {
			roundedPct = 1
		result.value = Math.min(100, roundedPct)
	} = 'battery'
	result.translatable = true
	result.descriptionText = "${device.displayName} battery was ${result.value}%"
	return result

private Map getSoundDetectionResult(value) {
	def text = "Sound was ${value}"
	def result = [name: "sound", value: value, descriptionText: text, displayed: true]
	return result

private sendCheckIntervalEvent() {
	sendEvent(name: "checkInterval", value: 60 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"])

def ping() {

def refresh() {
			zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS)

def configure() {

	//send zone enroll response, configure short and long poll, fast poll timeout and check in interval
	def enrollCmds = (zigbee.command(POLL_CONTROL_CLUSTER, SET_LONG_POLL_INTERVAL_CMD, "B0040000") + zigbee.command(POLL_CONTROL_CLUSTER, SET_SHORT_POLL_INTERVAL_CMD, "0200") +
			zigbee.writeAttribute(POLL_CONTROL_CLUSTER, FAST_POLL_TIMEOUT_ATTR, DataType.UINT16, 0x0028) + zigbee.writeAttribute(POLL_CONTROL_CLUSTER, CHECK_IN_INTERVAL_ATTR, DataType.UINT32, 0x00001950))

	//send enroll commands, configures battery reporting to happen every 30 minutes, create binding for check in attribute so check ins will occur
	return zigbee.enrollResponse() + zigbee.configureReporting(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS, DataType.BITMAP16, 30, 60 * 30, null) + zigbee.batteryConfig(60 * 30, 60 * 30 + 1) + zigbee.temperatureConfig(60 * 30, 60 * 30 + 1) + zigbee.configureReporting(POLL_CONTROL_CLUSTER, CHECK_IN_INTERVAL_ATTR, DataType.UINT32, 0, 3600, null) + refresh() + enrollCmds

private boolean isZoneMessage(description) {
	return (description?.startsWith('zone status') || description?.startsWith('zone report'))

Hi there!

Did you manage to make this thing working? I tried the smartthings driver (with modifications) and it throw an error about "zigbee.ATTRIBUTE_IAS_ZONE_STATUS" and thus failing.

Sorry, no, I gave up on it and just exchanged it for the Zwave version. I couldn't wait for HE to include native driver. I figured they had hands full with the C7 issues.

Now that the 2.2.4 firmware solved the problem with the aeotec doorbell, I'm left with this device which does not work on hubitat. Is there anyone who could help with that zigbee version of the ecolink firefighter?

I hate having to recommission my ST hub just for that one device.

Maybe @mike.maxwell can have a look at it, probably need some signature info from you.

+1 for a driver to get the zigbee version working with HE.

@mike.maxwell any chance in the near future?


1 Like

well i just ordered one, we'll see what happens, should be straight forward...


So I got the thing included, got tamper working, it reports battery, temperature, however I'm unable to get it to trip using any of my smoke alarm test signals, the led on the device will flash red, but will not stay solid, therefore I'm unable to verify the frame data send when it alarms...
Is someone out there is able to get theirs to trip when testing let me know and I'll send you the driver to test with.


I just got one and have put it on the ceiling. I can test it. When it hears a smoke chime pattern, the LED on the device should illuminate and stay on, is that right?

That's what the funny papers say, they don't however say for how long...
Mine will just blink on occasion when the detector alarms.

OK lemme see what mine does. I’ve also had a z-wave version working great for like a year. Can’t say I’ve ever actually watched it for what the LED does while testing the smoke detectors, though.

1 Like

When it hears the smoke detector go off, the LED blinks.

Happy to try out a driver for testing if that’s helpful. Feel free to PM.

1 Like

@mike.maxwell Did you ever get this working? I was just about to buy one.

I have not been able to verify the alarm codes sent, so no.
I'm not going to publish a driver for it since I'm unable verify the data produced when it alarms.
I've had no success in triggering the thing...


Thanks for the update, I really appreciate.

That’s so weird, because the zwave version is so well-behaved.

Yup it’s frustrating. I never had any problems with the z-wave sensor, though I was interested in adding a few more of the zigbee version for my new house.

It’s back to the z-wave sensor for me, though.

1 Like

Mine was working fine with smartthings though. When I switch to hubitat I changed it for the zwave version and gave it away with my ST hub.

Sorry to drag this back up, but +1 frustrated purchaser of the zigbee model.

After reading through this, I tested mine out. I get the same result, a short flash of the LED, and nothing further.

@mike.maxwell - First, thank you for your efforts to get a driver out. With the understanding that this is not ready to be published, is there any chance I could get the driver you've got so far?

I wouldn't presume to know anything about it that you don't, but I'm happy to beat my head against the wall for a while to see if I can contribute anything further. Being that the zwave model isn't on Amazon currently, I'd be surprised if there aren't others in my shoes who saw/will-see that the Firefighter works, without noticing that it's only the zwave version.