Temperature sensor with 0.1 precision? (EU)

Hi! I just got SmartThings Hub + Multipurpose Sensor for measuring & adjusting my house’s warming thru IFTTT and Tado. I got them up & running easily, but for my surprise and disappointment, it seems that this sensor can measure the temperature only at the precision of one full degree (celcius, in my case), not decimals at all! Or at least, this is what the Samsung app is showing…

So my questions are:

  • Is it correct that Samsung’s own sensor’s precision is only one full degree?
  • Are there any better temperature sensor available for this hub, with the precision of ~0.1 degrees?

What country are you in? The device selection does vary somewhat.

BATTERY POWERED AMBIENT TEMPERATURE SENSORS

In general, devices intended for the European or Asian market are more likely to offer temperature intervals of less than 1°. Even then, most of the ones in the price range that most smartthings customers are looking for only guarantee accuracy down to 0.5°C.

The Fibaro multisensor available on either the US or UK frequency is a good example. It can be set to report in intervals down to as low as 0.1°C, but it only guarantees accuracy down to 0.5°. The Sensative Comfort sensors are the same: they will report in 0.1°C intervals, but only guarantee accuracy of 0.5°

Probe sensors

There are some DS18B20 probe sensors which have a higher accuracy, quite often down to 0.01°C. These are not smart in themselves, you attach them physically to a smart device, probably most commonly a Fibaro RGBW controller which can handle up to four probe sensors. Or there’s a Qubino model that comes with its own probe. These are frequently used in gardening or aquaculture or brewing.

However, they usually aren’t good at just checking the ambient temperature in the air, as they get confused when the temperature is different on different sides of the probe. They are really designed for immersive applications like soil or water temperatures.

Mains powered sensors

You will generally get better accuracy from a mains powered sensor then from a battery powered sensor just because the battery powered sensors tend to try not to take too many readings in order to extend the battery life.

A community member has designed and built a plug-in zigbee sensor with excellent accuracy which has been very popular among community members who don’t require branded products and aren’t worried about childproofing or similar issues. I don’t think he has any for sale right now, but you could create your own version if you have strong technical skills, and it can also give you an idea of the difference in using Mains powered devices. you can read about it in the following thread:

Another Zigbee Repeater Solution (With Temperature, Humidity, Pressure and Light sensors)

Or you could use an Arduino and add high accuracy temperature sensors to it and then use the “ST_anything” project to report the values into smartthings. Again, that requires significant maker skills, but some community members are doing it.

[RELEASE] ST_Anything - Arduino/ESP8266/ESP32

There are some ready-made high accuracy mains power sensors, but they tend to be much more expensive than most smartthings customers want to use.

Summary

For for normal climate comfort control, adjusting room heating or cooling, you should find quite a few options that are battery powered, respond to the ambient air temperature, can report in intervals of 0.1°C but are only accurate up to 0.5°C.

You are more likely to find these in devices which are branded “multisensor“ rather than a “motion sensor“ that happens to have a temperature sensor as well. That’s just a matter of keeping costs down on the part of the manufacturer.

For use cases requiring greater accuracy that can use an immersed probe sensor, you should also find several options. Note that these are not usually good for measuring room temperature in the ambient air because the probe gets confused when temperatures are different on different sides of the probe itself.

If you really need better accuracy than 0.5°C for ambient temperature, you will probably have to go to a Mains powered device and may even have to build your own.

I hope that helps. :sunglasses:

1 Like

Thank you very much for you support JDRoberts, you’re the best! :=)

Actually I think that the accuracy of 0.5° is just fine with me, if the reporting interval is 0.1°. I will check Fibaro and Sensative in more detail, they both seem to be available here in Finland below 50€ so I can easily afford one just for this purpose.

1 Like

I know the device specification side pretty well, but to be honest I’m not that familiar with the smartthings app as it is not very voice friendly. So it would be good to confirm with someone else that you can actually display values and create automation based on something less then 1,0 .

@aruffell or @a4refillpad might know.

I’ve got a fibaro UBS that gives me 0.1 degrees with a DS18B20 sensorand I use 0.5 degree increments in my automations

1 Like

Look at this topic for a fix of the multisensor’s DH, if you don’t mind that it would run in the cloud, not locally.

