-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathload-alert.js
More file actions
260 lines (230 loc) · 8.02 KB
/
load-alert.js
File metadata and controls
260 lines (230 loc) · 8.02 KB
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
// This script alerts the user if the power drops below a
// given threshold while the device is still switched on.
// Compatible with both PM and EM devices.
//
// USER SETTINGS:
// Enter the power consumption value of your device (units = Watts):
threshold_off = 0 // Device is considered OFF when power is less than or equal to this value
threshold_on = 5 // Device is considered ON again only when power rises above this value. (Prevents false alerts from minor power fluctuations.)
// For EM devices, choose how often to poll for power consumption value (in milliseconds):
poll_interval = 1000
// Choose notification endpoint:
let CONFIG = {
notifyEndpoint: "http://push-notification-endpoint-url",
};
// Enter the error message of your choice:
user_msg = "Load may have failed."
//
//
//
// FALLBACK:
// On error “Could not detect device type automatically.”, specify device type below:
// "pm" or "em" (leave blank to auto-detect)
let user_device_type = "";
//
// -------------------DO NOT MODIFY PAST THIS POINT-------------------
let notifications = [];
let last_state = "on"; // default to on to catch first time off
if (poll_interval < 1000) poll_interval = 1000; // avoid overload polling
// user msg encoding
function _simple_encode(str) {
let res = "";
for (let i = 0; i < str.length; i++) {
if (str.at(i) === 0x20) {
res += "%20";
} else {
res += chr(str.at(i));
}
}
return res;
}
// debug msgs toggle
let DEBUG = false;
function debug(msg) { if (DEBUG) print(msg); }
// -------------------AUTO DETECT DEVICE TYPE-------------------
// polling device type first
let device_type = "unknown";
Shelly.call("Shelly.GetDeviceInfo", {}, function (result, error_code) {
if (error_code !== 0 || result === null) {
print("Could not retrieve device info.");
return;
}
// check for user override on device type auto-detect
if (user_device_type === "pm" || user_device_type === "em") {
device_type = user_device_type.toLowerCase();
print("Using user-defined device type: " + device_type);
} else {
// getting model from the GetDeviceInfo call
let model = result.model || "";
let modelUpper = model.toUpperCase();
debug("Detected model: " + model);
// search for EM or PM in model string from call
if (
modelUpper.indexOf("EM") >= 0 &&
(modelUpper.indexOf("PM") >= 0 || modelUpper.indexOf("PLUG") >= 0 || modelUpper.indexOf("SNPL") >= 0)
) {
device_type = "unknown"; // detected both strings, will ask user for override
} else if (modelUpper.indexOf("EM") >= 0) {
device_type = "em";
} else if (modelUpper.indexOf("PM") >= 0 || modelUpper.indexOf("PLUG") >= 0 || modelUpper.indexOf("SNPL") >= 0) {
device_type = "pm";
} else {
device_type = "unknown"; // unable to determine model type, will ask user for override
}
debug("Auto-detected device type: " + device_type);
}
// if auto-detection failed
if (device_type === "unknown") {
print("Could not detect device type automatically. Please set your device type manually in the FALLBACK section under USER SETTINGS.");
return; // stops script unless device type can be determined
}
if (device_type === "pm") {
handlePM();
} else if (device_type === "em") {
handleEM();
}
});
// -------------------PM DEVICE HANDLER-------------------
function handlePM() {
let has_switch = true;
// check for switch
Shelly.call("switch.getstatus", { id: 0 }, function (result, error_code, error_message) {
if (error_code !== 0 || result === null || typeof result.output === "undefined") {
has_switch = false;
debug("no switch");
} else {
has_switch = true;
debug("has switch");
}
// main function — runs on events ie switch flip etc
Shelly.addEventHandler(function (event, user_data) {
if (typeof event.info.apower === "undefined") {
// if the event has no apower info, exit function
debug("Event occurred, but without power info");
return;
}
let power = event.info.apower;
let new_state = last_state;
// determine state
if (power <= threshold_off) {
new_state = "off";
} else if (power > threshold_on) {
new_state = "on";
} else {
// detected power but below the threshold
debug("Detected power below user threshold.");
return;
}
// on state change
if (new_state !== last_state) {
debug("State changed from " + last_state + " to " + new_state);
last_state = new_state;
if (new_state === "off") {
// only sends notification if it turned off
if (!has_switch) {
// no switch: push message & end function here
debug("Event occurred (switchless device), pushing message");
notifications.push(user_msg);
} else {
// if switch exists...
Shelly.call("switch.getstatus", { id: 0 }, function (result, error_code) {
if (result.output) {
// if the switch is on, push notification
debug("Device is ON but power dropped. Pushing message.");
notifications.push(user_msg);
} else {
debug("Device is OFF. No need to notify.");
}
});
}
} else {
debug("Device is back ON. No alert needed.");
}
} else {
debug("No state change.");
}
}, null);
// send notifications if any queued
// if there is a notif to be sent then it sends
Timer.set(1000, true, function () {
if (notifications.length) {
let message = notifications[0];
notifications.splice(0, 1);
print("ALERT: ", message);
let nEndpoint = CONFIG.notifyEndpoint + _simple_encode(message);
Shelly.call(
"http.get",
{ url: nEndpoint },
function (result, error_code, error_message) {
print(JSON.stringify(result));
},
null
);
}
});
});
}
// -------------------EM DEVICE HANDLER-------------------
function handleEM() {
let new_state = last_state;
Timer.set(poll_interval, true, function () {
Shelly.call("Shelly.GetStatus", {}, function (result, error_code) {
if (error_code !== 0 || result === null) {
print("Error calling Shelly.GetStatus. Error code: ", error_code);
return;
}
// scan for any channels with power info
let totalPower = 0;
for (let key in result) {
let entry = result[key];
if (entry && typeof entry === "object" && entry.hasOwnProperty("act_power")) {
let p = entry.act_power;
if (typeof p === "number") {
debug("Found power in key " + key + ": " + p + " W");
totalPower += p;
}
}
}
debug("Total combined power (EM): " + totalPower + " W");
// determine state
if (totalPower <= threshold_off) {
new_state = "off";
} else if (totalPower > threshold_on) {
new_state = "on";
} else {
debug("Detected power below user threshold.");
return;
}
// on state change
if (new_state !== last_state) {
debug("State changed from " + last_state + " to " + new_state);
last_state = new_state;
if (new_state === "off") {
debug("Power dropped below threshold on EM device, pushing message");
notifications.push(user_msg);
} else {
debug("Power is back ON. No alert needed.");
}
} else {
// print("No state change.");
}
});
});
// notifs (same as pm)
Timer.set(1000, true, function () {
if (notifications.length) {
let message = notifications[0];
notifications.splice(0, 1);
print("ALERT: ", message);
let nEndpoint = CONFIG.notifyEndpoint + _simple_encode(message);
Shelly.call(
"http.get",
{ url: nEndpoint },
function (result, error_code, error_message) {
print(JSON.stringify(result));
},
null
);
}
});
}