-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
176 lines (148 loc) · 6.75 KB
/
index.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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
const Unifi = require('node-unifi');
const hap = require('hap-nodejs');
const qrcode = require('qrcode-terminal');
const unifi_host = process.env.UNIFI_HOST;
const unifi_user = process.env.UNIFI_USER;
const unifi_pass = process.env.UNIFI_PASS;
const unifi_port = process.env.UNIFI_PORT || 443;
const unifi_access_device_name = process.env.UNIFI_ACCESS_DEVICE_NAME;
let unifi_device_hostname = process.env.UNIFI_DEVICE_HOSTNAME;
let unifi_device_mac = process.env.UNIFI_DEVICE_MAC;
const unifi_rule_name_template = process.env.UNIFI_RULE_NAME_TEMPLATE || "HomeKit rule for <DEVICE_HOSTNAME>";
const unifi_sslverify = false;
const service_port = process.env.SERVICE_PORT || 8080;
const service_ip = process.env.SERVICE_IP || "0.0.0.0";
const homekit_service_name_template = process.env.HOMEKIT_SERVICE_NAME_TEMPLATE || "Stop Internet Access for <DEVICE_HOSTNAME>";
const homekit_pin_code = process.env.HOMEKIT_PIN_CODE || '123-45-678';
const {Service, Characteristic} = hap;
class UnifiHomeKit {
constructor(options) {
this.options = options;
this.unifiController = new Unifi.Controller({
host: this.options.unifi_host,
port: this.options.unifi_port,
sslverify: this.options.unifi_sslverify,
username: this.options.unifi_user,
password: this.options.unifi_pass
});
this.unifi_host = options.unifi_host;
this.unifi_user = options.unifi_user;
this.unifi_pass = options.unifi_pass;
this.unifi_port = options.unifi_port;
this.unifi_access_device_name = options.unifi_access_device_name;
this.unifi_device_hostname = options.unifi_device_hostname;
this.unifi_device_mac = options.unifi_device_mac;
this.unifi_rule_name_template = options.unifi_rule_name_template;
this.unifi_sslverify = options.unifi_sslverify;
this.unifi_rule_id = null;
this.device_update_callback = options.device_update_callback;
}
static async createInstance(options) {
const unifi = new UnifiHomeKit(options);
await unifi.init();
return unifi;
}
generateJSONRule(enabled) {
const ret = {
action: 'BLOCK',
description: this.unifi_rule_name,
enabled,
matching_target: 'INTERNET',
traffic_direction: 'TO',
target_devices: [{client_mac: this.unifi_device_mac, type: 'CLIENT'}],
};
if (this.unifi_rule_id)
ret._id = this.unifi_rule_id;
return ret;
}
async init() {
await this.unifiController.login();
const managing_device = (await this.unifiController.getAccessDevices()).filter(x => x.name === unifi_access_device_name)?.[0];
let managed_device;
if (this.unifi_device_hostname) {
managed_device = (await this.unifiController.getClientDevices()).filter(x => x.hostname === unifi_device_hostname)?.[0];
this.unifi_device_mac = managed_device?.mac;
}
if (this.unifi_device_mac) {
managed_device = (await this.unifiController.getClientDevices()).filter(x => x.mac === this.unifi_device_mac.toLowerCase())?.[0];
this.unifi_device_hostname = managed_device?.hostname;
}
if (!managed_device || !managing_device) {
console.error(`Device not found: managing_device:${managing_device} managed_device:${managed_device}`);
return;
}
this.unifi_rule_name = unifi_rule_name_template.replace('<DEVICE_HOSTNAME>', this.unifi_device_hostname);
let traffic_rule = await this.getRule();
if (!traffic_rule) {
console.log(`Creating rule: ${this.unifi_rule_name}`);
traffic_rule = await this.unifiController.customApiRequest(`/v2/api/site/<SITE>/trafficrules`, 'POST', this.generateJSONRule(false));
console.log(`Rule created: ${this.unifi_rule_name}`);
} else {
console.log(`Rule already exists: ${this.unifi_rule_name}`);
}
this.unifi_rule_id = traffic_rule._id;
const listenData = await this.unifiController.listen();
this.unifiController.on('device:update.generic', data => this.device_update_callback(data).bind(this));
}
async setRuleState(value) {
console.log(`Switching rule ${this.unifi_rule_name} to ${value}`);
await this.unifiController.customApiRequest(`/v2/api/site/<SITE>/trafficrules/${this.unifi_rule_id}`, 'PUT', this.generateJSONRule(value));
console.log(`Rule ${this.unifi_rule_name} switched to ${value}`);
}
async getRule() { // true if rule is disabled
return (await this.unifiController.customApiRequest(`/v2/api/site/<SITE>/trafficrules`, 'GET')).filter(x => x.description === this.unifi_rule_name)?.[0];
}
async getRuleState() { // true if rule is disabled
return (await this.getRule())?.enabled;
}
destroy() {
this.unifiController.logout();
}
}
const main = async () => {
const unifiHomeKit = await UnifiHomeKit.createInstance({
unifi_host,
unifi_user,
unifi_pass,
unifi_port,
unifi_access_device_name,
unifi_device_hostname,
unifi_device_mac,
unifi_rule_name_template,
unifi_sslverify,
});
const homekit_service_name = homekit_service_name_template.replace('<DEVICE_HOSTNAME>', unifiHomeKit.unifi_device_hostname);
const accessory = new hap.Accessory(homekit_service_name, hap.uuid.generate(unifiHomeKit.unifi_device_mac));
const service = new Service.Switch(homekit_service_name);
accessory.addService(service);
service.getCharacteristic(Characteristic.On)
.on('get', async (callback) => {
console.log('getter called');
callback(null, (await unifiHomeKit.getRuleState()));
})
.on('set', async (value, callback) => {
console.log(`setter called. Switching to ${value}`);
await unifiHomeKit.setRuleState(value);
callback();
});
unifiHomeKit.device_update_callback = async (data) => {
const relevant_event = data.filter(x => x.mac === this.unifi_device_mac)?.[0];
if (relevant_event?.state === 1) {
// setTimeout(async () => {
const value = await unifiHomeKit.getRuleState();
console.log(`update called. Switching to ${value}`);
service.getCharacteristic(Characteristic.On).updateValue(value);
// }, 5000);
}
};
await accessory.publish({
username: unifiHomeKit.unifi_device_mac,
pincode: homekit_pin_code,
category: hap.Categories.SWITCH,
port: service_port,
bind: service_ip,
});
console.log(accessory.setupURI())
qrcode.generate(accessory.setupURI(), {small: true});
};
main().catch(console.error);