Zigbee Frient Intelligent Smoke Sensor driver?

I bought a some smoke sensors and took a chance that they would would work with Heiman zigbee driver.....nope, they didn't.
Anyone found a suitable driver for them?

Quick google brings up a smartthings driver which you'll need to convert

What else would be needed?
[Modified with appropriate (I hope) License Language]

/*
  *  Copyright 2018 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.
  *  Author : Fen Mei / f.mei@samsung.com
  *  Date : 2018-07-06
  *  Based on original by Fen Mei
  *  Modified for use on the Hubitat Elevation Hub by Eric Miller
  *  Modification date: 2021-03-03
  */

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

metadata {
	definition (name: "Zigbee Smoke Sensor", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "x.com.st.d.sensor.smoke", vid: "generic-smoke", genericHandler: "Zigbee") {
		capability "Smoke Detector"
		capability "Sensor"
		capability "Battery"
		capability "Configuration"
		capability "Refresh"
		capability "Health Check"

		fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,0500,0502,0009", outClusters: "0019", manufacturer: "Heiman", model: "b5db59bfd81e4f1f95dc57fdbba17931", deviceJoinName: "Orvibo Smoke Detector" //欧瑞博 烟雾报警器(SF21)
		fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,0500,0502,0009", outClusters: "0019", manufacturer: "HEIMAN", model: "98293058552c49f38ad0748541ee96ba", deviceJoinName: "Orvibo Smoke Detector" //欧瑞博 烟雾报警器(SF21)
		fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,0500,0502", outClusters: "0019", manufacturer: "HEIMAN", model: "SmokeSensor-EM", deviceJoinName: "HEIMAN Smoke Detector" //HEIMAN Smoke Sensor (HS1SA-E)
		fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,0500,0502,0B05", outClusters: "0019", manufacturer: "HEIMAN", model: "SmokeSensor-N-3.0", deviceJoinName: "HEIMAN Smoke Detector" //HEIMAN Smoke Sensor (HS3SA)
		fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,000F,0020,0500,0502", outClusters: "000A,0019", manufacturer: "frient A/S", model :"SMSZB-120", deviceJoinName: "frient Smoke Detector" // frient Intelligent Smoke Alarm
	}

	
}

def getBATTERY_VOLTAGE_ATTR() { 0x0020 }
def getBATTERY_PERCENT_ATTR() { 0x0021 }

def installed(){
	log.debug "installed"

	response(refresh())
}

def parse(String description) {
	log.debug "description(): $description"
	def map = zigbee.getEvent(description)
	if (!map) {
		if (description?.startsWith('zone status')) {
			map = parseIasMessage(description)
		} else {
			map = parseAttrMessage(description)
		}
	}
	log.debug "Parse returned $map"
	def result = map ? createEvent(map) : [:]
	if (description?.startsWith('enroll request')) {
		List cmds = zigbee.enrollResponse()
		log.debug "enroll response: ${cmds}"
		result = cmds?.collect { new hubitat.device.HubAction(it) }
	}
	return result
}

def parseAttrMessage(String description){
	def descMap = zigbee.parseDescriptionAsMap(description)
	def map = [:]
	if (descMap?.clusterInt == zigbee.POWER_CONFIGURATION_CLUSTER && descMap.commandInt != 0x07 && descMap.value) {
		if (descMap.attrInt == BATTERY_VOLTAGE_ATTR) {
			map = getBatteryResult(Integer.parseInt(descMap.value, 16))
		} else {
			map = getBatteryPercentageResult(Integer.parseInt(descMap.value, 16))
		}
	} else if (descMap?.clusterInt == zigbee.IAS_ZONE_CLUSTER && descMap.attrInt == zigbee.ATTRIBUTE_IAS_ZONE_STATUS) {
		def zs = new ZoneStatus(zigbee.convertToInt(descMap.value, 16))
		map = translateZoneStatus(zs)
	}
	return map;
}

def parseIasMessage(String description) {
	ZoneStatus zs = zigbee.parseZoneStatus(description)
	return getDetectedResult(zs.isAlarm1Set() || zs.isAlarm2Set())
}

private Map translateZoneStatus(ZoneStatus zs) {
	return getDetectedResult(zs.isAlarm1Set() || zs.isAlarm2Set())
}

private Map getBatteryResult(rawValue) {
	log.debug "Battery rawValue = ${rawValue}"
	def linkText = getLinkText(device)

	def result = [:]

	def volts = rawValue / 10

	if (!(rawValue == 0 || rawValue == 255)) {
		result.name = 'battery'
		result.translatable = true
		result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"

		def minValue = 23
		def maxValue = 30
		def pct = Math.round((rawValue - minValue) * 100 / (maxValue - minValue))
		pct = pct > 0 ? pct : 1
		result.value = Math.min(100, pct)
	}

	return result
}

private Map getBatteryPercentageResult(rawValue) {
	log.debug "Battery Percentage rawValue = ${rawValue} -> ${rawValue / 2}%"
	def result = [:]

	if (0 <= rawValue && rawValue <= 200) {
		result.name = 'battery'
		result.translatable = true
		result.value = Math.round(rawValue / 2)
		result.descriptionText = "${device.displayName} battery was ${result.value}%"
	}

	return result
}

def getDetectedResult(value) {
	def detected = value ? 'detected': 'clear'
	String descriptionText = "${device.displayName} smoke ${detected}"
	return [name:'smoke',
			value: detected,
			descriptionText:descriptionText,
			translatable:true]
}

def refresh() {
	log.debug "Refreshing Values"
	def refreshCmds = []
	def batteryAttr = isFrientSensor() ? BATTERY_VOLTAGE_ATTR : BATTERY_PERCENT_ATTR
	refreshCmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, batteryAttr) +
					zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS)
	return refreshCmds
}

/**
 * PING is used by Device-Watch in attempt to reach the Device
 * */
def ping() {
	log.debug "ping "
	zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS)
}

def configure() {
	log.debug "configure"
	sendEvent(name: "checkInterval", value:20 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"])
	Integer minReportTime = 0
	Integer maxReportTime = 180
	Integer reportableChange = null
	Integer batteryAttr = isFrientSensor() ? BATTERY_VOLTAGE_ATTR : BATTERY_PERCENT_ATTR
	Integer batteryReportChange = isFrientSensor() ? 0x1 : 0x10
	return refresh() + 
			zigbee.enrollResponse() +
			zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, batteryAttr, DataType.UINT8, 30, 1200, batteryReportChange) +
			zigbee.configureReporting(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS, DataType.BITMAP16, minReportTime, maxReportTime, reportableChange)
}

private Boolean isFrientSensor() {
	device.getDataValue("manufacturer") == "frient A/S"
}

@Eric.C.Miller Thank's :+1: I know that there is a driver for Smartthings, i just don't have the knowledge how to tweak them into HE.

But this one did not work either, i dont get any configuration settings/detection and i smell like bushfire now...burning cardboard in garage. :grin:

I did the "tweaks" that I know about. I am far from an expert at this - I was able to "tweak" one app that I had in SmartThings when I moved over to Hubitat. I was hoping that someone who knows more about coding and migrating code could suggest what I may have missed. Without having one of the units, I don't know what else I can do. Sorry it didn't work . . .

1 Like

@simon I saw you worked on the Heiman smoke detector driver - do you have any idea if this Frient unit is sufficiently similar that you could modify your driver to work with it? I ask because, before seeing your driver, I picked up the SmartThings driver that is (apparently) for this Frient unit and the other devices that this driver (apparently) recognizes are the Heiman and Orvibo.

I don't have the Frient but the original poster seems cooperative if he hasn't burned down his garage yet . . .

1 Like

No worries @Eric.C.Miller Thank you for trying to help!

Ok - I've put the frient fingerprint into my driver.
Can we test and see what happens? Looking at the ST code it looks like battery reports may need some tweaking.

/**
 * Heiman (and maybe frient) Zigbee Smoke Detector
 *
 *  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.
 *
 *   Original DTH on SmartThings by cuboy29
 *       Converted to Hubitat and extensivly modified by Scruffy-SJB
 *
 *   Code snippets copied from veeceeoh, Daniel Terryn and Marcus Liljergren - with thanks.
 */

// Ver: 1.9 - Added fingerprint for frient Intelligent Smoke Alarm
// Ver: 1.8 - Added fingerprint for model SmokeSensor-EF-3.0  Enhanced status message processing.  New Current State - Date/Time last status recorded.
// Ver: 1.7 - Added fingerprint for model HS1CA-M HEIMAN Smart Carbon Monoxide Sensor
// Ver: 1.6 - Added fingerprint for model HS1SA-M
// Ver: 1.5 - Added ability to detect a device test and to trigger a last tested event
// Ver: 1.4 - Parsed another unhandled catch-all
// Ver: 1.3 - Updated to support Zigbee 3.0 version HS1SA-E

import hubitat.zigbee.clusters.iaszone.ZoneStatus
 
metadata {
	definition (name: "Heiman Zigbee Smoke Detector", namespace: "scruffy-sjb", author: "scruffy-sjb and cuboy29") {
		
        capability "Configuration"
        capability "Smoke Detector"
        capability "SmokeDetector"
        capability "Sensor"
        capability "Refresh"
        capability "Battery"
        
		command "resetToClear"
        command "resetBatteryReplacedDate"
        
        attribute "smoke", "string"
        attribute "batteryLastReplaced", "string"
        attribute "sensorLastTested", "string"
        attribute "lastStatus", "string"
          
        fingerprint profileID: "0104", deviceID: "0402", inClusters: "0000,0001,0003,0500,0502", outClusters: "0019", manufacturer: "HEIMAN", model: "SmokeSensor-EM", deviceJoinName: "HEIMAN Smoke Detector" //HEIMAN Smoke Sensor (HS1SA-E)
        fingerprint profileID: "0104", deviceID: "0402", inClusters: "0000,0003,0500,0001,0009,0502", outClusters: "0019", manufacturer: "HEIMAN", model: "SMOK_V16", deviceJoinName: "HEIMAN Smoke Detector M" //HEIMAN Smoke Sensor (HS1SA-M)
        fingerprint profileID: "0104", deviceID: "0402", inClusters: "0000,0003,0001,0500,0502,0B05", outClusters: "0019", manufacturer: "HEIMAN", model: "SmokeSensor-N-3.0", deviceJoinName: "HEIMAN Smoke Detector 3.0" //HEIMAN Smoke Sensor (HS1SA-E)
        fingerprint profileID: "0104", deviceID: "0402", inClusters: "0000,0001,0003,0500", manufacturer: "HEIMAN", model: "COSensor-EM", deviceJoinName: "HEIMAN CO Sensor" //HEIMAN Smart Carbon Monoxide Sensor (HS1CA-E)
        fingerprint profileID: "0104", deviceID: "0402", inClusters: "0000,0001,0003,0500,0502,0B05", outClusters: "0019", manufacturer: "HEIMAN", model: "SmokeSensor-EF-3.0", deviceJoinName: "HEIMAN Smoke Detector" //HEIMAN Smoke Sensor 
        fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,000F,0020,0500,0502", outClusters: "000A,0019", manufacturer: "frient A/S", model :"SMSZB-120", deviceJoinName: "frient Smoke Detector" // frient Intelligent Smoke Alarm
    }
}

def SensorTestOptions = [:]
	SensorTestOptions << ["1" : "Yes"] // 0x01
	SensorTestOptions << ["0" : "No"]  // 0x00

preferences {
	input "SensorTest", "enum", title: "Enable Sensor Testing", options: SensorTestOptions, description: "Default: Yes", required: false, displayDuringSetup: true
}        

def parse(String description) {
    def descMap = [:]
    
	if (description?.startsWith('zone status')) {
			descMap = parseIasMessage(description)
    }else if (description?.startsWith('enroll request')) {
		    List cmds = zigbee.enrollResponse()
		    descMap = cmds?.collect { new hubitat.device.HubAction(it) }
	}else if (description?.startsWith('catchall')) {
            descMap = parseCatchAllMessage(description)
    }else if (description?.startsWith('read attr'))  {  
            descMap = zigbee.parseDescriptionAsMap(description)
            if ((descMap?.cluster == "0500" && descMap.attrInt == 0x0001) && (descMap.value == '0028')){  //Zone Type
                log.debug "Zone Type is Fire Sensor"
			}else if ((descMap?.cluster == "0500" && descMap.attrInt == 0x0000) && (descMap.value == '01')){  //Zone State
                log.debug "Zone State is enrolled"
			}else if ((descMap?.cluster == "0500" && descMap.attrInt == 0x0002) && ((descMap.value == '20') || (descMap.value == '0020'))){  //Zone Status Clear
                SmokeOrClear("clear")    
			}else if ((descMap?.cluster == "0500" && descMap.attrInt == 0x0002) && ((descMap.value == '30') || (descMap.value == '0030'))){  //Zone Status Clear
                SmokeOrClear("clear") 
			}else if ((descMap?.cluster == "0500" && descMap.attrInt == 0x0002) && ((descMap.value == '0031') || (descMap.value == '0021'))){  //Zone Status Smoke
                SmokeOrClear("detected")              
			}else if ((descMap?.cluster == "0502" && descMap.attrInt == 0x0000)){  //Alarm Max Duration
                def int alarmMinutes = Integer.parseInt(descMap.value,16) 
                log.debug "Max Alarm Duration is ${alarmMinutes} seconds"              
			}else if ((descMap?.cluster == "0000" && descMap.attrInt == 0x0007) && ((descMap.value == '03') )){  //PowerSource
                log.debug "${device.displayName} is Battery Powered"    
			}else if ((descMap?.cluster == "0001" && descMap.attrInt == 0x0020)) {  //Battery Voltage
                def batteryVoltage = ConvertHexValue(descMap.value)
                handleBatteryEvent(batteryVoltage)
			}else if ((descMap?.cluster == "0001" && descMap.attrInt == 0x0021)) {  //Battery Cells
                def batteryCells = ConvertHexValue(descMap.value)
                handleCellsEvent(batteryCells)                
            }else if (descMap?.cluster == "0000" && descMap.attrInt == 0x0004){  //Manufacture
                sendEvent(name: "manufacture", value: descMap.value)
                log.debug "Manufacturer is ${descMap.value}"
            }else if (descMap?.cluster == "0000" && descMap.attrInt == 0x0005){  //Model 
                sendEvent(name: "model", value: descMap.value)
                log.debug "Model is ${descMap.value}"
            }else {log.debug "Unknown --> Cluster-> ${descMap?.cluster}  AttrInt-> ${descMap.attrInt}  Value-> ${descMap.value}"
            }
       // log.debug "Cluster-> ${descMap?.cluster}  AttrInt-> ${descMap.attrInt}  Value-> ${descMap.value}"
    }else { 
        log.debug "Unparsed -> $description" 
        descMap = zigbee.parseDescriptionAsMap(description)
    }
    // log.debug "$descMap"
	return descMap   
}    

private parseCatchAllMessage(String description) {
    
    Map resultMap = [:]
    def descMap = zigbee.parse(description)  
    if (shouldProcessMessage(descMap)) {
        log.debug descMap.inspect()               
    }
    return resultMap
}

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

def refresh() {
	log.debug "Refreshing..."
	def refreshCmds = []
    
    refreshCmds +=
	zigbee.readAttribute(0x0500, 0x0001) +	   // IAS ZoneType
    zigbee.readAttribute(0x0500, 0x0000) +	   // IAS ZoneState
    zigbee.readAttribute(0x0500, 0x0002) +	   // IAS ZoneStatus
    zigbee.readAttribute(0x0502, 0x0000) +	   // Alarm Max Duration
    zigbee.readAttribute(0x0000, 0x0007) +	   // Power Source
    zigbee.readAttribute(0x0001, 0x0020) +     // Battery Voltage      
    zigbee.readAttribute(0x0001, 0x0033) +     // Battery Cells         
    zigbee.readAttribute(0x0000, 0x0004) +	   // Manufacturer Name
    zigbee.readAttribute(0x0000, 0x0005) +	   // Model Indentification
    zigbee.enrollResponse()
    
	return refreshCmds
}

def configure() {
    log.debug "Configuring..."
    
	if (!device.currentState('batteryLastReplaced')?.value)
		resetBatteryReplacedDate(true)
    
    def cmds = [
            //bindings
            "zdo bind 0x${device.deviceNetworkId} 1 1 0x0500 {${device.zigbeeId}} {}", "delay 200",
        ] +  zigbee.enrollResponse(1200) + zigbee.configureReporting(0x0500, 0x0002, 0x19, 0, 3600, 0x00) + "delay 200" + 
        zigbee.configureReporting(0x0001, 0x0020, 0x20, 600, 7200, 0x01) + refresh()
    
    return cmds 
}

def resetBatteryReplacedDate(paired) {
	def newlyPaired = paired ? " for newly paired sensor" : ""
	sendEvent(name: "batteryLastReplaced", value: new Date())
	log.debug "${device.displayName} Setting Battery Last Replaced to Current date${newlyPaired}"
}

def resetSensorTestedDate() {
    def newlyTested=""
    sendEvent(name: "sensorLastTested", value: new Date())
    log.debug "${device.displayName} Setting Sensor Last Tested to Current date${newlyTested}"
}

def resetToClear() {
	sendEvent(name:"smoke", value:"clear")
    sendEvent(name: "lastStatus", value: new Date(), displayed: True)
    log.debug "Resetting to Clear..."
	didWeGetClear = 0
}

/**
 * Code borrowed (mixed and matched) from both Daniel Terryn and veeceeoh
 *
 * Create battery event from reported battery voltage.
 *
 */

private handleBatteryEvent(rawVolts) {
    rawVolts = rawVolts / 10.0
	def minVolts = voltsmin ? voltsmin : 2.5
	def maxVolts = voltsmax ? voltsmax : 3.0
	def pct = (rawVolts - minVolts) / (maxVolts - minVolts)
	def roundedPct = Math.min(100, Math.round(pct * 100))
	log.debug "Battery level is ${roundedPct}% (${rawVolts} Volts)"
	
    sendEvent(name:"battery", value: roundedPct, unit: "%", isStateChange: true)
	return 
}

private handleCellsEvent(noCells) {
	log.debug "Battery reports that it has (${noCells} Cells)"
	return 
}

def ConvertHexValue(value) {
	if (value != null)
	{
		return Math.round(Integer.parseInt(value, 16))
	}
}

def SmokeOrClear(value) {
    if (value == "clear") {
        sendEvent(name:"smoke", value:"clear", isStateChange: true)
        log.info "${device.displayName} reports status is all clear"
    } else {
        sendEvent(name:"smoke", value:"detected", isStateChange: true)
        log.info "${device.displayName} reports smoke is detected"
    }
    
    sendEvent(name: "lastStatus", value: new Date(), displayed: True)
}

private Map parseIasMessage(String description) {
    // log.debug "Zone Status Received--> ${description}"
    ZoneStatus zs = zigbee.parseZoneStatus(description)    
    translateZoneStatus(zs)    
}

private Map translateZoneStatus(ZoneStatus zs) {
	// Some sensor models that use this DTH use alarm1 and some use alarm2 to signify motion       
	return getSmokeResult(zs.isAlarm1Set() || zs.isAlarm2Set())
}

private Map getSmokeResult(value) {
	if (value) {
        if (SensorTest == "") {
            SensorTest = "1"    // Default is Yes
        }
        
        if (SensorTest == "1") {
    		def descriptionText = "${device.displayName} status is pending"
            sendEvent(name: "lastStatus", value: new Date(), displayed: True)
	    	log.debug descriptionText
		    runIn(3,EventOrTest)
		    return [
			    name			: 'smoke',
			    value			: 'pending',
                isStateChange: false,
			    descriptionText : descriptionText
            ]
        } else {
    		def descriptionText = "${device.displayName} testing disabled - smoke detected !!!"
	    	log.debug descriptionText
            sendEvent(name: "lastStatus", value: new Date(), displayed: True)
		    return [
			    name			: 'smoke',
			    value			: 'detected',
                isStateChange: true,
			    descriptionText : descriptionText
            ]
        }                 
   } else {
		def descriptionText = "${device.displayName} all clear"
		log.debug descriptionText
        sendEvent(name: "lastStatus", value: new Date(), displayed: True)
		return [
			name			: 'smoke',
			value			: 'clear',
            isStateChange: true,
			descriptionText : descriptionText
		]
	}
}		