Both the multi purpose and the motion sensors are reporting decimal values, but the DH rounds up or down.

1 Like

Sorry but I do not quite understand this part… First of all; what do you mean with DH? And; if I get Fibaro or Sensative sensor which will give decimal values for the Samsung’s hub, will the hub round those up and I still cannot get precise decimal values for IFTTT?

Some overview here:

Thanks GSzabados, but how about my previous question of Fibaro or Sensative, will they work out-of-the-box? I tried to read that article about custom code, but did not understand ANYTHING since I’m no coder etc. I just wanted to get some automation that works…

I don’t have the Fibaro or Sensative devices, so I cannot answer that question. I don’t know are they use custom code or stock DHs. @JDRoberts might be able to answer that.

My answer was for your original question regarding the SmartThings multipurpose device. It can report decimal values with a change of the DH code. If you don’t want to change the code, you can buy other devices which has code already displaying decimal temperature values. But changing a few lines in that DH’s code is cheaper than spending 50 Euros on another device.

1 Like

Sorry if I once again did not be precise enough (english is my third language anyway ;=)… But I’m not concerned about the Fibaro or Sensative sensors as such, because they surely CAN report the temp with 0.1 degree steps. What I meant to ask is that does the Samsung’s hub always round those numbers for full degrees, no matter which sensor I use?

No, it doesn’t. It depends on what the device reports to the Hub and how the Device Handler’s code is processing that report. The stock SmartThings Multipurpose sensor’s code rounds it. The code can be changed to do not round the reported values, what are originally reported in decimal values.
(The whole rounding is coming from the US view point of ST, as nobody defines Fahrenheit in decimal values…)

1 Like

The hub doesn’t.

The hub communicates with end devices through a DTH (device type handler), a tiny bit of code that determines the format for messages sent to/from a specific device AND what will be displayed in the SmartThings app for that device. Sort of like a “printer driver” works with a laptop to format messages to a specific printer model.

SmartThings has some “stock” DTHs which are available to all customers. In many cases customers can modify these somewhat, which was the suggestion @GSzabados was making to change the display format from whole integer to one showing decimals.

Customers can also create their own custom DTHs from scratch, usually to expose features of a specific device that the stock DTHs don’t reveal.

So you might have a choice of several different DTHs for any one device. There could be a stock generic DTH. A modified version of that DTH. And one or more custom DTHs created by community members. You choose the one that has the features and display format you prefer.

I hope that wasn’t too confusing. :sunglasses:

1 Like

We do, but only for scientific or industrial purposes. Not for Home thermostats. :sunglasses:

The classic example is a medical thermometer, when fractions count. The average human body temperature is 98.6° Fahrenheit. (37°C.)

The temperature over 100.4°F (38°C) typically indicates illness.

So medical thermometers sold in the US always show fractions.

However, room thermostats often do not, as it allows for larger numbers which are easier to read from across the room and most humans can’t feel a difference. :face_with_monocle:

2 Likes

@GSzabados - thanks for this. I’m trying to figure out how to report decimal temp values from my newly bought ST Multisensors. I grabbed the latest code from Github and it’s no obvious where the code converts the value to an integer.

I found your suggested code in this thread. So my temperature section looks like this:

} else if (maps[0].name == "temperature") {
    def map = maps[0]
    def decimalValue = Double.parseDouble(description.split(": ")[1])
    map.value = (float) Math.round( (decimalValue as Float) * 10.0 ) / 10
    if (tempOffset) {
        map.value = (float) map.value + (float) tempOffset
    }
    map.descriptionText = temperatureScale == 'C' ? '{{ device.displayName }} was {{ value }}°C' : '{{ device.displayName }} was {{ value  }}°F'
    map.translatable = true
}

I also changed "runlocally to “false”.

