AeoTec or Aeon Multisensor Gen5

Thanks to @ chuck.schwer and @ krlaframboise for their help to make this one work

https://aeotec.freshdesk.com/support/solutions/articles/6000056451-multisensor-gen5-user-guide-

   /**
     *  
     *
     *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
     *  in compliance with the License. You may obtain a copy of the License at:
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
     *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
     *  for the specific language governing permissions and limitations under the License.
     *
     */
    metadata {
    	definition (name: "Aeon Multisensor Gen5", namespace: "Hubitat", author: "keltymd") {
    		capability "Motion Sensor"
    		capability "Temperature Measurement"
    		capability "Relative Humidity Measurement"
    		capability "Illuminance Measurement"
    		capability "Configuration"
    		capability "Sensor"
    		capability "Battery"
    		capability "Health Check"

    		fingerprint deviceId: "0x0701", inClusters: "0x5E,0x86,0x72,0x59,0x85,0x73,0x71,0x84,0x80,0x30,0x31,0x70,0x98,0x7A", outClusters:"0x5A"
    		fingerprint mfr:"0086", prod:"0102", model:"004A", deviceJoinName: "Aeotec MultiSensor (Gen 5)"
    	}

    	simulator {
    		status "no motion" : "command: 9881, payload: 00300300"
    		status "motion"    : "command: 9881, payload: 003003FF"

    		for (int i = 0; i <= 100; i += 20) {
    			status "temperature ${i}F": new hubitat.zwave.Zwave().securityV1.securityMessageEncapsulation().encapsulate(
    				new hubitat.zwave.Zwave().sensorMultilevelV2.sensorMultilevelReport(
    					scaledSensorValue: i, precision: 1, sensorType: 1, scale: 1)
    				).incomingMessage()
    		}

    		for (int i = 0; i <= 100; i += 20) {
    			status "humidity ${i}%":  new hubitat.zwave.Zwave().securityV1.securityMessageEncapsulation().encapsulate(
    				new hubitat.zwave.Zwave().sensorMultilevelV2.sensorMultilevelReport(scaledSensorValue: i, sensorType: 5)
    			).incomingMessage()
    		}

    		for (int i in [0, 20, 89, 100, 200, 500, 1000]) {
    			status "illuminance ${i} lux":  new hubitat.zwave.Zwave().securityV1.securityMessageEncapsulation().encapsulate(
    				new hubitat.zwave.Zwave().sensorMultilevelV2.sensorMultilevelReport(scaledSensorValue: i, sensorType: 3)
    			).incomingMessage()
    		}

    		for (int i in [0, 5, 10, 15, 50, 99, 100]) {
    			status "battery ${i}%":  new hubitat.zwave.Zwave().securityV1.securityMessageEncapsulation().encapsulate(
    				new hubitat.zwave.Zwave().batteryV1.batteryReport(batteryLevel: i)
    			).incomingMessage()
    		}
    		status "low battery alert":  new hubitat.zwave.Zwave().securityV1.securityMessageEncapsulation().encapsulate(
    			new hubitat.zwave.Zwave().batteryV1.batteryReport(batteryLevel: 255)
    		).incomingMessage()

    		status "wake up" : "command: 8407, payload: "
    	}

    	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("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
    			state "temperature", label:'${currentValue}°',
    			backgroundColors:[
    				[value: 32, color: "#153591"],
    				[value: 44, color: "#1e9cbb"],
    				[value: 59, color: "#90d2a7"],
    				[value: 74, color: "#44b621"],
    				[value: 84, color: "#f1d801"],
    				[value: 92, color: "#d04e00"],
    				[value: 98, color: "#bc2323"]
    			]
    		}
    		valueTile("humidity", "device.humidity", inactiveLabel: false, width: 2, height: 2) {
    			state "humidity", label:'${currentValue}% humidity', unit:""
    		}
    		valueTile("illuminance", "device.illuminance", inactiveLabel: false, width: 2, height: 2) {
    			state "luminosity", label:'${currentValue} lux', unit:""
    		}
    		valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
    			state "battery", label:'${currentValue}% battery', unit:""
    		}
    		standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
    			state "configure", label:'', action:"configure", icon:"st.secondary.configure"
    		}

    		main(["motion", "temperature", "humidity", "illuminance"])
    		details(["motion", "temperature", "humidity", "illuminance", "battery", "configure"])
    	}
    }

    def installed(){
    // Device-Watch simply pings if no device events received for 32min(checkInterval)
    	sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
    }

    def updated(){
    // Device-Watch simply pings if no device events received for 32min(checkInterval)
    	sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
    }

    def parse(String description)
    {
    	def result = null
    	if (description == "updated") {
    		result = null
    	} else {
    	def cmd = zwave.parse(description, [0x31: 5, 0x30: 1, 0x84: 1])
    		if (cmd) {
    			result = zwaveEvent(cmd)
    		}
    	}
    	log.debug "Parsed '${description}' to ${result.inspect()}"
    	return result
    }

    def zwaveEvent(hubitat.zwave.commands.wakeupv1.WakeUpNotification cmd)
    {
    	def result = [createEvent(descriptionText: "${device.displayName} woke up", isStateChange: false)]

    	if (!isConfigured()) {
    		// we're still in the process of configuring a newly joined device
    		log.debug("not sending wakeUpNoMoreInformation yet: late configure")
    		result += response(configure())
    	} else {
    		result += response(zwave.wakeUpV1.wakeUpNoMoreInformation())
    	}
    	result
    }

    def zwaveEvent(hubitat.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
    	def encapsulatedCommand = cmd.encapsulatedCommand([0x31: 5, 0x30: 1, 0x84: 1])
    	// log.debug "encapsulated: ${encapsulatedCommand}"
    	if (encapsulatedCommand) {
    		zwaveEvent(encapsulatedCommand)
    	} else {
    		log.warn "Unable to extract encapsulated cmd from $cmd"
    		createEvent(descriptionText: cmd.toString())
    	}
    }

    def zwaveEvent(hubitat.zwave.commands.batteryv1.BatteryReport cmd) {
    	def map = [ name: "battery", unit: "%" ]
    	if (cmd.batteryLevel == 0xFF) {
    		map.value = 1
    		map.descriptionText = "${device.displayName} battery is low"
    		map.isStateChange = true
    	} else {
    		map.value = cmd.batteryLevel
    	}
    	state.lastbatt = new Date().time
    	createEvent(map)
    }

    def zwaveEvent(hubitat.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd)
    {
    	def map = [:]
    	switch (cmd.sensorType) {
    		case 1:
    			map.name = "temperature"
    			def cmdScale = cmd.scale == 1 ? "F" : "C"
    			map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision)
    			map.unit = getTemperatureScale()
    			break;
    		case 3:
    			map.name = "illuminance"
    			map.value = cmd.scaledSensorValue.toInteger()
    			map.unit = "lux"
    			break;
    		case 5:
    			map.name = "humidity"
    			map.value = cmd.scaledSensorValue.toInteger()
    			map.unit = "%"
    			break;
    		default:
    			map.descriptionText = cmd.toString()
    	}
    	createEvent(map)
    }
    def zwaveEvent(hubitat.zwave.commands.sensorbinaryv1.SensorBinaryReport cmd) {
    	log.trace "SensorBinaryReport: $cmd"
    	createEvent(name:"motion", value:cmd.sensorValue ? "active" : "inactive")
    }


    def zwaveEvent(hubitat.zwave.Command cmd) {
    	createEvent(descriptionText: cmd.toString(), isStateChange: false)
    }

    /**
     * PING is used by Device-Watch in attempt to reach the Device
     * */
    def ping() {
    	secure(zwave.batteryV1.batteryGet())
    }

    def configure() {
    	// log.debug "configure()"
    	def request = []
    	// send temperature, humidity, and illuminance every 1 minute
    	request << zwave.configurationV1.configurationSet(parameterNumber: 101, size: 4, scaledConfigurationValue: 128|64|32)
    	request << zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: 60)

    	// send battery every 20 hours
    	request << zwave.configurationV1.configurationSet(parameterNumber: 102, size: 4, scaledConfigurationValue: 1)
    	request << zwave.configurationV1.configurationSet(parameterNumber: 112, size: 4, scaledConfigurationValue: 20*60*60)

    	// send no-motion report 60 seconds after motion stops
    	request << zwave.configurationV1.configurationSet(parameterNumber: 3, size: 2, scaledConfigurationValue: 60)

    	// send binary sensor report instead of basic set for motion
    	request << zwave.configurationV1.configurationSet(parameterNumber: 5, size: 1, scaledConfigurationValue: 2)

    	// Turn on the Multisensor Gen5 PIR sensor
    	request << zwave.configurationV1.configurationSet(parameterNumber: 4, size: 1, scaledConfigurationValue: 1)

    	// disable notification-style motion events
    	request << zwave.notificationV3.notificationSet(notificationType: 7, notificationStatus: 1)

    	request << zwave.batteryV1.batteryGet()
    	request << zwave.sensorBinaryV2.sensorBinaryGet(sensorType: 0x0C) //motion
    	request << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 0x01) //temperature
    	request << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 0x03) //illuminance
    	request << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 0x05) //humidity

    	setConfigured()

    	secureSequence(request) + ["delay 20000", zwave.wakeUpV1.wakeUpNoMoreInformation().format()]
    }

    private setConfigured() {
    	device.updateDataValue("configured", "true")
    }

    private isConfigured() {
    	device.getDataValue(["configured"]).toString() == "true"
    }

    private secure(hubitat.zwave.Command cmd) {
    	zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
    }

    private secureSequence(commands, delay=200) {
    	delayBetween(commands.collect{ secure(it) }, delay)
    }