def EventOrTest() {
    if (device.currentValue("smoke") == "clear") {
		log.debug "${device.displayName} was tested sucessfully"
        resetSensorTestedDate()
	} else {
		log.debug "${device.displayName} This was not a test - smoke detected !!!"
		sendEvent(name:"smoke", value:"detected")
        sendEvent(name: "lastStatus", value: new Date(), displayed: True)
	}
}

Thank you @simon for helping out!
Sorry to say that it didn´t work any better, no respons when i push on the sensor or keep burning stuff in my garage... :man_firefighter:
I understand that it is hard/annoying to code if you don´t have the device but i appreciate it.

Just a thought - is the device trying to pair with the hub - or just timing out the whole time.....

Can you keep a good check on the log - see if there is a "catchall" message - if so copy it and show it to me please.

@simon Here is the log, i tried out all possible drivers so it might be a little bit messy, sorry for that. But the last one is yours.

dev:15082021-03-03 19:49:53.562 errorgroovy.lang.MissingMethodException: No signature of method: user_driver_scruffy_sjb_Heiman_Zigbee_Smoke_Detector_1351.logsOff() is applicable for argument types: () values: [] Possible solutions: notify() (logsOff)

dev:15082021-03-03 19:24:28.921 debugRefreshing...

dev:15082021-03-03 19:24:24.611 debugRefreshing...

dev:15082021-03-03 19:24:24.607 debugConfiguring...

dev:15082021-03-03 19:24:04.624 debugRefreshing...

dev:15082021-03-03 19:24:02.404 debugResetting to Clear...

dev:15082021-03-03 19:23:55.745 debugRefreshing...

dev:15082021-03-03 19:23:55.738 debugConfiguring...

dev:15082021-03-03 19:23:51.426 debugRefreshing...

dev:15082021-03-03 19:23:12.102 infoSmoke garage was set to clear

dev:15082021-03-03 19:23:07.863 infoSmoke garage was set to tested

dev:15082021-03-03 19:23:06.106 infoSmoke garage was set to clear

dev:15082021-03-03 19:22:58.921 infoSmoke garage was set to detected

dev:15082021-03-03 19:22:10.687 infoSmoke garage was set to clear

dev:15082021-03-03 19:22:05.296 infoSmoke garage was set to tested

dev:15082021-03-03 19:22:04.096 infoSmoke garage was set to clear

dev:15082021-03-03 19:22:02.327 infoSmoke garage was set to detected

dev:15082021-03-03 19:22:00.335 infoSmoke garage was set to clear

dev:15082021-03-03 19:19:53.457 warndescription logging is: true

dev:15082021-03-03 19:19:53.455 warndebug logging is: true

dev:15082021-03-03 19:19:53.452 infoupdated...

dev:15082021-03-03 19:18:12.020 warnconfigure...

dev:15082021-03-03 19:18:10.141 warnconfigure...

dev:15082021-03-03 19:18:05.210 warnconfigure...

dev:15082021-03-03 19:17:32.736 warnconfigure...

dev:15082021-03-03 19:15:54.941 warnSmoke, Carbon Monoxide, and Tamper have been set to clear. Battery and Temperature will get updated the next time the device wakes up. You can force the device to wake up by opening and closing its case.

dev:15082021-03-03 19:15:41.316 warnSmoke, Carbon Monoxide, and Tamper have been set to clear. Battery and Temperature will get updated the next time the device wakes up. You can force the device to wake up by opening and closing its case.

dev:15082021-03-03 19:13:23.590 debugRefreshing...

dev:15082021-03-03 19:13:21.112 debugRefreshing...

dev:15082021-03-03 19:13:21.105 debugConfiguring...

dev:15082021-03-03 19:13:04.884 debugRefreshing...

dev:15082021-03-03 19:09:33.996 debugResetting to Clear...

dev:15082021-03-03 19:09:30.824 debugRefreshing...

dev:15082021-03-03 19:09:27.341 debugRefreshing...

dev:15082021-03-03 19:09:27.330 debugConfiguring...

dev:15082021-03-03 19:06:39.792 debugResetting to Clear...

dev:15082021-03-03 19:06:35.353 debugRefreshing...

dev:15082021-03-03 19:06:32.172 debugRefreshing...

dev:15082021-03-03 19:06:32.080 debugConfiguring...

dev:15082021-03-03 18:39:02.155 debugParse returned [:]

dev:15082021-03-03 18:39:02.153 debugdescription(): catchall: 0000 0006 00 00 0040 00 8C5D 00 00 0000 00 00 4700000401010A0000

dev:15082021-03-03 18:38:52.143 debugParse returned [:]

dev:15082021-03-03 18:38:52.140 debugdescription(): catchall: 0000 0006 00 00 0040 00 8C5D 00 00 0000 00 00 450000040100012000

dev:15082021-03-03 18:38:43.256 debugenroll response: [he wattr 0x8C5D 0x01 0x0500 0x0010 0xF0 {54E29413006F0D00}, delay 2000, he raw 0x8C5D 1 0x01 0x0500 {01 23 00 00 00}, delay 2000]

dev:15082021-03-03 18:38:43.247 debugParse returned [:]

dev:15082021-03-03 18:38:43.245 debugdescription(): enroll request endpoint 0x23 : data 0x0028

dev:15082021-03-03 18:38:42.157 debugParse returned [:]

dev:15082021-03-03 18:38:42.152 debugdescription(): catchall: 0000 0006 00 00 0040 00 8C5D 00 00 0000 00 00 44FDFF040100010005

dev:15082021-03-03 18:24:00.951 debugParse returned [:]

dev:15082021-03-03 18:24:00.948 debugdescription(): catchall: 0000 0006 00 00 0040 00 8C5D 00 00 0000 00 00 3E00000401010A0000

dev:15082021-03-03 18:23:50.964 debugParse returned [:]

dev:15082021-03-03 18:23:50.961 debugdescription(): catchall: 0000 0006 00 00 0040 00 8C5D 00 00 0000 00 00 3C0000040100012000

dev:15082021-03-03 18:23:41.344 debugenroll response: [he wattr 0x8C5D 0x01 0x0500 0x0010 0xF0 {54E29413006F0D00}, delay 2000, he raw 0x8C5D 1 0x01 0x0500 {01 23 00 00 00}, delay 2000]

dev:15082021-03-03 18:23:41.335 debugParse returned [:]

dev:15082021-03-03 18:23:41.333 debugdescription(): enroll request endpoint 0x23 : data 0x0028

dev:15082021-03-03 18:23:40.962 debugParse returned [:]

dev:15082021-03-03 18:23:40.956 debugdescription(): catchall: 0000 0006 00 00 0040 00 8C5D 00 00 0000 00 00 3BFDFF040100010005

dev:15082021-03-03 18:09:00.799 debugParse returned [:]

dev:15082021-03-03 18:09:00.797 debugdescription(): catchall: 0000 0006 00 00 0040 00 8C5D 00 00 0000 00 00 3400000401010A0000

dev:15082021-03-03 18:08:50.790 debugParse returned [:]

dev:15082021-03-03 18:08:50.788 debugdescription(): catchall: 0000 0006 00 00 0040 00 8C5D 00 00 0000 00 00 320000040100012000

dev:15082021-03-03 18:08:41.921 debugenroll response: [he wattr 0x8C5D 0x01 0x0500 0x0010 0xF0 {54E29413006F0D00}, delay 2000, he raw 0x8C5D 1 0x01 0x0500 {01 23 00 00 00}, delay 2000]

dev:15082021-03-03 18:08:41.913 debugParse returned [:]

dev:15082021-03-03 18:08:41.912 debugdescription(): enroll request endpoint 0x23 : data 0x0028

dev:15082021-03-03 18:08:40.791 debugParse returned [:]

dev:15082021-03-03 18:08:40.787 debugdescription(): catchall: 0000 0006 00 00 0040 00 8C5D 00 00 0000 00 00 31FDFF040100010005

dev:15082021-03-03 17:54:00.087 debugParse returned [:]

dev:15082021-03-03 17:54:00.085 debugdescription(): catchall: 0000 0006 00 00 0040 00 8C5D 00 00 0000 00 00 2B00000401010A0000

dev:15082021-03-03 17:53:50.103 debugParse returned [:]

dev:15082021-03-03 17:53:50.100 debugdescription(): catchall: 0000 0006 00 00 0040 00 8C5D 00 00 0000 00 00 290000040100012000

dev:15082021-03-03 17:53:41.199 debugenroll response: [he wattr 0x8C5D 0x01 0x0500 0x0010 0xF0 {54E29413006F0D00}, delay 2000, he raw 0x8C5D 1 0x01 0x0500 {01 23 00 00 00}, delay 2000]

dev:15082021-03-03 17:53:41.193 debugParse returned [:]

dev:15082021-03-03 17:53:41.192 debugdescription(): enroll request endpoint 0x23 : data 0x0028

dev:15082021-03-03 17:53:40.113 debugParse returned [:]

dev:15082021-03-03 17:53:40.107 debugdescription(): catchall: 0000 0006 00 00 0040 00 8C5D 00 00 0000 00 00 28FDFF040100010005

dev:15082021-03-03 17:38:59.396 debugParse returned [:]

dev:15082021-03-03 17:38:59.389 debugdescription(): catchall: 0000 0006 00 00 0040 00 8C5D 00 00 0000 00 00 9100000401010A0000

dev:15082021-03-03 17:38:49.381 debugParse returned [:]

dev:15082021-03-03 17:38:49.379 debugdescription(): catchall: 0000 0006 00 00 0040 00 8C5D 00 00 0000 00 00 8D0000040100012000

dev:15082021-03-03 17:38:40.502 debugenroll response: [he wattr 0x8C5D 0x01 0x0500 0x0010 0xF0 {54E29413006F0D00}, delay 2000, he raw 0x8C5D 1 0x01 0x0500 {01 23 00 00 00}, delay 2000]

dev:15082021-03-03 17:38:40.495 debugParse returned [:]

dev:15082021-03-03 17:38:40.494 debugdescription(): enroll request endpoint 0x23 : data 0x0028

dev:15082021-03-03 17:38:39.407 debugParse returned [:]

dev:15082021-03-03 17:38:39.402 debugdescription(): catchall: 0000 0006 00 00 0040 00 8C5D 00 00 0000 00 00 8CFDFF040100010005

dev:15082021-03-03 17:34:14.075 errorgroovy.lang.MissingPropertyException: No such property: ATTRIBUTE_IAS_ZONE_STATUS for class: com.hubitat.zigbee.Zigbee on line 149 (refresh)

dev:15082021-03-03 17:34:14.038 debugRefreshing Values

dev:15082021-03-03 17:33:26.840 errorgroovy.lang.MissingPropertyException: No such property: ATTRIBUTE_IAS_ZONE_STATUS for class: com.hubitat.zigbee.Zigbee on line 149 (refresh)

dev:15082021-03-03 17:33:26.812 debugRefreshing Values

dev:15082021-03-03 17:33:24.945 errorgroovy.lang.MissingPropertyException: No such property: ATTRIBUTE_IAS_ZONE_STATUS for class: com.hubitat.zigbee.Zigbee on line 158 (ping)

dev:15082021-03-03 17:33:24.920 debugping

dev:15082021-03-03 17:33:20.958 errorgroovy.lang.MissingPropertyException: No such property: ATTRIBUTE_IAS_ZONE_STATUS for class: com.hubitat.zigbee.Zigbee on line 149 (configure)

dev:15082021-03-03 17:33:20.922 debugRefreshing Values

dev:15082021-03-03 17:33:20.920 debugconfigure

dev:15082021-03-03 17:32:58.645 debugRefreshing...

dev:15082021-03-03 17:32:54.253 debugRefreshing...

dev:15082021-03-03 17:32:54.245 debugConfiguring...

dev:15082021-03-03 17:32:51.632 debugRefreshing...

dev:15082021-03-03 17:32:23.892 debugResetting to Clear...

dev:15082021-03-03 17:32:21.297 debugRefreshing...

dev:15082021-03-03 17:32:17.306 debugRefreshing...

dev:15082021-03-03 17:32:17.298 debugSmoke garage Setting Battery Last Replaced to Current date for newly paired sensor

dev:15082021-03-03 17:32:17.290 debugConfiguring...

dev:15082021-03-03 17:31:44.845 debugRefreshing...

dev:15082021-03-03 17:31:42.613 debugRefreshing...

dev:15082021-03-03 17:31:42.612 debugConfiguring...

dev:15082021-03-03 17:31:41.245 debugRefreshing...

dev:15082021-03-03 17:31:41.243 debugConfiguring...

dev:15082021-03-03 17:31:39.445 debugRefreshing...

dev:15082021-03-03 17:31:23.206 debugconfigure: Write IAS CIE

dev:15082021-03-03 17:31:23.205 debugConfuguring Reporting, IAS CIE, and Bindings.

dev:15082021-03-03 17:31:21.989 debugrefreshing

dev:15082021-03-03 17:31:19.740 debugSending enroll response

dev:15082021-03-03 17:31:17.455 debugrefreshing

dev:15082021-03-03 17:31:15.604 debugconfigure: Write IAS CIE

dev:15082021-03-03 17:31:15.599 debugConfuguring Reporting, IAS CIE, and Bindings.

dev:15082021-03-03 17:30:42.751 errorgroovy.lang.MissingPropertyException: No such property: ATTRIBUTE_IAS_ZONE_STATUS for class: com.hubitat.zigbee.Zigbee on line 149 (configure)

