[OBSOLETE] Xioami Zigbee Door/Window Sensor, Motion Sensor, & Smart Button Device Type

Am I correct that the motion sensor doesn’t report battery level? I’m still testing my motion sensor but can’t get it to report any battery info nor keep it from disconnecting overnight when there is no activity in the hallway. Are there instructions/video to add the motion sensor to stay connected/paired properly as pressing the button many times doesn’t seem to work for me?

Are you using the DTH posted by @a4refillpad above?

Hi from Greece,i would like to congradualate everyone in this forum and topic for all the efforts.
I buy before two weeks two temp sensors and they work pretty well except battery report,that will like to ask is if it’s possible to make the sensor report with even more accuaracy of 0.1 degrees right now is reporting every 0.5 degrees of difference.Is a way to do it with the device handler?
And again thanks to everyone for all the efforts

See post 510

I’m using a4’s DTH. Funny thing is I was about to give up on this motion sensor after messing around with it for a week now with no luck on battery or staying connected. Yesterday afternoon on my last attempt to pairing and frantically pushing the buttons, it seemed to have stuck this time. I was able to see battery level and it has not disconnected in about 20hrs. I do see weird numbers with battery level but seems to have stabilized around 90-92% now after initially starting out at 93% and rising up to 98% before going back down to 90%. If it stays connected for a week, I’ll order some more to test as family really likes the really compact size of this sensor and responsive when connected.

Battery will fluctuate depending on temp if you are moving around. Glad you got it working!

Is there a device handler for Xiaomi Temperature/Humidity Sensor?

No, not yet.

I think @a4refillpad intends to look at it when he gets his delivered.

In the meantime you need the catcall code and then you can use the standard “SmartSense Temp/Humidity Sensor”

Thanks. Just installed!

Tidied up for cut and paste here:

/**
* Copyright 2015 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.
*
*/
metadata {
definition (name: "Xiaomi Temperature Sensor", namespace: "smartthings", author: "SmartThings") {
capability "Configuration"
capability "Battery"
capability "Refresh"
capability "Temperature Measurement"
capability "Relative Humidity Measurement"
capability "Health Check"
capability "Sensor"

	fingerprint endpointId: "01", inClusters: "0001,0003,0020,0402,0B05,FC45", outClusters: "0019,0003"
}

simulator {
	status 'H 40': 'catchall: 0104 FC45 01 01 0140 00 D9B9 00 04 C2DF 0A 01 000021780F'
	status 'H 45': 'catchall: 0104 FC45 01 01 0140 00 D9B9 00 04 C2DF 0A 01 0000218911'
	status 'H 57': 'catchall: 0104 FC45 01 01 0140 00 4E55 00 04 C2DF 0A 01 0000211316'
	status 'H 53': 'catchall: 0104 FC45 01 01 0140 00 20CD 00 04 C2DF 0A 01 0000219814'
	status 'H 43':  'read attr - raw: BF7601FC450C00000021A410, dni: BF76, endpoint: 01, cluster: FC45, size: 0C, attrId: 0000, result: success, encoding: 21, value: 10a4'
}

preferences {
	input title: "Temperature Offset", description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
	input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
}

tiles(scale: 2) {
    multiAttributeTile(name:"temperature", type: "generic", width: 6, height: 4){
		tileAttribute ("device.temperature", key: "PRIMARY_CONTROL") {
			attributeState "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("humidity", "device.humidity", inactiveLabel: false, width: 2, height: 2) {
		state "humidity", label:'${currentValue}% humidity', unit:""
	}
    
	valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {
		state "battery", label:'${currentValue}% battery'
	}
    
    /*
	standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
		state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
	}
	*/
    
	main "temperature", "humidity"
	details(["temperature", "humidity", "battery", "refresh"])
}
}

def parse(String description) {
log.debug "description: $description"

Map map = [:]
if (description?.startsWith('catchall:')) {
	map = parseCatchAllMessage(description)
}
else if (description?.startsWith('read attr -')) {
	map = parseReportAttributeMessage(description)
}
else if (description?.startsWith('temperature: ') || description?.startsWith('humidity: ')) {
	map = parseCustomMessage(description)
}

log.debug "Parse returned $map"
return map ? createEvent(map) : [:]
}

private Map parseCatchAllMessage(String description) {
Map resultMap = [:]
def cluster = zigbee.parse(description)
if (shouldProcessMessage(cluster)) {
switch(cluster.clusterId) {
case 0x0001:
// 0x07 - configure reporting
if (cluster.command != 0x07) {
resultMap = getBatteryResult(cluster.data.last())
}
break

        case 0x0402:
			if (cluster.command == 0x07) {
				if (cluster.data[0] == 0x00){
					log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster
					resultMap = [name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]]
				}
				else {
					log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
				}
			}
			else {
				// temp is last 2 data values. reverse to swap endian
				String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()               
				def value = getTemperature(temp)
				resultMap = getTemperatureResult(value)
			}
			break

		case 0xFC45:
			// 0x07 - configure reporting
			if (cluster.command != 0x07) {
				String pctStr = cluster.data[-1, -2].collect { Integer.toHexString(it) }.join('')
				String display = Math.round(Integer.valueOf(pctStr, 16) / 100)
				resultMap = getHumidityResult(display)
			}
            break
    }
}

return resultMap
}

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

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()]
}
log.debug "Desc Map: $descMap"

