Last active
September 25, 2015 05:49
-
-
Save dotMorten/e3ffea21730f05b7e09b to your computer and use it in GitHub Desktop.
ZBHT-2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Smartenit ZHBT-2 | |
* | |
* Copyright 2015 Morten Nielsen | |
* | |
* 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. | |
* | |
*/ | |
// ZBHT-2 Smartenit Temperature/Humidity sensor | |
// http://smartenit.com/product/zbht-2/ | |
// Product brief: http://smartenit.com/sandbox/downloads/ZBHT-2_Product%20Brief.pdf | |
metadata { | |
definition (name: "Smartenit ZBHT-2", namespace: "dotMorten", author: "Morten Nielsen") { | |
capability "Relative Humidity Measurement" | |
capability "Temperature Measurement" | |
capability "Sensor" | |
capability "Battery" | |
//Manufacturer ID: 0x1075 | |
//Device ID: 0x0302 | |
//profileId | |
//0104 = Zigbee HA | |
//Cluster IDs: | |
//0x0000 Basic | |
//0x0001 Power Configuration | |
//0x0003 Identify | |
//0x0009 Alarms | |
//0x0402 Temperature Measurement | |
//0x0405 Relative Humidity Measurement | |
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,0402,0405", manufacturer: "Smartenit", model: "ZBHT-2" | |
//Question: Do I need outClusters? Most have ' outClusters: "0019" ', but no documentation what that is | |
} | |
// simulator metadata | |
simulator { | |
for (int i = 0; i <= 100; i += 10) { | |
status "${i}F": "temperature: $i F" | |
} | |
for (int i = 0; i <= 100; i += 10) { | |
status "${i}%": "humidity: ${i}%" | |
} | |
} | |
// UI tile definitions | |
tiles { | |
valueTile("temperature", "device.temperature", width: 2, height: 2) { | |
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("humidity", "device.humidity") { | |
state "humidity", label:'${currentValue}%', unit:"" | |
} | |
main(["temperature", "humidity"]) | |
details(["temperature", "humidity"]) | |
} | |
} | |
def refresh() { | |
log.debug "_____________refresh begin" | |
[ | |
"st rattr 0x${device.deviceNetworkId} 1 0x0402 0x0000", | |
"st rattr 0x${device.deviceNetworkId} 1 0x0405 0x0000" | |
] | |
} | |
def configure() { | |
log.debug "_____________configure begin" | |
def configCmds = [ | |
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 1 {${device.zigbeeId}} {}", "delay 500", | |
"zcl global send-me-a-report 1 0x20 0x20 30 21600 {01}", //checkin time 6 hrs | |
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500", | |
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x402 {${device.zigbeeId}} {}", "delay 500", | |
"zcl global send-me-a-report 0x402 0 0x29 30 3600 {6400}", | |
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500", | |
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x405 {${device.zigbeeId}} {}", "delay 500", | |
"zcl global send-me-a-report 0x405 0 0x29 30 3600 {6400}", | |
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500" | |
] | |
return configCmds + refresh() | |
} | |
private getEndpointId() { | |
new BigInteger(device.endpointId, 16).toString() | |
} | |
//Called when the device configuration has been updated | |
def updated() { | |
log.debug "___________DEBUG_____________: updated called" | |
configure() | |
} | |
// Parse incoming device messages to generate events | |
def parse(String description) { | |
log.debug "__________________Recieved msg:" | |
log.debug "${description}" | |
if (description?.startsWith('read attr -')) { | |
map = parseReportAttributeMessage(description) | |
def attrresult = map ? createEvent(map) : null | |
return attrresult; | |
} | |
null | |
} | |
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()] | |
} | |
Map resultMap = [:] | |
if (descMap.cluster == "0402" && descMap.attrId == "0000") { | |
def value = getTemperature(descMap.value) | |
return [ | |
name: 'temperature', | |
value: value, | |
descriptionText: "" | |
] | |
} | |
else if (descMap.cluster == "0405" && descMap.attrId == "0000") { | |
def value = getHumidity(descMap.value) | |
return [ | |
name: 'humidity', | |
value: value, | |
descriptionText: "" | |
] | |
} | |
else if (descMap.cluster == "0001" && descMap.attrId == "0020") { | |
return getBatteryResult(Integer.parseInt(descMap.value, 16)) | |
} | |
return resultMap | |
} | |
def getTemperature(value) { | |
def celsius = Integer.parseInt(value, 16).shortValue() / 100 | |
if(getTemperatureScale() == "C"){ | |
return celsius | |
} else { | |
return celsiusToFahrenheit(celsius) as Integer | |
} | |
} | |
def getHumidity(value) | |
{ | |
def humidity = Integer.parseInt(value, 16).shortValue() / 100 | |
return humidity | |
} | |
private Map getBatteryResult(rawValue) { | |
log.debug "Battery" | |
log.debug rawValue | |
def linkText = getLinkText(device) | |
def result = [ | |
name: 'battery', | |
value: '--' | |
] | |
def volts = rawValue / 10 | |
def descriptionText | |
if (rawValue == 255) {} | |
else { | |
if (volts > 3.5) { | |
result.descriptionText = "${linkText} battery has too much power (${volts} volts)." | |
} | |
else { | |
def minVolts = 2.1 | |
def maxVolts = 3.0 | |
def pct = (volts - minVolts) / (maxVolts - minVolts) | |
result.value = Math.min(100, (int) pct * 100) | |
result.descriptionText = "${linkText} battery was ${result.value}%" | |
} | |
} | |
return result | |
} |
@workingmonk Thanks. When pressing the refresh button, I'm told "you are not authorized to perform the requested operation" ?
I'm adding a call to configure and refresh from updated() to trigger running this code - I also press the announce button on the device right before to wake it up so it should respond immediately (hint from a Smartenit guy)
Interestingly enough when I press the "Announce" button on the device, the hue bridge is reporting it:
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@dotMorten
You missed the delay in the refresh code. the other issue is these are sleepy devices, so they may not get your messages unless they are awake. They check for messages close to every 7 seconds. Please add the refresh tile using the code:
After that hit the refresh button couple of times and you should see the parse method being called.