dev:15082021-03-03 17:30:42.716 debugRefreshing Values

dev:15082021-03-03 17:30:42.713 debugconfigure

dev:15082021-03-03 17:30:41.491 errorgroovy.lang.MissingPropertyException: No such property: ATTRIBUTE_IAS_ZONE_STATUS for class: com.hubitat.zigbee.Zigbee on line 149 (refresh)

dev:15082021-03-03 17:30:41.466 debugRefreshing Values

dev:15082021-03-03 17:30:31.093 errorgroovy.lang.MissingPropertyException: No such property: ATTRIBUTE_IAS_ZONE_STATUS for class: com.hubitat.zigbee.Zigbee on line 149 (configure)

dev:15082021-03-03 17:30:31.055 debugRefreshing Values

dev:15082021-03-03 17:30:31.053 debugconfigure

dev:15082021-03-03 17:30:29.184 errorgroovy.lang.MissingPropertyException: No such property: ATTRIBUTE_IAS_ZONE_STATUS for class: com.hubitat.zigbee.Zigbee on line 149 (refresh)

dev:15082021-03-03 17:30:29.151 debugRefreshing Values

dev:15082021-03-03 17:28:37.075 errorgroovy.lang.MissingPropertyException: No such property: ATTRIBUTE_IAS_ZONE_STATUS for class: com.hubitat.zigbee.Zigbee on line 149 (refresh)

dev:15082021-03-03 17:28:37.046 debugRefreshing Values

dev:15082021-03-03 17:28:18.195 errorgroovy.lang.MissingPropertyException: No such property: ATTRIBUTE_IAS_ZONE_STATUS for class: com.hubitat.zigbee.Zigbee on line 149 (refresh)

dev:15082021-03-03 17:28:18.170 debugRefreshing Values

dev:15082021-03-03 17:28:14.198 errorgroovy.lang.MissingPropertyException: No such property: ATTRIBUTE_IAS_ZONE_STATUS for class: com.hubitat.zigbee.Zigbee on line 149 (configure)

dev:15082021-03-03 17:28:14.163 debugRefreshing Values

dev:15082021-03-03 17:28:14.160 debugconfigure

dev:15082021-03-03 17:27:59.924 errorgroovy.lang.MissingPropertyException: No such property: ATTRIBUTE_IAS_ZONE_STATUS for class: com.hubitat.zigbee.Zigbee on line 149 (configure)

dev:15082021-03-03 17:27:59.891 debugRefreshing Values

dev:15082021-03-03 17:27:59.889 debugconfigure

dev:15082021-03-03 17:27:56.984 errorgroovy.lang.MissingPropertyException: No such property: ATTRIBUTE_IAS_ZONE_STATUS for class: com.hubitat.zigbee.Zigbee on line 149 (refresh)

dev:15082021-03-03 17:27:56.956 debugRefreshing Values

dev:15082021-03-03 17:26:46.838 errorgroovy.lang.MissingPropertyException: No such property: ATTRIBUTE_IAS_ZONE_STATUS for class: com.hubitat.zigbee.Zigbee on line 149 (refresh)

dev:15082021-03-03 17:26:46.808 debugRefreshing Values

dev:15082021-03-03 17:26:31.044 errorgroovy.lang.MissingPropertyException: No such property: ATTRIBUTE_IAS_ZONE_STATUS for class: com.hubitat.zigbee.Zigbee on line 149 (configure)

dev:15082021-03-03 17:26:31.005 debugRefreshing Values

dev:15082021-03-03 17:26:31.003 debugconfigure

dev:15082021-03-03 17:26:29.458 errorgroovy.lang.MissingPropertyException: No such property: ATTRIBUTE_IAS_ZONE_STATUS for class: com.hubitat.zigbee.Zigbee on line 149 (refresh)

dev:15082021-03-03 17:26:29.430 debugRefreshing Values

dev:15082021-03-03 17:26:21.309 errorgroovy.lang.MissingPropertyException: No such property: ATTRIBUTE_IAS_ZONE_STATUS for class: com.hubitat.zigbee.Zigbee on line 158 (ping)

dev:15082021-03-03 17:26:21.288 debugping

dev:15082021-03-03 17:26:17.078 errorgroovy.lang.MissingPropertyException: No such property: ATTRIBUTE_IAS_ZONE_STATUS for class: com.hubitat.zigbee.Zigbee on line 149 (refresh)

dev:15082021-03-03 17:26:17.045 debugRefreshing Values

dev:15082021-03-03 17:25:58.997 errorgroovy.lang.MissingPropertyException: No such property: ATTRIBUTE_IAS_ZONE_STATUS for class: com.hubitat.zigbee.Zigbee on line 149 (refresh)

dev:15082021-03-03 17:25:58.971 debugRefreshing Values

dev:15082021-03-03 17:25:54.394 errorgroovy.lang.MissingPropertyException: No such property: ATTRIBUTE_IAS_ZONE_STATUS for class: com.hubitat.zigbee.Zigbee on line 149 (configure)

dev:15082021-03-03 17:25:54.361 debugRefreshing Values

dev:15082021-03-03 17:25:54.359 debugconfigure

dev:15082021-03-03 17:25:50.052 errorgroovy.lang.MissingPropertyException: No such property: ATTRIBUTE_IAS_ZONE_STATUS for class: com.hubitat.zigbee.Zigbee on line 149 (refresh)

dev:15082021-03-03 17:25:50.025 debugRefreshing Values

dev:15082021-03-03 17:25:48.570 errorgroovy.lang.MissingPropertyException: No such property: ATTRIBUTE_IAS_ZONE_STATUS for class: com.hubitat.zigbee.Zigbee on line 158 (ping)

dev:15082021-03-03 17:25:48.543 debugping

dev:15082021-03-03 17:25:46.644 errorgroovy.lang.MissingPropertyException: No such property: ATTRIBUTE_IAS_ZONE_STATUS for class: com.hubitat.zigbee.Zigbee on line 149 (configure)

dev:15082021-03-03 17:25:46.598 debugRefreshing Values

dev:15082021-03-03 17:25:46.593 debugconfigure

dev:15082021-03-03 17:24:43.104 debuggetting info for unknown Zigbee device...

dev:15082021-03-03 17:24:41.082 debugconfigure() called...

Did you delete the previous device and after installing the above driver let Hubitat find and assign this driver to it?

I'm not sure but I'm guessing the logsoff() error is a hang over from a previous driver.

I'd be interested in seeing the log where the device gets paired and configured.

Thanks,

Sorry @simon for the absens, i deleted the device and old driver before reinstalling but no difference.
Although i found out that "Frient" is a rebranded Delvaco.
Here is a link to there site with api-stuff, if it is any help for you? No pressure to assist me if you don´t have the time for it, i´m thankful for the first tries. :+1:
APIs and applications

I saw online about it being a rebranded Delvaco. nope, API stuff not helpful for a ZigBee device - WI-FI yes, but not ZigBee.

