[Deprecated] Xiaomi / Aqara ZigBee device drivers (possibly may no longer be maintained)

I've got an error when directly copying Shinjiang ST driver into Hubitat:

Value too long for column "PROFILE_ID VARCHAR(4)": "'0x0104' (6)"; SQL statement: INSERT INTO FINGERPRINT(DEVICE_TYPE_ID, PROFILE_ID, ENDPOINT_ID, IN_CLUSTERS, OUT_CLUSTERS, MANUFACTURER, MODEL, DEVICE_JOIN_NAME, DEVICE_ID, MFR, PROD, CONTROLLER_TYPE) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [22001-197]

Have you made any modifications to his driver?

No, I posted the direct link to his Github account

Do Xiaomi outlets (either plug or wall) not work at all with Hubitat, or will the generic driver work, but it won't support all the features (like power-usage reporting)?

Edit: To answer my own question - Generic Zigbee Driver outlet works, but it doesn't handle power reporting. I see entries in the log when I turn device connected to the outlet on, as expected, but nothing is shown in device's event list.

I have another question - how do you go about showing the pressure reading from Aqara Temperature Sensor?

I know there was a SuperTile app from Cobra, but it's now pulled from github, so are there any other options?

You can show any attribute from a device by selecting the Attribute tile and then picking the attribute.

I managed to get it done that way, thanks!
I tried it earlier, but attributes I got in the list didn't correspond to the Temperature sensor for some reason.

You have to make sure you've picked the right device first.

This version will indeed provide power-usage reporting/
"
/**

  • Xiaomi Smart Plug - model ZNCZ02LM
  • Device Handler for SmartThings
  • Version 1.2
  • 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.
  • Based on original device handler by Lazcad / RaveTam
  • Updates and contributions to code by a4refillpad, bspranger, marcos-mvs, mike-debney, Tiago_Goncalves, and veeceeoh
  • Modified by Pablo to work under Hubitat --- REVERSE HEX ORDER!!!
    */

metadata {
definition (name: "Xiaomi Zigbee Outlet", namespace: "bspranger", author: "bspranger") {
capability "Actuator"
capability "Configuration"
capability "Refresh"
capability "Switch"
capability "Temperature Measurement"
capability "Sensor"
capability "Power Meter"
capability "Energy Meter"

	attribute "lastCheckin", "string"
	attribute "lastCheckinDate", "String"
}

// simulator metadata
simulator {
	// status messages
	status "on": "on/off: 1"
	status "off": "on/off: 0"
	// reply messages
	reply "zcl on-off on": "on/off: 1"
	reply "zcl on-off off": "on/off: 0"
}

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.lastCheckin", key: "SECONDARY_CONTROL") {
			attributeState("default", label:'Last Update:\n ${currentValue}',icon: "st.Health & Wellness.health9")
		}
	}
	valueTile("temperature", "device.temperature", width: 2, height: 2) {
		state("temperature", label:'${currentValue}°',
			backgroundColors:[
				[value: 31, color: "#153591"],
				[value: 44, color: "#1e9cbb"],
				[value: 59, color: "#90d2a7"],
				[value: 74, color: "#44b621"],
				[value: 84, color: "#f1d801"],
				[value: 95, color: "#d04e00"],
				[value: 96, color: "#bc2323"]
			]
		)
	}
	valueTile("power", "device.power", width: 2, height: 2) {
		state("power", label:'${currentValue}W', backgroundColors:[
			[value: 0, color: "#ffffff"],
			[value: 1, color: "#00a0dc"]
		])
	}
	valueTile("energy", "device.energy", width: 2, height: 2) {
		state("energy", label:'${currentValue}kWh')
	}
	standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
		state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
	}
	main (["switch", "power"])
		details(["switch", "power", "energy", "temperature", "refresh"])
}

preferences {
	//Temp Offset Config
	input description: "", type: "paragraph", element: "paragraph", title: "OFFSETS & UNITS"
	input "tempOffset", "decimal", title:"Temperature Offset", description:"Adjust temperature by this many degrees", range:"*..*"
	//Date & Time Config
	input description: "", type: "paragraph", element: "paragraph", title: "DATE & CLOCK"
	input name: "dateformat", type: "enum", title: "Set Date Format\n US (MDY) - UK (DMY) - Other (YMD)", description: "Date Format", options:["US","UK","Other"]
	input name: "clockformat", type: "bool", title: "Use 24 hour clock?"
}

}

// Parse incoming device messages to generate events
def parse(String description) {
log.debug "${device.displayName}: Parsing message: '${description}'"
def value = zigbee.parse(description)?.text
log.debug "${device.displayName}: Zigbee parse value: $value"
Map map = [:]

// Determine current time and date in the user-selected date format and clock style
def now = formatDate()
def nowDate = new Date(now).getTime()

// The receipt of any message results in a lastCheckin (heartbeat) event
sendEvent(name: "lastCheckin", value: now, displayed: false)
sendEvent(name: "lastCheckinDate", value: nowDate, displayed: false)

if (description?.startsWith('catchall:')) {
	map = parseCatchAllMessage(description)
}
else if (description?.startsWith('read attr -')) {
	map = parseReportAttributeMessage(description)
}
else if (description?.startsWith('on/off: ')){
	map = parseCustomMessage(description)
}

if (map) {
	log.debug "${device.displayName}: Creating event ${map}"
	return createEvent(map)
} else
	return [:]

}

