node-red-contrib-eskomsepush 0.0.7 → 0.0.9
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/.github/ISSUE_TEMPLATE/bug_report.md +33 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +21 -0
- package/README.md +43 -13
- package/img/eskomsepush-configuration.png +0 -0
- package/package.json +1 -1
- package/src/nodes/eskomsepush.html +143 -88
- package/src/nodes/eskomsepush.js +106 -48
- package/src/nodes/icons/venus-webassembly.zip +0 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Create a report to help us improve
|
|
4
|
+
title: "[BUG]"
|
|
5
|
+
labels: ''
|
|
6
|
+
assignees: ''
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
**Describe the bug**
|
|
11
|
+
A clear and concise description of what the bug is.
|
|
12
|
+
|
|
13
|
+
**To Reproduce**
|
|
14
|
+
Steps to reproduce the behavior:
|
|
15
|
+
1. Using node '...'
|
|
16
|
+
2. Click on '....'
|
|
17
|
+
3. See error
|
|
18
|
+
|
|
19
|
+
**Expected behavior**
|
|
20
|
+
A clear and concise description of what you expected to happen.
|
|
21
|
+
|
|
22
|
+
**Screenshots**
|
|
23
|
+
If applicable, add screenshots to help explain your problem.
|
|
24
|
+
|
|
25
|
+
**Flow**
|
|
26
|
+
If applicable, add a flow to help explain your problem.
|
|
27
|
+
|
|
28
|
+
**Software (please complete the following information):**
|
|
29
|
+
- node-red-contrib-eskomsepush version:
|
|
30
|
+
|
|
31
|
+
**Additional context**
|
|
32
|
+
Add any other context about the problem here.
|
|
33
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature request
|
|
3
|
+
about: Suggest an idea for the EskomSePush node
|
|
4
|
+
title: "[Feature]"
|
|
5
|
+
labels: enhancement
|
|
6
|
+
assignees: ''
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
**Is your feature request related to a problem? Please describe.**
|
|
11
|
+
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
12
|
+
|
|
13
|
+
**Describe the solution you'd like**
|
|
14
|
+
A clear and concise description of what you want to happen.
|
|
15
|
+
|
|
16
|
+
**Describe alternatives you've considered**
|
|
17
|
+
A clear and concise description of any alternative solutions or features you've considered.
|
|
18
|
+
|
|
19
|
+
**Additional context**
|
|
20
|
+
Add any other context or screenshots about the feature request here.
|
|
21
|
+
|
package/README.md
CHANGED
|
@@ -4,35 +4,61 @@
|
|
|
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) file.
|
|
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
|
-
### Configuration
|
|
23
|
+
### Installation & Configuration
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
Install the node from within Node-RED from the menu: Manage pallete -> Install (tab) -> Search for "eskom" and click "install".
|
|
26
|
+
|
|
27
|
+
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
28
|
|
|
25
29
|

|
|
26
30
|
|
|
27
|
-
Next you need to insert the correct area.
|
|
31
|
+
Next you need to insert the correct area id. This can be done by either:
|
|
32
|
+
|
|
33
|
+
* 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
|
|
34
|
+
* 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.
|
|
28
35
|
|
|
29
|
-
|
|
36
|
+
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.
|
|
30
37
|
|
|
31
|
-
|
|
38
|
+
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.
|
|
39
|
+
|
|
40
|
+
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`:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
% curl --location --request GET 'https://developer.sepush.co.za/business/2.0/areas_search?text=ballito' --header 'token: 2DFB82AC-46254F6E-A68B26A4-8DF1303E'
|
|
44
|
+
{
|
|
45
|
+
"areas":[
|
|
46
|
+
{"id":"eskmo-15-ballitokwadukuzakwazulunatal","name":"Ballito (15)","region":"Eskom Municipal, Kwadukuza, Kwazulu-Natal"},
|
|
47
|
+
{"id":"eskdo-15-ballitokwadukuzakwazulunatal","name":"Ballito (15)","region":"Eskom Direct, KwaDukuza, KwaZulu-Natal"}
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Then you need to fill out which status to follow. This can be either _National_ (Eskom) or _Cape Town_.
|
|
53
|
+
|
|
54
|
+
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
55
|
|
|
33
56
|
### Outputs
|
|
34
57
|
|
|
35
|
-
The
|
|
58
|
+
The note has two outputs. In most cases, the first (upper) output will be used.
|
|
59
|
+
|
|
60
|
+
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:
|
|
61
|
+
|
|
36
62
|
```
|
|
37
63
|
{
|
|
38
64
|
"payload":false,
|
|
@@ -72,7 +98,7 @@ The first output of the node outputs a boolean value. When load shedding is acti
|
|
|
72
98
|
}
|
|
73
99
|
```
|
|
74
100
|
|
|
75
|
-
The start and end objects contain the time as unix
|
|
101
|
+
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
102
|
|
|
77
103
|
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
104
|
|
|
@@ -82,12 +108,16 @@ The status will show the situation regarding the API calls and when the next
|
|
|
82
108
|
shedding wil start or end. It also shows the count of API calls that have been
|
|
83
109
|
done and how many are left. This updates every 10 minutes.
|
|
84
110
|
|
|
111
|
+
The red color status can be either filled (dot) or not (ring). In case of a dot,
|
|
112
|
+
the load schedding is because of an _event_. When it is a ring, it is caused by
|
|
113
|
+
a matching _schedule_.
|
|
85
114
|
|
|
86
115
|
### Documentation
|
|
87
116
|
|
|
88
117
|
Documentation for the API can be found [here](https://documenter.getpostman.com/view/1296288/UzQuNk3E)
|
|
89
118
|
|
|
90
119
|
When quota has been exceeded:
|
|
120
|
+
|
|
91
121
|
```
|
|
92
122
|
{"error":"Quota Exceeded - Reminder: you can use the 'test' query param for development. Check the docs! \ud83d\ude05"}
|
|
93
123
|
```
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,4 +1,33 @@
|
|
|
1
1
|
<script type="text/javascript">
|
|
2
|
+
function checkForValidLicense() {
|
|
3
|
+
var licensekey = $('#node-input-licensekey').val();
|
|
4
|
+
|
|
5
|
+
var regexValidator = RED.validators.regex(/^[0-9A-F\-]{35}$/)
|
|
6
|
+
var isValid = regexValidator(licensekey)
|
|
7
|
+
|
|
8
|
+
if (isValid) {
|
|
9
|
+
document.getElementById("searchAreaText").disabled = false
|
|
10
|
+
$.ajax({
|
|
11
|
+
url: '/eskomsepush/api',
|
|
12
|
+
method: 'GET',
|
|
13
|
+
data: {
|
|
14
|
+
token: licensekey,
|
|
15
|
+
},
|
|
16
|
+
success: function(response) {
|
|
17
|
+
$('#api_info').text(response.allowance.count + '/' + response.allowance.limit)
|
|
18
|
+
if (response.allowance.count >= response.allowance.limit) {
|
|
19
|
+
document.getElementById("searchAreaText").disabled = true
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
error: function(error) {
|
|
23
|
+
node.warn(error)
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
} else {
|
|
27
|
+
document.getElementById("searchAreaText").disabled = true
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
2
31
|
RED.nodes.registerType('eskomsepush',{
|
|
3
32
|
category: 'network',
|
|
4
33
|
color: '#ffffff',
|
|
@@ -16,53 +45,48 @@
|
|
|
16
45
|
return this.name||"EskomSePush API";
|
|
17
46
|
},
|
|
18
47
|
oneditprepare: function oneditprepare() {
|
|
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
|
-
|
|
48
|
+
checkForValidLicense()
|
|
49
|
+
|
|
50
|
+
$("button#searchAreaId").on("click", function() {
|
|
51
|
+
var licensekey = $("#node-input-licensekey").val()
|
|
52
|
+
var regexValidator = RED.validators.regex(/^[0-9A-F\-]{35}$/)
|
|
53
|
+
var isValid = regexValidator(licensekey)
|
|
54
|
+
var search = $("input#searchAreaText").val()
|
|
55
|
+
var arealist = ''
|
|
56
|
+
|
|
57
|
+
if (isValid && search != '') {
|
|
58
|
+
$.ajax({
|
|
59
|
+
url: '/eskomsepush/search',
|
|
60
|
+
method: 'GET',
|
|
61
|
+
data: {
|
|
62
|
+
token: licensekey,
|
|
63
|
+
search
|
|
64
|
+
},
|
|
65
|
+
success: function(response) {
|
|
66
|
+
for (var i = 0; i < response.areas.length; i++) {
|
|
67
|
+
arealist += '<li id="' + response.areas[i].id + '">' + response.areas[i].name +
|
|
68
|
+
', '+ response.areas[i].region + '</li>'
|
|
69
|
+
}
|
|
70
|
+
$('#list-areas').html(arealist)
|
|
71
|
+
$('#list-areas li').on('click', function() {
|
|
72
|
+
$('#node-input-area').val($(this).attr("id"))
|
|
73
|
+
$('#node-input-name').val($(this).text().split(',')[0])
|
|
74
|
+
})
|
|
75
|
+
checkForValidLicense()
|
|
76
|
+
},
|
|
77
|
+
error: function(error) {
|
|
78
|
+
node.warn(error)
|
|
79
|
+
}
|
|
80
|
+
})
|
|
52
81
|
}
|
|
53
82
|
})
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
dropdown.style.display = "none";
|
|
83
|
+
|
|
84
|
+
$("#node-input-licensekey").on("input", function() {
|
|
85
|
+
checkForValidLicense()
|
|
58
86
|
});
|
|
59
|
-
input.addEventListener("focusout", function() {
|
|
60
|
-
dropdown.style.display = "none"
|
|
61
|
-
})
|
|
62
|
-
} else {
|
|
63
|
-
dropdown.style.display = "none";
|
|
64
87
|
}
|
|
65
|
-
}
|
|
88
|
+
});
|
|
89
|
+
|
|
66
90
|
</script>
|
|
67
91
|
|
|
68
92
|
<script type="text/html" data-template-name="eskomsepush">
|
|
@@ -75,13 +99,8 @@
|
|
|
75
99
|
<input type="password" id="node-input-licensekey" placeholder="License key" required>
|
|
76
100
|
</div>
|
|
77
101
|
<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
|
|
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">
|
|
102
|
+
<label for="node-input-area"><i class="fa fa-tag"></i> Area id</label>
|
|
103
|
+
<input type="text" id="node-input-area" placeholder="Area id">
|
|
85
104
|
</div>
|
|
86
105
|
<div class="form-row">
|
|
87
106
|
<label for="node-input-statusselect"><i class="fa fa-location-arrow"></i> Status select</label>
|
|
@@ -90,6 +109,27 @@
|
|
|
90
109
|
<option value="capetown">Cape Town</option>
|
|
91
110
|
</select>
|
|
92
111
|
</div>
|
|
112
|
+
<div class="form-row">
|
|
113
|
+
<label style="min-width:190px" for="node-input-test"><i class="fa fa-volume-up"></i> Use test data</label>
|
|
114
|
+
<input type="checkbox" checked id="node-input-test" style="max-width:30px">
|
|
115
|
+
</div>
|
|
116
|
+
<div class="form-tips">
|
|
117
|
+
<p>In order to fill out the properties, you will need to add the correct license key, which can be
|
|
118
|
+
obtained <a href="https://eskomsepush.gumroad.com/l/api">here</a>. Apart from that, the
|
|
119
|
+
<em>Aread id</em> needs to be filled out. The search form below can help you to obtain that.<br />
|
|
120
|
+
Once you have a valid licence key filled out in the form, a query willl be done, checking
|
|
121
|
+
how many credits you have left. If you have credits left, the form
|
|
122
|
+
can be used to search for the needed area id. <strong>Each search will cost you 5 credits.</strong>
|
|
123
|
+
</p>
|
|
124
|
+
|
|
125
|
+
<ul id="list-areas"></ul>
|
|
126
|
+
|
|
127
|
+
<p>
|
|
128
|
+
Queries left: <span id="api_info">unknown/unknown</span><br />
|
|
129
|
+
<input type="text" id="searchAreaText" placeholder="Area" disabled />
|
|
130
|
+
<button id="searchAreaId">Search</button><br />
|
|
131
|
+
</p>
|
|
132
|
+
</div>
|
|
93
133
|
</script>
|
|
94
134
|
|
|
95
135
|
<script type="text/html" data-help-name="eskomsepush">
|
|
@@ -111,8 +151,7 @@ see how many queries you have left.</p>
|
|
|
111
151
|
<h3 id="configuration">Configuration</h3>
|
|
112
152
|
|
|
113
153
|
<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
|
|
115
|
-
key.</p>
|
|
154
|
+
<p>Next you need to insert the correct area. Once a valid license is filled out, the API can be used for searching the correct area id. 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 id manually.</p>
|
|
116
155
|
<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
156
|
<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
157
|
|
|
@@ -122,47 +161,53 @@ key.</p>
|
|
|
122
161
|
|
|
123
162
|
<pre>
|
|
124
163
|
{
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
"
|
|
138
|
-
"
|
|
139
|
-
|
|
140
|
-
|
|
164
|
+
"payload": false,
|
|
165
|
+
"LoadShedding": {
|
|
166
|
+
"schedule": {
|
|
167
|
+
"next": {
|
|
168
|
+
"start": 1684267200000,
|
|
169
|
+
"end": 1684276200000,
|
|
170
|
+
"type": "schedule",
|
|
171
|
+
"duration": 9000,
|
|
172
|
+
"islong": false
|
|
173
|
+
},
|
|
174
|
+
"active": false
|
|
175
|
+
},
|
|
176
|
+
"event": {
|
|
177
|
+
"next": {
|
|
178
|
+
"start": 1684267200000,
|
|
179
|
+
"end": 1684276200000
|
|
180
|
+
},
|
|
181
|
+
"active": false
|
|
182
|
+
},
|
|
183
|
+
"checked": "15:38",
|
|
184
|
+
"next": {
|
|
185
|
+
"start": 1684267200000,
|
|
186
|
+
"end": 1684276200000,
|
|
187
|
+
"type": "schedule",
|
|
188
|
+
"duration": 9000,
|
|
189
|
+
"islong": false
|
|
190
|
+
},
|
|
191
|
+
"active": false
|
|
141
192
|
},
|
|
142
|
-
"
|
|
143
|
-
"
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
193
|
+
"stage": "3",
|
|
194
|
+
"statusselect": "capetown",
|
|
195
|
+
"api": {
|
|
196
|
+
"count": 45,
|
|
197
|
+
"limit": 50,
|
|
198
|
+
"lastStatusUpdate": "Tue May 16 2023 15:38:15 GMT+0200 (Central European Summer Time)",
|
|
199
|
+
"lastScheduleUpdate": "Tue May 16 2023 15:38:15 GMT+0200 (Central European Summer Time)"
|
|
147
200
|
},
|
|
148
|
-
"
|
|
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
|
-
}
|
|
201
|
+
"_msgid": "dfa429a93a16472d"
|
|
202
|
+
}
|
|
160
203
|
</pre>
|
|
161
204
|
|
|
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)
|
|
205
|
+
<p>The <tt>start</tt> and <tt>end</tt> objects contain the time as unix timestamp in the Javascript format (milliseconds after the epoch),
|
|
206
|
+
while the <tt>duration</tt> is in seconds. The <code>msg.LoadShedding.next.islong</code> value is boolean and will be <em>true</em> if the
|
|
207
|
+
shedding lasts 4 hours or longer.</p>
|
|
163
208
|
|
|
164
209
|
<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>
|
|
210
|
+
This output is mainly useful when debugging or writing your own functions and logic.</p>
|
|
166
211
|
|
|
167
212
|
<h3 id="status">Status</h3>
|
|
168
213
|
|
|
@@ -170,9 +215,16 @@ This output is mainly useful when writing your own functions and logic.</p>
|
|
|
170
215
|
It also shows the count of API calls that have been done and
|
|
171
216
|
how many are left. This updates every 10 minutes.</p>
|
|
172
217
|
|
|
218
|
+
<p>
|
|
219
|
+
The red color status can be either filled (dot) or not (ring). In case of a dot,
|
|
220
|
+
the load schedding is because of an <em>event</em>. When it is a ring, it is caused by
|
|
221
|
+
a matching <em>schedule</em>.
|
|
222
|
+
</p>
|
|
223
|
+
|
|
173
224
|
<h1 id="documentation">Documentation</h1>
|
|
174
|
-
<p>Documentation for the API can be found <a href="https://documenter.getpostman.com/view/1296288/UzQuNk3E">here</a></p>
|
|
175
225
|
|
|
226
|
+
<p>The GitHub site for the node can be found <a href="https://github.com/dirkjanfaber/node-red-contrib-eskomsepush">here</a>, while the documentation for the API can be found <a href="https://documenter.getpostman.com/view/1296288/UzQuNk3E">here</a>.
|
|
227
|
+
For issues and/or suggestions for improving the node, please use the <a href="https://github.com/dirkjanfaber/node-red-contrib-eskomsepush/issues">issue tracker</a>.</p>
|
|
176
228
|
</script>
|
|
177
229
|
|
|
178
230
|
<style>
|
|
@@ -183,4 +235,7 @@ select#areaDropdown {
|
|
|
183
235
|
position: absolute;
|
|
184
236
|
left: 120px;
|
|
185
237
|
}
|
|
238
|
+
#list-areas li {
|
|
239
|
+
cursor: pointer;
|
|
240
|
+
}
|
|
186
241
|
</style>
|
package/src/nodes/eskomsepush.js
CHANGED
|
@@ -5,11 +5,39 @@ 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
|
+
function updateStatus (node) {
|
|
14
|
+
let fill = 'green'
|
|
15
|
+
let shape = 'ring'
|
|
16
|
+
let statusText = ''
|
|
17
|
+
if (Stage && Stage.status && Stage.status[node.config.statusselect].stage) {
|
|
18
|
+
statusText += 'Stage ' + Stage.status[node.config.statusselect].stage
|
|
19
|
+
}
|
|
20
|
+
if (LoadShedding && LoadShedding.active && LoadShedding.next && LoadShedding.next.end) {
|
|
21
|
+
statusText += ' - ' + new Date(LoadShedding.next.end).toLocaleTimeString()
|
|
22
|
+
fill = 'red'
|
|
23
|
+
if (LoadShedding.type === 'event') {
|
|
24
|
+
shape = 'dot'
|
|
25
|
+
}
|
|
26
|
+
} else {
|
|
27
|
+
if (LoadShedding && LoadShedding.next && LoadShedding.next.start) {
|
|
28
|
+
statusText += ' - ' + new Date(LoadShedding.next.start).toLocaleTimeString()
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (EskomSePushAPI) {
|
|
32
|
+
statusText += ` (API: ${EskomSePushAPI.allowance.count}/${EskomSePushAPI.allowance.limit})`
|
|
33
|
+
if (EskomSePushAPI.allowance.count >= EskomSePushAPI.allowance.limit) {
|
|
34
|
+
fill = 'red'
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
node.status({
|
|
38
|
+
fill, shape, text: statusText
|
|
39
|
+
})
|
|
40
|
+
}
|
|
13
41
|
|
|
14
42
|
function checkAllowance (node) {
|
|
15
43
|
const options = {}
|
|
@@ -17,6 +45,7 @@ module.exports = function (RED) {
|
|
|
17
45
|
axios.get('https://developer.sepush.co.za/business/2.0/api_allowance',
|
|
18
46
|
{ params: options, headers }).then(function (response) {
|
|
19
47
|
EskomSePushAPI = response.data
|
|
48
|
+
updateStatus(node)
|
|
20
49
|
})
|
|
21
50
|
.catch(error => {
|
|
22
51
|
node.warn({ error: error.message })
|
|
@@ -29,6 +58,7 @@ module.exports = function (RED) {
|
|
|
29
58
|
axios.get('https://developer.sepush.co.za/business/2.0/status',
|
|
30
59
|
{ params: options, headers }).then(function (response) {
|
|
31
60
|
Stage = response.data
|
|
61
|
+
updateSheddingStatus(node)
|
|
32
62
|
})
|
|
33
63
|
.catch(error => {
|
|
34
64
|
node.warn({ error: error.message })
|
|
@@ -46,15 +76,15 @@ module.exports = function (RED) {
|
|
|
46
76
|
{ params: options, headers }).then(function (response) {
|
|
47
77
|
Schedule = response.data
|
|
48
78
|
Schedule.info.area = node.config.area
|
|
79
|
+
updateSheddingStatus(node)
|
|
49
80
|
})
|
|
50
81
|
.catch(error => {
|
|
51
82
|
node.warn({ error: error.message })
|
|
52
83
|
})
|
|
53
84
|
}
|
|
54
85
|
|
|
55
|
-
function
|
|
86
|
+
function updateSheddingStatus (node) {
|
|
56
87
|
const now = new Date()
|
|
57
|
-
let statusText = ''
|
|
58
88
|
|
|
59
89
|
if (EskomSePushAPI === null || (now.getTime() - lastStatusUpdate.getTime()) > 600000) {
|
|
60
90
|
checkAllowance(node)
|
|
@@ -66,27 +96,24 @@ module.exports = function (RED) {
|
|
|
66
96
|
}
|
|
67
97
|
|
|
68
98
|
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
|
-
}
|
|
99
|
+
updateStatus(node)
|
|
100
|
+
return
|
|
101
|
+
}
|
|
78
102
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
103
|
+
if (Stage === null || (now.getTime() - lastStageUpdate.getTime()) > 3600000) {
|
|
104
|
+
checkStage(node)
|
|
105
|
+
lastStageUpdate = now
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (Schedule === null || (now.getTime() - lastScheduleUpdate.getTime()) > 3600000) {
|
|
109
|
+
checkSchedule(node)
|
|
110
|
+
lastScheduleUpdate = now
|
|
84
111
|
}
|
|
85
112
|
|
|
86
113
|
if (Stage && Schedule && EskomSePushAPI) {
|
|
87
114
|
const stage = Stage.status[node.config.statusselect].stage
|
|
88
115
|
const nowtime = now.toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit' })
|
|
89
|
-
|
|
116
|
+
LoadShedding = {
|
|
90
117
|
schedule: {
|
|
91
118
|
next: {},
|
|
92
119
|
active: false
|
|
@@ -97,23 +124,34 @@ module.exports = function (RED) {
|
|
|
97
124
|
},
|
|
98
125
|
checked: nowtime
|
|
99
126
|
}
|
|
100
|
-
fill = 'green'
|
|
101
127
|
for (const schedule of Schedule.schedule.days[0].stages[stage - 1]) {
|
|
102
128
|
if (nowtime >= schedule.split('-')[0] && nowtime <= schedule.split('-')[1]) {
|
|
103
129
|
LoadShedding.schedule = {
|
|
104
|
-
active: true
|
|
105
|
-
start: Date.parse(Schedule.schedule.days[0].date + ' ' + schedule.split('-')[0]),
|
|
106
|
-
end: Date.parse(Schedule.schedule.days[0].date + ' ' + schedule.split('-')[1])
|
|
130
|
+
active: true
|
|
107
131
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
132
|
+
if (schedule.split('-')[0] < schedule.split('-')[1]) {
|
|
133
|
+
LoadShedding.schedule.next = {
|
|
134
|
+
start: Date.parse(Schedule.schedule.days[0].date + ' ' + schedule.split('-')[0]),
|
|
135
|
+
end: Date.parse(Schedule.schedule.days[0].date + ' ' + schedule.split('-')[1])
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
LoadShedding.schedule.next = {
|
|
139
|
+
start: Date.parse(Schedule.schedule.days[0].date + ' ' + schedule.split('-')[0]),
|
|
140
|
+
end: Date.parse(Schedule.schedule.days[1].date + ' ' + schedule.split('-')[1])
|
|
141
|
+
}
|
|
111
142
|
}
|
|
112
143
|
}
|
|
113
144
|
if (nowtime < schedule.split('-')[0]) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
145
|
+
if (schedule.split('-')[0] < schedule.split('-')[1]) {
|
|
146
|
+
LoadShedding.schedule.next = {
|
|
147
|
+
start: Date.parse(Schedule.schedule.days[0].date + ' ' + schedule.split('-')[0]),
|
|
148
|
+
end: Date.parse(Schedule.schedule.days[0].date + ' ' + schedule.split('-')[1])
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
LoadShedding.schedule.next = {
|
|
152
|
+
start: Date.parse(Schedule.schedule.days[0].date + ' ' + schedule.split('-')[0]),
|
|
153
|
+
end: Date.parse(Schedule.schedule.days[1].date + ' ' + schedule.split('-')[1])
|
|
154
|
+
}
|
|
117
155
|
}
|
|
118
156
|
}
|
|
119
157
|
}
|
|
@@ -128,13 +166,13 @@ module.exports = function (RED) {
|
|
|
128
166
|
start: Date.parse(Schedule.events[0].start),
|
|
129
167
|
end: Date.parse(Schedule.events[0].end)
|
|
130
168
|
}
|
|
131
|
-
if (
|
|
169
|
+
if (now >= LoadShedding.event.next.start && now < LoadShedding.event.next.end) {
|
|
132
170
|
LoadShedding.event.active = true
|
|
133
|
-
LoadShedding.start = LoadShedding.event.start
|
|
134
|
-
LoadShedding.end = LoadShedding.event.end
|
|
135
171
|
}
|
|
136
172
|
|
|
137
|
-
if (!LoadShedding.schedule
|
|
173
|
+
if (!LoadShedding.schedule | !LoadShedding.schedule.next ||
|
|
174
|
+
!LoadShedding.event || !LoadShedding.event.next ||
|
|
175
|
+
!LoadShedding.schedule.next.start || !LoadShedding.event.next.start) {
|
|
138
176
|
node.warn('Unable to find next scheduled event and/or schedule')
|
|
139
177
|
node.warn(LoadShedding)
|
|
140
178
|
return
|
|
@@ -147,14 +185,21 @@ module.exports = function (RED) {
|
|
|
147
185
|
LoadShedding.next.type = 'event'
|
|
148
186
|
}
|
|
149
187
|
|
|
150
|
-
|
|
188
|
+
LoadShedding.next.duration = (LoadShedding.next.end - LoadShedding.next.start) / 1000
|
|
189
|
+
LoadShedding.next.islong = LoadShedding.next.duration >= (4 * 3600)
|
|
190
|
+
|
|
151
191
|
LoadShedding.active = (LoadShedding.schedule.active || LoadShedding.event.active)
|
|
152
|
-
if (LoadShedding.active) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
statusText += ' - ' + new Date(LoadShedding.next.start).toLocaleTimeString()
|
|
192
|
+
if (LoadShedding.schedule.active) {
|
|
193
|
+
LoadShedding.type = 'schedule'
|
|
194
|
+
LoadShedding.start = LoadShedding.schedule.next.start
|
|
195
|
+
LoadShedding.end = LoadShedding.schedule.next.end
|
|
157
196
|
}
|
|
197
|
+
if (LoadShedding.event.active) {
|
|
198
|
+
LoadShedding.type = 'event'
|
|
199
|
+
LoadShedding.start = LoadShedding.event.next.start
|
|
200
|
+
LoadShedding.end = LoadShedding.event.next.end
|
|
201
|
+
}
|
|
202
|
+
|
|
158
203
|
node.send([{
|
|
159
204
|
payload: LoadShedding.active,
|
|
160
205
|
LoadShedding,
|
|
@@ -172,13 +217,7 @@ module.exports = function (RED) {
|
|
|
172
217
|
}])
|
|
173
218
|
}
|
|
174
219
|
|
|
175
|
-
|
|
176
|
-
statusText += ` (API: ${EskomSePushAPI.allowance.count}/${EskomSePushAPI.allowance.limit})`
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
node.status({
|
|
180
|
-
fill, shape, text: (statusText || 'Ok')
|
|
181
|
-
})
|
|
220
|
+
updateStatus(node)
|
|
182
221
|
}
|
|
183
222
|
|
|
184
223
|
function EskomSePush (config) {
|
|
@@ -187,9 +226,9 @@ module.exports = function (RED) {
|
|
|
187
226
|
const node = this
|
|
188
227
|
node.config = config
|
|
189
228
|
|
|
190
|
-
|
|
229
|
+
updateSheddingStatus(node)
|
|
191
230
|
const intervalId = setInterval(function () {
|
|
192
|
-
|
|
231
|
+
updateSheddingStatus(node)
|
|
193
232
|
}, 60000)
|
|
194
233
|
|
|
195
234
|
node.on('close', function () {
|
|
@@ -220,4 +259,23 @@ module.exports = function (RED) {
|
|
|
220
259
|
return res.send({ error: error.message })
|
|
221
260
|
})
|
|
222
261
|
})
|
|
262
|
+
RED.httpNode.get('/eskomsepush/api', (req, res) => {
|
|
263
|
+
if (!req.query.token) {
|
|
264
|
+
res.setHeader('Content-Type', 'application/json')
|
|
265
|
+
return res.send('invalid')
|
|
266
|
+
}
|
|
267
|
+
const headers = {
|
|
268
|
+
token: req.query.token
|
|
269
|
+
}
|
|
270
|
+
const options = {}
|
|
271
|
+
|
|
272
|
+
res.setHeader('Content-Type', 'application/json')
|
|
273
|
+
axios.get('https://developer.sepush.co.za/business/2.0/api_allowance',
|
|
274
|
+
{ params: options, headers }).then(function (response) {
|
|
275
|
+
return res.send(response.data)
|
|
276
|
+
})
|
|
277
|
+
.catch(error => {
|
|
278
|
+
return res.send({ error: error.message })
|
|
279
|
+
})
|
|
280
|
+
})
|
|
223
281
|
}
|
|
Binary file
|