Skip to content

Commit

Permalink
More fluid window coverings
Browse files Browse the repository at this point in the history
  • Loading branch information
lprhodes committed Apr 19, 2017
1 parent 3535302 commit ea5139f
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 37 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ interval (optional) | The amount of time between each send of a hex code in seco
disableAutomaticOff (optional) | Prevent the switch from turning off automatically after a given amount of time. | false | true
onDurationOpen (optional) | The amount of time before the window covering automatically turns itself off when opening (used in conjunction with disableAutomaticOff). | 5 | 2
onDurationClose (optional) | The amount of time before the window covering automatically turns itself off when closing (used in conjunction with disableAutomaticOff). | 5 | 2
hold (optional) | Disabling this will force the window-covering to increment in intervals rather than in a fluid motion. | false | true
host (optional) | The IP or MAC address of the Broadlink RM device. | 192.168.1.32 | (auto-discovered)

#### "data" key-value object
Expand Down
133 changes: 98 additions & 35 deletions accessories/windowCovering.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,86 +6,149 @@ class WindowCoveringAccessory extends BroadlinkRMAccessory {

async setTargetPosition (hexData, previousValue) {
const { config, data, log, name } = this;
const { open, close } = data;
let { percentageChangePerSend } = config;
const { open, close, off } = data;

if (off && this.operationID ) {
log(`${name} setTargetPosition: cancel last operation`)
this.stop();
}

this.operationID = Date.now();
const currentOperationID = this.operationID;

if (!previousValue) previousValue = 0
if (!this.currentPosition) this.currentPosition = 0;

let { percentageChangePerSend } = config;
if (!percentageChangePerSend) percentageChangePerSend = 10;

let difference = this.targetPosition - previousValue;
let difference = this.targetPosition - this.currentPosition;

const opening = (difference > 0);
if (!opening) difference = -1 * difference;
this.opening = (difference > 0);
if (!this.opening) difference = -1 * difference;

// If the target position is not a multiple of the percentageChangePerSend
// value then make it so
if (this.targetPosition % percentageChangePerSend !== 0) {
let roundedTargetPosition;

if (opening) roundedTargetPosition = Math.ceil(this.targetPosition / percentageChangePerSend) * percentageChangePerSend;
if (!opening) roundedTargetPosition = Math.floor(this.targetPosition / percentageChangePerSend) * percentageChangePerSend;
if (this.opening) roundedTargetPosition = Math.ceil(this.targetPosition / percentageChangePerSend) * percentageChangePerSend;
if (!this.opening) roundedTargetPosition = Math.floor(this.targetPosition / percentageChangePerSend) * percentageChangePerSend;

this.targetPosition = previousValue;

log(`${name} setTargetPosition: (rounding to multiple of percentageChangePerSend; ${roundedTargetPosition})`);
log(`${name} setTargetPosition: (rounding to multiple of percentageChangePerSend; ${roundedTargetPosition}) ${currentOperationID}`);

setTimeout(() => {
if (currentOperationID !== this.operationID) return

this.windowCoveringService.setCharacteristic(Characteristic.TargetPosition, roundedTargetPosition);
}, 200);

return;
}

const sendCount = Math.ceil(difference / percentageChangePerSend);
const increments = Math.ceil(difference / percentageChangePerSend);

hexData = opening ? open : close
hexData = this.opening ? open : close

try {
this.openOrClose({ hexData, opening, sendCount, previousValue }) // Perform this asynchronously i.e. without await
} catch (err) {
console.log(err)
}
this.openOrClose({ hexData, increments, previousValue, currentOperationID }) // Perform this asynchronously i.e. without await
}

async openOrClose ({ hexData, opening, sendCount, previousValue }) {
async openOrClose ({ hexData, increments, previousValue, currentOperationID }) {
let { config, data, host, name, log } = this;
let { percentageChangePerSend, interval, disableAutomaticOff, onDuration, onDurationOpen, onDurationClose } = config;
let { hold, percentageChangePerSend, interval, disableAutomaticOff, onDuration, onDurationOpen, onDurationClose } = config;
const { off } = data;

if (!interval) percentageChangePerSend = 0.5;
if (!percentageChangePerSend) percentageChangePerSend = 10;
if (disableAutomaticOff === undefined) disableAutomaticOff = true;
if (!onDuration) onDuration = this.opening ? onDurationOpen : onDurationClose;
if (!onDuration) onDuration = 2;

let currentValue = previousValue;
if (hold) {
let difference = this.targetPosition - this.currentPosition
if (!this.opening) difference = -1 * difference;

// Itterate through each hex config in the array
for (let index = 0; index < sendCount; index++) {
const durationPerPercentage = onDuration / percentageChangePerSend;
const totalTime = durationPerPercentage * difference;

if (opening) currentValue += percentageChangePerSend
if (!opening) currentValue -= percentageChangePerSend
log(`${name} setTargetPosition: currently ${this.currentPosition}%, moving to ${this.targetPosition}%`);
log(`${name} setTargetPosition: ${totalTime}s (${onDuration} / ${percentageChangePerSend} * ${difference}) until auto-off ${currentOperationID}`);

sendData({ host, hexData, log });
this.windowCoveringService.setCharacteristic(Characteristic.CurrentPosition, currentValue);

if (!disableAutomaticOff) {
if (!onDuration) onDuration = opening ? onDurationOpen : onDurationClose;
if (!onDuration) onDuration = 2;
this.updateCurrentPositionAtIntervals(currentOperationID)

log(`${name} setTargetPosition: waiting ${onDuration}s for auto-off`);
await delayForDuration(onDuration);
this.autoStopTimeout = setTimeout(() => {
this.stop();
}, totalTime * 1000)
} else {
let currentValue = this.currentPosition || 0;
// Itterate through each hex config in the array
for (let index = 0; index < increments; index++) {
if (currentOperationID !== this.operationID) return;

if (!off) throw new Error('An "off" hex code must be set if "disableAutomaticOff" is set to false.')
if (this.opening) currentValue += percentageChangePerSend;
if (!this.opening) currentValue -= percentageChangePerSend;

log(`${name} setTargetPosition: auto-off`);
sendData({ host, hexData: off, log });
}
sendData({ host, hexData, log });
this.windowCoveringService.setCharacteristic(Characteristic.CurrentPosition, currentValue);

if (!disableAutomaticOff) {
log(`${name} setTargetPosition: waiting ${onDuration}s until auto-off ${currentOperationID}`);
await delayForDuration(onDuration);
if (currentOperationID !== this.operationID) return;

if (!off) throw new Error('An "off" hex code must be set if "disableAutomaticOff" is set to false.')

log(`${name} setTargetPosition: waiting ${interval}s for next send`);
log(`${name} setTargetPosition: auto-off`);
sendData({ host, hexData: off, log });
}

if (index < sendCount) await delayForDuration(interval);
log(`${name} setTargetPosition: waiting ${interval}s for next send ${currentOperationID}`);

if (index < sendCount) await delayForDuration(interval);
if (currentOperationID !== this.operationID) return;
}
}
}

stop () {
const { data, host, log, name } = this;
const { off } = data;

if (this.autoStopTimeout) clearTimeout(this.autoStopTimeout)
if (this.updateCurrentPositionTimeout) clearTimeout(this.updateCurrentPositionTimeout)

this.operationID = undefined;
this.opening = undefined;

log(`${name} setTargetPosition: off`);
if (off) sendData({ host, hexData: off, log });
}

updateCurrentPositionAtIntervals (currentOperationID) {
const { config } = this;
let { onDuration, onDurationOpen, onDurationClose, percentageChangePerSend } = config;

if (!onDuration) onDuration = this.opening ? onDurationOpen : onDurationClose;
if (!onDuration) onDuration = 2;

const durationPerPercentage = onDuration / percentageChangePerSend;

this.updateCurrentPositionTimeout = setTimeout(() => {
if (currentOperationID !== this.operationID) return;

let currentValue = this.currentPosition || 0;
if (this.opening) currentValue++;
if (!this.opening) currentValue--;

this.windowCoveringService.setCharacteristic(Characteristic.CurrentPosition, currentValue);
this.updateCurrentPositionAtIntervals(currentOperationID);
}, durationPerPercentage * 1000)

}

getServices () {
const services = super.getServices();

Expand Down
1 change: 1 addition & 0 deletions config-sample.json
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@
"disableAutomaticOff": false,
"onDurationOpen": 2,
"onDurationClose": 2,
"hold": true,
"data":{
"open":"2600500000012...",
"close":"2600500000012...",
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "homebridge-broadlink-rm",
"version": "1.3.3",
"version": "1.3.4",
"description": "Broadlink RM plugin (including the mini and pro) for homebridge: https://github.com/nfarina/homebridge",
"license": "ISC",
"keywords": [
Expand All @@ -21,6 +21,6 @@
"url": "git@github.com:lprhodes/homebridge-broadlink-rm.git"
},
"dependencies": {
"broadlinkjs": "git+https://github.com/lprhodes/broadlinkjs.git#0fe3146"
"broadlinkjs": "git+https://github.com/lprhodes/broadlinkjs.git#0fe3146",
}
}

0 comments on commit ea5139f

Please sign in to comment.