Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
1 contributor

Users who have contributed to this file

163 lines (147 sloc) 4.95 KB
/*
* XBee Presence
*
* Copyright 2019 Daniel Terryn
*
* 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.
*
* Change History:
*
* Date Who What
* ---- --- ----
* 2019-05-21 Daniel Terryn Original Creation
*
*/
metadata {
definition (name: "XBee Presence", namespace: "dan.t", author: "Daniel Terryn") {
capability "Sensor"
capability "Configuration"
capability "Battery"
capability "Presence Sensor"
}
preferences {
input "fullVoltageValue", "enum", title:"Battery 100% mV:", required:true, defaultValue:3300, options:[3000:"3000 mV",3300:"3300 mV",3600:"3600 mV"]
input "checkInterval", "enum", title:"Minutes elapsed until sensor is not present", required:true, defaultValue:3, options:[1:"1 Minute",2:"2 Minutes",3:"3 Minutes", 4:"4 Minutes",5:"5 Minutes"]
}
}
def updated() {
stopTimer()
startTimer()
}
def installed() {
// Arrival sensors only goes OFFLINE when Hub is off
}
def configure() {
log.warn "configure..."
return []
}
def parse(String description) {
state.lastCheckin = now()
handlePresenceEvent(true)
if (description?.startsWith('catchall')) {
parseCatchAllMessage(description)
}
return []
}
private Map parseCatchAllMessage(String description) {
Map resultMap = [:]
def cluster = zigbee.parse(description)
if (cluster.clusterId == 0x0011 && cluster.command == 0x01){
handleBatteryEvent(cluster)
}
return resultMap
}
/**
* Create battery event from reported battery voltage.
*
* @param volts Battery voltage in mV
*/
private handleBatteryEvent(cluster) {
def descriptionText
def battery_string = ""
for (element in cluster.data) {
battery_string = battery_string + Integer.toString(element,16).padLeft(2, '0')
}
battery_mV = Integer.parseInt(battery_string)
log.debug "Battery mV: ${battery_mV}"
def value = 100
if (battery_mV <= 2100) {
value = 0
}
else {
/* Formula
Minimum Voltage = 2100mV
Divider = (100% Voltage in mV - 2100) (max and default is 3600)
*/
def offset = battery_mV - 2100
value = Math.round((offset / (Integer.parseInt(fullVoltageValue)-2100)) * 100)
if (value > 100)
value = 100
}
def linkText = getLinkText(device)
def currentPercentage = device.currentState("battery")?.value
if (currentPercentage && (Integer.parseInt(currentPercentage, 10) == value)) {
return
}
descriptionText = '{{ linkText }} battery was {{ value }}'
def eventMap = [
name: 'battery',
value: value,
descriptionText: descriptionText,
translatable: true
]
log.debug "Creating battery event for voltage=${battery_mV/1000}V: ${linkText} ${eventMap.name} is ${eventMap.value}%"
sendEvent(eventMap)
}
private handlePresenceEvent(present) {
def wasPresent = device.currentState("presence")?.value == "present"
if (!wasPresent && present) {
log.debug "Sensor is present"
startTimer()
} else if (!present) {
log.debug "Sensor is not present"
stopTimer()
} else if (wasPresent && present) {
log.debug "Sensor already present"
return
}
def linkText = getLinkText(device)
def descriptionText
if ( present )
descriptionText = "{{ linkText }} has arrived"
else
descriptionText = "{{ linkText }} has left"
def eventMap = [
name: "presence",
value: present ? "present" : "not present",
linkText: linkText,
descriptionText: descriptionText,
translatable: true
]
log.debug "Creating presence event: ${device.displayName} ${eventMap.name} is ${eventMap.value}"
sendEvent(eventMap)
}
private startTimer() {
log.debug "Scheduling periodic timer"
runEvery1Minute("checkPresenceCallback")
}
private stopTimer() {
log.debug "Stopping periodic timer"
// Always unschedule to handle the case where the DTH was running in the cloud and is now running locally
unschedule("checkPresenceCallback")
}
def checkPresenceCallback() {
def timeSinceLastCheckin = (now() - state.lastCheckin ?: 0) / 1000
def theCheckInterval = Integer.parseInt(checkInterval) * 60
log.debug "Sensor checked in ${timeSinceLastCheckin} seconds ago"
if (timeSinceLastCheckin >= theCheckInterval) {
handlePresenceEvent(false)
}
}
You can’t perform that action at this time.