node-red-contrib-eskomsepush 0.0.6 → 0.0.8
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 +37 -13
- package/package.json +1 -1
- package/src/nodes/eskomsepush.js +68 -41
package/README.md
CHANGED
|
@@ -4,35 +4,59 @@
|
|
|
4
4
|
|
|
5
5
|
A node for retrieving info from the [EskomSePush API](https://eskomsepush.gumroad.com/l/api).
|
|
6
6
|
|
|
7
|
-
The EskomSePush-API node
|
|
7
|
+
The EskomSePush-API node makes it easier for South African users to incorporate the load shedding schedules into their flows.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
### Summary
|
|
10
|
+
|
|
11
|
+
The node must be configured by entering the license key and the correct area id (which needs to fetched from the API).
|
|
10
12
|
|
|
11
13
|
Once deployed, the node will fetch the data from EskomSePush every hour. As every fetch from the API takes 2 calls, the 50 free queries per day on a free account should suffice. Every ten minutes the API status is checked to see how many queries you have left.
|
|
12
14
|
|
|
13
|
-
Internally the node checks every minute if a schedule is currently active or not. It will also output a message on the first deployment.
|
|
15
|
+
Internally, the node checks every minute if a schedule is currently active or not. It will also output a message on the first deployment.
|
|
14
16
|
|
|
15
17
|

|
|
16
18
|
|
|
17
|
-
The node has been made to work well together with the [@victronenergy/node-red-contrib-victron](https://flows.nodered.org/node/@victronenergy/node-red-contrib-victron) nodes.
|
|
18
|
-
|
|
19
|
-
example flow for this via importing the [victron-minsoc-stage-based.json](examples/victron-minsoc-stage-based.json)
|
|
19
|
+
The node has been made to work well together with the [@victronenergy/node-red-contrib-victron](https://flows.nodered.org/node/@victronenergy/node-red-contrib-victron) nodes. The ESS control node for setting the Minimum State of Charge is an obvious combination to enable a user to change the MinSoC value based on the currently active loadshedding stage.
|
|
20
|
+
|
|
21
|
+
You can find the example flow for this via importing the [victron-minsoc-stage-based.json](examples/victron-minsoc-stage-based.json) file.
|
|
20
22
|
|
|
21
23
|
### Configuration
|
|
22
24
|
|
|
23
|
-
First you will need a _licence key_. You can get one from [here](https://eskomsepush.gumroad.com/l/api), by
|
|
25
|
+
First you will need a _licence key_. You can get one from [here](https://eskomsepush.gumroad.com/l/api), by subscribing to the Free model. Note that this is for personal use only.
|
|
24
26
|
|
|
25
27
|

|
|
26
28
|
|
|
27
|
-
Next you need to insert the correct area.
|
|
29
|
+
Next you need to insert the correct area id. This can be done by either:
|
|
30
|
+
|
|
31
|
+
* making a manual API call to search for the area and paste the id from the API response into the area field in the node; or
|
|
32
|
+
* by first entering a valid API license key into the node, and then entering at least 5 characters, the node will use the API to search for the area. Note that this will consume some of the daily API quota.
|
|
33
|
+
|
|
34
|
+
If you don't want to use API quota by searching, and you already know the id of the area, fill out the area first and then the license key.
|
|
28
35
|
|
|
29
|
-
|
|
36
|
+
To fetch the area id manually, make an `areas_search` API call using your API license key `token`, a word of search `text`. In the response returned by the API, copy the `id` value of the matching area.
|
|
30
37
|
|
|
31
|
-
|
|
38
|
+
In the example below (on MacOS), `curl` is used to query the API and the search text value is 'ballito' (the license key token is invalid and must be replaced with a valid key). The area id value that will be used from this example is `eskmo-15-ballitokwadukuzakwazulunatal`:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
% curl --location --request GET 'https://developer.sepush.co.za/business/2.0/areas_search?text=ballito' --header 'token: 2DFB82AC-46254F6E-A68B26A4-8DF1303E'
|
|
42
|
+
{
|
|
43
|
+
"areas":[
|
|
44
|
+
{"id":"eskmo-15-ballitokwadukuzakwazulunatal","name":"Ballito (15)","region":"Eskom Municipal, Kwadukuza, Kwazulu-Natal"},
|
|
45
|
+
{"id":"eskdo-15-ballitokwadukuzakwazulunatal","name":"Ballito (15)","region":"Eskom Direct, KwaDukuza, KwaZulu-Natal"}
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Then you need to fill out which status to follow. This can be either _National_ (Eskom) or _Cape Town_.
|
|
51
|
+
|
|
52
|
+
If the _test_ checkbox has been selected, test data for the specified area will be fetched instead of the actual schedule. This is useful when debugging.
|
|
32
53
|
|
|
33
54
|
### Outputs
|
|
34
55
|
|
|
35
|
-
The
|
|
56
|
+
The note has two outputs. In most cases, the first (upper) output will be used.
|
|
57
|
+
|
|
58
|
+
The first output of the node outputs a boolean value and some related data. When load shedding is active, the `msg.payload` will be _true_, otherwise it will be _false_. It also outputs some extra values:
|
|
59
|
+
|
|
36
60
|
```
|
|
37
61
|
{
|
|
38
62
|
"payload":false,
|
|
@@ -72,7 +96,7 @@ The first output of the node outputs a boolean value. When load shedding is acti
|
|
|
72
96
|
}
|
|
73
97
|
```
|
|
74
98
|
|
|
75
|
-
The start and end objects contain the time as unix
|
|
99
|
+
The start and end objects contain the time of next load shedding period as unix timestamps in the Javascript format (milliseconds after the epoch).
|
|
76
100
|
|
|
77
101
|
The second output shows `msg.stage` and `msg.schedule`, containing the latest information as retrieved from the API. This output is mainly useful when writing your own functions and logic.
|
|
78
102
|
|
|
@@ -82,12 +106,12 @@ The status will show the situation regarding the API calls and when the next
|
|
|
82
106
|
shedding wil start or end. It also shows the count of API calls that have been
|
|
83
107
|
done and how many are left. This updates every 10 minutes.
|
|
84
108
|
|
|
85
|
-
|
|
86
109
|
### Documentation
|
|
87
110
|
|
|
88
111
|
Documentation for the API can be found [here](https://documenter.getpostman.com/view/1296288/UzQuNk3E)
|
|
89
112
|
|
|
90
113
|
When quota has been exceeded:
|
|
114
|
+
|
|
91
115
|
```
|
|
92
116
|
{"error":"Quota Exceeded - Reminder: you can use the 'test' query param for development. Check the docs! \ud83d\ude05"}
|
|
93
117
|
```
|
package/package.json
CHANGED
package/src/nodes/eskomsepush.js
CHANGED
|
@@ -5,11 +5,40 @@ module.exports = function (RED) {
|
|
|
5
5
|
let EskomSePushAPI = null
|
|
6
6
|
let Stage = null
|
|
7
7
|
let Schedule = null
|
|
8
|
+
let LoadShedding = null
|
|
8
9
|
let lastStatusUpdate = new Date()
|
|
9
10
|
let lastStageUpdate = new Date()
|
|
10
11
|
let lastScheduleUpdate = new Date()
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
function updateStatus(node) {
|
|
15
|
+
let fill = 'green'
|
|
16
|
+
let shape = 'ring'
|
|
17
|
+
let statusText = ''
|
|
18
|
+
if (Stage && Stage.status && Stage.status[node.config.statusselect].stage) {
|
|
19
|
+
statusText += 'Stage '+Stage.status[node.config.statusselect].stage
|
|
20
|
+
}
|
|
21
|
+
if (LoadShedding && LoadShedding.active && LoadShedding.next && LoadShedding.next.end) {
|
|
22
|
+
statusText += ' - ' + new Date(LoadShedding.next.end).toLocaleTimeString()
|
|
23
|
+
fill = 'red'
|
|
24
|
+
if (LoadShedding.type === 'event') {
|
|
25
|
+
shape = 'dot'
|
|
26
|
+
}
|
|
27
|
+
} else {
|
|
28
|
+
if (LoadShedding && LoadShedding.next && LoadShedding.next.start) {
|
|
29
|
+
statusText += ' - ' + new Date(LoadShedding.next.start).toLocaleTimeString()
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (EskomSePushAPI) {
|
|
33
|
+
statusText += ` (API: ${EskomSePushAPI.allowance.count}/${EskomSePushAPI.allowance.limit})`
|
|
34
|
+
if (EskomSePushAPI.allowance.count >= EskomSePushAPI.allowance.limit) {
|
|
35
|
+
fill = 'red'
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
node.status({
|
|
39
|
+
fill, shape, text: statusText
|
|
40
|
+
})
|
|
41
|
+
}
|
|
13
42
|
|
|
14
43
|
function checkAllowance (node) {
|
|
15
44
|
const options = {}
|
|
@@ -17,6 +46,7 @@ module.exports = function (RED) {
|
|
|
17
46
|
axios.get('https://developer.sepush.co.za/business/2.0/api_allowance',
|
|
18
47
|
{ params: options, headers }).then(function (response) {
|
|
19
48
|
EskomSePushAPI = response.data
|
|
49
|
+
updateStatus(node)
|
|
20
50
|
})
|
|
21
51
|
.catch(error => {
|
|
22
52
|
node.warn({ error: error.message })
|
|
@@ -29,6 +59,7 @@ module.exports = function (RED) {
|
|
|
29
59
|
axios.get('https://developer.sepush.co.za/business/2.0/status',
|
|
30
60
|
{ params: options, headers }).then(function (response) {
|
|
31
61
|
Stage = response.data
|
|
62
|
+
updateSheddingStatus(node)
|
|
32
63
|
})
|
|
33
64
|
.catch(error => {
|
|
34
65
|
node.warn({ error: error.message })
|
|
@@ -46,15 +77,15 @@ module.exports = function (RED) {
|
|
|
46
77
|
{ params: options, headers }).then(function (response) {
|
|
47
78
|
Schedule = response.data
|
|
48
79
|
Schedule.info.area = node.config.area
|
|
80
|
+
updateSheddingStatus(node)
|
|
49
81
|
})
|
|
50
82
|
.catch(error => {
|
|
51
83
|
node.warn({ error: error.message })
|
|
52
84
|
})
|
|
53
85
|
}
|
|
54
86
|
|
|
55
|
-
function
|
|
87
|
+
function updateSheddingStatus (node) {
|
|
56
88
|
const now = new Date()
|
|
57
|
-
let statusText = ''
|
|
58
89
|
|
|
59
90
|
if (EskomSePushAPI === null || (now.getTime() - lastStatusUpdate.getTime()) > 600000) {
|
|
60
91
|
checkAllowance(node)
|
|
@@ -66,27 +97,24 @@ module.exports = function (RED) {
|
|
|
66
97
|
}
|
|
67
98
|
|
|
68
99
|
if (EskomSePushAPI && EskomSePushAPI.allowance.count >= EskomSePushAPI.allowance.limit) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
} else {
|
|
73
|
-
if (Stage === null || (now.getTime() - lastStageUpdate.getTime()) > 3600000) {
|
|
74
|
-
node.status({ fill: 'yellow', shape, text: 'Fetching stage' })
|
|
75
|
-
checkStage(node)
|
|
76
|
-
lastStageUpdate = now
|
|
77
|
-
}
|
|
100
|
+
updateStatus(node)
|
|
101
|
+
return
|
|
102
|
+
}
|
|
78
103
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
104
|
+
if (Stage === null || (now.getTime() - lastStageUpdate.getTime()) > 3600000) {
|
|
105
|
+
checkStage(node)
|
|
106
|
+
lastStageUpdate = now
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (Schedule === null || (now.getTime() - lastScheduleUpdate.getTime()) > 3600000) {
|
|
110
|
+
checkSchedule(node)
|
|
111
|
+
lastScheduleUpdate = now
|
|
84
112
|
}
|
|
85
113
|
|
|
86
114
|
if (Stage && Schedule && EskomSePushAPI) {
|
|
87
115
|
const stage = Stage.status[node.config.statusselect].stage
|
|
88
116
|
const nowtime = now.toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit' })
|
|
89
|
-
|
|
117
|
+
LoadShedding = {
|
|
90
118
|
schedule: {
|
|
91
119
|
next: {},
|
|
92
120
|
active: false
|
|
@@ -97,11 +125,12 @@ module.exports = function (RED) {
|
|
|
97
125
|
},
|
|
98
126
|
checked: nowtime
|
|
99
127
|
}
|
|
100
|
-
fill = 'green'
|
|
101
128
|
for (const schedule of Schedule.schedule.days[0].stages[stage - 1]) {
|
|
102
129
|
if (nowtime >= schedule.split('-')[0] && nowtime <= schedule.split('-')[1]) {
|
|
103
130
|
LoadShedding.schedule = {
|
|
104
|
-
active: true
|
|
131
|
+
active: true
|
|
132
|
+
}
|
|
133
|
+
LoadShedding.schedule.next = {
|
|
105
134
|
start: Date.parse(Schedule.schedule.days[0].date + ' ' + schedule.split('-')[0]),
|
|
106
135
|
end: Date.parse(Schedule.schedule.days[0].date + ' ' + schedule.split('-')[1])
|
|
107
136
|
}
|
|
@@ -113,7 +142,7 @@ module.exports = function (RED) {
|
|
|
113
142
|
}
|
|
114
143
|
}
|
|
115
144
|
}
|
|
116
|
-
if (Object.keys(LoadShedding.schedule.next).length === 0) {
|
|
145
|
+
if (!LoadShedding.schedule.active && Object.keys(LoadShedding.schedule.next).length === 0) {
|
|
117
146
|
const s = Schedule.schedule.days[1].stages[stage - 1][0]
|
|
118
147
|
LoadShedding.schedule.next = {
|
|
119
148
|
start: Date.parse(Schedule.schedule.days[1].date + ' ' + s.split('-')[0]),
|
|
@@ -124,13 +153,13 @@ module.exports = function (RED) {
|
|
|
124
153
|
start: Date.parse(Schedule.events[0].start),
|
|
125
154
|
end: Date.parse(Schedule.events[0].end)
|
|
126
155
|
}
|
|
127
|
-
if (
|
|
156
|
+
if (now >= LoadShedding.event.next.start && now < LoadShedding.event.next.end) {
|
|
128
157
|
LoadShedding.event.active = true
|
|
129
|
-
LoadShedding.start = LoadShedding.event.start
|
|
130
|
-
LoadShedding.end = LoadShedding.event.end
|
|
131
158
|
}
|
|
132
159
|
|
|
133
|
-
if (!LoadShedding.schedule
|
|
160
|
+
if (!LoadShedding.schedule | !LoadShedding.schedule.next ||
|
|
161
|
+
!LoadShedding.event || !LoadShedding.event.next ||
|
|
162
|
+
!LoadShedding.schedule.next.start || !LoadShedding.event.next.start) {
|
|
134
163
|
node.warn('Unable to find next scheduled event and/or schedule')
|
|
135
164
|
node.warn(LoadShedding)
|
|
136
165
|
return
|
|
@@ -143,14 +172,18 @@ module.exports = function (RED) {
|
|
|
143
172
|
LoadShedding.next.type = 'event'
|
|
144
173
|
}
|
|
145
174
|
|
|
146
|
-
statusText += 'Stage '+stage
|
|
147
175
|
LoadShedding.active = (LoadShedding.schedule.active || LoadShedding.event.active)
|
|
148
|
-
if (LoadShedding.active) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
176
|
+
if (LoadShedding.schedule.active) {
|
|
177
|
+
LoadShedding.type = 'schedule'
|
|
178
|
+
LoadShedding.start = LoadShedding.schedule.next.start
|
|
179
|
+
LoadShedding.end = LoadShedding.schedule.next.end
|
|
180
|
+
}
|
|
181
|
+
if (LoadShedding.event.active) {
|
|
182
|
+
LoadShedding.type = 'event'
|
|
183
|
+
LoadShedding.start = LoadShedding.event.next.start
|
|
184
|
+
LoadShedding.end = LoadShedding.event.next.end
|
|
153
185
|
}
|
|
186
|
+
|
|
154
187
|
node.send([{
|
|
155
188
|
payload: LoadShedding.active,
|
|
156
189
|
LoadShedding,
|
|
@@ -168,13 +201,7 @@ module.exports = function (RED) {
|
|
|
168
201
|
}])
|
|
169
202
|
}
|
|
170
203
|
|
|
171
|
-
|
|
172
|
-
statusText += ` (API: ${EskomSePushAPI.allowance.count}/${EskomSePushAPI.allowance.limit})`
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
node.status({
|
|
176
|
-
fill, shape, text: (statusText || 'Ok')
|
|
177
|
-
})
|
|
204
|
+
updateStatus(node)
|
|
178
205
|
}
|
|
179
206
|
|
|
180
207
|
function EskomSePush (config) {
|
|
@@ -183,9 +210,9 @@ module.exports = function (RED) {
|
|
|
183
210
|
const node = this
|
|
184
211
|
node.config = config
|
|
185
212
|
|
|
186
|
-
|
|
213
|
+
updateSheddingStatus(node)
|
|
187
214
|
const intervalId = setInterval(function () {
|
|
188
|
-
|
|
215
|
+
updateSheddingStatus(node)
|
|
189
216
|
}, 60000)
|
|
190
217
|
|
|
191
218
|
node.on('close', function () {
|