Map resultMap = [:]
if (descMap.cluster == "0402" && descMap.attrId == "0000") {
	def value = getTemperature(descMap.value)
	resultMap = getTemperatureResult(value)
}
else if (descMap.cluster == "0001" && descMap.attrId == "0020") {
	resultMap = getBatteryResult(Integer.parseInt(descMap.value, 16))
}
else if (descMap.cluster == "FC45" && descMap.attrId == "0000") {
	def value = getReportAttributeHumidity(descMap.value)
	resultMap = getHumidityResult(value)
}

return resultMap
}

def getReportAttributeHumidity(String value) {
def humidity = null
if (value?.trim()) {
try {
// value is hex with no decimal
def pct = Integer.parseInt(value.trim(), 16) / 100
humidity = String.format('%.0f', pct)
} catch(NumberFormatException nfe) {
log.debug "Error converting $value to humidity"
}
}
return humidity
}

private Map parseCustomMessage(String description) {
Map resultMap = [:]
if (description?.startsWith('temperature: ')) {
//def value = zigbee.parseHATemperatureValue(description, "temperature: ", getTemperatureScale()) as Float
def value = ((description - "temperature: ").trim()) as Float
if (value >= 100) {
log.debug 'TEMPERATURE: over 100'
value = 0 - (value - 100)
}
if (getTemperatureScale() != "C") {
value = Math.round(celsiusToFahrenheit(value))
}
resultMap = getTemperatureResult(value)
}
else if (description?.startsWith('humidity: ')) {
def pct = (description - "humidity: " - "%").trim()
if (pct.isNumber()) {
def value = Math.round(new BigDecimal(pct)).toString()
resultMap = getHumidityResult(value)
} else {
log.error "invalid humidity: ${pct}"
}
}
return resultMap
}

def getTemperature(value) {
def celsius = Integer.parseInt(value, 16).shortValue() / 100
if (getTemperatureScale() == "C") {
return celsius
} 
else {
return celsiusToFahrenheit(celsius) as Integer
}
}

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

def result = [:]

def volts = rawValue / 10
if (!(rawValue == 0 || rawValue == 255)) {
	def minVolts = 2.1
	def maxVolts = 3.0
	def pct = (volts - minVolts) / (maxVolts - minVolts)
	def roundedPct = Math.round(pct * 100)
	if (roundedPct <= 0)
		roundedPct = 1
	result.value = Math.min(100, roundedPct)
	result.descriptionText = "${linkText} battery was ${result.value}%"
	result.name = 'battery'

}

