node-red-contrib-eskomsepush 0.0.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.
- package/LICENSE +21 -0
- package/README.md +85 -0
- package/examples/simple-api-query.flow +56 -0
- package/img/eskomsepush-flow.png +0 -0
- package/node-red-contrib-eskomsepush-0.0.1.tgz +0 -0
- package/package.json +39 -0
- package/src/nodes/eskomsepush.html +185 -0
- package/src/nodes/eskomsepush.js +218 -0
- package/src/nodes/icons/eskomsepush.png +0 -0
- package/src/nodes/icons/eskomsepush.svg +4 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Victron Energy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
### EskomSePush API
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
A node for retrieving info from the EskomSePush API.
|
|
6
|
+
|
|
7
|
+
The EskomSePush-API node gives the tools to make working with the load shedding in South Africa as easy as possible.
|
|
8
|
+
|
|
9
|
+
First you need to configure the node by entering the license key and entering the correct area.
|
|
10
|
+
|
|
11
|
+
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
|
+
|
|
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.
|
|
14
|
+
|
|
15
|
+
### Configuration
|
|
16
|
+
|
|
17
|
+
First you will need a _licence key_. You can get one from [here](https://eskomsepush.gumroad.com/l/api), by subsribing to the Free model. Note that this is for personal use only.
|
|
18
|
+
|
|
19
|
+
Next you need to insert the correct area. Once you entered 5 characters (and a valid license is used), the API will be used for searching the correct area. Do note that this will cost some of the daily queries. If you don't want that and you already know the id of the area, fill out the area first and then the license key.
|
|
20
|
+
|
|
21
|
+
Then you need to fill out which status to follow. This can be either _National_ (eskom) or _Capetown_.
|
|
22
|
+
|
|
23
|
+
If the _test_ checkbox has been selected, test data for the area will be fetched instead of the actual schedule. This is useful when debugging.
|
|
24
|
+
|
|
25
|
+
### Outputs
|
|
26
|
+
|
|
27
|
+
The first output of the node outputs a boolean value. When load shedding is active, the `msg.payload` will be _true_, otherwise it will be _false_. It also outputs some extra values:
|
|
28
|
+
```
|
|
29
|
+
{
|
|
30
|
+
"payload":false,
|
|
31
|
+
"LoadShedding":{
|
|
32
|
+
"schedule":{
|
|
33
|
+
"next":{
|
|
34
|
+
"start":1683561600000,
|
|
35
|
+
"end":1683570600000,
|
|
36
|
+
"type":"schedule"
|
|
37
|
+
},
|
|
38
|
+
"active":false
|
|
39
|
+
},
|
|
40
|
+
"event":{
|
|
41
|
+
"next":{
|
|
42
|
+
"start":1683561600000,
|
|
43
|
+
"end":1683570600000
|
|
44
|
+
},
|
|
45
|
+
"active":false
|
|
46
|
+
},
|
|
47
|
+
"checked":"13:35",
|
|
48
|
+
"next":{
|
|
49
|
+
"start":1683561600000,
|
|
50
|
+
"end":1683570600000,
|
|
51
|
+
"type":"schedule"
|
|
52
|
+
},
|
|
53
|
+
"active":false
|
|
54
|
+
},
|
|
55
|
+
"stage":"5",
|
|
56
|
+
"statusselect":"capetown",
|
|
57
|
+
"api":{
|
|
58
|
+
"count":12,
|
|
59
|
+
"limit":50,
|
|
60
|
+
"lastStatusUpdate":"Mon May 08 2023 13:34:30 GMT+0200 (Central European Summer Time)",
|
|
61
|
+
"lastScheduleUpdate":"Mon May 08 2023 13:34:30 GMT+0200 (Central European Summer Time)"
|
|
62
|
+
},
|
|
63
|
+
"\_msgid":"6e77b593b7f9eda4"
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The start and end objects contain the time as unix timestamp in the Javascript format (milliseconds after the epoch).
|
|
68
|
+
|
|
69
|
+
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.
|
|
70
|
+
|
|
71
|
+
### Status
|
|
72
|
+
|
|
73
|
+
The status will show the situation regarding the API calls and when the next
|
|
74
|
+
shedding wil start or end. It also shows the count of API calls that have been
|
|
75
|
+
done and how many are left. This updates every 10 minutes.
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
### Documentation
|
|
79
|
+
|
|
80
|
+
Documentation for the API can be found [here](https://documenter.getpostman.com/view/1296288/UzQuNk3E)
|
|
81
|
+
|
|
82
|
+
When quota has been exceeded:
|
|
83
|
+
```
|
|
84
|
+
{"error":"Quota Exceeded - Reminder: you can use the 'test' query param for development. Check the docs! \ud83d\ude05"}
|
|
85
|
+
```
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "d083105e39f8ba68",
|
|
4
|
+
"type": "debug",
|
|
5
|
+
"z": "614c1c00b0cf1810",
|
|
6
|
+
"name": "Load shedding",
|
|
7
|
+
"active": true,
|
|
8
|
+
"tosidebar": true,
|
|
9
|
+
"console": false,
|
|
10
|
+
"tostatus": false,
|
|
11
|
+
"complete": "true",
|
|
12
|
+
"targetType": "full",
|
|
13
|
+
"statusVal": "",
|
|
14
|
+
"statusType": "auto",
|
|
15
|
+
"x": 340,
|
|
16
|
+
"y": 60,
|
|
17
|
+
"wires": []
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": "1d3e18f144e4911a",
|
|
21
|
+
"type": "debug",
|
|
22
|
+
"z": "614c1c00b0cf1810",
|
|
23
|
+
"name": "Schedule and events",
|
|
24
|
+
"active": true,
|
|
25
|
+
"tosidebar": true,
|
|
26
|
+
"console": false,
|
|
27
|
+
"tostatus": false,
|
|
28
|
+
"complete": "true",
|
|
29
|
+
"targetType": "full",
|
|
30
|
+
"statusVal": "",
|
|
31
|
+
"statusType": "auto",
|
|
32
|
+
"x": 360,
|
|
33
|
+
"y": 100,
|
|
34
|
+
"wires": []
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"id": "854cb9223eb1d95f",
|
|
38
|
+
"type": "eskomsepush",
|
|
39
|
+
"z": "614c1c00b0cf1810",
|
|
40
|
+
"name": "",
|
|
41
|
+
"licensekey": "",
|
|
42
|
+
"area": "capetown-3-helderbergvillage",
|
|
43
|
+
"statusselect": "capetown",
|
|
44
|
+
"test": false,
|
|
45
|
+
"x": 130,
|
|
46
|
+
"y": 80,
|
|
47
|
+
"wires": [
|
|
48
|
+
[
|
|
49
|
+
"d083105e39f8ba68"
|
|
50
|
+
],
|
|
51
|
+
[
|
|
52
|
+
"1d3e18f144e4911a"
|
|
53
|
+
]
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
]
|
|
Binary file
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "node-red-contrib-eskomsepush",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Node-RED interface for the Eskomsepush API",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "standard --fix"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"node-red",
|
|
11
|
+
"eskomsepush",
|
|
12
|
+
"loadshedding",
|
|
13
|
+
"south africa",
|
|
14
|
+
"scheduled charging",
|
|
15
|
+
"victron",
|
|
16
|
+
"ess"
|
|
17
|
+
],
|
|
18
|
+
"author": "Dirk-Jan Faber <dfaber@victronenergy.com>",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"node-red": {
|
|
21
|
+
"nodes": {
|
|
22
|
+
"eskomepush": "./src/nodes/eskomsepush.js"
|
|
23
|
+
},
|
|
24
|
+
"version": ">=3.0.2"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/dirkjanfaber/node-red-contrib-eskomepush"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"axios": "^1.3.5"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"standard": "^17.0.0"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=14.17.4"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType('eskomsepush',{
|
|
3
|
+
category: 'network',
|
|
4
|
+
color: '#ffffff',
|
|
5
|
+
defaults: {
|
|
6
|
+
name: {value:""},
|
|
7
|
+
licensekey: {value:"", validate: RED.validators.regex(/^[0-9A-F\-]{35}$/)},
|
|
8
|
+
area: {value:""},
|
|
9
|
+
statusselect: {value:"", validate: RED.validators.regex(/^(eskom|capetown)$/)},
|
|
10
|
+
test: { value: false }
|
|
11
|
+
},
|
|
12
|
+
inputs:0,
|
|
13
|
+
outputs:2,
|
|
14
|
+
icon: "eskomsepush.svg",
|
|
15
|
+
label: function() {
|
|
16
|
+
return this.name||"EskomSePush API";
|
|
17
|
+
},
|
|
18
|
+
oneditprepare: function oneditprepare() {
|
|
19
|
+
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
function searchArea() {
|
|
24
|
+
var input = document.getElementById("node-input-area");
|
|
25
|
+
var dropdown = document.getElementById("areaDropdown");
|
|
26
|
+
var licensekey = document.getElementById("node-input-licensekey");
|
|
27
|
+
|
|
28
|
+
if (!RED.validators.regex(licensekey.value, /^[0-9A-F\-]{35}$/)) {
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (input.value.length >= 5) {
|
|
33
|
+
dropdown.innerHTML = "";
|
|
34
|
+
|
|
35
|
+
$.ajax({
|
|
36
|
+
url: '/eskomsepush/search',
|
|
37
|
+
method: 'GET',
|
|
38
|
+
data: {
|
|
39
|
+
token: licensekey.value,
|
|
40
|
+
search: input.value
|
|
41
|
+
},
|
|
42
|
+
success: function(response) {
|
|
43
|
+
for (var i = 0; i < response.areas.length; i++) {
|
|
44
|
+
var option = document.createElement("option");
|
|
45
|
+
option.text = response.areas[i].name;
|
|
46
|
+
option.value = response.areas[i].id;
|
|
47
|
+
dropdown.add(option);
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
error: function(error) {
|
|
51
|
+
node.warn(error)
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
dropdown.style.display = "block";
|
|
55
|
+
dropdown.addEventListener("change", function() {
|
|
56
|
+
input.value = dropdown.value;
|
|
57
|
+
dropdown.style.display = "none";
|
|
58
|
+
});
|
|
59
|
+
input.addEventListener("focusout", function() {
|
|
60
|
+
dropdown.style.display = "none"
|
|
61
|
+
})
|
|
62
|
+
} else {
|
|
63
|
+
dropdown.style.display = "none";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<script type="text/html" data-template-name="eskomsepush">
|
|
69
|
+
<div class="form-row">
|
|
70
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
71
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
|
72
|
+
</div>
|
|
73
|
+
<div class="form-row">
|
|
74
|
+
<label for="node-input-licensekey"><i class="fa fa-user-secret"></i> License key</label>
|
|
75
|
+
<input type="password" id="node-input-licensekey" placeholder="License key" required>
|
|
76
|
+
</div>
|
|
77
|
+
<div class="form-row">
|
|
78
|
+
<label for="node-input-area"><i class="fa fa-tag"></i> Area</label>
|
|
79
|
+
<input type="text" id="node-input-area" placeholder="Area" onkeyup="searchArea()">
|
|
80
|
+
<select id="areaDropdown" style="display: none;"></select>
|
|
81
|
+
</div>
|
|
82
|
+
<div class="form-row">
|
|
83
|
+
<label style="min-width:190px" for="node-input-test"><i class="fa fa-volume-up"></i> Use test data</label>
|
|
84
|
+
<input type="checkbox" checked id="node-input-test" style="max-width:30px">
|
|
85
|
+
</div>
|
|
86
|
+
<div class="form-row">
|
|
87
|
+
<label for="node-input-statusselect"><i class="fa fa-location-arrow"></i> Status select</label>
|
|
88
|
+
<select id="node-input-statusselect" required>
|
|
89
|
+
<option value="eskom">National (eskom)</option>
|
|
90
|
+
<option value="capetown">Cape Town</option>
|
|
91
|
+
</select>
|
|
92
|
+
</div>
|
|
93
|
+
</script>
|
|
94
|
+
|
|
95
|
+
<script type="text/html" data-help-name="eskomsepush">
|
|
96
|
+
<h3 id=""eskomsepushapi>EskomSePush API</h3>
|
|
97
|
+
|
|
98
|
+
<p>A node for retrieving info from the EskomSePush API.</p>
|
|
99
|
+
|
|
100
|
+
<p>The EskomSePush-API node gives the tools to make working with the load shedding
|
|
101
|
+
in South Africa as easy as possible.</p>
|
|
102
|
+
<p>First you need to configure the node by entering the license key and entering
|
|
103
|
+
the correct area.</p>
|
|
104
|
+
<p>Once deployed, the node will fetch the data from EskomSePush every hour. As
|
|
105
|
+
every fetch from the API takes 2 calls, the 50 free queries per day on a
|
|
106
|
+
free account should suffice. Every ten minutes the API status is checked to
|
|
107
|
+
see how many queries you have left.</p>
|
|
108
|
+
<p>Internally the node checks every minute if a schedule is currently active or not. It
|
|
109
|
+
will also output a message on the first deployment.</p>
|
|
110
|
+
|
|
111
|
+
<h3 id="configuration">Configuration</h3>
|
|
112
|
+
|
|
113
|
+
<p>First you will need a <em>licence key</em>. You can get one from <a href="https://eskomsepush.gumroad.com/l/api">here</a>, by subsribing to the Free model. Note that this is for personal use only.</p>
|
|
114
|
+
<p>Next you need to insert the correct area. Once you entered 5 characters (and a valid license is used), the API will be used for searching the correct area. Do note that this will cost some of the daily queries. If you don't want that and you already know the id of the area, fill out the area first and then the license
|
|
115
|
+
key.</p>
|
|
116
|
+
<p>Then you need to fill out which status to follow. This can be either <em>National</em> (eskom) or <em>Capetown</em>.</p>
|
|
117
|
+
<p>If the <em>test</em> checkbox has been selected, test data for the area will be fetched instead of the actual schedule. This is useful when debugging.</p>
|
|
118
|
+
|
|
119
|
+
<h3 id="outputs">Outputs</h3>
|
|
120
|
+
|
|
121
|
+
<p>The first output of the node outputs a boolean value. When load shedding is active, the <code>msg.payload</code> will be <em>true</em>, otherwise it will be <em>false</em>. It also outputs some extra values:</p>
|
|
122
|
+
|
|
123
|
+
<pre>
|
|
124
|
+
{
|
|
125
|
+
"payload":false,
|
|
126
|
+
"LoadShedding":{
|
|
127
|
+
"schedule":{
|
|
128
|
+
"next":{
|
|
129
|
+
"start":1683561600000,
|
|
130
|
+
"end":1683570600000,
|
|
131
|
+
"type":"schedule"
|
|
132
|
+
},
|
|
133
|
+
"active":false
|
|
134
|
+
},
|
|
135
|
+
"event":{
|
|
136
|
+
"next":{
|
|
137
|
+
"start":1683561600000,
|
|
138
|
+
"end":1683570600000
|
|
139
|
+
},
|
|
140
|
+
"active":false
|
|
141
|
+
},
|
|
142
|
+
"checked":"13:35",
|
|
143
|
+
"next":{
|
|
144
|
+
"start":1683561600000,
|
|
145
|
+
"end":1683570600000,
|
|
146
|
+
"type":"schedule"
|
|
147
|
+
},
|
|
148
|
+
"active":false
|
|
149
|
+
},
|
|
150
|
+
"stage":"5",
|
|
151
|
+
"statusselect":"capetown",
|
|
152
|
+
"api":{
|
|
153
|
+
"count":12,
|
|
154
|
+
"limit":50,
|
|
155
|
+
"lastStatusUpdate":"Mon May 08 2023 13:34:30 GMT+0200 (Central European Summer Time)",
|
|
156
|
+
"lastScheduleUpdate":"Mon May 08 2023 13:34:30 GMT+0200 (Central European Summer Time)"
|
|
157
|
+
},
|
|
158
|
+
"_msgid":"6e77b593b7f9eda4"
|
|
159
|
+
}
|
|
160
|
+
</pre>
|
|
161
|
+
|
|
162
|
+
<p>The <tt>start</tt> and <tt>end</tt> objects contain the time as unix timestamp in the Javascript format (milliseconds after the epoch).</p>
|
|
163
|
+
|
|
164
|
+
<p>The second output shows <code>msg.stage</code> and <code>msg.schedule</code>, containing the latest information as retrieved from the API.
|
|
165
|
+
This output is mainly useful when writing your own functions and logic.</p>
|
|
166
|
+
|
|
167
|
+
<h3 id="status">Status</h3>
|
|
168
|
+
|
|
169
|
+
<p>The status will show the situation regarding the API calls. It will show the count of API calls that have been done and
|
|
170
|
+
how many are left. This updates every 10 minutes.</p>
|
|
171
|
+
|
|
172
|
+
<h1 id="documentation">Documentation</h1>
|
|
173
|
+
<p>Documentation for the API can be found <a href="https://documenter.getpostman.com/view/1296288/UzQuNk3E">here</a></p>
|
|
174
|
+
|
|
175
|
+
</script>
|
|
176
|
+
|
|
177
|
+
<style>
|
|
178
|
+
input#node-input-area {
|
|
179
|
+
margin-bottom: 5px;
|
|
180
|
+
}
|
|
181
|
+
select#areaDropdown {
|
|
182
|
+
position: absolute;
|
|
183
|
+
left: 120px;
|
|
184
|
+
}
|
|
185
|
+
</style>
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
module.exports = function (RED) {
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
const axios = require('axios')
|
|
5
|
+
let EskomSePushAPI = null
|
|
6
|
+
let Stage = null
|
|
7
|
+
let Schedule = null
|
|
8
|
+
let lastStatusUpdate = new Date()
|
|
9
|
+
let lastStageUpdate = new Date()
|
|
10
|
+
let lastScheduleUpdate = new Date()
|
|
11
|
+
let LoadShedding = false
|
|
12
|
+
let fill = 'green'
|
|
13
|
+
let shape = 'ring'
|
|
14
|
+
|
|
15
|
+
function checkAllowance (node) {
|
|
16
|
+
const options = {}
|
|
17
|
+
const headers = { token: node.config.licensekey }
|
|
18
|
+
axios.get('https://developer.sepush.co.za/business/2.0/api_allowance',
|
|
19
|
+
{ params: options, headers }).then(function (response) {
|
|
20
|
+
EskomSePushAPI = response.data
|
|
21
|
+
})
|
|
22
|
+
.catch(error => {
|
|
23
|
+
node.warn({ error: error.message })
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function checkStage (node) {
|
|
28
|
+
const options = {}
|
|
29
|
+
const headers = { token: node.config.licensekey }
|
|
30
|
+
axios.get('https://developer.sepush.co.za/business/2.0/status',
|
|
31
|
+
{ params: options, headers }).then(function (response) {
|
|
32
|
+
Stage = response.data
|
|
33
|
+
})
|
|
34
|
+
.catch(error => {
|
|
35
|
+
node.warn({ error: error.message })
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function checkSchedule (node) {
|
|
40
|
+
const options = { id: node.config.area }
|
|
41
|
+
const headers = { token: node.config.licensekey }
|
|
42
|
+
let url = 'https://developer.sepush.co.za/business/2.0/area'
|
|
43
|
+
if (node.config.test) {
|
|
44
|
+
options.test = 'current'
|
|
45
|
+
}
|
|
46
|
+
axios.get(url,
|
|
47
|
+
{ params: options, headers }).then(function (response) {
|
|
48
|
+
Schedule = response.data
|
|
49
|
+
Schedule.info.area = node.config.area
|
|
50
|
+
})
|
|
51
|
+
.catch(error => {
|
|
52
|
+
node.warn({ error: error.message })
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function updateStatus(node) {
|
|
57
|
+
const now = new Date();
|
|
58
|
+
let statusText = ''
|
|
59
|
+
|
|
60
|
+
if (EskomSePushAPI === null || (now.getTime() - lastStatusUpdate.getTime()) > 600000) {
|
|
61
|
+
checkAllowance(node)
|
|
62
|
+
lastStatusUpdate = now
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (Schedule && Schedule.info.area !== node.config.area) {
|
|
66
|
+
Schedule = null
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (EskomSePushAPI && EskomSePushAPI.allowance.count >= EskomSePushAPI.allowance.limit) {
|
|
70
|
+
statusText += 'API quota reached'
|
|
71
|
+
fill = 'red'
|
|
72
|
+
shape = 'dot'
|
|
73
|
+
|
|
74
|
+
} else {
|
|
75
|
+
if (Stage === null || (now.getTime() - lastStatusUpdate.getTime()) > 3600000) {
|
|
76
|
+
node.status({fill: 'yellow', shape, text: 'Fetching status'})
|
|
77
|
+
checkStage(node)
|
|
78
|
+
lastStageUpdate = now
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (Schedule === null || (now.getTime() - lastScheduleUpdate.getTime()) > 3600000) {
|
|
82
|
+
node.status({fill: 'yellow', shape, text: 'Fetching schedule'})
|
|
83
|
+
checkSchedule(node)
|
|
84
|
+
lastScheduleUpdate = now
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (Stage && Schedule && EskomSePushAPI) {
|
|
89
|
+
let stage = Stage.status[node.config.statusselect].stage
|
|
90
|
+
let nowtime = now.toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit'})
|
|
91
|
+
let LoadShedding = {
|
|
92
|
+
schedule: {
|
|
93
|
+
next: {},
|
|
94
|
+
active: false
|
|
95
|
+
},
|
|
96
|
+
event: {
|
|
97
|
+
next: {},
|
|
98
|
+
active: false
|
|
99
|
+
},
|
|
100
|
+
checked: nowtime
|
|
101
|
+
}
|
|
102
|
+
fill = 'green'
|
|
103
|
+
for (let schedule of Schedule.schedule.days[0].stages[stage-1]) {
|
|
104
|
+
if ( nowtime >= schedule.split('-')[0] && nowtime <= schedule.split('-')[1] ) {
|
|
105
|
+
LoadShedding.schedule = {
|
|
106
|
+
active: true,
|
|
107
|
+
start: Date.parse(Schedule.schedule.days[0].date + ' ' + schedule.split('-')[0]),
|
|
108
|
+
end: Date.parse(Schedule.schedule.days[0].date + ' ' + schedule.split('-')[1])
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if ( Object.keys(LoadShedding.schedule.next).length === 0 && nowtime < schedule.split('-')[0] ) {
|
|
112
|
+
LoadShedding.schedule.next = {
|
|
113
|
+
start: Date.parse(Schedule.schedule.days[0].date + ' ' + schedule.split('-')[0]),
|
|
114
|
+
end: Date.parse(Schedule.schedule.days[0].date + ' ' + schedule.split('-')[1])
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (Object.keys(LoadShedding.schedule.next).length === 0) {
|
|
119
|
+
let s = Schedule.schedule.days[1].stages[stage-1][0]
|
|
120
|
+
LoadShedding.schedule.next = {
|
|
121
|
+
start: Date.parse(Schedule.sschedule.days[1].date + s.split('-')[0]),
|
|
122
|
+
end: Date.parse(Schedule.sschedule.days[1].date + s.split('-')[1])
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
LoadShedding.event.next = {
|
|
126
|
+
start: Date.parse(Schedule.events[0].start),
|
|
127
|
+
end: Date.parse(Schedule.events[0].end)
|
|
128
|
+
}
|
|
129
|
+
if ( nowtime >= LoadShedding.event.start && nowtime < LoadShedding.event.end ) {
|
|
130
|
+
LoadShedding.event.active = true
|
|
131
|
+
LoadShedding.start = LoadShedding.event.start
|
|
132
|
+
LoadShedding.end = LoadShedding.event.end
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (!LoadShedding.schedule.next.start || !LoadShedding.event.next.start) {
|
|
136
|
+
node.warn("Unable to find next scheduled event and/or schedule")
|
|
137
|
+
return
|
|
138
|
+
}
|
|
139
|
+
if (LoadShedding.schedule.next.start <= LoadShedding.event.next.start) {
|
|
140
|
+
LoadShedding.next = LoadShedding.schedule.next
|
|
141
|
+
LoadShedding.next.type = 'schedule'
|
|
142
|
+
} else {
|
|
143
|
+
LoadShedding.next = LoadShedding.event.next
|
|
144
|
+
LoadShedding.next.type = 'event'
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
LoadShedding.active = (LoadShedding.schedule.active || LoadShedding.event.active)
|
|
148
|
+
if (LoadShedding.active) {
|
|
149
|
+
statusText += 'Until: '+ new Date(LoadShedding.end).toLocaleTimeString()
|
|
150
|
+
fill = 'red'
|
|
151
|
+
} else {
|
|
152
|
+
statusText += 'Next: ' + new Date(LoadShedding.next.start).toLocaleTimeString()
|
|
153
|
+
}
|
|
154
|
+
node.send([{
|
|
155
|
+
payload: LoadShedding.active,
|
|
156
|
+
LoadShedding,
|
|
157
|
+
stage,
|
|
158
|
+
statusselect: node.config.statusselect,
|
|
159
|
+
api: {
|
|
160
|
+
count: EskomSePushAPI.allowance.count,
|
|
161
|
+
limit: EskomSePushAPI.allowance.limit,
|
|
162
|
+
lastStatusUpdate: lastStatusUpdate.toString(),
|
|
163
|
+
lastScheduleUpdate: lastScheduleUpdate.toString()
|
|
164
|
+
}
|
|
165
|
+
}, {
|
|
166
|
+
stage: Stage,
|
|
167
|
+
schedule: Schedule }])
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (EskomSePushAPI) {
|
|
171
|
+
statusText += ` (API: ${EskomSePushAPI.allowance.count}/${EskomSePushAPI.allowance.limit})`
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
node.status({
|
|
175
|
+
fill, shape, text: ( statusText || 'Ok' )
|
|
176
|
+
})
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function EskomSePush (config) {
|
|
180
|
+
RED.nodes.createNode(this, config)
|
|
181
|
+
|
|
182
|
+
const node = this
|
|
183
|
+
node.config = config
|
|
184
|
+
|
|
185
|
+
updateStatus(node)
|
|
186
|
+
const intervalId = setInterval(function () {
|
|
187
|
+
updateStatus(node)
|
|
188
|
+
}, 60000)
|
|
189
|
+
|
|
190
|
+
node.on('close', function () {
|
|
191
|
+
clearInterval(intervalId)
|
|
192
|
+
})
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
RED.nodes.registerType('eskomsepush', EskomSePush)
|
|
196
|
+
|
|
197
|
+
RED.httpNode.get('/eskomsepush/search', (req, res) => {
|
|
198
|
+
if (!req.query || !req.query.token || !req.query.search) {
|
|
199
|
+
res.setHeader('Content-Type', 'application/json')
|
|
200
|
+
return res.send('invalid')
|
|
201
|
+
}
|
|
202
|
+
const headers = {
|
|
203
|
+
token: req.query.token
|
|
204
|
+
}
|
|
205
|
+
const options = {
|
|
206
|
+
text: req.query.search
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
res.setHeader('Content-Type', 'application/json')
|
|
210
|
+
axios.get('https://developer.sepush.co.za/business/2.0/areas_search',
|
|
211
|
+
{ params: options, headers }).then(function (response) {
|
|
212
|
+
return res.send(response.data)
|
|
213
|
+
})
|
|
214
|
+
.catch(error => {
|
|
215
|
+
return res.send({ error: error.message })
|
|
216
|
+
})
|
|
217
|
+
})
|
|
218
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<circle cx="512" cy="512" r="512" fill="white"/>
|
|
3
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M418.116 126C396.51 208.249 354.298 358.268 354.298 358.268L469.894 412.544L412.096 568.21L496.587 594.526L430.541 807.246L609.333 568.21L527.504 541.414L655.131 358.268L548.085 318.998L653.067 140.876C803.266 197.97 910.002 343.27 910.002 513.499C910.002 733.585 731.587 912 511.501 912C291.415 912 113 733.585 113 513.499C113 325.573 243.082 168.03 418.116 126Z" fill="#FF4738"/>
|
|
4
|
+
</svg>
|