private Map parseCatchAllMessage(String description) {
Map resultMap = [:]
def zigbeeParse = zigbee.parse(description)
log.debug "${device.displayName}: Catchall parsed as $cluster"

if (zigbeeParse.clusterId == 0x0006 && zigbeeParse.command == 0x01){
	def onoff = zigbeeParse.data[-1]
	if (onoff == 1)
		resultMap = createEvent(name: "switch", value: "on")
	else if (onoff == 0)
		resultMap = createEvent(name: "switch", value: "off")
}
return resultMap

}

private Map parseReportAttributeMessage(String description) {
Map descMap = (description - "read attr - ").split(",").inject([:]) {
map, param ->
def nameAndValue = param.split(":")
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
}

Map resultMap = [:]

if (descMap.cluster == "0001" && descMap.attrId == "0020") {
	resultMap = getBatteryResult(convertHexToInt(descMap.value / 2))
}
if (descMap.cluster == "0002" && descMap.attrId == "0000") {
	log.debug "${device.displayName}: description is  ${description}"
    def tempScale = getTemperatureScale()
    log.debug "${device.displayName}: argument HEX is ${descMap.value}"
    def revtemp = reverseHexString (descMap.value)
    log.debug "${device.displayName}: argument HEX (rev) is ${revtemp}"
    log.debug "${device.displayName}: calculo original INT /2 is ${convertHexToInt(descMap.value) / 2}"
    log.debug "${device.displayName}: calculo bigendian argument INT (rev) /2 is ${convertHexToInt(revtemp) / 2}"
    def tempValue = convertHexToInt(revtemp) / 2 + (tempOffset ? tempOffset : 0)

	//def tempValue = zigbee.parseHATemperatureValue("temperature: " + (convertHexToInt(descMap.value) / 2), "temperature: ", tempScale) + (tempOffset ? tempOffset : 0)
    log.debug "${device.displayName}: description is  ${description}"
    //def value = ((description - "temperature: ").trim()) as Float
	resultMap = createEvent(name: "temperature", value: tempValue, unit: tempScale, translatable: true)
	log.debug "${device.displayName}: Reported temperature is ${resultMap.value}°$tempScale"
}
else if (descMap.cluster == "0008" && descMap.attrId == "0000") {
	resultMap = createEvent(name: "switch", value: "off")
}
else if (descMap.cluster == "000C" && descMap.attrId == "0055" && descMap.endpoint == "02") {
    def rev = reverseHexString (descMap.value)
    //log.debug "${device.displayName}: HEX value reversed is ${rev}"
    //def wattage_int = Long.parseLong(descMap.value, 16)
    def wattage_int = Long.parseLong(rev, 16)
	def wattage = Float.intBitsToFloat(wattage_int.intValue())
	wattage = Math.round(wattage * 10) * 0.1
	resultMap = createEvent(name: "power", value: wattage, unit: 'W')
	log.debug "${device.displayName}: Reported power use is ${wattage}W"
}
else if (descMap.cluster == "000C" && descMap.attrId == "0055" && descMap.endpoint == "03") {
	
    def revener = reverseHexString (descMap.value)
    def energy_int = Long.parseLong(revener, 16)
	def energy = Float.intBitsToFloat(energy_int.intValue())
	energy = Math.round(energy * 100) * 0.0001
	resultMap = createEvent(name: "energy", value: energy, unit: 'kWh')
	log.debug "${device.displayName}: Reported energy usage is ${energy}kWh"
}
return resultMap

}

def off() {
log.debug "${device.displayName}: Turning switch off"
sendEvent(name: "switch", value: "off")
"st cmd 0x${device.deviceNetworkId} 1 6 0 {}"
}

def on() {
log.debug "${device.displayName}: Turning switch on"
sendEvent(name: "switch", value: "on")
"st cmd 0x${device.deviceNetworkId} 1 6 1 {}"
}

def refresh() {
log.debug "${device.displayName}: Attempting to refresh all values"
def refreshCmds = [
"st rattr 0x${device.deviceNetworkId} 1 6 0", "delay 500",
"st rattr 0x${device.deviceNetworkId} 1 6 0", "delay 250",
"st rattr 0x${device.deviceNetworkId} 1 2 0", "delay 250",
"st rattr 0x${device.deviceNetworkId} 1 1 0", "delay 250",
"st rattr 0x${device.deviceNetworkId} 1 0 0", "delay 250",
"st rattr 0x${device.deviceNetworkId} 2 0x000C 0x0055",
"delay 250",
"st rattr 0x${device.deviceNetworkId} 3 0x000C 0x0055"
]
return refreshCmds
}