return result
}

private Map getTemperatureResult(value) {
def linkText = getLinkText(device)
if (tempOffset) {
def offset = tempOffset //as int
Float v = value as Float
value = (v + offset) as Float
}
Float nv = Math.round( (value as Float) * 10.0 ) / 10	// Need at least one decimal point
value = nv as Float
def descriptionText = "${linkText} was ${value}°${temperatureScale}"
return [
name: 'temperature',
value: value,
descriptionText: descriptionText,
unit: temperatureScale
]
}

private Map getHumidityResult(value) {
log.debug 'Humidity'
return value ? [name: 'humidity', value: value, unit: '%'] : [:]
}

/*
*
* PING is used by Device-Watch in attempt to reach the Device
*
*/

def ping() {
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
}

def refresh()
{
log.debug "refresh temperature, humidity, and battery"
return zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0xC2DF]) + // Original firmware
zigbee.readAttribute(0x0402, 0x0000) +
zigbee.readAttribute(0x0001, 0x0020)
}

def configure() {
// 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 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])

log.debug "Configuring Reporting and Bindings."
def humidityConfigCmds = [
	"zdo bind 0x${device.deviceNetworkId} 1 1 0xFC45 {${device.zigbeeId}} {}", "delay 500",
	"zcl global send-me-a-report 0xFC45 0 0x29 30 3600 {6400}",
	"send 0x${device.deviceNetworkId} 1 1", "delay 500"
]

// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
// battery minReport 30 seconds, maxReportTime 6 hrs by default
return refresh() + humidityConfigCmds + zigbee.batteryConfig() + zigbee.temperatureConfig(30, 300)  // send refresh cmds as part of config
}

private hex(value) {
new BigInteger(Math.round(value).toString()).toString(16)
}

private String swapEndianHex(String hex) {
reverseArray(hex.decodeHex()).encodeHex()
}

private byte[] reverseArray(byte[] array) {
int i = 0;
int j = array.length - 1;
byte tmp;
while (j > i) {
tmp = array[j];
array[j] = array[i];
array[i] = tmp;
j--;
i++;
}
return array
}
2 Likes

yep…
was gonna posted it easier… a few posts back…

link to the DHT for temperature / humidity sensor

https://community.smartthings.com/t/xiaomi-zigbee-door-window-sensor-motion-sensor-smart-button-device-type-beta/31948/550?u=paulo_nobrega

and how to add it:

https://community.smartthings.com/t/xiaomi-zigbee-door-window-sensor-motion-sensor-smart-button-device-type-beta/31948/535?u=paulo_nobrega

2 Likes

Missed that one! Thanks for clarifying. Does it report battery then?

damn…just hate this kind of forum… LOL cant just link to a previous… well…

@brumster it doesn’t report battery… but thats the only DH i could found here… with such a long post it’s really hard for a newbie to find anything here…
but it works… i have some doubts about humidity sensor, but i’ve just received a new one and gonna use it to compare…

1 Like

Does this work with smarttthings?

Xiaomi Magic Cube

Yep. Installed one this morning!

Everything you need to know is here:

What’s that for??

The Xiaomi cube ???

One of these: http://xiaomi-mi.com/mi-smart-home/xiaomi-mi-smart-home-cube-white/

Probably best to consider it as a 6 function button (but slightly cooler IMO). I intend to use for control of lights in the lounge via some simple CoRE rules.

They are not expensive either!

1 Like

For the Xiaomi Cube, you may refer to link below

Already linked above.

I’m in the UK and looking to get apme contact and motion sensors.
Amazom uk is not showing anything and ebay are all posted fron China.
Where are you uk peeps getting them from.

Contact sensors available from eBay in Salford I think:

I use Banggood.com. Check the EU warehouse as opposed to the Chinese one, if you can get them from there the delivery is usually quite good.