I'm in the US until mid-April - if you still have not got it working maybe you could send it to me in Ireland and mid-April I can get it working for you?

Hi, I've found myself with the same problem with the Frient smoke alarm. Don't suppose anybody got this working? any help would be greatly appreciated. Thanks all.

If your looking for the Heiman stuff, look here.

I got the Carbon Monoxide detector, your driver picks it up as a smoke detector. Dashboard also only see's it as a smoke detector as well. Which leads me to how to get the dashboard to see it as a Carbon Monoxide detector?

This one...?

Zigbee ZHA 1.2 Protocol Carbon Monoxide Detector, CO Detector, Zigbee : Amazon.co.uk: DIY & Tools

Yeah, thats the one I bought. Build quality appears good. Siren is loud. Pairs easily picking your driver up first time. Just the driver says its a smoke detector and I don't mean the driver name either.

The dashboard wont show any status unless smoke is selected even though there is a Carbon Monoxide option.

I've created a version of the Heiman Smoke Sensor Driver just for the CO Detectors.

HE is not discovering the device as a CO Detector - thinks it's a Smoke Detector. So I had to manually change the device driver to this one.

Can you test please? I tried to get the HE dashboard to show it as a CO Detector - but I'm just getting a ? for an icon. Setting up a custom template didn't help......

/**
 * Heiman Zigbee Carbon Monoxide Detector  (CO Detector)
 *
 *  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.
 *
 *   Original DTH on SmartThings by cuboy29
 *       Converted to Hubitat and extensivly modified by Scruffy-SJB
 *
 *   Code snippets copied from veeceeoh, Daniel Terryn and Marcus Liljergren - with thanks.
 */

// Ver: 1.0 - Initial Version

import hubitat.zigbee.clusters.iaszone.ZoneStatus
 
metadata {
	definition (name: "Heiman Zigbee CO Detector", namespace: "scruffy-sjb", author: "scruffy-sjb") {
		
        capability "Carbon Monoxide Detector"
        capability "Configuration"
        capability "Sensor"
        capability "Refresh"
        capability "Battery"
        
		command "resetToClear"
        command "resetBatteryReplacedDate"
        
        attribute "CO", "string"
        attribute "batteryLastReplaced", "string"
        attribute "sensorLastTested", "string"
        attribute "lastStatus", "string"
          
        fingerprint profileID: "0104", deviceID: "0402", inClusters: "0000,0001,0003,0500", manufacturer: "HEIMAN", model: "COSensor-EM", deviceJoinName: "HEIMAN CO Sensor" //HEIMAN Smart Carbon Monoxide Sensor (HS1CA-E)
        fingerprint profileID: "0104", deviceID: "0402", inClusters: "0000,0001,0003,0500", manufacturer: "HEIMAN", model: "COSensor-EF-3.0", deviceJoinName: "HEIMAN CO Sensor" //HEIMAN Smart Carbon Monoxide Sensor (HS1CA-EF-3.0)
    }
}

def SensorTestOptions = [:]
	SensorTestOptions << ["1" : "Yes"] // 0x01
	SensorTestOptions << ["0" : "No"]  // 0x00

preferences {
	input "SensorTest", "enum", title: "Enable Sensor Testing", options: SensorTestOptions, description: "Default: Yes", required: false, displayDuringSetup: true
}        

def parse(String description) {
    def descMap = [:]
    
	if (description?.startsWith('zone status')) {
			descMap = parseIasMessage(description)
    }else if (description?.startsWith('enroll request')) {
		    List cmds = zigbee.enrollResponse()
		    descMap = cmds?.collect { new hubitat.device.HubAction(it) }
	}else if (description?.startsWith('catchall')) {
            descMap = parseCatchAllMessage(description)
    }else if (description?.startsWith('read attr'))  {  
            descMap = zigbee.parseDescriptionAsMap(description)
            if ((descMap?.cluster == "0500" && descMap.attrInt == 0x0001) && (descMap.value == '0028')){  //Zone Type
                log.debug "Zone Type is Fire Sensor"
            }else if ((descMap?.cluster == "0500" && descMap.attrInt == 0x0001) && (descMap.value == '002B')){  //Zone Type
                log.debug "Zone Type is CO Sensor"
			}else if ((descMap?.cluster == "0500" && descMap.attrInt == 0x0000) && (descMap.value == '01')){  //Zone State
                log.debug "Zone State is enrolled"
			}else if ((descMap?.cluster == "0500" && descMap.attrInt == 0x0002) && ((descMap.value == '20') || (descMap.value == '0020'))){  //Zone Status Clear
                SmokeOrClear("clear")    
			}else if ((descMap?.cluster == "0500" && descMap.attrInt == 0x0002) && ((descMap.value == '30') || (descMap.value == '0030'))){  //Zone Status Clear
                SmokeOrClear("clear") 
			}else if ((descMap?.cluster == "0500" && descMap.attrInt == 0x0002) && ((descMap.value == '0031') || (descMap.value == '0021'))){  //Zone Status CO Present
                SmokeOrClear("detected")              
			}else if ((descMap?.cluster == "0502" && descMap.attrInt == 0x0000)){  //Alarm Max Duration
                def int alarmMinutes = Integer.parseInt(descMap.value,16) 
                log.debug "Max Alarm Duration is ${alarmMinutes} seconds"              
			}else if ((descMap?.cluster == "0000" && descMap.attrInt == 0x0007) && ((descMap.value == '03') )){  //PowerSource
                log.debug "${device.displayName} is Battery Powered"    
			}else if ((descMap?.cluster == "0001" && descMap.attrInt == 0x0020)) {  //Battery Voltage
                def batteryVoltage = ConvertHexValue(descMap.value)
                handleBatteryEvent(batteryVoltage)
            }else if ((descMap?.cluster == "0001" && descMap.attrInt == 0x0051)) {  //Battery Cells
                log.debug "Battery Attribute 0x0051 Received"
                def batteryCells = ConvertHexValue(descMap.value)
                handleCellsEvent(batteryCells)   
			}else if ((descMap?.cluster == "0001" && descMap.attrInt == 0x0033)) {  //Battery Cells
                log.debug "Battery Attribute 0x0033 Received"
                def batteryCells = ConvertHexValue(descMap.value)
                handleCellsEvent(batteryCells)   
            }else if ((descMap?.cluster == "0001" && descMap.attrInt == 0x0021)) {  //Battery Percentage Remaining
                log.debug "Battery Attribute 0x0021 Received"                                                                 //  -- Ignore Attribute                                                             
            }else if (descMap?.cluster == "0000" && descMap.attrInt == 0x0004){  //Manufacture
                sendEvent(name: "manufacture", value: descMap.value)
                log.debug "Manufacturer is ${descMap.value}"
            }else if (descMap?.cluster == "0000" && descMap.attrInt == 0x0005){  //Model 
                sendEvent(name: "model", value: descMap.value)
                log.debug "Model is ${descMap.value}"
            }else {log.debug "Unknown --> Cluster-> ${descMap?.cluster}  AttrInt-> ${descMap.attrInt}  Value-> ${descMap.value}"
            }
       // log.debug "Cluster-> ${descMap?.cluster}  AttrInt-> ${descMap.attrInt}  Value-> ${descMap.value}"
    }else { 
        log.debug "Unparsed -> $description" 
        descMap = zigbee.parseDescriptionAsMap(description)
    }
    // log.debug "$descMap"
	return descMap   
}    