private Map parseCustomMessage(String description) {
def result
if (description?.startsWith('on/off: ')) {
if (description == 'on/off: 0')
result = createEvent(name: "switch", value: "off")
else if (description == 'on/off: 1')
result = createEvent(name: "switch", value: "on")
}
return result
}

private Integer convertHexToInt(hex) {
Integer.parseInt(hex,16)
}

// Reverses order of bytes in hex string
def reverseHexString(hexString) {
def reversed = ""
for (int i = hexString.length(); i > 0; i -= 2) {
reversed += hexString.substring(i - 2, i )
}
return reversed
}

def formatDate() {
def correctedTimezone = ""
def timeString = clockformat ? "HH:mm:ss" : "h:mm:ss aa"

// If user's hub timezone is not set, display error messages in log and events log, and set timezone to GMT to avoid errors
if (!(location.timeZone)) {
	correctedTimezone = TimeZone.getTimeZone("GMT")
	log.error "${device.displayName}: Time Zone not set, so GMT was used. Please set up your location in the SmartThings mobile app."
	sendEvent(name: "error", value: "", descriptionText: "ERROR: Time Zone not set, so GMT was used. Please set up your location in the SmartThings mobile app.")
}
else {
	correctedTimezone = location.timeZone
}
if (dateformat == "US" || dateformat == "" || dateformat == null) {
	return new Date().format("EEE MMM dd yyyy ${timeString}", correctedTimezone)
}
else if (dateformat == "UK") {
	return new Date().format("EEE dd MMM yyyy ${timeString}", correctedTimezone)
}
else {
	return new Date().format("EEE yyyy MMM dd ${timeString}", correctedTimezone)
}

}
"

2 Likes

Example:

Current States

  • energy : 0.3287
  • lastCheckin : Sat Oct 19 2019 10:28:34 PM
  • lastCheckinDate : 1571534914000
  • power : 3.1
  • switch : on
  • temperature : 18.5

Thanks! It's nice to have the outlet working properly too.

I've just noticed this error in the logs for 2 of my Aqara Motion Sensors.
Everything is still working OK but I just thought I would report it in case there is a fix needed.

2019-10-26 16:10:03.568 errorjava.lang.NullPointerException: null on line 71 (parse)

EDIT: Actually the devices are not reporting. Probably because I removed a zigbee outlet (it wasn't routing anything) and I turned my hub off for 30 mins so that the outlet disappeared from the routing table. Strange that the motion sensor must be sending info to the hub but not reporting motion. No worries a quick re-pair has done the job.

Anyone with experience of this device with HE?

AQara Smart Door Touch Lock ZigBee Connection For Home Security Anti-Peeping Design Work With Mi Home APP Support IOS Android
https://s.click.aliexpress.com/e/FTiIWR48

I have a non-driver related question, but I hope people here have experience - what do you do to tell your buttons apart? I mean not in the app, but physically when you have a couple of them on the desk? I have this memory that I saw pictures of mijia buttons with logo in different colours like red and blue, but I can't find those images online anywhere, so I must have dreamt it :slight_smile:

I have a Xiaomi motion sensor that goes active when my curtains open.
Does anyone know if there is a way to temporarily disable the device.
This way I could write a rule to disable the device between certain times.
TIA

How about take different colored Sharpie pens and color in the logos on each?
But do you really have that many different actions you want all on buttons? Each Xiaomi Mijia button can do 6 different button actions!

Hello, I just added a Xiaomi Original Button and motion sensor to my HE.

Maybe this can help someone who is trying to pair these devices.
After I click reset button and led light flash 3 times, my hubitat detected the devices but it was stuck on "Initializing" message. I tried countless times with no success.
Then I copied the Zigbee Id that was above the "initializing" message and I added it to a new virtual device with this zigbee id.

Then I selected the community device driver in the checkbox "type". I saved the device, then I put again my Hubitat in Zigbee discovery mode again, and I clicked again in the reset button. Leds flashed three times and after some seconds led flashed three times again and the devices were finally paired.

That's the only way I found to do it, but maybe there's some easier trick

3 Likes

I have also found that if they get stuck in the initialising state you can sometimes press the restart zigbee pairing and keep pushing the button to wake them up and they pair OK.

3 Likes

More a question for @veeceeoh - the use of the Aqaa vibration sensor for washing machine detection isn't going well. It just isn't sensing enough motion to register.

Is there a way to access the activity level generated? That way I can set the threshold in my rules instead of relying on the sensitivity.

I have one of those. Looking for a use for it, because as you discovered, it’s not very sensitive. The New Samsung SmartThings Multisensor is not expensive and much more sensitive.

However, owning one of the very energy efficient, and smooth running washing machines out there, I’ve come to the conclusion that if you have a running light on your machine, you’ll be hard pressed to find a better solution than the Homeseer HS-FS100-L

Thanks, I might try the Samsung sensor it I can't get access to the activity attribute. Unfortunately the FS100 is just a little bit expensive here in Australia !