homebridge-bond 3.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/bug_report.md +51 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/.github/workflows/build.yml +28 -0
- package/.prettierrc +5 -0
- package/.travis.yml +8 -0
- package/LICENSE +21 -0
- package/README.md +109 -0
- package/config.schema.json +66 -0
- package/dist/BondApi.js +304 -0
- package/dist/BondUri.js +33 -0
- package/dist/Observer.js +17 -0
- package/dist/Services.js +218 -0
- package/dist/accessories/CeilingFanAccessory.js +344 -0
- package/dist/accessories/FireplaceAccessory.js +59 -0
- package/dist/accessories/GenericAccessory.js +66 -0
- package/dist/accessories/LightAccessory.js +54 -0
- package/dist/accessories/ShadesAccessory.js +98 -0
- package/dist/enum/Action.js +29 -0
- package/dist/enum/DeviceType.js +11 -0
- package/dist/index.js +10 -0
- package/dist/interface/Bond.js +124 -0
- package/dist/interface/Device.js +125 -0
- package/dist/interface/Properties.js +2 -0
- package/dist/interface/Version.js +2 -0
- package/dist/interface/config.js +61 -0
- package/dist/platform.js +258 -0
- package/dist/platformAccessory.js +48 -0
- package/dist/settings.js +11 -0
- package/docs/bond-settings.jpeg +0 -0
- package/global.d.ts +1 -0
- package/images/homebridge-ui-debug.png +0 -0
- package/images/homebridge-ui-settings.png +0 -0
- package/images/hoobs_configuration.png +0 -0
- package/package.json +59 -0
- package/tests/device.spec.ts +263 -0
- package/tests/factories/device.ts +54 -0
- package/tsconfig-build.json +6 -0
package/dist/platform.js
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.BondPlatform = void 0;
|
|
7
|
+
const Bond_1 = require("./interface/Bond");
|
|
8
|
+
const platformAccessory_1 = require("./platformAccessory");
|
|
9
|
+
const config_1 = require("./interface/config");
|
|
10
|
+
const Device_1 = require("./interface/Device");
|
|
11
|
+
const settings_1 = require("./settings");
|
|
12
|
+
const dgram_1 = __importDefault(require("dgram"));
|
|
13
|
+
class BondPlatform {
|
|
14
|
+
constructor(log, config, api) {
|
|
15
|
+
this.log = log;
|
|
16
|
+
this.config = config;
|
|
17
|
+
this.api = api;
|
|
18
|
+
this.Service = this.api.hap.Service;
|
|
19
|
+
this.Characteristic = this.api.hap.Characteristic;
|
|
20
|
+
this.UUIDGen = this.api.hap.uuid;
|
|
21
|
+
this.accessories = [];
|
|
22
|
+
if (config === null) {
|
|
23
|
+
this.log.error('No config defined.');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (!config_1.BondPlatformConfig.isValid(this)) {
|
|
27
|
+
this.log.error(`Config: ${JSON.stringify(config, null, 2)}`);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
this.log.debug(`Config: ${JSON.stringify(config, null, 2)}`);
|
|
31
|
+
api.on('didFinishLaunching', () => {
|
|
32
|
+
// Delaying the initialization of bonds property because we need to
|
|
33
|
+
// get the device ids before doing anything
|
|
34
|
+
this.validateBonds();
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
// Validate that all of the bonds provided in the config are online and authenticaiton is working
|
|
38
|
+
validateBonds() {
|
|
39
|
+
const bonds = Bond_1.Bond.objects(this);
|
|
40
|
+
const validated = [];
|
|
41
|
+
Bond_1.Bond.validate(bonds).then(res => {
|
|
42
|
+
res.forEach(res => {
|
|
43
|
+
if (res !== undefined) {
|
|
44
|
+
validated.push(res);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
this.setupBonds(validated);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
setupBonds(bonds) {
|
|
51
|
+
if (bonds.length === 0) {
|
|
52
|
+
this.log.warn('No valid Bonds available.');
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
// const bonds = Bond.objects(this);
|
|
56
|
+
Bond_1.Bond.updateDeviceIds(bonds).then(() => {
|
|
57
|
+
this.bonds = bonds;
|
|
58
|
+
this.log(`${this.accessories.length} cached accessories were loaded`);
|
|
59
|
+
this.bonds.forEach(bond => {
|
|
60
|
+
this.getDevices(bond);
|
|
61
|
+
this.setupBPUP(bond);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
getDevices(bond) {
|
|
66
|
+
this.cleanupBondData(bond);
|
|
67
|
+
this.log(`Getting devices for this Bond (${bond.version.bondid})...`);
|
|
68
|
+
this.log(`${bond.deviceIds.length} devices were found on this Bond (${bond.version.bondid}).`);
|
|
69
|
+
const filtered = bond.deviceIds.filter(deviceId => {
|
|
70
|
+
const accessories = this.accessories.filter(acc => {
|
|
71
|
+
return acc.context.device.uniqueId === bond.uniqueDeviceId(deviceId);
|
|
72
|
+
});
|
|
73
|
+
return accessories.length === 0;
|
|
74
|
+
});
|
|
75
|
+
if (filtered.length === 0) {
|
|
76
|
+
this.log(`No new devices to add for this Bond (${bond.version.bondid}).`);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
this.log(`Attempting to add ${filtered.length} devices that were not previously added.`);
|
|
80
|
+
bond.api
|
|
81
|
+
.getDevices(filtered)
|
|
82
|
+
.then(devices => {
|
|
83
|
+
devices.forEach(device => {
|
|
84
|
+
// Set the unique id
|
|
85
|
+
device.uniqueId = bond.uniqueDeviceId(device.id);
|
|
86
|
+
// Set the bond id
|
|
87
|
+
device.bondId = bond.version.bondid;
|
|
88
|
+
});
|
|
89
|
+
this.addAccessories(devices);
|
|
90
|
+
})
|
|
91
|
+
.catch(error => {
|
|
92
|
+
this.log(`Error getting devices: ${error}`);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
cleanupBondData(bond) {
|
|
96
|
+
// Data cleanup - Make sure all cached devices have uniqueId and bondId on them
|
|
97
|
+
bond.deviceIds.forEach(deviceId => {
|
|
98
|
+
this.accessories.forEach(accessory => {
|
|
99
|
+
// Only run if device does not have uniqueId
|
|
100
|
+
if (accessory.context.device.uniqueId === undefined
|
|
101
|
+
&& accessory.context.device.id === deviceId) {
|
|
102
|
+
const uniqueId = bond.uniqueDeviceId(deviceId);
|
|
103
|
+
this.log.debug(`Updating device data with uniqueId ${uniqueId}`);
|
|
104
|
+
accessory.context.device.uniqueId = bond.uniqueDeviceId(deviceId);
|
|
105
|
+
accessory.context.device.bondId = bond.version.bondid;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
addAccessories(devices) {
|
|
111
|
+
devices.forEach(device => {
|
|
112
|
+
this.addAccessory(device);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
// Accessory
|
|
116
|
+
/**
|
|
117
|
+
* Add a new accessory that hasn't been added before.
|
|
118
|
+
*/
|
|
119
|
+
addAccessory(device) {
|
|
120
|
+
const bond = this.bondForDevice(device);
|
|
121
|
+
// Make sure Bond exists
|
|
122
|
+
if (bond === undefined) {
|
|
123
|
+
this.log(`[${device.name}] Bond does not exist for device id: ${device.id}.`);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// Make sure device shouldn't be excluded
|
|
127
|
+
if ((bond.config.hide_device_ids !== undefined
|
|
128
|
+
&& bond.config.hide_device_ids.includes(device.id))) {
|
|
129
|
+
this.log(`[${device.name}] Excluding ${device.id}.`);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
// Make sure device has supported actions
|
|
133
|
+
if (!Device_1.Device.isSupported(device)) {
|
|
134
|
+
this.log(`[${device.name}] Device has no supported actions.`);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const uuid = this.UUIDGen.generate(device.uniqueId);
|
|
138
|
+
if (this.accessoryAdded(uuid)) {
|
|
139
|
+
this.log(`[${device.name}] Accessory already added.`);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const displayName = Device_1.Device.displayName(device);
|
|
143
|
+
const accessory = new this.api.platformAccessory(`${displayName}`, uuid);
|
|
144
|
+
accessory.context.device = device;
|
|
145
|
+
this.create(accessory);
|
|
146
|
+
this.api.registerPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, [accessory]);
|
|
147
|
+
this.accessories.push(accessory);
|
|
148
|
+
this.log(`Adding Accessory ${accessory.displayName}`);
|
|
149
|
+
this.log.debug(`Device unique id: ${device.uniqueId}`);
|
|
150
|
+
}
|
|
151
|
+
removeAccessory(accessory) {
|
|
152
|
+
this.log(`Removing Accessory: ${accessory.displayName}`);
|
|
153
|
+
const index = this.accessories.indexOf(accessory);
|
|
154
|
+
if (index > -1) {
|
|
155
|
+
this.accessories.splice(index, 1);
|
|
156
|
+
}
|
|
157
|
+
this.api.unregisterPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, [accessory]);
|
|
158
|
+
}
|
|
159
|
+
configureAccessory(accessory) {
|
|
160
|
+
if (this.config === null || this.config.bonds === undefined) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
this.accessories.push(accessory);
|
|
164
|
+
// If bonds hasn't been initilized, attempt to configure the accessory
|
|
165
|
+
// after a delay
|
|
166
|
+
if (this.bonds) {
|
|
167
|
+
this.log(`Configuring Accessory: ${accessory.displayName}`);
|
|
168
|
+
this.create(accessory);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
const that = this;
|
|
172
|
+
const timer = setInterval(() => {
|
|
173
|
+
if (this.bonds) {
|
|
174
|
+
that.log(`Configuring Accessory: ${accessory.displayName}`);
|
|
175
|
+
that.create(accessory);
|
|
176
|
+
clearInterval(timer);
|
|
177
|
+
}
|
|
178
|
+
}, 500);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
create(accessory) {
|
|
182
|
+
const device = accessory.context.device;
|
|
183
|
+
const bond = this.bondForDevice(device);
|
|
184
|
+
if (!bond) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if ((bond.config.hide_device_ids
|
|
188
|
+
&& bond.config.hide_device_ids.includes(device.id))) {
|
|
189
|
+
this.removeAccessory(accessory);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
this.logAccessory(accessory, `actions: ${device.actions}`);
|
|
193
|
+
const bondAccessory = platformAccessory_1.BondAccessory.create(this, accessory, bond);
|
|
194
|
+
bond.accessories.push(bondAccessory);
|
|
195
|
+
}
|
|
196
|
+
setupBPUP(bond) {
|
|
197
|
+
const PORT = 30007;
|
|
198
|
+
const HOST = bond.config.ip_address;
|
|
199
|
+
const message = Buffer.from('');
|
|
200
|
+
const client = dgram_1.default.createSocket('udp4');
|
|
201
|
+
const log = this.log;
|
|
202
|
+
function send() {
|
|
203
|
+
client.send(message, 0, message.length, PORT, HOST, (err) => {
|
|
204
|
+
if (err) {
|
|
205
|
+
log.error(`Erorr sending UDP message: ${err}`);
|
|
206
|
+
throw err;
|
|
207
|
+
}
|
|
208
|
+
log.debug(`UDP message sent to ${HOST}:${PORT}`);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
send();
|
|
212
|
+
// From Bond API Docs: The client should continue to send the Keep-Alive datagram on
|
|
213
|
+
// the same socket every 60 seconds to keep the connection active.
|
|
214
|
+
setInterval(send, 1000 * 60);
|
|
215
|
+
client.on('message', (message, remote) => {
|
|
216
|
+
const msg = message.toString().trim();
|
|
217
|
+
const packet = JSON.parse(msg);
|
|
218
|
+
log.debug(`UDP Message received from ${remote.address}:${remote.port} - ${msg}`);
|
|
219
|
+
bond.receivedBPUPPacket(packet);
|
|
220
|
+
});
|
|
221
|
+
client.on('close', () => {
|
|
222
|
+
this.log('Connection closed');
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
bondForDevice(device) {
|
|
226
|
+
if (this.bonds) {
|
|
227
|
+
const bond = this.bonds.find(x => x.version.bondid === device.bondId && x.deviceIds.includes(device.id));
|
|
228
|
+
if (bond === undefined) {
|
|
229
|
+
this.log.error(`No Bond found for Device: ${device.name}.
|
|
230
|
+
This Device may have been removed from your Bond but still exists in cachedAccessories.`);
|
|
231
|
+
}
|
|
232
|
+
return bond;
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
this.log.error('config.bonds is not defined');
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
// Helper Methods
|
|
239
|
+
accessoryAdded(uuid) {
|
|
240
|
+
const accessories = this.accessories.filter(acc => {
|
|
241
|
+
return acc.UUID === uuid;
|
|
242
|
+
});
|
|
243
|
+
return accessories.length > 0;
|
|
244
|
+
}
|
|
245
|
+
debug(accessory, message) {
|
|
246
|
+
const device = accessory.context.device;
|
|
247
|
+
this.log.debug(`[${device.name}] ${message}`);
|
|
248
|
+
}
|
|
249
|
+
logAccessory(accessory, message) {
|
|
250
|
+
const device = accessory.context.device;
|
|
251
|
+
this.log(`[${device.name}] ${message}`);
|
|
252
|
+
}
|
|
253
|
+
error(accessory, message) {
|
|
254
|
+
const device = accessory.context.device;
|
|
255
|
+
this.log.error(`[${device.name}] ${message}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
exports.BondPlatform = BondPlatform;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BondAccessory = void 0;
|
|
4
|
+
const CeilingFanAccessory_1 = require("./accessories/CeilingFanAccessory");
|
|
5
|
+
const DeviceType_1 = require("./enum/DeviceType");
|
|
6
|
+
const FireplaceAccessory_1 = require("./accessories/FireplaceAccessory");
|
|
7
|
+
const GenericAccessory_1 = require("./accessories/GenericAccessory");
|
|
8
|
+
const LightAccessory_1 = require("./accessories/LightAccessory");
|
|
9
|
+
const ShadesAccessory_1 = require("./accessories/ShadesAccessory");
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
11
|
+
var BondAccessory;
|
|
12
|
+
(function (BondAccessory) {
|
|
13
|
+
function create(platform, accessory, bond) {
|
|
14
|
+
var _a;
|
|
15
|
+
const device = accessory.context.device;
|
|
16
|
+
const service = accessory.getService(platform.Service.AccessoryInformation);
|
|
17
|
+
service
|
|
18
|
+
.setCharacteristic(platform.Characteristic.Manufacturer, (_a = bond.version.make) !== null && _a !== void 0 ? _a : `Bond (${bond.version.target})`)
|
|
19
|
+
// FirmwareRevision only supports semantic versioning, so the 'v' in the firmware version needs to be dropped
|
|
20
|
+
.setCharacteristic(platform.Characteristic.FirmwareRevision, bond.version.fw_ver.replace('v', ''))
|
|
21
|
+
// SerialNumber must be at least 2 characters. Use uniqueId to prevent warnings.
|
|
22
|
+
.setCharacteristic(platform.Characteristic.SerialNumber, device.uniqueId);
|
|
23
|
+
if (bond.version.model) {
|
|
24
|
+
service
|
|
25
|
+
.setCharacteristic(platform.Characteristic.Model, bond.version.model);
|
|
26
|
+
}
|
|
27
|
+
if (bond.version.mcu_ver) {
|
|
28
|
+
service
|
|
29
|
+
.setCharacteristic(platform.Characteristic.HardwareRevision, bond.version.mcu_ver);
|
|
30
|
+
}
|
|
31
|
+
switch (device.type) {
|
|
32
|
+
case DeviceType_1.DeviceType.CeilingFan:
|
|
33
|
+
return new CeilingFanAccessory_1.CeilingFanAccessory(platform, accessory, bond);
|
|
34
|
+
case DeviceType_1.DeviceType.Generic:
|
|
35
|
+
return new GenericAccessory_1.GenericAccessory(platform, accessory, bond);
|
|
36
|
+
case DeviceType_1.DeviceType.Fireplace:
|
|
37
|
+
return new FireplaceAccessory_1.FireplaceAccessory(platform, accessory, bond);
|
|
38
|
+
case DeviceType_1.DeviceType.Shades:
|
|
39
|
+
return new ShadesAccessory_1.ShadesAccessory(platform, accessory, bond);
|
|
40
|
+
case DeviceType_1.DeviceType.Light:
|
|
41
|
+
return new LightAccessory_1.LightAccessory(platform, accessory, bond);
|
|
42
|
+
default: {
|
|
43
|
+
throw 'Invalid Device Type';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
BondAccessory.create = create;
|
|
48
|
+
})(BondAccessory = exports.BondAccessory || (exports.BondAccessory = {}));
|
package/dist/settings.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PLUGIN_NAME = exports.PLATFORM_NAME = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* This is the name of the platform that users will use to register the plugin in the Homebridge config.json
|
|
6
|
+
*/
|
|
7
|
+
exports.PLATFORM_NAME = 'Bond';
|
|
8
|
+
/**
|
|
9
|
+
* This must match the name of your plugin as defined the package.json
|
|
10
|
+
*/
|
|
11
|
+
exports.PLUGIN_NAME = 'homebridge-bond';
|
|
Binary file
|
package/global.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare module 'biguint-format';
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "homebridge-bond",
|
|
3
|
+
"version": "3.2.9",
|
|
4
|
+
"description": "A homebridge plugin to control your Bond devices over the v2 API.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"lint": "eslint src/**.ts",
|
|
9
|
+
"watch": "npm run build && npm link && nodemon",
|
|
10
|
+
"dev": "tsc --watch & nodemon dist",
|
|
11
|
+
"build": "rimraf ./dist && tsc -p tsconfig-build.json",
|
|
12
|
+
"prepublishOnly": "npm run lint && npm run build",
|
|
13
|
+
"test": "mocha -r ts-node/register tests/**/*.spec.ts",
|
|
14
|
+
"coverage": "nyc -r lcov -e .ts -x \"*.spec.ts\" npm run test"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"homebridge-plugin",
|
|
18
|
+
"bond",
|
|
19
|
+
"bond-home",
|
|
20
|
+
"rf",
|
|
21
|
+
"ir"
|
|
22
|
+
],
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git://github.com/aarons22/homebridge-bond.git"
|
|
26
|
+
},
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "http://github.com/aarons22/homebridge-bond/issues"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=10.17.0",
|
|
32
|
+
"homebridge": ">=1.3.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/chai": "^4.2.14",
|
|
36
|
+
"@types/flake-idgen": "^0.1.30",
|
|
37
|
+
"@types/mocha": "^8.2.0",
|
|
38
|
+
"@types/node": "^14.14.31",
|
|
39
|
+
"@typescript-eslint/eslint-plugin": "^4.18.0",
|
|
40
|
+
"@typescript-eslint/parser": "^4.18.0",
|
|
41
|
+
"chai": "^4.2.0",
|
|
42
|
+
"eslint": "^7.22.0",
|
|
43
|
+
"homebridge": "^1.3.1",
|
|
44
|
+
"mocha": "^9.1.2",
|
|
45
|
+
"nodemon": "^2.0.7",
|
|
46
|
+
"nyc": "^15.1.0",
|
|
47
|
+
"rimraf": "^3.0.2",
|
|
48
|
+
"ts-node": "^9.1.1",
|
|
49
|
+
"typescript": "^4.2.2"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"axios": "^0.21.4",
|
|
53
|
+
"axios-retry": "^3.1.8",
|
|
54
|
+
"biguint-format": "^1.0.2",
|
|
55
|
+
"flake-idgen": "^1.4.0"
|
|
56
|
+
},
|
|
57
|
+
"homepage": "https://github.com/aarons22/homebridge-bond#readme",
|
|
58
|
+
"author": "Aaron Sapp"
|
|
59
|
+
}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { expect } from 'chai';
|
|
2
|
+
import 'mocha';
|
|
3
|
+
import { Action } from '../src/enum/Action';
|
|
4
|
+
import { DeviceType } from '../src/enum/DeviceType';
|
|
5
|
+
import { Device } from '../src/interface/Device';
|
|
6
|
+
import { DeviceFactory } from './factories/device';
|
|
7
|
+
|
|
8
|
+
describe('displayName', () => {
|
|
9
|
+
it('is correct', () => {
|
|
10
|
+
const device = DeviceFactory.createDevice();
|
|
11
|
+
expect(Device.displayName(device)).equal('location name');
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe('isSupported', () => {
|
|
16
|
+
it('ceiling fan', () => {
|
|
17
|
+
const device = DeviceFactory.createDevice({ type: DeviceType.CeilingFan });
|
|
18
|
+
expect(Device.isSupported(device)).equal(true);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('shades', () => {
|
|
22
|
+
const device = DeviceFactory.createDevice({ type: DeviceType.Shades });
|
|
23
|
+
expect(Device.isSupported(device)).equal(true);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('generic', () => {
|
|
27
|
+
const device = DeviceFactory.createDevice({ type: DeviceType.Generic });
|
|
28
|
+
expect(Device.isSupported(device)).equal(true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('fireplace', () => {
|
|
32
|
+
const device = DeviceFactory.createDevice({ type: DeviceType.Fireplace });
|
|
33
|
+
expect(Device.isSupported(device)).equal(true);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('light', () => {
|
|
37
|
+
const device = DeviceFactory.createDevice({ type: DeviceType.Light });
|
|
38
|
+
expect(Device.isSupported(device)).equal(true);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('HasDimmer', () => {
|
|
43
|
+
it('has dimmer', () => {
|
|
44
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop, Action.StartDimmer] });
|
|
45
|
+
expect(Device.HasDimmer(device)).equal(true);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('does not have dimmer', () => {
|
|
49
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop] });
|
|
50
|
+
expect(Device.HasDimmer(device)).equal(false);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe('HasSeparateDimmers', () => {
|
|
55
|
+
it('has dimmer', () => {
|
|
56
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop, Action.StartIncreasingBrightness, Action.StartDecreasingBrightness] });
|
|
57
|
+
expect(Device.HasSeparateDimmers(device)).equal(true);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('does not have separate dimmers', () => {
|
|
61
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop] });
|
|
62
|
+
expect(Device.HasSeparateDimmers(device)).equal(false);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe('CFhasLightbulb', () => {
|
|
67
|
+
it('has lightbulb', () => {
|
|
68
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop, Action.ToggleLight] });
|
|
69
|
+
expect(Device.CFhasLightbulb(device)).equal(true);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('does not have lightbulb', () => {
|
|
73
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop] });
|
|
74
|
+
expect(Device.CFhasLightbulb(device)).equal(false);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('CFhasUpDownLight', () => {
|
|
79
|
+
it('has up down light', () => {
|
|
80
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop, Action.ToggleUpLight, Action.ToggleDownLight] });
|
|
81
|
+
expect(Device.CFhasUpDownLight(device)).equal(true);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('does not have up down light', () => {
|
|
85
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop] });
|
|
86
|
+
expect(Device.CFhasUpDownLight(device)).equal(false);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('canSetSpeed', () => {
|
|
91
|
+
it('can set speed', () => {
|
|
92
|
+
const device = DeviceFactory.createDevice({
|
|
93
|
+
actions: [Action.Stop, Action.SetSpeed],
|
|
94
|
+
maxSpeed: 1,
|
|
95
|
+
});
|
|
96
|
+
expect(Device.canSetSpeed(device)).equal(true);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('does not have set speed', () => {
|
|
100
|
+
const device = DeviceFactory.createDevice({
|
|
101
|
+
actions: [Action.Stop],
|
|
102
|
+
maxSpeed: 1,
|
|
103
|
+
});
|
|
104
|
+
expect(Device.canSetSpeed(device)).equal(false);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('does not have max speed', () => {
|
|
108
|
+
const device = DeviceFactory.createDevice({
|
|
109
|
+
actions: [Action.Stop, Action.SetSpeed],
|
|
110
|
+
});
|
|
111
|
+
expect(Device.canSetSpeed(device)).equal(false);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('canIncreaseDecreaseSpeed', () => {
|
|
116
|
+
it('can increase and decrease speed', () => {
|
|
117
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop, Action.IncreaseSpeed, Action.DecreaseSpeed] });
|
|
118
|
+
expect(Device.canIncreaseDecreaseSpeed(device)).equal(true);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('cannnot increase and decrease speed', () => {
|
|
122
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop] });
|
|
123
|
+
expect(Device.canIncreaseDecreaseSpeed(device)).equal(false);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('hasOffOn', () => {
|
|
128
|
+
it('has off on', () => {
|
|
129
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop, Action.TurnOff, Action.TurnOn] });
|
|
130
|
+
expect(Device.hasOffOn(device)).equal(true);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('does not have off on', () => {
|
|
134
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop] });
|
|
135
|
+
expect(Device.hasOffOn(device)).equal(false);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe('hasReverseSwitch', () => {
|
|
140
|
+
it('has reverse switch', () => {
|
|
141
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop, Action.ToggleDirection] });
|
|
142
|
+
expect(Device.hasReverseSwitch(device)).equal(true);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('does not have reverse switch', () => {
|
|
146
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop] });
|
|
147
|
+
expect(Device.hasReverseSwitch(device)).equal(false);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe('GXhasToggle', () => {
|
|
152
|
+
it('has toggle', () => {
|
|
153
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop, Action.TogglePower] });
|
|
154
|
+
expect(Device.GXhasToggle(device)).equal(true);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('does not have toggle', () => {
|
|
158
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop] });
|
|
159
|
+
expect(Device.GXhasToggle(device)).equal(false);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe('FPhasToggle', () => {
|
|
164
|
+
it('has toggle', () => {
|
|
165
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop, Action.TogglePower] });
|
|
166
|
+
expect(Device.FPhasToggle(device)).equal(true);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('does not have toggle', () => {
|
|
170
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop] });
|
|
171
|
+
expect(Device.FPhasToggle(device)).equal(false);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
describe('FPhasFlame', () => {
|
|
176
|
+
it('has flame and toggle power', () => {
|
|
177
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop, Action.SetFlame, Action.TogglePower] });
|
|
178
|
+
expect(Device.FPhasFlame(device)).equal(true);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('does not have flame and toggle power', () => {
|
|
182
|
+
const device = DeviceFactory.createDevice({ actions: [Action.TogglePower] });
|
|
183
|
+
expect(Device.FPhasFlame(device)).equal(false);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe('MShasToggle', () => {
|
|
188
|
+
it('has toggle', () => {
|
|
189
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop, Action.ToggleOpen] });
|
|
190
|
+
expect(Device.MShasToggle(device)).equal(true);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('does not have toggle', () => {
|
|
194
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop] });
|
|
195
|
+
expect(Device.MShasToggle(device)).equal(false);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
describe('MShasPreset', () => {
|
|
200
|
+
it('has preset', () => {
|
|
201
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop, Action.Preset] });
|
|
202
|
+
expect(Device.MShasPreset(device)).equal(true);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('does not have preset', () => {
|
|
206
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop] });
|
|
207
|
+
expect(Device.MShasPreset(device)).equal(false);
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
describe('LThasLightbulb', () => {
|
|
212
|
+
it('has lightbulb', () => {
|
|
213
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop, Action.ToggleLight] });
|
|
214
|
+
expect(Device.LThasLightbulb(device)).equal(true);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('does not have lightbulb', () => {
|
|
218
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop] });
|
|
219
|
+
expect(Device.LThasLightbulb(device)).equal(false);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
describe('LThasBrightness', () => {
|
|
224
|
+
it('has brightness', () => {
|
|
225
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop, Action.SetBrightness, Action.TurnLightOff] });
|
|
226
|
+
expect(Device.LThasBrightness(device)).equal(true);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('does not have brightness', () => {
|
|
230
|
+
const device = DeviceFactory.createDevice({ actions: [Action.Stop] });
|
|
231
|
+
expect(Device.LThasBrightness(device)).equal(false);
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
describe('fanSpeeds', () => {
|
|
236
|
+
context('has commands', () => {
|
|
237
|
+
it('returns correct values', () => {
|
|
238
|
+
const device = DeviceFactory.createFanWithSpeeds([1,2,3]);
|
|
239
|
+
expect(Device.fanSpeeds(device)).to.deep.equal([1,2,3]);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('returns correct values', () => {
|
|
243
|
+
const device = DeviceFactory.createFanWithSpeeds([1,2,4]);
|
|
244
|
+
expect(Device.fanSpeeds(device)).to.deep.equal([1,2,4]);
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
context('does not have commands', () => {
|
|
249
|
+
context('has max speed', () => {
|
|
250
|
+
it('returns correct values', () => {
|
|
251
|
+
const device = DeviceFactory.createDevice({maxSpeed: 6});
|
|
252
|
+
expect(Device.fanSpeeds(device)).to.deep.equal([1,2,3,4,5,6]);
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
context('does not have max speed', () => {
|
|
257
|
+
it('returns empty array', () => {
|
|
258
|
+
const device = DeviceFactory.createDevice();
|
|
259
|
+
expect(Device.fanSpeeds(device)).to.deep.equal([]);
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
});
|