This is my first device driver.
I cannot get the illuminance value. The driver detects the button pushes, it receives and parses correctly the messages but never intercept the illuminance value.
Some more experienced DD/Groovy guy can take a look at the code?
Please do not expect much from it...
/**
* My Xiaomi Mijia Smart Light Sensor
*
* 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.
*/
// see: https://github.com/dresden-elektronik/deconz-rest-plugin/issues/2300
import hubitat.zigbee.zcl.DataType
import groovy.json.JsonSlurper
def getVersionNum() { return "0.0.0" }
private def getVersionLabel() { return "My Xiaomi Mijia Smart Light Sensor ${getVersionNum()}" }
metadata {
definition (name: "My Xiaomi Mijia Smart Light Sensor", namespace: "r.brunialti", author: "r.brunialti") {
capability "Illuminance Measurement"
capability "Sensor"
capability "Battery"
attribute "batteryLastReplaced", "String"
attribute "lastCheckin", "String"
attribute "version", "String"
//fingerprint profileId: "0104", inClusters: "0000,0400,0003,0001", outClusters: "0003", manufacturer: "LUMI", model: "lumi.sen_ill.mgl01", deviceJoinName: "Xiaomi Mijia Smart Home Light Sensor", ocfDeviceType: "oic.r.sensor.illuminance"
fingerprint profileId: "0104", inClusters: "0000,0400,0003,0001", outClusters: "0003", manufacturer: "LUMI", model: "lumi.sen_ill.mgl01", deviceJoinName: "Xiaomi Mijia Smart Home Light Sensor", ocfDeviceType: "oic.r.illuminance"
command "resetBatteryReplacedDate"
command "configure"
}
preferences {
input "luxOffset", "decimal", title: "Lux Value Offsetv", description: "", range: "*..*"
//Battery Voltage Range
input name: "voltsmin", title: "Min Volts (0% battery = ___ volts, range 2.0 to 2.9). Default = 2.5 Volts", description: "", type: "decimal", range: "2..2.7"
input name: "voltsmax", title: "Max Volts (100% battery = ___ volts, range 2.8 to 3.5). Default = 3.0 Volts", description: "Default = 3.0 Volts", type: "decimal", range: "2.8..3.4"
//Logging Message Config
input name: "infoLogging", type: "bool", title: "Enable info message logging", description: ""
input name: "debugLogging", type: "bool", title: "Enable debug message logging", description: ""
}
}
def parse(String description) {
Map map = [:]
def descMap = zigbee.parseDescriptionAsMap(description)
displayDebugLog("A - Received message: ${description}")
displayDebugLog("B - Parsed message: ${descMap}")
// lastCheckin can be used with webCoRE
sendEvent(name: "lastCheckin", value: now())
if (description?.startsWith('read attr - ')) {
description = description - "read attr - "
//def encoding = Integer.parseInt(description.split(",").find {it.split(":")[0].trim() == "encoding"}?.split(":")[1].trim(), 16)
def encoding = Integer.parseInt(descMap.encoding, 16)
if (descMap.value != null & encoding > 0x18 & encoding < 0x3e) {
displayDebugLog("C1 - Data type of payload is little-endian; reversing byte order")
// Reverse order of bytes in description's payload for LE data types - required for Hubitat firmware 2.0.5 or newer
descMap.value = reverseHexString(descMap.value)
displayDebugLog("C2 - Reversed payload value: ${descMap.value}")
}
// Send message data to appropriate parsing function based on the type of report
if (descMap.cluster == "0400" & descMap.attrInt == 0)
// Parse illuminance value report
map = parseIlluminance(descMap.value)
else if (descMap.cluster == "0000" & descMap.attrInt == 5)
// Button short-pressed
displayDebugLog("C3 - Reset button of [${descMap.value}] was short-pressed")
// Button management: insert code here following
else if (descMap.cluster == "0001" & descMap.attrInt == 32)
// Parse battery level from hourly announcement message
map = parseBattery(descMap.value)
else
displayDebugLogelvalueHex.("C4 - Unknown cluster/attribute: cluster=${cluster}, attribute=${descMap.attrId}")
} else if (description?.startsWith('cat')){
displayDebugLog("C5 - Catchall message ignored")
} else
displayDebugLog("C6 - Unknown message type")
if (map != [:]) {
displayDebugLog("D - Creating event $map")
return createEvent(map)
}
}
private parseIlluminance(description) {
def offset = luxOffset ? luxOffset : 0
def lux = Integer.parseInt(description,16) + offset
return [
name: 'illuminance',
value: lux,
unit: 'lux',
isStateChange: true,
descriptionText: "Illuminance is ${lux} lux"
]
}
// Convert raw 4 digit integer voltage value -> percentage based on minVolts/maxVolts range
private parseBattery(description) {
displayDebugLog("Battery parse string = ${description}")
def MsgLength = description.size()
def rawValue
for (int i = 4; i < (MsgLength-3); i+=2) {
if (description[i..(i+1)] == "21") { // Search for byte preceeding battery voltage bytes
rawValue = Integer.parseInt((description[(i+4)..(i+5)] + description[(i+2)..(i+3)]),16)
break
}
}
def rawVolts = rawValue / 1000
def minVolts = voltsmin ? voltsmin : 2.9
def maxVolts = voltsmax ? voltsmax : 3.05
def pct = (rawVolts - minVolts) / (maxVolts - minVolts)
def roundedPct = Math.min(100, Math.round(pct * 100))
def descText = "Battery level is ${roundedPct}% (${rawVolts} Volts)"
// lastCheckinEpoch is for apps that can use Epoch time/date and lastCheckinTime can be used with Hubitat Dashboard
if (lastCheckinEnable) {
sendEvent(name: "lastCheckinEpoch", value: now())
sendEvent(name: "lastCheckinTime", value: new Date().toLocaleString())
}
displayInfoLog(descText)
return [
name: 'battery',
value: roundedPct,
unit: "%",
descriptionText: descText
]
}
// installed() runs just after a sensor is paired
def installed() {
displayDebugLog("Installing")
state.prefsSetCount = 0
init()
}
//
def init() {
if (!device.currentValue('batteryLastReplaced'))
resetBatteryReplacedDate(true)
}
// configure() runs after installed() when a sensor is paired
def configure() {
displayInfoLog("Configuring")
//Configure: empty stublatest/ref-doc/zigbee-ref.html
//zigbee.configureReporting(Integer Cluster, Integer attributeId, Integer dataType, Integer minReportTime, Integer MaxReportTime, [Integer reportableChange], Map additionalParams=[:])
//zigbee.readAttribute(Integer Cluster, Integer attributeId, Map additionalParams=[])
init()
sendEvent(name: "version", value:getVersionNum())
state.prefsSetCount = 1
}
//Reset the batteryLastReplaced date to current date
def resetBatteryReplacedDate(paired) {
def newlyPaired = paired ? " for newly paired sensor" : ""
sendEvent(name: "batteryLastReplaced", value: new Date().format("MMM dd yyyy", location.timeZone))
displayInfoLog("Setting Battery Laast Replace date")
}
// updated() will run every time user saves preferences
def updated() {
displayInfoLog("Updating preference settings")
init()
displayInfoLog("Info message logging enabled")
displayInfoLog("Debug message logging enabled")
}
//
private def displayDebugLog(message) {
if (debugLogging)
log.debug "${device.displayName}: ${message}"
}
//
private def displayInfoLog(message) {
if (infoLogging || state.prefsSetCount != 1)
log.info "${device.displayName}: ${message}"
}