node-red-contrib-eskomsepush 0.0.10 → 0.0.11
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/README.md +1 -1
- package/package.json +1 -1
- package/src/nodes/eskomsepush.js +174 -166
package/README.md
CHANGED
|
@@ -108,7 +108,7 @@ The status will show the situation regarding the API calls and when the next
|
|
|
108
108
|
shedding wil start or end. It also shows the count of API calls that have been
|
|
109
109
|
done and how many are left. This updates every 10 minutes.
|
|
110
110
|
|
|
111
|
-
The
|
|
111
|
+
The yellow color status can be either filled (dot) or not (ring). In case of a dot,
|
|
112
112
|
the load schedding is because of an _event_. When it is a ring, it is caused by
|
|
113
113
|
a matching _schedule_.
|
|
114
114
|
|
package/package.json
CHANGED
package/src/nodes/eskomsepush.js
CHANGED
|
@@ -2,45 +2,29 @@ module.exports = function (RED) {
|
|
|
2
2
|
'use strict'
|
|
3
3
|
|
|
4
4
|
const axios = require('axios')
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
+
}
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
statusText += ' - ' + new Date(LoadShedding.next.end).toLocaleTimeString()
|
|
26
|
-
fill = 'yellow'
|
|
27
|
-
if (LoadShedding.type === 'event') {
|
|
28
|
-
shape = 'dot'
|
|
29
|
-
}
|
|
30
|
-
} else {
|
|
31
|
-
if (LoadShedding && LoadShedding.next && LoadShedding.next.start) {
|
|
32
|
-
statusText += ' - ' + new Date(LoadShedding.next.start).toLocaleTimeString()
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
if (EskomSePushAPI) {
|
|
36
|
-
statusText += ` (API: ${EskomSePushAPI.allowance.count}/${EskomSePushAPI.allowance.limit})`
|
|
37
|
-
if (EskomSePushAPI.allowance.count >= EskomSePushAPI.allowance.limit) {
|
|
38
|
-
fill = 'red'
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
node.status({
|
|
42
|
-
fill, shape, text: statusText
|
|
43
|
-
})
|
|
21
|
+
function getMinutesLeftInDay () {
|
|
22
|
+
const currentDate = new Date()
|
|
23
|
+
const tomorrow = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate() + 1)
|
|
24
|
+
const timeDiff = tomorrow.getTime() - currentDate.getTime()
|
|
25
|
+
const minutesLeft = Math.floor(timeDiff / (1000 * 60))
|
|
26
|
+
|
|
27
|
+
return minutesLeft
|
|
44
28
|
}
|
|
45
29
|
|
|
46
30
|
function checkAllowance (node) {
|
|
@@ -52,8 +36,12 @@ module.exports = function (RED) {
|
|
|
52
36
|
}
|
|
53
37
|
axios.get('https://developer.sepush.co.za/business/2.0/api_allowance',
|
|
54
38
|
{ params: options, headers }).then(function (response) {
|
|
55
|
-
|
|
56
|
-
|
|
39
|
+
EskomSePushInfo.api.info = response.data
|
|
40
|
+
if (EskomSePushInfo.api.lastUpdate === null) {
|
|
41
|
+
EskomSePushInfo.api.lastUpdate = new Date()
|
|
42
|
+
updateSheddingStatus(node)
|
|
43
|
+
}
|
|
44
|
+
EskomSePushInfo.api.lastUpdate = new Date()
|
|
57
45
|
})
|
|
58
46
|
.catch(error => {
|
|
59
47
|
node.warn({ error: error.message })
|
|
@@ -65,11 +53,19 @@ module.exports = function (RED) {
|
|
|
65
53
|
const headers = { token: node.config.licensekey }
|
|
66
54
|
|
|
67
55
|
if (node.config.verbose === true) {
|
|
68
|
-
|
|
56
|
+
let warnstring = 'Running function checkStage'
|
|
57
|
+
if (EskomSePushInfo.status.lastUpdate === null) {
|
|
58
|
+
warnstring += ' - initial run'
|
|
59
|
+
} else {
|
|
60
|
+
warnstring += ' after ' + ((new Date() - EskomSePushInfo.status.lastUpdate) / 60000).toFixed(0) + ' minutes'
|
|
61
|
+
}
|
|
62
|
+
node.warn(warnstring)
|
|
69
63
|
}
|
|
70
64
|
axios.get('https://developer.sepush.co.za/business/2.0/status',
|
|
71
65
|
{ params: options, headers }).then(function (response) {
|
|
72
|
-
|
|
66
|
+
EskomSePushInfo.status.info = response.data
|
|
67
|
+
EskomSePushInfo.status.lastUpdate = new Date()
|
|
68
|
+
// Call updateSheddingStatus again now we have new data
|
|
73
69
|
updateSheddingStatus(node)
|
|
74
70
|
})
|
|
75
71
|
.catch(error => {
|
|
@@ -77,21 +73,28 @@ module.exports = function (RED) {
|
|
|
77
73
|
})
|
|
78
74
|
}
|
|
79
75
|
|
|
80
|
-
function
|
|
76
|
+
function checkArea (node) {
|
|
81
77
|
const options = { id: node.config.area }
|
|
82
78
|
const headers = { token: node.config.licensekey }
|
|
83
79
|
const url = 'https://developer.sepush.co.za/business/2.0/area'
|
|
84
80
|
|
|
85
81
|
if (node.config.verbose === true) {
|
|
86
|
-
|
|
82
|
+
let warnstring = 'Running function checkArea'
|
|
83
|
+
if (EskomSePushInfo.area.lastUpdate === null) {
|
|
84
|
+
warnstring += ' - initial run'
|
|
85
|
+
} else {
|
|
86
|
+
warnstring += ' after ' + ((new Date() - EskomSePushInfo.area.lastUpdate) / 60000).toFixed(0) + ' minutes'
|
|
87
|
+
}
|
|
88
|
+
node.warn(warnstring)
|
|
87
89
|
}
|
|
88
90
|
if (node.config.test) {
|
|
89
91
|
options.test = 'current'
|
|
90
92
|
}
|
|
91
93
|
axios.get(url,
|
|
92
94
|
{ params: options, headers }).then(function (response) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
+
EskomSePushInfo.area.info = response.data
|
|
96
|
+
EskomSePushInfo.area.lastUpdate = new Date()
|
|
97
|
+
// Call updateSheddingStatus again now we have new data
|
|
95
98
|
updateSheddingStatus(node)
|
|
96
99
|
})
|
|
97
100
|
.catch(error => {
|
|
@@ -102,153 +105,158 @@ module.exports = function (RED) {
|
|
|
102
105
|
function updateSheddingStatus (node) {
|
|
103
106
|
const now = new Date()
|
|
104
107
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
if (EskomSePushAPI === null || (now.getTime() - lastStatusUpdate.getTime()) > 600000) {
|
|
108
|
+
// Check allowance every ten minutes
|
|
109
|
+
if (EskomSePushInfo.api.lastUpdate === null || (now.getTime() - EskomSePushInfo.api.lastUpdate.getTime()) > 600000) {
|
|
109
110
|
checkAllowance(node)
|
|
110
|
-
lastStatusUpdate = now
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
// If we don't have API info, we just return
|
|
114
|
+
if (Object.entries(EskomSePushInfo.api.info).length === 0) {
|
|
115
|
+
node.warn('No API info (yet), refusing to continue')
|
|
116
|
+
return
|
|
115
117
|
}
|
|
116
118
|
|
|
117
|
-
if
|
|
118
|
-
|
|
119
|
+
// The same is true if we have no API calls left
|
|
120
|
+
if (EskomSePushInfo.api.info.allowance.count >= EskomSePushInfo.api.info.allowance.limit) {
|
|
121
|
+
node.warn('No API calls left, not checking status/schedule')
|
|
119
122
|
return
|
|
120
123
|
}
|
|
121
124
|
|
|
122
|
-
|
|
125
|
+
// Fetching actual information takes 2 calls, so calculate how long the day lasts and see how we
|
|
126
|
+
// can divide the available calls over the day
|
|
127
|
+
if ((EskomSePushInfo.api.info.allowance.limit - EskomSePushInfo.api.info.allowance.count) > 0) {
|
|
128
|
+
EskomSePushInfo.calc.sleeptime = (getMinutesLeftInDay() / ((EskomSePushInfo.api.info.allowance.limit - EskomSePushInfo.api.info.allowance.count) / 2)).toFixed(0)
|
|
129
|
+
} else {
|
|
130
|
+
EskomSePushInfo.calc.sleeptime = 30
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (EskomSePushInfo.status.lastUpdate === null || (now.getTime() - EskomSePushInfo.status.lastUpdate) > (EskomSePushInfo.calc.sleeptime * 60000)) {
|
|
123
134
|
checkStage(node)
|
|
124
|
-
lastStageUpdate = now
|
|
125
135
|
}
|
|
126
136
|
|
|
127
|
-
if (
|
|
128
|
-
|
|
129
|
-
lastScheduleUpdate = now
|
|
137
|
+
if (EskomSePushInfo.area.lastUpdate === null || (now.getTime() - EskomSePushInfo.area.lastUpdate) > (EskomSePushInfo.calc.sleeptime * 60000)) {
|
|
138
|
+
checkArea(node)
|
|
130
139
|
}
|
|
131
140
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
},
|
|
140
|
-
event: {
|
|
141
|
-
next: {},
|
|
142
|
-
active: false
|
|
143
|
-
},
|
|
144
|
-
checked: nowtime
|
|
145
|
-
}
|
|
141
|
+
// Now we have all info to continue. Just making sure that all update values are non null.
|
|
142
|
+
if (EskomSePushInfo.api.lastUpdate === null ||
|
|
143
|
+
EskomSePushInfo.status.lastUpdate === null ||
|
|
144
|
+
EskomSePushInfo.area.lastUpdate === null) {
|
|
145
|
+
(node.config.verbose === true) && node.warn('Not enough info to continue.')
|
|
146
|
+
return
|
|
147
|
+
}
|
|
146
148
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
return
|
|
150
|
-
}
|
|
149
|
+
// Determine the current stage
|
|
150
|
+
EskomSePushInfo.calc.stage = EskomSePushInfo.status.info.status[node.config.statusselect].stage
|
|
151
151
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
152
|
+
if (node.config.verbose === true) {
|
|
153
|
+
node.warn('API call status: ' + EskomSePushInfo.api.info.allowance.count + '/' + EskomSePushInfo.api.info.allowance.limit)
|
|
154
|
+
node.warn(EskomSePushInfo)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Default to false, overrule of loadshedding is active
|
|
158
|
+
EskomSePushInfo.calc.active = false
|
|
159
|
+
|
|
160
|
+
// Are there any events going on?
|
|
161
|
+
if (Object.entries(EskomSePushInfo.area.info.events).length > 0) {
|
|
162
|
+
EskomSePushInfo.calc.type = 'event'
|
|
163
|
+
const EventStart = Date.parse(EskomSePushInfo.area.info.events[0].start)
|
|
164
|
+
const EventEnd = Date.parse(EskomSePushInfo.area.info.events[0].end)
|
|
165
|
+
if (now >= EventStart && now < EventEnd) {
|
|
166
|
+
EskomSePushInfo.calc.active = true
|
|
167
|
+
if (EskomSePushInfo.area.info.events[0].note.match(/Stage (\d+)/i)) {
|
|
168
|
+
EskomSePushInfo.calc.stage = EskomSePushInfo.area.info.events[0].note.match(/Stage (\d+)/i)[1]
|
|
168
169
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
170
|
+
} else {
|
|
171
|
+
EskomSePushInfo.calc.next = {
|
|
172
|
+
type: 'event',
|
|
173
|
+
start: EventStart,
|
|
174
|
+
end: EventEnd,
|
|
175
|
+
stage: EskomSePushInfo.area.info.events[0].note.match(/Stage (\d+)/i)[1]
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Scheduled downtime has the thing that the time is in locatime
|
|
181
|
+
// So not just like events, where they are in UTC with an offset
|
|
182
|
+
let BreakLoop = false
|
|
183
|
+
for (const dates of EskomSePushInfo.area.info.schedule.days) {
|
|
184
|
+
for (const schedule of dates.stages[EskomSePushInfo.calc.stage]) {
|
|
185
|
+
const ScheduleStart = Date.parse(dates.date + ' ' + schedule.split('-')[0])
|
|
186
|
+
const ScheduleEnd = Date.parse(dates.date + ' ' + schedule.split('-')[1])
|
|
187
|
+
if (now < ScheduleEnd) {
|
|
188
|
+
BreakLoop = true
|
|
189
|
+
// This schedule is either active or will be next
|
|
190
|
+
if (now >= ScheduleStart) {
|
|
191
|
+
EskomSePushInfo.calc.active = true
|
|
192
|
+
EskomSePushInfo.calc.type = 'schedule'
|
|
193
|
+
EskomSePushInfo.calc.start = ScheduleStart
|
|
194
|
+
EskomSePushInfo.calc.end = ScheduleEnd
|
|
175
195
|
} else {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
196
|
+
EskomSePushInfo.calc.next = {
|
|
197
|
+
type: 'schedule',
|
|
198
|
+
start: ScheduleStart,
|
|
199
|
+
end: ScheduleEnd
|
|
179
200
|
}
|
|
180
201
|
}
|
|
181
202
|
}
|
|
203
|
+
if (BreakLoop) { break }
|
|
182
204
|
}
|
|
183
|
-
if (
|
|
184
|
-
|
|
185
|
-
LoadShedding.schedule.next = {
|
|
186
|
-
start: Date.parse(Schedule.schedule.days[1].date + ' ' + s.split('-')[0]),
|
|
187
|
-
end: Date.parse(Schedule.schedule.days[1].date + ' ' + s.split('-')[1])
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
LoadShedding.event.next = {
|
|
191
|
-
start: Date.parse(Schedule.events[0].start),
|
|
192
|
-
end: Date.parse(Schedule.events[0].end)
|
|
193
|
-
}
|
|
194
|
-
if (now >= LoadShedding.event.next.start && now < LoadShedding.event.next.end) {
|
|
195
|
-
LoadShedding.event.active = true
|
|
196
|
-
if (Schedule.events[0].note.match(/Stage (\d+)/i)) {
|
|
197
|
-
stage = Schedule.events[0].note.match(/Stage (\d+)/i)[1]
|
|
198
|
-
Stage.active = stage
|
|
199
|
-
}
|
|
200
|
-
} else {
|
|
201
|
-
Stage.active = stage
|
|
202
|
-
}
|
|
205
|
+
if (BreakLoop) { break }
|
|
206
|
+
}
|
|
203
207
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
return
|
|
210
|
-
}
|
|
211
|
-
if (LoadShedding.schedule.next.start <= LoadShedding.event.next.start) {
|
|
212
|
-
LoadShedding.next = LoadShedding.schedule.next
|
|
213
|
-
LoadShedding.next.type = 'schedule'
|
|
214
|
-
} else {
|
|
215
|
-
LoadShedding.next = LoadShedding.event.next
|
|
216
|
-
LoadShedding.next.type = 'event'
|
|
217
|
-
}
|
|
208
|
+
if (EskomSePushInfo.calc.next) {
|
|
209
|
+
EskomSePushInfo.calc.next.duration = (EskomSePushInfo.calc.next.end - EskomSePushInfo.calc.next.start) / 1000
|
|
210
|
+
EskomSePushInfo.calc.next.islong = EskomSePushInfo.calc.next.duration >= (4 * 3600)
|
|
211
|
+
EskomSePushInfo.calc.secondstostatechange = parseInt((EskomSePushInfo.calc.next.start - now) / 1000)
|
|
212
|
+
}
|
|
218
213
|
|
|
219
|
-
|
|
220
|
-
|
|
214
|
+
if (EskomSePushInfo.calc.active) {
|
|
215
|
+
EskomSePushInfo.calc.duration = (EskomSePushInfo.calc.end - EskomSePushInfo.calc.start) / 1000
|
|
216
|
+
EskomSePushInfo.calc.islong = EskomSePushInfo.calc.duration >= (4 * 3600)
|
|
217
|
+
EskomSePushInfo.calc.secondstostatechange = parseInt((EskomSePushInfo.calc.end - now) / 1000)
|
|
218
|
+
}
|
|
221
219
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
LoadShedding.start = LoadShedding.schedule.next.start
|
|
226
|
-
LoadShedding.end = LoadShedding.schedule.next.end
|
|
227
|
-
}
|
|
228
|
-
if (LoadShedding.event.active) {
|
|
229
|
-
LoadShedding.type = 'event'
|
|
230
|
-
LoadShedding.start = LoadShedding.event.next.start
|
|
231
|
-
LoadShedding.end = LoadShedding.event.next.end
|
|
232
|
-
}
|
|
220
|
+
if (node.config.verbose === true) {
|
|
221
|
+
node.warn(EskomSePushInfo.calc)
|
|
222
|
+
}
|
|
233
223
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
224
|
+
// Send output
|
|
225
|
+
node.send([{
|
|
226
|
+
payload: EskomSePushInfo.calc.active,
|
|
227
|
+
stage: EskomSePushInfo.calc.stage,
|
|
228
|
+
statusselect: node.config.statusselect,
|
|
229
|
+
api: {
|
|
230
|
+
count: EskomSePushInfo.api.info.allowance.count,
|
|
231
|
+
limit: EskomSePushInfo.api.info.allowance.limit
|
|
232
|
+
},
|
|
233
|
+
calc: EskomSePushInfo.calc
|
|
234
|
+
}, {
|
|
235
|
+
stage: EskomSePushInfo.status,
|
|
236
|
+
schedule: EskomSePushInfo.area
|
|
237
|
+
}])
|
|
238
|
+
|
|
239
|
+
// And update the status
|
|
240
|
+
let fill = 'green'
|
|
241
|
+
let shape = 'ring'
|
|
242
|
+
let statusText = 'Stage ' + EskomSePushInfo.calc.stage
|
|
243
|
+
|
|
244
|
+
if (EskomSePushInfo.calc.active) {
|
|
245
|
+
fill = 'yellow'
|
|
246
|
+
if (EskomSePushInfo.calc.type === 'event') {
|
|
247
|
+
shape = 'dot'
|
|
248
|
+
}
|
|
249
|
+
statusText += ' - ' + new Date(EskomSePushInfo.calc.start).toLocaleTimeString()
|
|
250
|
+
statusText += ' - ' + new Date(EskomSePushInfo.calc.end).toLocaleTimeString()
|
|
251
|
+
} else {
|
|
252
|
+
statusText += ' - ' + new Date(EskomSePushInfo.calc.next.start).toLocaleTimeString()
|
|
253
|
+
statusText += ' - ' + new Date(EskomSePushInfo.calc.next.end).toLocaleTimeString()
|
|
249
254
|
}
|
|
250
255
|
|
|
251
|
-
|
|
256
|
+
statusText += ' (API: ' + EskomSePushInfo.api.info.allowance.count + '/' + EskomSePushInfo.api.info.allowance.limit + ')'
|
|
257
|
+
node.status({
|
|
258
|
+
fill, shape, text: statusText
|
|
259
|
+
})
|
|
252
260
|
}
|
|
253
261
|
|
|
254
262
|
function EskomSePush (config) {
|