Sengled Smart LED PAR38 with built in Motion and Daylight Sensors

Is anyone else trying to use these bulbs? They are element bulbs and I can get the hub to see them but only appears to be able to turn the light on and off. The motion and daylight sensors apparently aren't seen.

So I'm guessing the motion and daylight sensors are not separate Zigbee devices. They appear to be only internal to the bulb since motion triggered the lights to turn on last night.

I was curious about these. The description online says motion events get reported to the app via the Sengled or Smartthing hub. That would mean they either send motion state as a capability of the bulb or it's a separate device altogether and needs its own driver.

They are separate devices in SmartThings but until someone writes a Hubitat driver for them they will only show up as a light.

Thanks for the info, I checked the ST community site and it's an official integration so no user created DTH for us convert. Another device that we just need to be patient and wait for HE team to get working.

I don't have one of these bulbs so won't be much of help but you can find the ST driver for these bulbs here:

Wow, thanks! I don't have this bulb either, they just look interesting. Outdoor rated AND a motion sensor. The OP has one and just uses as a bulb, hopefully he can try this out and let us know how it does.

I have a pair of them on the front of my house. I'll see if I can modify this driver to work with Hubitat, when I get the time.

2 Likes

Well I got it working as a light with the normal substitutions for Hubitat in the groovy code but that's about it.

I'm getting this error:
groovy.lang.MissingPropertyException: No such property: ATTRIBUTE_IAS_ZONE_STATUS for class: com.hubitat.zigbee.Zigbee on line 143 (configure)

And I see nothing as far as a motion device showing up.

I am getting motion reports in the logs but I can't use the light as a motion device to trigger anything.

[dev:130]2019-07-08 09:13:57.793 pm [debug]Parse returned [name:motion, value:inactive, descriptionText:Front Flood South motion has stopped, translatable:true]

[dev:130]2019-07-08 09:13:57.790 pm [debug]description: zone status 0x0020 -- extended status 0x00

[dev:130]2019-07-08 09:13:36.604 pm [debug]Parse returned [name:switch, value:on]

[dev:130]2019-07-08 09:13:36.595 pm [debug]Parse returned [name:level, value:100]

[dev:130]2019-07-08 09:13:36.593 pm [debug]description: read attr - raw: 645F01000808000020FF, dni: 645F, endpoint: 01, cluster: 0008, size: 08, attrId: 0000, encoding: 20, command: 0A, value: FF

[dev:130]2019-07-08 09:13:36.587 pm [debug]description: read attr - raw: 645F0100060800001001, dni: 645F, endpoint: 01, cluster: 0006, size: 08, attrId: 0000, encoding: 10, command: 0A, value: 01

[dev:130]2019-07-08 09:13:36.560 pm [debug]Parse returned [name:motion, value:active, descriptionText:Front Flood South detected motion, translatable:true]

[dev:130]2019-07-08 09:13:36.556 pm [debug]description: zone status 0x0021 -- extended status 0x00

Correction, it is triggering things based on motion. My initial test was to have Hubitat txt me on motion but I guess txt'ing isn't working right now.

1 Like

DISCLAIMER: I Haven't actually learned Groovy but I know enough to hack things together sometimes. lol

Ok, got it working. Apparently the motion portion of it doesn't immediately kick in, it takes about 10/15 minutes. This could be because of the check interval but I don't think so because I lowered it and it still took a while to start detecting motion on the 2nd light I changed to this driver.

Also, after about 10/15 minutes the "ATTRIBUTE_IAS_ZONE_STATUS" error goes away. I'm guessing it takes a couple of refreshes to get all the info it needs. Someone who knows Groovy better would know for sure.

/**
 *  Copyright 2019 cstory777
 *
 *  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.
 *
 */
import hubitat.zigbee.clusters.iaszone.ZoneStatus

metadata {
	definition (name: "Singled PAR38 with Motion Sensor", namespace: "cstory777", author: "cstory777", ocfDeviceType: "oic.d.light", runLocally: true, minHubCoreVersion: '000.019.00012', executeCommandsLocally: true) {
		capability "Actuator"
		capability "Configuration"
		capability "Refresh"
		capability "Light"
		capability "Switch"
		capability "Switch Level"
		capability "Motion Sensor"
		capability "Health Check"

		fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0500, 0702, 0B05, FC01", outClusters: "0019", manufacturer: "sengled", model: "E13-N11", deviceJoinName: "Sengled Smart LED with Motion Sensor PAR38 Bulb"
	}

	tiles(scale: 2) {
		multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
			tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
				attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#00A0DC", nextState:"turningOff"
				attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
				attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#00A0DC", nextState:"turningOff"
				attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
			}
			tileAttribute ("device.level", key: "SLIDER_CONTROL") {
				attributeState "level", action:"switch level.setLevel"
			}
		}
		standardTile("motion", "device.motion", decoration: "flat", width: 2, height: 2) {
			state "active", label: 'motion', icon: "st.motion.motion.active", backgroundColor: "#00A0DC"
			state "inactive", label: 'no motion', icon: "st.motion.motion.inactive", backgroundColor: "#cccccc"
		}
		standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
			state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
		}
		main "switch"
		details(["switch", "motion", "refresh"])
	}
}

