As I’m progressing with my DSL to JavaScript migration, here’s a small sequel to Automating Hunter Douglas PowerView automations.
These rules are created for monitoring the voltage/battery level on battery-powered Hunter Douglas/Luxaflex PowerView shades (< Gen 3).
By principle this should be a matter of reacting when channel lowBattery
changes to ON.
However, from my experience the voltage reported by some of these shades (such as Top Down/Bottom Up with two motors) is very unreliable:
This is the cause of false positives - not only in openHAB, but also shown as notifications when launching the PowerView app. By default battery status is refreshed once per week; however the frequency can be overridden by Bridge configuration. This can not solve the problem though, it will only increase the resolution, but cause more spikes.
In my use-case I have two battery-powered shades, Blind10 and Blind11, which I’m monitoring in order to get notifications when I need to recharge the batteries - for real!
powerview.items:
Number Blind10_BatteryLevel "Battery Level" <battery> (Blind10) ["Point"] { channel="hdpowerview:shade:hub:blind10:batteryLevel" }
Switch Blind10_LowBattery "Low Battery" <lowbattery> (Blind10) ["Point"] { channel="hdpowerview:shade:hub:blind10:lowBattery" }
Number:ElectricPotential Blind10_BatteryVoltage "Battery Voltage" <energy> (Blind10) ["Point"] { channel="hdpowerview:shade:hub:blind10:batteryVoltage", unit="V" }
Number Blind11_BatteryLevel "Battery Level" <battery> (Blind11) ["Point"] { channel="hdpowerview:shade:hub:blind11:batteryLevel" }
Switch Blind11_LowBattery "Low Battery" <lowbattery> (Blind11) ["Point"] { channel="hdpowerview:shade:hub:blind11:lowBattery" }
Number:ElectricPotential Blind11_BatteryVoltage "Battery Voltage" <energy> (Blind11) ["Point"] { channel="hdpowerview:shade:hub:blind11:batteryVoltage", unit="V" }
persistence:
Blind10_BatteryVoltage,
Blind11_BatteryVoltage: strategy = everyChange
powerview.js:
function scheduleBatteryStateRefresh(item) {
console.log("Scheduling refresh of battery state in 1 minute for " + item.name);
setTimeout(() => {
console.log("Triggering REFRESH for " + item.name);
item.sendCommand("REFRESH");
}, 60*1000);
}
function updateBatterWarningTimer(item) {
if (item.isUninitialized) {
var timeoutId = item.name.startsWith("Blind10_") ? blind10BatteryWarningTimeoutId : blind11BatteryWarningTimeoutId;
if (timeoutId > 0) {
// Already scheduled.
console.log(item.name + " -> battery warning timer already scheduled");
return;
}
console.log(item.name + " -> scheduling battery warning timer");
var timeoutId = setTimeout(() => {
notification.send("PowerView" , "Duette battery voltage in " + getRoomFromItem(item) + " could not be read for two hours.");
}, 2*60*60*1000);
if (item.name.startsWith("Blind10_")) {
blind10BatteryWarningTimeoutId = timeoutId;
} else {
blind11BatteryWarningTimeoutId = timeoutId;
}
} else {
if (item.name.startsWith("Blind10_")) {
if (blind10BatteryWarningTimeoutId > 0) {
console.log(item.name + " -> cancelling battery warning timer");
clearTimeout(blind10BatteryWarningTimeoutId);
blind10BatteryWarningTimeoutId = 0;
}
} else {
if (blind11BatteryWarningTimeoutId > 0) {
console.log(item.name + " -> cancelling battery warning timer");
clearTimeout(blind11BatteryWarningTimeoutId);
blind11BatteryWarningTimeoutId = 0;
}
}
}
}
function isBatteryStateValid(item) {
if (item.isUninitialized) {
console.log(item.name + " is UNDEF");
return false;
}
var currentState = item.quantityState;
if (currentState.lessThan("11 V")) {
console.log(item.name + " is suspiciously low: " + currentState);
return false;
}
var previousState = item.history.previousState(true, "jdbc").quantityState;
var average = item.history.averageSince(time.ZonedDateTime.now().minusHours(6), "jdbc");
var diff = average - item.numericState;
if (diff >= 0.5) {
console.log(item.name + " dropped more than 0.5 V compared to average voltage " + diff + " V in the last 6 hours: " + currentState);
return false;
}
return true;
}
function verifyAndRefreshBatteryState(item) {
updateBatterWarningTimer(item);
if (!isBatteryStateValid(item)) {
scheduleBatteryStateRefresh(item);
}
}
function getRoomFromItem(item) {
return item.name.startsWith("Blind10_") ? "bedroom" : "walk-in";
}
rules.when()
.item("Blind10_BatteryVoltage").changed()
.or()
.item("Blind11_BatteryVoltage").changed()
.then(event =>
{
verifyAndRefreshBatteryState(items.getItem(event.itemName));
})
.build("PowerView Duette voltage monitoring (event)", "Monitor and correct PowerView voltage readings");
rules.when()
.cron("0 0/5 * * * ?")
.then(event =>
{
verifyAndRefreshBatteryState(items.Blind10_BatteryVoltage);
verifyAndRefreshBatteryState(items.Blind11_BatteryVoltage);
})
.build("PowerView Duette voltage monitoring (periodic)", "Monitor missing PowerView voltage readings");
rules.when()
.item("Blind10_LowBattery").changed().toOn()
.or()
.item("Blind11_LowBattery").changed().toOn()
.then(event =>
{
var item = items.getItem(event.itemName.substring(0, 8) + "BatteryVoltage");
if (!isBatteryStateValid(item)) {
return;
}
notification.send("PowerView", "Battery level in " + getRoomFromItem(item) + " is low: " + item.state);
})
.build("PowerView Duette battery low", "Send notification when Duette battery level is low");