2 Likes

With ST my Gen5 multisensor worked fine. I've tried using this custom driver with Hubitat and see the motion status is stuck as active, even if nothing is near the sensor for many minutes. I recently replaced the batteries, so that shouldn't be the cause. I also removed and re-paired the device. Thoughts on what may be causing this and how to resolve the issue?

I had one doing that a few months ago. I though it was bad and started a return for warranty work. I had tried excluding, reseting, and re-paired with no luck. Right before sending it in, I tried a reset, then upgraded the firmware to the latest. Now works like brand new. I did end up buying an aditional one directly from the factory and was surprised that the one I received had an old firmware. came with 1.8 . New was 1.10

Did anyone actually get this working? Mine doesn't report anything -- in fact Motion isn't even a category when this driver is loaded.

I have the same issue, nothing reported. These devices worked well with ST but trying to get everything off ST onto HE.. Got 2 of these to move over

1 Like

anyone ever get the motion to work right?

How do you upgrade the firmware? Mine seem to be on 1.1 ???

Edit, mine is a gen 6 (I think)

Has anyone ever gotten the motion sensor to show up on Hubitat? I can get everything working except motion, which is what I need the most.

Yes, I'm running it with the built in gen 6 DTH and the sensor is sending motion events. I have ran this sensor on the gen 6 DTH as well as one I ported from ST but I prefer the built in gen 6 DTH.