Konke motion sensor

I saw someone in a SmartThings group mention the Konke Motion sensor (available on AliExpress, Amazon, and probably other places). They were wondering if it worked, as it is advertised as being Zigbee 3.0. Somewhat shockingly, I could not find any mention of it here or in the ST Community forums. Maybe it's just that new. But since it was less than $15 (on Amazon when I got it; probably cheaper elsewhere), I decided to buy one and try it anyway.

Even more shockingly than any of the above, it paired with a "real" driver on Hubitat:

Generic Zigbee Motion Sensor

Manufacturer: Konke
Product Name:
Model Number: 3AFE14010402000D
deviceTypeId: 18
manufacturer : Konke
idAsInt : 1
inClusters : 0000,0001,0003,0500
endpointId : 01
profileId : 0104
application : 14
outClusters : 0003
initialized : true
model : 3AFE14010402000D
stage : 4

Slightly less shockingly, it doesn't 100% work: I can only get it to send motion "active" events, nothing for "inactive." This could be because those are the only kind of events the device sends (very uncommon, but this is also how the Xiaomi sensors work, and the driver works around it with a timer; perhaps that could be done with a custom driver here). Alternatively, maybe it needs specific Zigbee configuration sent to it in order to report these events that the generic driver does not do, maybe it uses nonstandard clusters (don't see anything in the logs if the generic driver would still catch these), or maybe it does get sent the right configuration but only listens for it at a specific moment (noted as a possibility here on an integration someone was trying write for Zigbee Shepherd)--or even a combination of all of these.

