-
Notifications
You must be signed in to change notification settings - Fork 0
/
fancontrol.js
94 lines (81 loc) · 3.27 KB
/
fancontrol.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
// Homebridge plugin for Fantasia Viper fans
// Copyright © 2019 Alexander Thoukydides
'use strict';
let request = require('request');
// Button mappings
const BUTTON = {
LOW: 0,
LIGHT: 1,
DIM: 2,
MEDIUM: 3,
HIGH: 4,
OFF: 5,
// There is no button 6
REVERSE: 7 // (Viper Plus only)
};
// Transmitter timings in microseconds for a 4.4kHz oscillator
const TX_CLOCK = 225;
const TX_SYNC = [TX_CLOCK];
const TX_0 = [TX_CLOCK, 2 * TX_CLOCK];
const TX_1 = [2 * TX_CLOCK, TX_CLOCK];
const TX_PILOT = [20 * TX_CLOCK]; // (12 should suffice for the pilot period)
// Number of times to repeat each transmission
const TX_REPEAT = 5; // (4 should suffice)
// Control a single fan
module.exports = class FanControl {
// Constructor
constructor(log, address, host, port) {
this.log = log;
this.address = address.map(b => b ? 1 : 0);
this.host = host;
this.port = port;
}
// Button presses
buttonOff(callback) { this.button(BUTTON.OFF, callback); }
buttonLow(callback) { this.button(BUTTON.LOW, callback); }
buttonMedium(callback) { this.button(BUTTON.MEDIUM, callback); }
buttonHigh(callback) { this.button(BUTTON.HIGH, callback); }
buttonReverse(callback) { this.button(BUTTON.REVERSE, callback); }
buttonLight(callback) { this.button(BUTTON.LIGHT, callback); }
buttonDim(callback) { this.button(BUTTON.DIM, callback); }
// An arbitrary button press
button(pin, callback) {
// Construct the (inverted) bit sequence for this button
let word = Array(8).fill(0).concat(this.address);
word[pin] = 1;
this.log.debug('Transmit bits: ' + word.join('') + ' (inverted)');
// Convert to a sequence of on/off timings for the transmitter
let txWord = [].concat(...word.map(b => b ? TX_0 : TX_1));
let txSingle = [].concat(TX_SYNC, txWord, TX_PILOT);
let txCode = [].concat(...Array(TX_REPEAT).fill(txSingle)).join(' ');
this.log.debug('Transmit timings: ' + txCode + ' (microseconds)');
// Transmit the pattern
this.pilightSend('send', { protocol: "raw", code: "'" + txCode + "'" },
callback);
}
// Send data to the pilight daemon
pilightSend(cmd, args, callback) {
let options = {
url: 'http://' + this.host + ':' + this.port + '/' + cmd,
qs: args,
json: true
};
let logPrefix = 'pilight-' + cmd + ': ';
this.log.debug(logPrefix + 'GET');
let startTime = Date.now();
request.get(options, (err, response, body) => {
let msg = err || response.statusMessage;
if (body && body.message) msg = body.message;
let logMsg = msg + ' +' + (Date.now() - startTime) + 'ms ';
// Check whether the request was successful
if (err || (400 <= response.statusCode)
|| (body.message != 'success')) {
this.log.error(logPrefix + 'ERROR ' + logMsg);
callback(new Error('pilight error: ' + msg));
} else {
this.log.debug(logPrefix + logMsg);
callback(null);
}
});
}
}