I have a device driver for a Zigbee temperature and humidity measurement device I built. I am trying to set up the device driver. I created the driver and everything works showing temperature in Celsius. I want to show both Celsius and Farenheit on the multi-attribute tile so I added a custom attribute called tempf. My device handler correctly processes the data and I can see the attributes against the device but the multi-attribute tile shows the temperature attribute (Celsius) in both locations even though the secondary location is set to show tempf. When the device updates the temperature, if the device is showing in the UI (iPhone) the secondary attribute changes to show the correct Farenheit reading but if I switch to another device then back again it is back to showing the main temperature attribute again.
I tried adding another tile specifically for the tempf attribute and got the same results. Showing Celsius until the first update happens then showing Farenheit until closed and reopened. It seems like some sort of display initialisation problem. Here is my device handler.
Does anybody have any ideas?
/**
* NordicMultiSensor
*
* Copyright 2019 Ian Abercrombie
*
* 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.zcl.DataType
private getCLUSTER_POWER() { 0x0001 }
private getPOWER_ATTR_BATTERY_VOLTAGE() { 0x0020 }
private getPOWER_ATTR_BATTERY_REMAINING() { 0x0021 }
private getCLUSTER_TEMPERATURE_MEASUREMENT() { 0x0402 }
private getCLUSTER_RELATIVE_HUMIDITY_MEASUREMENT() { 0x0405 }
private getATTR_MEASUREDVALUE() { 0x0000 }
private getResourcesUrl() { return "https://raw.githubusercontent.com/krlaframboise/Resources/master/Zooz/" }
metadata {
definition (name: "NordicMultiSensor", namespace: "Cedar", author: "Ian Abercrombie") {
capability "Temperature Measurement"
capability "Relative Humidity Measurement"
capability "Sensor"
capability "Battery"
attribute "tempf","number"
command "refresh"
fingerprint profileId: "0104", deviceId: "0304", inClusters: "0000,0003,0402,0405", outClusters: "0003", manufacturer: "Cedar Technology", model: "NordicMultiSensor", deviceJoinName: "Nordic Multi Sensor"
}
tiles(scale: 2) {
multiAttributeTile(name:"temperatures", type:"generic", width:6, height:4) {
tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
attributeState("temperature", label:'${currentValue}°C', icon: "${resourcesUrl}temperature.png",
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"]])
}
tileAttribute("tempf", key: "SECONDARY_CONTROL") {
attributeState("tempf", label:'${currentValue}°F')
}
}
valueTile("temperature2", "device.temperature", inactiveLabel: false) {
state "temperature", label:'${currentValue}°', icon:"st.Weather.weather2",
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("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
// state "temperature", label:'${currentValue}°C', icon:"st.Weather.weather2"
// }
valueTile("humidity", "device.humidity", inactiveLabel: false, width: 2, height: 2) {
state "humidity", label:'${currentValue}%', unit:"", icon: "${resourcesUrl}humidity.png"
backgroundColors:[
[value: 0, color: "#FFFCDF"],
[value: 4, color: "#FDF789"],
[value: 20, color: "#A5CF63"],
[value: 23, color: "#6FBD7F"],
[value: 56, color: "#4CA98C"],
[value: 59, color: "#0072BB"],
[value: 76, color: "#085396"]]
}
valueTile("batteryVoltage", "device.batteryVoltage", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "batteryVoltage", label:'${currentValue}V', unit: "", icon: "${resourcesUrl}battery.png"
}
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "battery", label:'${currentValue}%', unit: "", icon: "${resourcesUrl}battery.png"
}
standardTile("refresh", "device.temperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:'refresh', action:"refresh"
}
standardTile("tempf", "device.tempf", inactiveLabel: false, width: 2, height: 2) {
state "tempf", label:'${currentValue}°F', unit:"F", icon: "${resourcesUrl}temperature.png"
}
standardTile("identify", "device.temperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:'identify', action:"identify"
}
main ("temperatures")
details(["temperatures","humidity","batteryVoltage","battery","refresh","tempf","identify"])
}
}
private Map parseCatchAllMessage(String description) {
Map resultMap = [:]
def cluster = zigbee.parse(description)
switch(cluster.clusterId) {
case 0x0402: // Temperature cluster
switch(cluster.command) {
case 0x07: log.debug "Configure temperature reporting response = ${cluster.data}"
return resultMap
break
}
log.debug "Parse temperature cluster data : ${cluster}"
break
case 0x0405: // Humidity cluster
switch(cluster.command) {
case 0x07: log.debug "Configure humidity reporting response = ${cluster.data}"
return resultMap
break
}
case 0x0001: // Power cluster
switch(cluster.command) {
case 0x07: log.debug "Configure power reporting response = ${cluster.data}"
return resultMap
break
}
log.debug "Parse power cluster data : ${cluster}"
break
case 0x8021: //Bind Response
log.debug "Bind response from NordicMultiSensor : ${cluster.command}"
break
default : // Unknown
log.debug "Description = '${description}' ; Parse returned ${cluster}"
}
return zigbee.getEvent(description)
}
private Map parseReadAttr(String description) {
Map resultMap = [:]
description.split(",").each { param ->
def pair = param.split(":")
resultMap[pair[0].trim()] = pair[1].trim()
}
return resultMap
}
// parse events into attributes
def parse(String description) {
Map map = [:]
log.debug "parsing : ${description}"
if (description?.startsWith('catchall:')) {
map = parseCatchAllMessage(description)
if (map[name] == 'temperature') {
log.debug "Setting temperature - ${map}"
createEvent(map)
} else {
log.debug "parse : ${description}"
}
} else {
def pair = description.split(":")
def name = pair[0].trim()
def value = pair[1].trim()
switch(name) {
case 'temperature' :
def farenheit = (((value.toFloat() / 5) * 9) + 32).round(2)
log.debug "Got temperature - ${description} - creating events ${name} : ${value} and farenheit : ${farenheit}"
def tempCelsiusEvent = createEvent(name: name, value: value, unit:"C", isStateChange: true)
def tempFarenheitEvent = createEvent(name: "tempf", value: farenheit, unit:"F", isStateChange: true)
return [tempCelsiusEvent, tempFarenheitEvent]
break
case 'humidity' :
log.debug "Got humidity - ${description} - creating event ${name} : ${value}"
return createEvent(name: name, value: value, unit:"%")
break
case 'read attr - raw' : log.debug "Read attr raw"
map = parseReadAttr(description)
log.debug "Attribute = ${map.attrId}"
switch(map["attrId"]) {
case "0020" : // Voltage
float v = Integer.parseInt(map["value"],16) / 10;
log.debug "Voltage updated ${v}"
return createEvent(name: "batteryVoltage", value: v, unit:"V")
break
case "0021" : // Percent Remaining
float r = Integer.parseInt(map["value"],16) / 2;
log.debug "Percent Remaining updated ${r}"
return createEvent(name: "battery", value: r, unit:"%")
break
default : log.debug "Unknown attribute - ${map["attrId"]}"
}
break;
default : log.debug "Parsing '${description}'"
}
}
return map
}
def configure() {
// reportableChange -------------------------------------------------------------------------------------------
// maxReportTime ----------------------------------------------------------------------------------------- |
// minReportTime ------------------------------------------------------------------------------------ | |
// dataType ------------------------------------------------------------------------------------ | | |
// attribute ----------------------------------------------------------- | | | |
// cluster -------------------------------------- | | | | |
// | | | | | |
return zigbee.configureReporting(CLUSTER_TEMPERATURE_MEASUREMENT, ATTR_MEASUREDVALUE, DataType.INT16, 5, 3600, 10) +
zigbee.configureReporting(CLUSTER_RELATIVE_HUMIDITY_MEASUREMENT, ATTR_MEASUREDVALUE, DataType.UINT16, 5, 3600, 50) +
zigbee.configureReporting(CLUSTER_POWER, POWER_ATTR_BATTERY_VOLTAGE, DataType.UINT8, 5, 3600, 1) +
zigbee.configureReporting(CLUSTER_POWER, POWER_ATTR_BATTERY_REMAINING, DataType.UINT8, 5, 3600, 1)
}
def refresh() {
log.debug "refresh, configuring reporting."
return configure()
}
def installed() {
initialize()
}
def updated() {
initialize()
}
def initialize() {
log.debug "Initialize device. Configuring..."
return configure()
}
def setLevel(value, rate = null) {
setTemperature(value)
}
def up() {
setTemperature(getTemperature() + 1)
log.debug "Temperature increased"
}
def down() {
setTemperature(getTemperature() - 1)
log.debug "Temperature decreased"
}
def setTemperature(value) {
sendEvent(name:"temperature", value: value)
}
private getTemperature() {
def ts = device.currentState("temperature")
Integer value = ts ? ts.integerValue : 72
return value
}