Skip to content

Instantly share code, notes, and snippets.

@dotMorten
Last active September 25, 2015 05:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dotMorten/e3ffea21730f05b7e09b to your computer and use it in GitHub Desktop.
Save dotMorten/e3ffea21730f05b7e09b to your computer and use it in GitHub Desktop.
ZBHT-2
/**
* 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
Copy link

@dotMorten

def refresh() {
    log.debug "_____________refresh begin" 
    [
      "st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0402 0x0000", "delay 500",
      "st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0405 0x0000", "delay 500"
    ]
}

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:

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", "refresh"])

After that hit the refresh button couple of times and you should see the parse method being called.

@dotMorten
Copy link
Author

@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:
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment