node-red-contrib-eskomsepush 0.0.18 → 0.1.1

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.
@@ -1,371 +0,0 @@
1
- module.exports = function (RED) {
2
- 'use strict'
3
-
4
- const axios = require('axios')
5
- const EskomSePushInfo = {
6
- api: {
7
- lastUpdate: null,
8
- info: {}
9
- },
10
- status: {
11
- lastUpdate: null,
12
- info: {}
13
- },
14
- area: {
15
- lastUpdate: null,
16
- info: {}
17
- },
18
- calc: {}
19
- }
20
-
21
- function getMinutesToAPIReset () {
22
- const now = new Date()
23
- const targetTime = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 2, 0, 0)
24
- if (now > targetTime) {
25
- targetTime.setDate(targetTime.getDate() + 1)
26
- }
27
- const timeDiff = targetTime - now
28
- const minutesLeft = Math.floor(timeDiff / (1000 * 60))
29
-
30
- return minutesLeft
31
- }
32
-
33
- function checkAllowance (node) {
34
- const options = {}
35
- const headers = { token: node.config.licensekey }
36
-
37
- if (node.config.verbose === true) {
38
- node.warn('Running function checkAllowance')
39
- }
40
- axios.get('https://developer.sepush.co.za/business/2.0/api_allowance',
41
- { params: options, headers }).then(function (response) {
42
- EskomSePushInfo.api.info = response.data
43
- if (EskomSePushInfo.api.lastUpdate === null) {
44
- EskomSePushInfo.api.lastUpdate = new Date()
45
- updateSheddingStatus(node)
46
- }
47
- EskomSePushInfo.api.lastUpdate = new Date()
48
- })
49
- .catch(error => {
50
- node.warn({ error: error.message })
51
- })
52
- }
53
-
54
- function checkStage (node) {
55
- const options = {}
56
- const headers = { token: node.config.licensekey }
57
-
58
- if (node.config.verbose === true) {
59
- let warnstring = 'Running function checkStage'
60
- if (EskomSePushInfo.status.lastUpdate === null) {
61
- warnstring += ' - initial run'
62
- } else {
63
- warnstring += ' after ' + ((new Date() - EskomSePushInfo.status.lastUpdate) / 60000).toFixed(0) + ' minutes'
64
- }
65
- node.warn(warnstring)
66
- }
67
- axios.get('https://developer.sepush.co.za/business/2.0/status',
68
- { params: options, headers }).then(function (response) {
69
- EskomSePushInfo.status.info = response.data
70
- EskomSePushInfo.status.lastUpdate = new Date()
71
- // Call updateSheddingStatus again now we have new data
72
- updateSheddingStatus(node)
73
- })
74
- .catch(error => {
75
- node.warn({ error: error.message })
76
- })
77
- }
78
-
79
- function checkArea (node) {
80
- const options = { id: node.config.area }
81
- const headers = { token: node.config.licensekey }
82
- const url = 'https://developer.sepush.co.za/business/2.0/area'
83
-
84
- if (node.config.verbose === true) {
85
- let warnstring = 'Running function checkArea'
86
- if (EskomSePushInfo.area.lastUpdate === null) {
87
- warnstring += ' - initial run'
88
- } else {
89
- warnstring += ' after ' + ((new Date() - EskomSePushInfo.area.lastUpdate) / 60000).toFixed(0) + ' minutes'
90
- }
91
- node.warn(warnstring)
92
- }
93
- if (node.config.test) {
94
- options.test = 'current'
95
- }
96
- axios.get(url,
97
- { params: options, headers }).then(function (response) {
98
- EskomSePushInfo.area.info = response.data
99
- EskomSePushInfo.area.lastUpdate = new Date()
100
- // Call updateSheddingStatus again now we have new data
101
- updateSheddingStatus(node)
102
- })
103
- .catch(error => {
104
- node.warn({ error: error.message })
105
- })
106
- }
107
-
108
- function updateSheddingStatus (node, msg) {
109
- const now = new Date()
110
-
111
- // Check allowance every ten minutes
112
- if ((msg && msg.payload === 'allowance') || EskomSePushInfo.api.lastUpdate === null || (now.getTime() - EskomSePushInfo.api.lastUpdate.getTime()) > 600000) {
113
- checkAllowance(node)
114
- }
115
-
116
- // If we don't have API info, we just return
117
- if (Object.entries(EskomSePushInfo.api.info).length === 0) {
118
- node.warn('No API info (yet), refusing to continue')
119
- return
120
- }
121
-
122
- // The same is true if we have no API calls left
123
- if (EskomSePushInfo.api.info.allowance.count >= EskomSePushInfo.api.info.allowance.limit) {
124
- node.warn('No API calls left, not checking status/schedule')
125
- return
126
- }
127
-
128
- // Fetching actual information takes 2 calls, so calculate how long until the next API count
129
- // reset and divide the calls over the day. Wait at least 60 minutes between calls
130
- // reduce limit by api_allowance_buffer to cater for units consumed by other API calls
131
- if (node.config.verbose === true) {
132
- node.warn('Minutes to API Reset: ' + getMinutesToAPIReset())
133
- }
134
- const allowanceRemaining = EskomSePushInfo.api.info.allowance.limit - node.config.api_allowance_buffer - EskomSePushInfo.api.info.allowance.count
135
-
136
- if (allowanceRemaining > 0) {
137
- EskomSePushInfo.calc.sleeptime = Math.round(getMinutesToAPIReset() / Math.ceil(allowanceRemaining / 2))
138
- if (node.config.verbose === true) {
139
- node.warn('API allowance limit: ' + EskomSePushInfo.api.info.allowance.limit)
140
- node.warn('API allowance count: ' + EskomSePushInfo.api.info.allowance.count)
141
- node.warn('Calculated sleeptime: ' + EskomSePushInfo.calc.sleeptime)
142
- }
143
- if (EskomSePushInfo.calc.sleeptime < 10) {
144
- EskomSePushInfo.calc.sleeptime = 10
145
- if (node.config.verbose === true) {
146
- node.warn('Calculated sleeptime was less than 10. Set it to 10: ' + EskomSePushInfo.calc.sleeptime)
147
- }
148
- }
149
- } else {
150
- EskomSePushInfo.calc.sleeptime = 60
151
- if (node.config.verbose === true) {
152
- node.warn('Set sleeptime to 60 since allowance count is low: ' + EskomSePushInfo.calc.sleeptime)
153
- }
154
- }
155
-
156
- if ((msg && msg.payload === 'stage') || EskomSePushInfo.status.lastUpdate === null || (now.getTime() - EskomSePushInfo.status.lastUpdate) > (EskomSePushInfo.calc.sleeptime * 60000)) {
157
- checkStage(node)
158
- }
159
-
160
- if ((msg && msg.payload === 'area') || EskomSePushInfo.area.lastUpdate === null || (now.getTime() - EskomSePushInfo.area.lastUpdate) > (EskomSePushInfo.calc.sleeptime * 60000)) {
161
- checkArea(node)
162
- }
163
-
164
- // Now we have all info to continue. Just making sure that all update values are non null.
165
- if (EskomSePushInfo.api.lastUpdate === null ||
166
- EskomSePushInfo.status.lastUpdate === null ||
167
- EskomSePushInfo.area.lastUpdate === null) {
168
- (node.config.verbose === true) && node.warn('Not enough info to continue.')
169
- return
170
- }
171
-
172
- // Determine the current stage
173
- EskomSePushInfo.calc.stage = EskomSePushInfo.status.info.status[node.config.statusselect].stage
174
-
175
- if (node.config.verbose === true) {
176
- node.warn('API call status: ' + EskomSePushInfo.api.info.allowance.count + '/' + EskomSePushInfo.api.info.allowance.limit)
177
- node.warn(EskomSePushInfo)
178
- }
179
-
180
- // Default to false, overrule of loadshedding is active
181
- EskomSePushInfo.calc.active = false
182
-
183
- // Are there any events going on?
184
- if (Object.entries(EskomSePushInfo.area.info.events).length > 0) {
185
- const EventStart = Date.parse(EskomSePushInfo.area.info.events[0].start)
186
- const EventEnd = Date.parse(EskomSePushInfo.area.info.events[0].end)
187
- if (now >= EventStart && now < EventEnd) {
188
- EskomSePushInfo.calc.type = 'event'
189
- EskomSePushInfo.calc.active = true
190
- if (EskomSePushInfo.area.info.events[0].note.match(/Stage (\d+)/i)) {
191
- EskomSePushInfo.calc.stage = EskomSePushInfo.area.info.events[0].note.match(/Stage (\d+)/i)[1]
192
- }
193
- EskomSePushInfo.calc.start = EventStart
194
- EskomSePushInfo.calc.end = EventEnd
195
- } else {
196
- EskomSePushInfo.calc.next = {
197
- type: 'event',
198
- start: EventStart,
199
- end: EventEnd,
200
- stage: EskomSePushInfo.area.info.events[0].note.match(/Stage (\d+)/i)[1]
201
- }
202
- }
203
- }
204
-
205
- // Scheduled downtime has the thing that the time is in locatime
206
- // So not just like events, where they are in UTC with an offset
207
- let BreakLoop = false
208
- for (const dates of EskomSePushInfo.area.info.schedule.days) {
209
- const stageIndex = EskomSePushInfo.calc.stage - 1
210
- if (stageIndex >= 0 && stageIndex < dates.stages.length) {
211
- if (Array.isArray(dates.stages[stageIndex])) {
212
- for (const schedule of dates.stages[stageIndex]) {
213
- const ScheduleStart = Date.parse(dates.date + ' ' + schedule.split('-')[0])
214
- let ScheduleEnd = Date.parse(dates.date + ' ' + schedule.split('-')[1])
215
- if (ScheduleEnd < ScheduleStart) {
216
- ScheduleEnd += (24 * 60 * 60 * 1000)
217
- }
218
- if (now < ScheduleEnd) {
219
- BreakLoop = true
220
- // This schedule is either active or will be next
221
- if (now >= ScheduleStart) {
222
- EskomSePushInfo.calc.active = true
223
- EskomSePushInfo.calc.type = 'schedule'
224
- EskomSePushInfo.calc.start = ScheduleStart
225
- EskomSePushInfo.calc.end = ScheduleEnd
226
- } else {
227
- EskomSePushInfo.calc.next = {
228
- type: 'schedule',
229
- start: ScheduleStart,
230
- end: ScheduleEnd,
231
- stage: EskomSePushInfo.calc.stage
232
- }
233
- }
234
- }
235
- if (BreakLoop) { break }
236
- }
237
- } else {
238
- console.warn('Not an array:', dates.stages[stageIndex]) // Warning if not an array
239
- }
240
- } else {
241
- console.warn(`Invalid stage index: ${stageIndex}. No loadshedding going on?`) // Warning if stage index is out of bounds
242
- BreakLoop = true
243
- }
244
- if (BreakLoop) { break }
245
- }
246
-
247
- if (EskomSePushInfo.calc.next) {
248
- EskomSePushInfo.calc.next.duration = (EskomSePushInfo.calc.next.end - EskomSePushInfo.calc.next.start) / 1000
249
- EskomSePushInfo.calc.next.islong = EskomSePushInfo.calc.next.duration >= (4 * 3600)
250
- EskomSePushInfo.calc.secondstostatechange = parseInt((EskomSePushInfo.calc.next.start - now) / 1000)
251
- EskomSePushInfo.calc.next.isHigherStage = EskomSePushInfo.calc.next.stage > EskomSePushInfo.calc.stage
252
- }
253
-
254
- if (EskomSePushInfo.calc.active) {
255
- EskomSePushInfo.calc.duration = (EskomSePushInfo.calc.end - EskomSePushInfo.calc.start) / 1000
256
- EskomSePushInfo.calc.islong = EskomSePushInfo.calc.duration >= (4 * 3600)
257
- EskomSePushInfo.calc.secondstostatechange = parseInt((EskomSePushInfo.calc.end - now) / 1000)
258
- }
259
-
260
- if (node.config.verbose === true) {
261
- node.warn(EskomSePushInfo.calc)
262
- }
263
-
264
- // Send output
265
- node.send([{
266
- payload: EskomSePushInfo.calc.active,
267
- stage: EskomSePushInfo.calc.stage,
268
- statusselect: node.config.statusselect,
269
- api: {
270
- count: EskomSePushInfo.api.info.allowance.count,
271
- limit: EskomSePushInfo.api.info.allowance.limit
272
- },
273
- calc: EskomSePushInfo.calc
274
- }, {
275
- stage: EskomSePushInfo.status,
276
- schedule: EskomSePushInfo.area
277
- }])
278
-
279
- // And update the status
280
- let fill = 'green'
281
- let shape = 'ring'
282
- let statusText = 'Stage ' + EskomSePushInfo.calc.stage + ': '
283
-
284
- if (EskomSePushInfo.calc.active) {
285
- fill = 'yellow'
286
- if (EskomSePushInfo.calc.type === 'event') {
287
- shape = 'dot'
288
- }
289
- if (EskomSePushInfo.calc.start) {
290
- statusText += new Date(EskomSePushInfo.calc.start).toLocaleTimeString([], { timeStyle: 'short' })
291
- statusText += ' - ' + new Date(EskomSePushInfo.calc.end).toLocaleTimeString([], { timeStyle: 'short' })
292
- }
293
- } else {
294
- if (EskomSePushInfo.calc.next) {
295
- if (new Date(EskomSePushInfo.calc.next.start).getUTCDay() !== now.getUTCDate()) {
296
- statusText += new Date(EskomSePushInfo.calc.next.start).toLocaleString([], { weekday: 'short' }) + ' '
297
- }
298
- statusText += new Date(EskomSePushInfo.calc.next.start).toLocaleTimeString([], { timeStyle: 'short' })
299
- statusText += ' - ' + new Date(EskomSePushInfo.calc.next.end).toLocaleTimeString([], { timeStyle: 'short' })
300
- }
301
- }
302
-
303
- statusText += ' (API: ' + EskomSePushInfo.api.info.allowance.count + '/' + EskomSePushInfo.api.info.allowance.limit + ')'
304
- node.status({
305
- fill, shape, text: statusText
306
- })
307
- }
308
-
309
- function EskomSePush (config) {
310
- RED.nodes.createNode(this, config)
311
-
312
- const node = this
313
- node.config = config
314
-
315
- updateSheddingStatus(node)
316
- const intervalId = setInterval(function () {
317
- updateSheddingStatus(node)
318
- }, 60000)
319
-
320
- node.on('input', function (msg) {
321
- updateSheddingStatus(node, msg)
322
- })
323
-
324
- node.on('close', function () {
325
- clearInterval(intervalId)
326
- })
327
- }
328
-
329
- RED.nodes.registerType('eskomsepush', EskomSePush)
330
-
331
- RED.httpNode.get('/eskomsepush/search', (req, res) => {
332
- if (!req.query || !req.query.token || !req.query.search) {
333
- res.setHeader('Content-Type', 'application/json')
334
- return res.send('invalid')
335
- }
336
- const headers = {
337
- token: req.query.token
338
- }
339
- const options = {
340
- text: req.query.search
341
- }
342
-
343
- res.setHeader('Content-Type', 'application/json')
344
- axios.get('https://developer.sepush.co.za/business/2.0/areas_search',
345
- { params: options, headers }).then(function (response) {
346
- return res.send(response.data)
347
- })
348
- .catch(error => {
349
- return res.send({ error: error.message })
350
- })
351
- })
352
- RED.httpNode.get('/eskomsepush/api', (req, res) => {
353
- if (!req.query.token) {
354
- res.setHeader('Content-Type', 'application/json')
355
- return res.send('invalid')
356
- }
357
- const headers = {
358
- token: req.query.token
359
- }
360
- const options = {}
361
-
362
- res.setHeader('Content-Type', 'application/json')
363
- axios.get('https://developer.sepush.co.za/business/2.0/api_allowance',
364
- { params: options, headers }).then(function (response) {
365
- return res.send(response.data)
366
- })
367
- .catch(error => {
368
- return res.send({ error: error.message })
369
- })
370
- })
371
- }