In any case, these are cheap (rivaling, perhaps even exeeding, Xiaomi on some sites) and small (again rivaling Xiaomi but really only mountable to flat surfaces like walls or ceilings--though Xiaomi, like nearly any other non-NYCE Zigbee sensor, wouldn't mount well to or look good on a ceiling, which I think this one would).

Other points to note:

  • Not sure if the battery reporting is off/non-standard or if mine just came with an old/slightly discharged battery--new, mine is reporting 62%
  • It is reported to work only in Zigbee channels 15, 20, and 25. Luckily, my Hubitat hub was on one of these. I was not able to test the original poster's question of SmartThings due to my ST hub being on 19 and ST's channel being non-changeable by the user.
  • No idea if these are really standard Zigbee 3.0 or if they'll be susceptible to the "falling off" problems the (in)famous similar cheap Xiaomi sensors are without careful selection of Zigbee routers

Anyway, just thought I'd share my findings here. Since they pair, there's at least some hope. :slight_smile:

PS - Should probably tag @mike.maxwell in case he's interested or has any experience with these (not sure if the generic driver caught it because enough of the fingerprint matches or if he too was playing around with one in the past).


Nice find, but not interested in supporting if it truly only works on three channels.
The inClusters matching one of the other fingerprints is the reason it picked that driver.
I've never heard of these before.

Definitely understand that--I struggled for a long time trying to get it paired to ST in order to answer someone's question about whether it worked there, then I did some Googling and discovered I probably never would get it to work since ST is stuck on channel 19 (assuming that person was correct about this device, of course, but my experience is certainly consistent).

The biggest problem I'm finding so far is that not only does the device not send "motion inactive" status, it doesn't seem to continually send "motion active" status, either, if motion activity is continually sustained. I know the Xiaomi devices don't send "inactive" either, but it's my understanding that they will send at least another "active" at (or within?) their 60-second reset window if continual motion is detected--this one doesn't seem to. There might be some configuration I'm missing but will likely not be able to figure out, so I guess the most decent workaround would be to use it where you don't anticipate a lot of continual motion and set the "fake" timeout in the driver to a relatively high value.

Here's a heavily modified Xiaomi driver (really should have just started from a "regular" Zigbee driver and added in the reset timer since all the parsing is redone, but too late now), which might be a good starting point if anyone else is interested in getting this to work. There are some sections where I'm not sure what the device does because I haven't managed to get it to do that yet (e.g., battery reports). Maybe someone will find this useful. I'll probably keep the sensor since it was so cheap but don't plan on getting any more unless someone figures out the inactivity problem. :slight_smile:

 *  Konke Motion Sensor (Kit-Pro BS Motion Sensor)
 *  Driver for Hubitat Elevation hub
 *  Version 0.1
 *  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.
 *  Portions of this code are based on the Xioami "Original" Motion Sensor code for Hubitat by veeceeoh,
 *  which in turn was based on the on SmartThings device handler code by a4refillpad.
 *  Previous Hubitat and ST code contains contributions by alecm, alixjg, bspranger, gn0st1c, foz333, jmagnuson, mike.maxwell, rinkek, ronvandegraaf,
 *  snalee, tmleafs, twonk, & veeceeoh

import hubitat.zigbee.clusters.iaszone.ZoneStatus

metadata {
	definition (name: "Konke Motion Sensor", namespace: "RMoRobert", author: "Robert Morris") {
		capability "Motion Sensor"
		capability "Sensor"
		capability "Battery"

		attribute "lastCheckin", "String"
		attribute "lastMotion", "String"
		attribute "lastInactive", "String"
		attribute "batteryLastReplaced", "String"

		fingerprint profileId: "0104", deviceId: "0104", inClusters: "0000, 0001, 0003, 0500", outClusters: "0003", manufacturer: "Konke", model: "3AFE14010402000D", deviceJoinName: "Konke Motion Sensor"

		command "resetBatteryReplacedDate"
		command "resetToMotionInactive"

	preferences {
		//Reset to No Motion Config
		input "motionreset", "number", title: "After motion is detected, wait ___ second(s) until resetting to inactive state. Default = 16 seconds (Hardware resets at 15 seconds)", description: "", range: "1..7200"
		//Battery Voltage Range
 		input name: "voltsmin", title: "Min Volts (0% battery = ___ volts, range 2.0 to 2.7). Default = 2.5 Volts", description: "", type: "decimal", range: "2..2.7"
 		input name: "voltsmax", title: "Max Volts (100% battery = ___ volts, range 2.8 to 3.4). Default = 3.0 Volts", description: "", type: "decimal", range: "2.8..3.4"
 		//Logging Message Config
 		input name: "infoLogging", type: "bool", title: "Enable info message logging", description: ""
 		input name: "debugLogging", type: "bool", title: "Enable debug message logging", description: ""

// Parse incoming device messages to generate events
def parse(String description) {
    Map map = [:]
    logDebug("Parsing: $description")
	if (description?.startsWith('zone status')) {	    
        def zs = zigbee.parseZoneStatus(description)
        if (zs.alarm1 == 1 && zs.battery == 0 && zs.trouble == 0) {
            logInfo("Motion detected")
            map = parseMotion()
        else if (zs.tamper == 1 && zs.battery == 1 && zs.trouble == 1 && zs.ac == 1) {
            log.debug "Device button pressed"
        else {
            log.warn "Zone status message not parsed"
            if (debugLogging) {
                log.warn "zs.alarm1 = $zs.alarm1"
                log.warn "zs.alarm2 = $zs.alarm2"
                log.warn "zs.tamper = $zs.tamper"
                log.warn "zs.battery = $zs.battery"
                log.warn "zs.supervisionReports = $zs.supervisionReports"
                log.warn "zs.restoreReports = $zs.restoreReports"
                log.warn "zs.trouble = $zs.trouble"
                log.warn "zs.ac = $zs.ac"
                log.warn "zs.test = $zs.test"
                log.warn "zs.batteryDefect = $zs.batteryDefect"
    else if (description?.startsWith("catchall") || description?.startsWith("read attr"))
        Map descMap = zigbee.parseDescriptionAsMap(description)
        // Guessing at how battery might work...
        if (descMap.value) { // should also check if power config cluster...
            List<Map> descMaps = collectAttributes(descMap)
            def battMap = descMaps.find { it.attrInt == 0x0020 }
		    if (battMap) {
	            map = getBatteryResult(Integer.parseInt(battMap.value, 16))
            if (map == [:] && descMaps.find { it.attrInt == 0x0021 }) log.warn "You may have guessed wrong on the battery cluster--check 0x0021?"
        if (map == [:]) {
            log.warn ("Description map not parsed: $dm")
    else {
        log.warn "Description not parsed"
    if (map != [:]) {
		logDebug("Creating event $map")
		return createEvent(map)
	} else
		return [:]

// Generate map for motion active report
private parseMotion() {
	def seconds = motionreset ? motionreset : 16
	// The sensor only sends a motion detected message so reset to motion inactive is performed in code
	runIn(seconds, resetToMotionInactive)
	sendEvent(name: "lastMotion", value: now())
	return [
		name: 'motion',
		value: 'active',
		isStateChange: true,
		descriptionText: "Detected motion",

// This is a guess...will want to see how device actually reports:
private getBatteryResult(rawValue) {
	logDebug("Parsing battery description: ${description}")
	def rawVolts = rawValue / 10
	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))
	def descText = "Battery level is ${roundedPct}% (${rawVolts} Volts)"
	def result = [
		name: 'battery',
		value: roundedPct,
		unit: "%",
		isStateChange: true,
		descriptionText: descText
	return result

// If currently in 'active' motion detected state, resetToMotionInactive() resets to 'inactive' state and displays 'no motion'
// Seems problematic for Konke because it does not repeatedly send "active" events if continually active (Xiaomi must?)
// May want to increase time...
def resetToMotionInactive() {
	if (device.currentState('motion')?.value == "active") {
		def seconds = motionreset ? motionreset : 16
		def descText = "Motion reset to inactive after ${seconds}s"
		sendEvent(name: "lastMotion", value: now())
			isStateChange: true,
			descriptionText: descText

//Reset the batteryLastReplaced date to current date
def resetBatteryReplacedDate(paired) {
	def newlyPaired = paired ? " for newly paired sensor" : ""
	sendEvent(name: "batteryLastReplaced", value: new Date())
	logInfo("Setting Battery Last Replaced to current date${newlyPaired}")

// installed() runs just after a sensor is paired
def installed() {
	state.prefsSetCount = 0

// configure() runs after installed() when a sensor is paired
def configure() {
	state.prefsSetCount = 1

// updated() will run every time user saves preferences
def updated() {
	logInfo("Updating preference settings")
	logInfo("Info message logging enabled")
	logDebug("Debug message logging enabled")

def init() {
	if (!device.currentState('batteryLastReplaced')?.value)

def logDebug(msg) {
    if (debugLogging) log.debug(msg)

def logInfo(msg) {
    if (infoLogging) log.info(msg)

just for curiosity's sake: did you succeeded in letting it work?

Everything I discovered is summarized above and still accurate. It reports motion, so that "works." It does not report inactivity, or at least I cannot figure out how to configure it to do so. This would make it problematic for most uses.

I just ordered one of these devices.. I guess I should have checked the forum first :frowning:

Any chance this will be picked up again and iterated to work with HE ?

Hey all,

I also got one of these and it works kind of fine.

I was able to tweak Zigbee motion code and added ability to detect the battery properly.

I have two problems:
1- Since I need to schedule a run for 30 sec to deactivate motion, I need to call "runIn" and for some reason when I set it to run Locally, that code won't run!
2- The sensor movement reporting has 3sec delay and I think it is because it is not run locally.

Any idea or help would be appreciated here.

Also, this is my code, I hope it can help other people to use the sensor :slight_smile:

 *  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.
 *  The code is based on zigbee-motion-detector from "jinkang zhang / jk0218.zhang@samsung.com"
import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
import physicalgraph.zigbee.zcl.DataType
metadata {
	definition(name: "Konko Motion Sensor", namespace: "ssalahi", author: "SmartThings", runLocally: false, mnmn: "SmartThings") {
		capability "Motion Sensor"
		capability "Configuration"
		capability "Battery"
		capability "Refresh"
		capability "Health Check"
		capability "Sensor"
		fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,0500,FCC0", outClusters: "0003,FCC0", manufacturer: "Konke", model: "3AFE14010402000D", deviceJoinName: "Konke Motion Sensor"
	simulator {
		status "active": "zone status 0x0001 -- extended status 0x00"
		for (int i = 0; i <= 100; i += 11) {
			status "battery ${i}%": "read attr - raw: 2E6D01000108210020C8, dni: 2E6D, endpoint: 01, cluster: 0001, size: 08, attrId: 0021, encoding: 20, value: ${i}"
	tiles(scale: 2) {
		multiAttributeTile(name: "motion", type: "generic", width: 6, height: 4) {
			tileAttribute("device.motion", key: "PRIMARY_CONTROL") {
				attributeState "active", label: 'motion', icon: "st.motion.motion.active", backgroundColor: "#00A0DC"
				attributeState "inactive", label: 'no motion', icon: "st.motion.motion.inactive", backgroundColor: "#cccccc"
		valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {
			state "battery", label: '${currentValue}% battery', unit: ""
		standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
			state "default", action: "refresh.refresh", icon: "st.secondary.refresh"
		details(["motion","battery", "refresh"])
    preferences {
		//Battery Voltage Offset
        input name: "voltsmin", title: "Min Volts (0% battery = ___ volts, range 2.0 to 2.9). Default = 2.5 Volts", description: "", type: "decimal", range: "2..2.9", defaultValue: 2.5
		input name: "voltsmax", title: "Max Volts (100% battery = ___ volts, range 2.95 to 3.4). Default = 3.2 Volts", description: "", type: "decimal", range: "2.95..3.4", defaultValue: 3.2

def stopMotion() {
	//log.debug "motion inactive"

def installed(){
	//log.debug "installed"
	return zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) +


def parse(String description) {
	//log.debug "description(): $description"
	def map = zigbee.getEvent(description)
	ZoneStatus zs
	if (!map) {
		if (description?.startsWith('zone status')) {
			zs = zigbee.parseZoneStatus(description)
			map = parseIasMessage(zs)
		} else {
			def descMap = zigbee.parseDescriptionAsMap(description)
			if (descMap?.clusterInt == zigbee.POWER_CONFIGURATION_CLUSTER) {
				map = batteyHandler(description)
			} else if (descMap?.clusterInt == zigbee.IAS_ZONE_CLUSTER && descMap.commandInt != 0x07 && descMap.value) {
				//log.debug "parseDescriptionAsMap: $descMap.value"
				zs = new ZoneStatus(zigbee.convertToInt(descMap.value, 16))
				map = parseIasMessage(zs)
	//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 physicalgraph.device.HubAction(it) }

	return result

def batteyHandler(String description){
	//log.debug "batteyHandler: ${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))
	return map

def parseIasMessage(ZoneStatus zs) {
	Boolean motionActive = zs.isAlarm1Set() || zs.isAlarm2Set()
    if (motionActive) {
        def timeout = 30
        //log.debug "Stopping motion in ${timeout} seconds"
        runIn(timeout, stopMotion)
	return getMotionResult(motionActive)

def getBatteryPercentageResult(rawValue) {	
    def rawVolts = rawValue / 10
    def minVolts = (voltsmin == null || voltsmin == "") ? 2.5 : voltsmin
    def maxVolts = (voltsmax == null || voltsmax == "") ? 3.2 : voltsmax

    def pct = (rawVolts - minVolts) / (maxVolts - minVolts)
    def roundedPct = Math.min(100, Math.round(pct * 100))
    //log.debug "Battery Percentage rawValue = ${rawValue} -> ${roundedPct}%"
    def result = [
        name: 'battery',
        value: roundedPct,
        unit: "%",
        isStateChange: true,
        descriptionText : "${device.displayName} Battery level is ${roundedPct}% (${rawVolts} Volts)"
	return result

def getMotionResult(value) {
	def descriptionText = value ? "${device.displayName} detected motion" : "${device.displayName} motion has stopped"
	return [
			name			: 'motion',
			value			: value ? 'active' : 'inactive',
			descriptionText : descriptionText,
			translatable	: true

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

def refresh() {
	//log.debug "Refreshing Values"
	return  zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) +
					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"])
		return zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021, DataType.UINT8, 30, 1200, 0x10) + refresh()

That driver has a lot of SmartThings leftovers in it and some things that will never work on Hubitat. I'd suggest reworking the battery code into mine. I may look more at that later. And if you mean "Running locally" in the definition, that does nothing on Hubitat so can be left out (and it also does nothing on SmartThings, at least now, unless maybe you wrote their hub firmware :slight_smile: ).

You are right, it has smartthings codes in it (I am using smartthings hub) but since bot of these hubs uses the same format, it can be reused in Hubitat as well.

Run locally is for running the code in the HUB instead of in the cloud, so in theory it should work faster and also when you don't have internet it should still work with your routines that are set locally.

BTW, since you are using Hubitat, do you also have around 3sec delay to get motion status?

Hubitat doesn't have an option for that in runIn, as everything is local.

Similar, not identical. For example, physicalgraph is not a namespace that exists in Hubitat. Often, the replacement is com.hubitat, but you can find more information on porting code if you search. I really did mean it when I said that some of this won't work on Hubitat. :slight_smile:

Again, What I said above is true. Specifying runLocally does nothing if you're not a SmartThings engineer (I don't even think it lets you, so that would explain the error...apparently you're on SmartThings? That would be good to state because it's not an assumption I'd make if you were posting in this thread), and it is not necessary on Hubitat because everything runs locally anyway.

No, mine is instant because it runs locally, and it's one of the reasons I left SmartThings. :slight_smile:


Smartthings allow you to have local Device Handler, but for some reason the Zigbee code require for this motion doesn't work on local!!

So, I took your advice and I am returning ST and getting a Hubitat hub :slight_smile:

Again, SmartThings does not really allow this. Not unless you work for them and decide which device handlers and apps get to run on the hub.

I think that is a good choice. :slight_smile:

It allowed me to run the Konke Temp/humidity on local and works perfectly fine!!

I'm sure what you say is true about the Smart App, but for device handler it is possible to run locally!

But anyway, it doesn't really matter anymore, since I'm moving to Hubitat. :+1:

No, the IDE just didn't throw an error when you set that value. I repeat: it does nothing, not unless you are an ST engineer baking specific SmartApps or DTHs into the ST hub firmware, the only way anything runs locally. This article explains more: Local processing – SmartThings Support (and a device running locally really doesn't matter when the majority of SmartAppps that allow you to actually use them don't run locally).

If local execution is important to you, I think Hubitat is a great choice. It's similar enough to SmartThings that if you've been using ST for a while, you're likely to get the hang of it quickly--and there are always a bunch of helpful people here if you get stuck!


Hello I'm very new to hubitat.
I bought the hub and I was thinking about a motion sensor.
I found Konke and I bought almost everything, the hub, the ir controlle, a couple of motion sensor and a contact switch for doors and windows.
I tried the konke hub but It hasn't configuration options.
SO my question is how can I configure all of them in hubitat hub??
I change my question, I menage to discover the sensor but I don't see any event.
How to set the door switch?

Thank you

The Konke hub does not integrate with Hubitat. (I'm not sure if that's what you were trying to do or just something you tried initially and now want to see if you can use these devices elsewhere instead.) If you want devices that work with Hubitat with a minimum of hassle, I'd recommend sticking to the list of compatible devices: List of Compatible Devices - Hubitat Documentation.

That being said, far more devices than are on this list will work with Hubitat. This includes most Z-Wave and Zigbee devices. It's still a good idea to check the list or the forums. For example, the Konke motion sensor is not officially compatible (not on that list) and can be made to somewhat work but doesn't seem to be able to be configured correctly to work well as far as anyone using Hubitat has been able to do (only been able to get it to report motion "active" events, so not really useful). I did have luck with the Konke temperature/humidity sensor, though no built-in driver exactly matches its capabilities.

I don't know about the door or window sensors. If any driver would work, it would be the "Generic Zigbee Contact Sensor" driver. Try resetting the device and pairing it to Hubitat via regular Zigbee discovery (you'll have to consult the Konke documentation to see how to reset it or put it in pairing mode). Change to this driver, click Save, and be sure to hit "Configure" after you switch to the new driver. This might not work but is your best hope.

tl;dr nobody knows about most of these because most people haven't tried most of these devices. At least one works well, and another pairs but doesn't really work fully. Your luck may vary with other devices. :slight_smile:


FWIW, the Konke temp/RH sensors work with Hubitat (albeit only on zigbee channels 15, 20, and 25).

1 Like

Had some extra time last night so I thought I'd pull this one out of the junk bin. Since converting my C-4 test hub to a second production hub, I've got a place for the problem children to live. Set the Zigbee channel to 20 and the Konke motion sensor joined right away. Loaded up your driver and after a few minutes (don't know why that initial delay) the motion sensor started working. I've been testing it every so often over the last 24 hours and it's not failed yet.

Thanks for this! Nice to have another motion sensor available, even if its field of view is very narrow. Might find a particularly good use for those characteristics. What have you used yours for, or are you using it at all?

I "liked" it so much that I'm using it outside, "protected" by clear packing tape in most directions (just wrapped some around the perimeter and made a little overhang on top). It's hung underneath a rail on my wooden fence but otherwise probably fairly exposed to the elements (rain, snow, cold, sun, etc.). So far, it's survived. :slight_smile:

Because it's pretty much useless for motion in most cases, this is (obviously) something I'm using outdoors, and my use case is simple: detect when I'm walking towards my backdoor from my backyard (often if I just drove and parked my car there). I use this as part of an outdoor lighting automation, and since I don't care to keep the light on if motion is continuously active (something not really likely to happen here, anyway), I don't care that I have no idea when activity stops--which, as you know, the driver just takes a stab at.