// Parse incoming device messages to generate events
def parse(String description) {
	log.debug "description: $description"

	Map map = zigbee.getEvent(description)
	if (!map) {
		if (description?.startsWith('zone status')) {
			map = parseIasMessage(description)
		} else {
			def descMap = zigbee.parseDescriptionAsMap(description)
			if (descMap && descMap.clusterInt == 0x0006 && descMap.commandInt == 0x07) {
				if (descMap.data[0] == "00") {
					log.debug "ON/OFF REPORTING CONFIG RESPONSE: " + cluster
					sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
				} else {
					log.warn "ON/OFF REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
				}
			} else if (device.getDataValue("manufacturer") == "sengled" && descMap && descMap.clusterInt == 0x0008 && descMap.attrInt == 0x0000) {
				// This is being done because the sengled element touch/classic incorrectly uses the value 0xFF for the max level.
				// Per the ZCL spec for the UINT8 data type 0xFF is an invalid value, and 0xFE should be the max.  Here we
				// manually handle the invalid attribute value since it will be ignored by getEvent as an invalid value.
				// We also set the level of the bulb to 0xFE so future level reports will be 0xFE until it is changed by
				// something else.
				if (descMap.value.toUpperCase() == "FF") {
					descMap.value = "FE"
				}
				sendHubCommand(zigbee.command(zigbee.LEVEL_CONTROL_CLUSTER, 0x00, "FE0000").collect { new hubitat.device.HubAction(it) }, 0)
				map = zigbee.getEventFromAttrData(descMap.clusterInt, descMap.attrInt, descMap.encoding, descMap.value)
			} else if (descMap?.clusterInt == 0x0500 && descMap.attrInt == 0x0002) {
				def zs = new ZoneStatus(zigbee.convertToInt(descMap.value, 16))
				map = translateZoneStatus(zs)
			} else if (descMap?.clusterInt == zigbee.IAS_ZONE_CLUSTER && descMap.attrInt == zigbee.ATTRIBUTE_IAS_ZONE_STATUS && descMap?.value) {
				map = translateZoneStatus(new ZoneStatus(zigbee.convertToInt(descMap?.value)))
			} else {
				log.warn "DID NOT PARSE MESSAGE for description : $description"
				log.debug "${descMap}"
			}
		}
	} else if (map.name == "level" && map.value == 0) {
		map = [:]
	}

    //log.info "${device.data}"
	log.debug "Parse returned $map"
	def result = map ? createEvent(map) : [:]

	return result
}

private Map parseIasMessage(String description) {
	ZoneStatus zs = zigbee.parseZoneStatus(description)

	return translateZoneStatus(zs)
}

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

private Map 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
	]
}

def off() {
	zigbee.off()
}

def on() {
	zigbee.on()
}

def setLevel(value, rate = null) {
	zigbee.setLevel(value)
}
/**
 * PING is used by Device-Watch in attempt to reach the Device
 * */
def ping() {
	return zigbee.onOffRefresh() + zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS)
}

def refresh() {
    zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS)
}

def setupHealthCheck() {
	// Device-Watch allows 2 check-in misses from device + ping (plus 1 min lag time)
	// enrolls with default periodic reporting until newer 5 min interval is confirmed
	sendEvent(name: "checkInterval", value: 2 * 10 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
}

def installed() {
	setupHealthCheck()
}

def configure() {
	log.debug "Configuring Reporting and Bindings."
	setupHealthCheck()

	// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
	zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.enrollResponse() + refresh()
}
2 Likes

Nice. I will definitely be giving this a try when I can sneak another bulb purchase.

Watch the price on them, it's not what I'd call stable. I paid $20 each for mine but I see right now they are going for $30 each.

btw, the motion detection works even in daylight even though Sengled says it takes "dusk" or darker to activate that.

Hi, has anyone gotten the code from cstory777 to work with the Sengled motion bulb. I can't get past loading this driver into hubitat. Thanks.

What errors are you getting?

Hi, I just added 2 Par38 bulbs using cstory777 driver. I added the driver, and paired the bulbs with no problem. I'm unable to get the motion sensor to work. It allows me to create rules off the motion attribute. Nothing shows up in the log file when I trigger the motion sensor. Any ideas on what to try?

Did you hit "configure" and then save? Also, it does take a bit for the motion to start showing up in the logs. About 15 minutes.

Yes I did. Will the motion status show up in the current states on the device settings? Or do you have to view the motion status from the log.

You should see it in events for the device and the logs for the device.

Events:

Logs: