-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
164 lines (154 loc) · 5.27 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
import { argv, stdout, exit } from 'process';
import MIMH03Device from '../devices/mim-h03.js';
const argumentList = [
['ip', 'Samsung RAC device IP address'],
['token', 'Device token used for authentication'],
['id', 'Sub device id'],
['name', 'Sub device name'],
['info', 'Output device info'],
['temp', 'Desired temperature'],
['mode', 'Desired mode'],
['speed', 'Desired fan speed'],
['proxy', 'Creates an HTTP server that proxies requests'],
['httpport', 'HTTP listening port'],
['httpsport', 'HTTPS listening port'],
['requesttoken', 'Listens for and requests device token'],
];
/**
* @param {string} [s='']
* @param {number} [length=0]
* @prop {string} [char=' ']
* @return {string}
*/
function padEnd(s = '', length = 0, char = ' ') {
let r = s;
while (r.length < length) r += char;
return r;
}
const args = argv.slice(2);
/** @type {Map<string,string>} */
const entries = new Map();
if (!args.length) {
stdout.write('USAGE: samsungrac [OPTION]...\n');
stdout.write('Perform variation operations against a Samsung RAC device.\n');
stdout.write('\n');
const padSize = Math.max(...argumentList.map(([arg]) => arg.length));
argumentList.forEach(([arg, description]) => {
stdout.write(` --${padEnd(arg, padSize)} ${description}.\n`);
});
exit(0);
}
for (let i = 0; i < args.length; i += 1) {
const arg = args[i];
if (arg.startsWith('--')) {
const key = arg.substr(2).toLowerCase();
const value = args[i + 1];
if (!value || !value.startsWith('--')) {
entries.set(key, value);
i += 1;
} else {
entries.set(key, null);
}
} else {
throw new Error(`Unknown argument: ${arg}`);
}
}
const controller = new MIMH03Device({
ip: entries.get('ip'),
token: entries.get('token'),
httpPort: Number.parseInt(entries.get('httpport'), 10) || null,
httpsPort: Number.parseInt(entries.get('httpsport'), 10) || null,
});
async function run() {
if (entries.has('requesttoken')) {
stdout.write('Requesting device token (AP button may need to be pressed)...\n');
const token = await controller.requestToken();
stdout.write(`DeviceToken: ${token}\n`);
controller.token = token;
controller.buildRequester();
}
const { Devices } = await controller.getDevices();
const defaultDevice = Devices[0];
let requestedDevice;
if (entries.has('id')) {
requestedDevice = Devices.find((device) => device.id === entries.get('id'));
} else if (entries.has('name')) {
requestedDevice = Devices.find((device) => device.name === entries.get('name'));
}
const target = requestedDevice || defaultDevice;
if (!target) {
throw new Error('Device not found!');
}
if (entries.has('mode')) {
if (!target.Mode) {
throw new Error('Mode not supported!');
}
const mode = entries.get('mode');
if (target.Mode.modes[0].toLowerCase() !== `opmode_${mode}`) {
/** @type {any} */
const newMode = `Opmode_${mode[0].toUpperCase()}${mode.substr(1)}`;
stdout.write(`Changing mode: ${target.Mode.modes[0]} => ${newMode}\n`);
await controller.setMode(target, newMode);
}
}
if (entries.has('temp')) {
if (!target.Temperatures?.[0]) {
throw new Error('Mode not supported!');
}
const temp = parseInt(entries.get('temp'), 10);
if (Number.isNaN(temp)) {
throw new Error('Invalid temperature!');
}
if ('minimum' in target.Temperatures[0]) {
if (temp < target.Temperatures[0].minimum) {
throw new Error('Temperature out of range!');
}
}
if ('maximum' in target.Temperatures[0]) {
if (temp > target.Temperatures[0].maximum) {
throw new Error('Temperature out of range!');
}
}
if (target.Temperatures[0].desired !== temp) {
stdout.write(`Changing mode: ${target.Temperatures[0].desired} => ${temp}\n`);
await controller.setTemperature(target, temp);
}
}
if (entries.has('speed')) {
if (!target.Wind || 'speedLevel' in target.Wind === false) {
throw new Error('Speed not supported!');
}
const speed = parseInt(entries.get('speed'), 10);
if (Number.isNaN(speed)) {
throw new Error('Invalid speed!');
}
if (target.Wind.speedLevel !== speed) {
stdout.write(`Changing mode: ${target.Wind.speedLevel} => ${speed}\n`);
await controller.setFanSpeed(target, speed);
}
}
if (entries.has('direction')) {
if (!target.Wind || 'direction' in target.Wind === false) {
throw new Error('Direction not supported!');
}
/** @type {any} */
const direction = entries.get('direction');
if (direction != null && target.Wind.direction !== direction) {
stdout.write(`Changing mode: ${target.Wind.direction} => ${direction}\n`);
await controller.setFanDirection(target, direction);
}
}
if (entries.has('info')) {
const info = await controller.getDevices();
const output = requestedDevice ? info.Devices.find((d) => d.id === target.id) : info;
stdout.write(JSON.stringify(output, null, 2));
stdout.write('\n');
}
if (entries.has('proxy')) {
await controller.proxy();
const minPort = controller.httpPort;
const maxPort = (controller.httpPort + controller.httpServers.length - 1);
stdout.write(`Proxy is running on ${minPort}${minPort !== maxPort ? `-${maxPort}` : ''}...\n`);
}
}
run().catch((err) => { throw err; });