definition(name: “SmartSense Multi Sensor 2”, namespace: “smartthings”, author: “SmartThings”, runLocally: false, minHubCoreVersion: ‘000.017.0012’, executeCommandsLocally: false, mnmn: “SmartThings”, vid: “generic-contact-2”) {

It doesn’t appear to change anything in either the old or new ST app. I noticed that the tile code near the top only appears to affect the “Classic app” as I bumped temperature so that it shows on the Classic dashboard however this doesn’t work for the new app dashboard. Strange that the two apps wouldn’t leverage the same code.

I’m clearly doing something wrong as it worked for @Jon_ST. Thoughts?

I guess, but have you saved, published and changed the DH selected for the device?

Otherwise has the temperature changed on the device itself? If it hasn’t reported a new temperature then it wouldn’t update anywhere in the apps.

Thanks for the quick reply. Published and being used. I can confirm that as I’ve adjusted the code in the tiles section and it has changed the layout in the Classic App for just that sensor. I’m only using the adjusted code on one sensor now so I can check for differences between it and other sensors.

Temperature has changed on the sensor but not decimal reporting. I’m using celsius but that shouldn’t matter. I’m wondering if @Jon_ST changed code somewhere else as he talked about replacing Int values but I don’t see anywhere else it makes sense.

The tiles section has this code:

valueTile(“temperature”, “device.temperature”, width: 6, height: 4) {
state(“temperature”, label: '${currentValue}

However I’m not sure if “currentValue” and “device.temperature” would be pulled from the map array. I don’t know why the code doesn’t load temp into a single variable and then use that variable throughout.

Can you post here your code or send me as a PM?

Look at live logging meanwhile, is there any error there?

I’ll watch the live logging but nothing unusual now. The full code is:

/*
 *  Copyright 2016 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.
 */
import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
import physicalgraph.zigbee.zcl.DataType

metadata {
	definition(name: "SmartSense Multi Sensor 2", namespace: "smartthings", author: "SmartThings", runLocally: false, minHubCoreVersion: '000.017.0012', executeCommandsLocally: false, mnmn: "SmartThings", vid: "generic-contact-2") {

		capability "Three Axis"
		capability "Battery"
		capability "Configuration"
		capability "Sensor"
		capability "Contact Sensor"
		capability "Acceleration Sensor"
		capability "Refresh"
		capability "Temperature Measurement"
		capability "Health Check"

		fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3320", deviceJoinName: "SmartThings Multipurpose Sensor"
		fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3321", deviceJoinName: "SmartThings Multipurpose Sensor"
		fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3321-S", deviceJoinName: "SmartThings Multipurpose Sensor" //Multipurpose Sensor
		fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500,FC02", outClusters: "0019", manufacturer: "SmartThings", model: "multiv4", deviceJoinName: "SmartThings Multipurpose Sensor" //Multipurpose Sensor
		fingerprint inClusters: "0000,0001,0003,0020,0402,0500,FC02", outClusters: "0019", manufacturer: "Samjin", model: "multi", deviceJoinName: "SmartThings Multipurpose Sensor" //Multipurpose Sensor

	}

	simulator {
		status "open": "zone report :: type: 19 value: 0031"
		status "closed": "zone report :: type: 19 value: 0030"

		status "acceleration": "acceleration: 1"
		status "no acceleration": "acceleration: 0"

		for (int i = 10; i <= 50; i += 10) {
			status "temp ${i}C": "contactState: 0, accelerationState: 0, temp: $i C, battery: 100"
		}

		// kinda hacky because it depends on how it is installed
		status "x,y,z: 0,0,0": "x: 0, y: 0, z: 0"
		status "x,y,z: 1000,0,0": "x: 1000, y: 0, z: 0"
		status "x,y,z: 0,1000,0": "x: 0, y: 1000, z: 0"
		status "x,y,z: 0,0,1000": "x: 0, y: 0, z: 1000"
	}
	preferences {
		section {
			image(name: 'educationalcontent', multiple: true, images: [
					"http://cdn.device-gse.smartthings.com/Multi/Multi1.jpg",
					"http://cdn.device-gse.smartthings.com/Multi/Multi2.jpg",
					"http://cdn.device-gse.smartthings.com/Multi/Multi3.jpg",
					"http://cdn.device-gse.smartthings.com/Multi/Multi4.jpg"
			])
		}
		section {
			input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
		}
		section {
			input("garageSensor", "enum", title: "Do you want to use this sensor on a garage door?", description: "Tap to set", options: ["Yes", "No"], defaultValue: "No", required: false, displayDuringSetup: false)
		}
	}

	tiles(scale: 2) {
		multiAttributeTile(name:"contact", type: "generic", width: 2, height: 2) {
			tileAttribute("device.contact", key: "PRIMARY_CONTROL") {
				attributeState("open", label: 'Open', icon: "st.contact.contact.open", backgroundColor: "#e86d13")
				attributeState("closed", label: 'Closed', icon: "st.contact.contact.closed", backgroundColor: "#00a0dc")
			}
		}
		standardTile("acceleration", "device.acceleration", width: 2, height: 2) {
			state("active", label: 'Active', icon: "st.motion.acceleration.active", backgroundColor: "#00a0dc")
			state("inactive", label: 'Inactive', icon: "st.motion.acceleration.inactive", backgroundColor: "#cccccc")
		}
		valueTile("temperature", "device.temperature", width: 6, height: 4) {
			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("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {
			state "battery", label: '${currentValue}% battery', unit: ""
		}
		standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
			state "default", action: "refresh.refresh", icon: "st.secondary.refresh"
		}


		main(["temperature", "acceleration", "contact"])
		details(["temperature", "acceleration", "contact", "battery", "refresh"])
	}
}

private List<Map> collectAttributes(Map descMap) {
	List<Map> descMaps = new ArrayList<Map>()

	descMaps.add(descMap)

	if (descMap.additionalAttrs) {
		descMaps.addAll(descMap.additionalAttrs)
	}

	return  descMaps
}

def parse(String description) {
	def maps = []
	maps << zigbee.getEvent(description)
	if (!maps[0]) {
		maps = []
		if (description?.startsWith('zone status')) {
			maps += parseIasMessage(description)
		} else {
			Map descMap = zigbee.parseDescriptionAsMap(description)

			if (descMap?.clusterInt == zigbee.POWER_CONFIGURATION_CLUSTER && descMap.commandInt != 0x07 && descMap.value) {
				List<Map> descMaps = collectAttributes(descMap)

				if (device.getDataValue("manufacturer") == "Samjin") {
					def battMap = descMaps.find { it.attrInt == 0x0021 }

					if (battMap) {
						maps += getBatteryPercentageResult(Integer.parseInt(battMap.value, 16))
					}
				} else {
					def battMap = descMaps.find { it.attrInt == 0x0020 }

					if (battMap) {
						maps += getBatteryResult(Integer.parseInt(battMap.value, 16))
					}
				}
			} else if (descMap?.clusterInt == 0x0500 && descMap.attrInt == 0x0002) {
				def zs = new ZoneStatus(zigbee.convertToInt(descMap.value, 16))
				maps += translateZoneStatus(zs)
			} else if (descMap?.clusterInt == zigbee.TEMPERATURE_MEASUREMENT_CLUSTER && descMap.commandInt == 0x07) {
				if (descMap.data[0] == "00") {
					log.debug "TEMP REPORTING CONFIG RESPONSE: $descMap"
					sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"])
				} else {
					log.warn "TEMP REPORTING CONFIG FAILED- error code: ${descMap.data[0]}"
				}
			} else if (descMap?.clusterInt == zigbee.IAS_ZONE_CLUSTER && descMap.attrInt == zigbee.ATTRIBUTE_IAS_ZONE_STATUS && descMap?.value) {
				maps += translateZoneStatus(new ZoneStatus(zigbee.convertToInt(descMap?.value)))
			} else {
				maps += handleAcceleration(descMap)
			}
		}
	} else if (maps[0].name == "temperature") {
        def map = maps[0]
        def decimalValue = Double.parseDouble(description.split(": ")[1])
        map.value = (float) Math.round( (decimalValue as Float) * 10.0 ) / 10
        if (tempOffset) {
            map.value = (float) map.value + (float) tempOffset
        }
        map.descriptionText = temperatureScale == 'C' ? '{{ device.displayName }} was {{ value }}°C' : '{{ device.displayName }} was {{ value }}°F'
        map.translatable = true
	}

	def result = maps.inject([]) {acc, it ->
		if (it) {
			acc << createEvent(it)
		}
	}
	if (description?.startsWith('enroll request')) {
		List cmds = zigbee.enrollResponse()
		log.debug "enroll response: ${cmds}"
		result = cmds?.collect { new physicalgraph.device.HubAction(it) }
	}
	return result
}

private List<Map> handleAcceleration(descMap) {
	def result = []
	if (descMap.clusterInt == 0xFC02 && descMap.attrInt == 0x0010) {
		def value = descMap.value == "01" ? "active" : "inactive"
		log.debug "Acceleration $value"
		result << [
				name           : "acceleration",
				value          : value,
				descriptionText: "{{ device.displayName }} was $value",
				isStateChange  : isStateChange(device, "acceleration", value),
				translatable   : true
		]

		if (descMap.additionalAttrs) {
			result += parseAxis(descMap.additionalAttrs)
		}
	} else if (descMap.clusterInt == 0xFC02 && descMap.attrInt == 0x0012) {
		def addAttrs = descMap.additionalAttrs ?: []
		addAttrs << ["attrInt": descMap.attrInt, "value": descMap.value]
		result += parseAxis(addAttrs)
	}
	return result
}

private List<Map> parseAxis(List<Map> attrData) {
	def results = []
	def x = hexToSignedInt(attrData.find { it.attrInt == 0x0012 }?.value)
	def y = hexToSignedInt(attrData.find { it.attrInt == 0x0013 }?.value)
	def z = hexToSignedInt(attrData.find { it.attrInt == 0x0014 }?.value)

	if ([x, y ,z].any { it == null }) {
		return []
	}

	def xyzResults = [:]
	if (device.getDataValue("manufacturer") == "SmartThings") {
		// This mapping matches the current behavior of the Device Handler for the Centralite sensors
		xyzResults.x = z
		xyzResults.y = y
		xyzResults.z = -x
	} else {
		// The axises reported by the Device Handler differ from the axises reported by the sensor
		// This may change in the future
		xyzResults.x = z
		xyzResults.y = x
		xyzResults.z = y
	}

	log.debug "parseAxis -- ${xyzResults}"

	if (garageSensor == "Yes")
		results += garageEvent(xyzResults.z)

	def value = "${xyzResults.x},${xyzResults.y},${xyzResults.z}"
	results << [
			name           : "threeAxis",
			value          : value,
			linkText       : getLinkText(device),
			descriptionText: "${getLinkText(device)} was ${value}",
			handlerName    : name,
			isStateChange  : isStateChange(device, "threeAxis", value),
			displayed      : false
	]
	results
}

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

	translateZoneStatus(zs)
}

private List<Map> translateZoneStatus(ZoneStatus zs) {
	List<Map> results = []

	if (garageSensor != "Yes") {
		def value = zs.isAlarm1Set() ? 'open' : 'closed'
		log.debug "Contact: ${device.displayName} value = ${value}"
		def descriptionText = value == 'open' ? '{{ device.displayName }} was opened' : '{{ device.displayName }} was closed'
		results << [name: 'contact', value: value, descriptionText: descriptionText, translatable: true]
	}

	return results
}

private Map getBatteryResult(rawValue) {
	log.debug "Battery rawValue = ${rawValue}"

	def result = [:]

	def volts = rawValue / 10

	if (!(rawValue == 0 || rawValue == 255)) {
		result.name = 'battery'
		result.translatable = true
		result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"

		if (device.getDataValue("manufacturer") == "SmartThings") {
			volts = rawValue // For the batteryMap to work the key needs to be an int
			def batteryMap = [28: 100, 27: 100, 26: 100, 25: 90, 24: 90, 23: 70,
							  22: 70, 21: 50, 20: 50, 19: 30, 18: 30, 17: 15, 16: 1, 15: 0]
			def minVolts = 15
			def maxVolts = 28

			if (volts < minVolts)
				volts = minVolts
			else if (volts > maxVolts)
				volts = maxVolts
			def pct = batteryMap[volts]
			result.value = pct
		} else {
			def useOldBatt = shouldUseOldBatteryReporting()
			def minVolts = 2.1
			def maxVolts = useOldBatt ? 3.0 : 2.7

			// Get the current battery percentage as a multiplier 0 - 1
			def curValVolts = Integer.parseInt(device.currentState("battery")?.value ?: "100") / 100.0
			// Find the corresponding voltage from our range
			curValVolts = curValVolts * (maxVolts - minVolts) + minVolts
			// Round to the nearest 10th of a volt
			curValVolts = Math.round(10 * curValVolts) / 10.0
			// Only update the battery reading if we don't have a last reading,
			// OR we have received the same reading twice in a row
			// OR we don't currently have a battery reading
			// OR the value we just received is at least 2 steps off from the last reported value
			// OR the device's firmware is older than 1.15.7
			if(useOldBatt || state?.lastVolts == null || state?.lastVolts == volts || device.currentState("battery")?.value == null || Math.abs(curValVolts - volts) > 0.1) {
				def pct = (volts - minVolts) / (maxVolts - minVolts)
				def roundedPct = Math.round(pct * 100)
				if (roundedPct <= 0)
					roundedPct = 1
				result.value = Math.min(100, roundedPct)
			} else {
				// Don't update as we want to smooth the battery values, but do report the last battery state for record keeping purposes
				result.value = device.currentState("battery").value
			}
			state.lastVolts = volts
		}
	}

	return result
}

private Map getBatteryPercentageResult(rawValue) {
	log.debug "Battery Percentage rawValue = ${rawValue} -> ${rawValue / 2}%"
	def result = [:]

	if (0 <= rawValue && rawValue <= 200) {
		result.name = 'battery'
		result.translatable = true
		result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
		result.value = Math.round(rawValue / 2)
	}

	return result
}

List<Map> garageEvent(zValue) {
	List<Map> results = []
	def absValue = zValue.abs()
	def contactValue = null
	if (absValue > 900) {
		contactValue = 'closed'
	} else if (absValue < 100) {
		contactValue = 'open'
	}
	if (contactValue != null) {
		def descriptionText = contactValue == 'open' ? '{{ device.displayName }} was opened' : '{{ device.displayName }} was closed'
		results << [name: 'contact', value: contactValue, descriptionText: descriptionText, translatable: true]
	}
	results
}

/**
 * PING is used by Device-Watch in attempt to reach the Device
 * */
def ping() {
	zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS)
}

def refresh() {
	log.debug "Refreshing Values "
	def refreshCmds = []

	if (device.getDataValue("manufacturer") == "Samjin") {
		refreshCmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021)
	} else {
		refreshCmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020)
	}
	refreshCmds += zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) +
		zigbee.readAttribute(0xFC02, 0x0010, [mfgCode: manufacturerCode]) +
		zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS) +
		zigbee.enrollResponse()

	return refreshCmds
}

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
	// Sets up low battery threshold reporting
	sendEvent(name: "DeviceWatch-Enroll", displayed: false, value: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID, scheme: "TRACKED", checkInterval: 2 * 60 * 60 + 1 * 60, lowBatteryThresholds: [15, 7, 3], offlinePingable: "1"].encodeAsJSON())
	sendEvent(name: "acceleration", value: "inactive", descriptionText: "{{ device.displayName }} was $value", displayed: false)

	log.debug "Configuring Reporting"
	def configCmds = [zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000), zigbee.readAttribute(0xFC02, 0x0010, [mfgCode: manufacturerCode])]
	def batteryAttr = device.getDataValue("manufacturer") == "Samjin" ? 0x0021 : 0x0020
	configCmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, batteryAttr)
	configCmds += zigbee.enrollResponse()
	configCmds += zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS)

	if (device.getDataValue("manufacturer") == "SmartThings") {
		log.debug "Refreshing Values for manufacturer: SmartThings "
		/* These values of Motion Threshold Multiplier(0x01) and Motion Threshold (0x0276)
		 seem to be giving pretty accurate results for the XYZ co-ordinates for this manufacturer.
		 Separating these out in a separate if-else because I do not want to touch Centralite part
		 as of now.
		*/
		configCmds += zigbee.writeAttribute(0xFC02, 0x0000, 0x20, 0x01, [mfgCode: manufacturerCode])
		// passed as little-endian as a bug-workaround
		configCmds += zigbee.writeAttribute(0xFC02, 0x0002, 0x21, "7602", [mfgCode: manufacturerCode])
	} else if (device.getDataValue("manufacturer") == "Samjin") {
		log.debug "Refreshing Values for manufacturer: Samjin "
		configCmds += zigbee.writeAttribute(0xFC02, 0x0000, 0x20, 0x14, [mfgCode: manufacturerCode])
	} else {
		// Write a motion threshold of 2 * .063g = .126g
		// Currently due to a Centralite firmware issue, this will cause a read attribute response that
		// indicates acceleration even when there isn't.
		configCmds += zigbee.writeAttribute(0xFC02, 0x0000, 0x20, 0x02, [mfgCode: manufacturerCode])
	}

	// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
	// battery minReport 30 seconds, maxReportTime 6 hrs by default
	if (device.getDataValue("manufacturer") == "Samjin") {
		configCmds += zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021, DataType.UINT8, 30, 21600, 0x10) +
				zigbee.temperatureConfig(30, 300) +
				zigbee.configureReporting(0xFC02, 0x0010, DataType.BITMAP8, 0, 3600, 0x01, [mfgCode: manufacturerCode]) +
				zigbee.configureReporting(0xFC02, 0x0012, DataType.INT16, 0, 3600, 0x0001, [mfgCode: manufacturerCode]) +
				zigbee.configureReporting(0xFC02, 0x0013, DataType.INT16, 0, 3600, 0x0001, [mfgCode: manufacturerCode]) +
				zigbee.configureReporting(0xFC02, 0x0014, DataType.INT16, 0, 3600, 0x0001, [mfgCode: manufacturerCode])
	} else {
		configCmds += zigbee.batteryConfig() +
				zigbee.temperatureConfig(30, 300) +
				zigbee.configureReporting(0xFC02, 0x0010, DataType.BITMAP8, 10, 3600, 0x01, [mfgCode: manufacturerCode]) +
				zigbee.configureReporting(0xFC02, 0x0012, DataType.INT16, 1, 3600, 0x0001, [mfgCode: manufacturerCode]) +
				zigbee.configureReporting(0xFC02, 0x0013, DataType.INT16, 1, 3600, 0x0001, [mfgCode: manufacturerCode]) +
				zigbee.configureReporting(0xFC02, 0x0014, DataType.INT16, 1, 3600, 0x0001, [mfgCode: manufacturerCode])
	}

	configCmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, batteryAttr)

	return configCmds
}

private hexToSignedInt(hexVal) {
	if (!hexVal) {
		return null
	}

	def unsignedVal = hexToInt(hexVal)
	unsignedVal > 32767 ? unsignedVal - 65536 : unsignedVal
}

private getManufacturerCode() {
	if (device.getDataValue("manufacturer") == "SmartThings") {
		return "0x110A"
	} else if (device.getDataValue("manufacturer") == "Samjin") {
		return "0x1241"
	} else {
		return "0x104E"
	}
}

private shouldUseOldBatteryReporting() {
	def isFwVersionLess = true // By default use the old battery reporting
	def deviceFwVer = "${device.getFirmwareVersion()}"
	def deviceVersion = deviceFwVer.tokenize('.')  // We expect the format ###.###.### where ### is some integer

	if (deviceVersion.size() == 3) {
		def targetVersion = [1, 15, 7] // Centralite Firmware 1.15.7 contains battery smoothing fixes, so versions before that should NOT be smoothed
		def devMajor = deviceVersion[0] as int
		def devMinor = deviceVersion[1] as int
		def devBuild = deviceVersion[2] as int

		isFwVersionLess = ((devMajor < targetVersion[0]) ||
			(devMajor == targetVersion[0] && devMinor < targetVersion[1]) ||
			(devMajor == targetVersion[0] && devMinor == targetVersion[1] && devBuild < targetVersion[2]))
	}

	return isFwVersionLess // If f/w version is less than 1.15.7 then do NOT smooth battery reports and use the old reporting
}

private hexToInt(value) {
	new BigInteger(value, 16)
}

I tried your code and it was working for me with decimal values.

But you can try this:

        def map = maps[0]
        log.debug "Description: $description" 
        def decimalValue = Double.parseDouble(description.split(": ")[1])

The log.debug line will log to the live logging the description’s value, from where we are reading the temperature.