private parseCatchAllMessage(String description) {
    
    Map resultMap = [:]
    def descMap = zigbee.parse(description)  
    if (shouldProcessMessage(descMap)) {
        log.debug descMap.inspect()               
    }
    return resultMap
}

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

def refresh() {
	log.debug "Refreshing..."
    def refreshCmds = []
    refreshCmds +=

    zigbee.readAttribute(0x0500, 0x0001) +	   // IAS ZoneType
    zigbee.readAttribute(0x0500, 0x0000) +	   // IAS ZoneState
    zigbee.readAttribute(0x0500, 0x0002) +	   // IAS ZoneStatus
    zigbee.readAttribute(0x0502, 0x0000) +	   // Alarm Max Duration
    zigbee.readAttribute(0x0000, 0x0007) +	   // Power Source
    zigbee.readAttribute(0x0001, 0x0020) +     // Battery Voltage      
    zigbee.readAttribute(0x0001, 0x0033) +     // Battery Cells         
    zigbee.readAttribute(0x0000, 0x0004) +	   // Manufacturer Name
    zigbee.readAttribute(0x0000, 0x0005) +	   // Model Indentification
    zigbee.enrollResponse()
    
	return refreshCmds
}

def configure() {
    log.debug "Configuring..."
    
	if (!device.currentState('batteryLastReplaced')?.value)
		resetBatteryReplacedDate(true)
    
    def cmds = [
        // Bindings
        "zdo bind 0x${device.deviceNetworkId} 1 1 0x0500 {${device.zigbeeId}} {}", "delay 200"
        ] 
        // cmds += zigbee.enrollResponse(1200) 
        cmds += zigbee.enrollResponse() 
        cmds += zigbee.configureReporting(0x0500, 0x0002, 0x19, 0, 3600, 0x00)
        cmds += zigbee.configureReporting(0x0001, 0x0020, 0x20, 600, 7200, 0x01) 
        return cmds + refresh()
}

def resetBatteryReplacedDate(paired) {
	def newlyPaired = paired ? " for newly paired sensor" : ""
	sendEvent(name: "batteryLastReplaced", value: new Date())
	log.debug "${device.displayName} Setting Battery Last Replaced to Current date${newlyPaired}"
}

def resetSensorTestedDate() {
    def newlyTested=""
    sendEvent(name: "sensorLastTested", value: new Date())
    log.debug "${device.displayName} Setting Sensor Last Tested to Current date${newlyTested}"
}

def resetToClear() {
	sendEvent(name:"CO", value:"clear")
    sendEvent(name: "lastStatus", value: new Date(), displayed: True)
    log.debug "Resetting to Clear..."
	didWeGetClear = 0
}

/**
 * Code borrowed (mixed and matched) from both Daniel Terryn and veeceeoh
 *
 * Create battery event from reported battery voltage.
 *
 */

private handleBatteryEvent(rawVolts) {
    rawVolts = rawVolts / 10.0
	def minVolts = voltsmin ? voltsmin : 2.5
	def maxVolts = voltsmax ? voltsmax : 3.0
	def pct = (rawVolts - minVolts) / (maxVolts - minVolts)
	def roundedPct = Math.min(100, Math.round(pct * 100))
	log.debug "Battery level is ${roundedPct}% (${rawVolts} Volts)"
	
    sendEvent(name:"battery", value: roundedPct, unit: "%", isStateChange: true)
	return 
}

private handleCellsEvent(noCells) {
	log.debug "Battery reports that it has (${noCells} Cells)"
	return 
}

def ConvertHexValue(value) {
	if (value != null)
	{
		return Math.round(Integer.parseInt(value, 16))
	}
}

def SmokeOrClear(value) {
    if (value == "clear") {
        sendEvent(name:"CO", value:"clear", isStateChange: true)
        log.info "${device.displayName} reports status is all clear"
    } else {
        sendEvent(name:"CO", value:"detected", isStateChange: true)
        log.info "${device.displayName} reports CO is detected"
    }
    
    sendEvent(name: "lastStatus", value: new Date(), displayed: True)
}

private Map parseIasMessage(String description) {
    // log.debug "Zone Status Received--> ${description}"
    ZoneStatus zs = zigbee.parseZoneStatus(description)    
    translateZoneStatus(zs)    
}

private Map translateZoneStatus(ZoneStatus zs) {
	// Some sensor models that use this DTH use alarm1 and some use alarm2 to signify motion       
	return getSmokeResult(zs.isAlarm1Set() || zs.isAlarm2Set())
}

private Map getSmokeResult(value) {
    log.debug "${SensorTest}"
	if (value) {
        if (SensorTest == null ) {
            SensorTest = "1"    // Default is Yes
        }
        
        if (SensorTest == "1") {
    		def descriptionText = "${device.displayName} status is pending"
            sendEvent(name: "lastStatus", value: new Date(), displayed: True)
	    	log.debug descriptionText
		    runIn(3,EventOrTest)
		    return [
			    name			: 'CO',
			    value			: 'pending',
                isStateChange: false,
			    descriptionText : descriptionText
            ]
        } else {
    		def descriptionText = "${device.displayName} testing disabled - CO detected !!!"
	    	log.debug descriptionText
            sendEvent(name: "lastStatus", value: new Date(), displayed: True)
		    return [
			    name			: 'CO',
			    value			: 'detected',
                isStateChange: true,
			    descriptionText : descriptionText
            ]
        }                 
   } else {
		def descriptionText = "${device.displayName} all clear"
		log.debug descriptionText
        sendEvent(name: "lastStatus", value: new Date(), displayed: True)
		return [
			name			: 'CO',
			value			: 'clear',
            isStateChange: true,
			descriptionText : descriptionText
		]
	}
}		

def EventOrTest() {
    if (device.currentValue("CO") == "clear") {
		log.debug "${device.displayName} was tested sucessfully"
        resetSensorTestedDate()
	} else {
		log.debug "${device.displayName} This was not a test - CO detected !!!"
		sendEvent(name:"CO", value:"detected")
        sendEvent(name: "lastStatus", value: new Date(), displayed: True)
	}
}
1 Like

Did anyone get the Frient Intelligent Smoke Sensor to work with Hubitat?

And if you did.......is it possible to trigger the smoke alarm from Hubitat, e.g. as an extra siren when the burglar alarm goes off?