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.
@@ -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 gives the tools to make working with the load shedding in South Africa as easy as possible.
7
+ The EskomSePush-API node makes it easier for South African users to incorporate the load shedding schedules into their flows.
8
8
 
9
- First you need to configure the node by entering the license key and entering the correct area.
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
  ![EskomsePush Victron MinSOC](img/eskomsepush-victron-minsoc.png)
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. One of the main ones being the
18
- ESS control node for setting the MinSoc, based on the currently active stage. You can find the
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
- 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.
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
  ![EskomsePush configuration](img/eskomsepush-configuration.png)
26
30
 
27
- 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.
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
- Then you need to fill out which status to follow. This can be either _National_ (eskom) or _Capetown_.
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
- 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.
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 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:
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 timestamp in the Javascript format (milliseconds after the epoch).
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,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-eskomsepush",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "Node-RED interface for the Eskomsepush API",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -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
- 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)
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
- dropdown.style.display = "block";
55
- dropdown.addEventListener("change", function() {
56
- input.value = dropdown.value;
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" 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">
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 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&#39;t want that and you already know the id of the area, fill out the area first and then the license
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&#39;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
- "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
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
- "checked":"13:35",
143
- "next":{
144
- "start":1683561600000,
145
- "end":1683570600000,
146
- "type":"schedule"
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
- "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
- }
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).</p>
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>
@@ -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
- let fill = 'green'
12
- let shape = 'ring'
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 updateStatus (node) {
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
- statusText += 'API quota reached'
70
- fill = 'red'
71
- shape = 'dot'
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
- if (Schedule === null || (now.getTime() - lastScheduleUpdate.getTime()) > 3600000) {
80
- node.status({ fill: 'yellow', shape, text: 'Fetching schedule' })
81
- checkSchedule(node)
82
- lastScheduleUpdate = now
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
- const LoadShedding = {
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
- LoadShedding.schedule.next = {
109
- start : LoadShedding.schedule.start,
110
- end : LoadShedding.schedule.end
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
- LoadShedding.schedule.next = {
115
- start: Date.parse(Schedule.schedule.days[0].date + ' ' + schedule.split('-')[0]),
116
- end: Date.parse(Schedule.schedule.days[0].date + ' ' + schedule.split('-')[1])
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 (nowtime >= LoadShedding.event.start && nowtime < LoadShedding.event.end) {
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.next.start || !LoadShedding.event.next.start) {
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
- statusText += 'Stage '+stage
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
- statusText += ' - ' + new Date(LoadShedding.next.end).toLocaleTimeString()
154
- fill = 'red'
155
- } else {
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
- if (EskomSePushAPI) {
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
- updateStatus(node)
229
+ updateSheddingStatus(node)
191
230
  const intervalId = setInterval(function () {
192
- updateStatus(node)
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
  }