SmartThings Community

Xiaomi Zigbee Door/Window Sensor, Motion Sensor, & Smart Button Device Type [beta]

dth_motionsensor
dth_remotes
dth_sensors

(Jan) #657

One question regarding the Mi Temperature/Humidity Sensor. Mine is working reliably, but I had to add it manually (by creating a device in the IDE using the Network Device ID from the ST Hub log). That way, however, battery reporting doesn’t seem to work - as opposed to the other Xiaomi devices that I was able to add regularly using the the “Add a thing” function. No matter what I tried, the Temperature Sensor wouldn’t show up on the regular device search.

Has anyone succeeded getting battery readings from the Temperature Sensor?


(Paul) #658

No one has modified the DTH for the temp sensor to read battery yet (I think).


(Wayne) #659

Not sure if it has or not, (big thread now) but once I receive mine I will give it a go.


(Paulo Nóbrega) #660

the hun update already happened right???
all is still good?

at least my sensors seem to still work

@brumster are those kudled switchs still for sale? didnt saw any


(Darren) #661

My sensors are still active too.

@Paulo_Nobrega no, the Kudled switches are no longer available. The link in the other thread was the only place anyone has managed to find these switches, AFAIK todate. Based on a private conversation @a4refillpad had with the sellers, it looks like they did a specific production run for the switches themselves and have sold out and aren’t planning a new run (at least any time soon).


#662

#663

I’m using 3 buttons and a motion sensor. Using the latest DTH on this thread everything is really stable, the sensor UX and logging looks great. Thanks a lot for all the hard work.


(Justn ) #664

Using a motion sensor, temp sensor, and 3 contacts sensors. All good so far after a few weeks.

Motion detector doest like the cold though. Here in Florida it got down into the 30s last night and it stopped working. Checked it this afternoon when I got home and it’s fine now that’s it’s warmed up. It’s fast and great field of view. Very happy especially for the price.


(Not Dexter) #665

Hi all,

I’ve read much of this thread, but can’t seem to definitively determine if these Xiaomi sensors work with the US (United States) ST v2 hub? I see most of you are overseas from me, so before I drop any $, wondered if they even work over here? Again, maybe I missed it way up in the thread.

Thanks!


(Neal (www.zebrablinds.com / www.zebrablinds.ca)) #666

I can confirm that they work (although I only have 10 xiaomi motion sensors). The funny thing is better range and coverage than my ST motion sensor and significantly smaller form factor.


(Not Dexter) #667

Thanks @ZebraBlinds. I’m in for 1 door/window sensor to give it a shot.


#668

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?


(Paul) #669

Are you using the DTH posted by @a4refillpad above?


#670

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


(Paul) #671

See post 510


#672

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.


(Paul) #673

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


(Yi Nagel) #674

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


(Paul) #675

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”


(Paul) #677

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
}