node-red-contrib-eskomsepush 0.0.11 → 0.0.13

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 CHANGED
@@ -53,6 +53,18 @@ Then you need to fill out which status to follow. This can be either _National_
53
53
 
54
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.
55
55
 
56
+ ### Inputs
57
+
58
+ The input side is not needed in most cases. The node will output its status every ten minutes and won't update the information it gets from the API more often. The input node however can be used to overrule this behaviour. When inserting a timestamp, the node will output the latest information it got and re-calculate all fields.
59
+
60
+ There are also special strings that can be injected as payload to force updates:
61
+
62
+ - `allowance` - for retrieving the latest API count values
63
+ - `stage` - for retrieving the latest active load shedding stage
64
+ - `area` - for retrieving the latest schedule information
65
+
66
+ So usually there is no reason to connect anything to the input. It is only needed if you want to have more control over the node.
67
+
56
68
  ### Outputs
57
69
 
58
70
  The note has two outputs. In most cases, the first (upper) output will be used.
@@ -61,46 +73,34 @@ The first output of the node outputs a boolean value and some related data. When
61
73
 
62
74
  ```
63
75
  {
64
- "payload":false,
65
- "LoadShedding":{
66
- "schedule":{
67
- "next":{
68
- "start":1683561600000,
69
- "end":1683570600000,
70
- "type":"schedule"
71
- },
72
- "active":false
73
- },
74
- "event":{
75
- "next":{
76
- "start":1683561600000,
77
- "end":1683570600000
78
- },
79
- "active":false
80
- },
81
- "checked":"13:35",
82
- "next":{
83
- "start":1683561600000,
84
- "end":1683570600000,
85
- "type":"schedule"
86
- },
87
- "active":false
76
+ "payload": false,
77
+ "stage": "0",
78
+ "statusselect": "capetown",
79
+ "api": {
80
+ "count": 30,
81
+ "limit": 50
88
82
  },
89
- "stage":"5",
90
- "statusselect":"capetown",
91
- "api":{
92
- "count":12,
93
- "limit":50,
94
- "lastStatusUpdate":"Mon May 08 2023 13:34:30 GMT+0200 (Central European Summer Time)",
95
- "lastScheduleUpdate":"Mon May 08 2023 13:34:30 GMT+0200 (Central European Summer Time)"
83
+ "calc": {
84
+ "sleeptime": "58",
85
+ "stage": "0",
86
+ "active": false,
87
+ "type": "event",
88
+ "next": {
89
+ "type": "schedule",
90
+ "start": 1686830400000,
91
+ "end": 1686839400000,
92
+ "duration": 9000,
93
+ "islong": false
94
+ },
95
+ "secondstostatechange": 84912
96
96
  },
97
- "\_msgid":"6e77b593b7f9eda4"
97
+ "_msgid": "9fa5d503c47f083e"
98
98
  }
99
99
  ```
100
100
 
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).
101
+ The _start_ and _end_ objects contain the time as unix timestamp in the Javascript format (milliseconds after the epoch), while the duration is in seconds. The `msg.calc.next.islong` value is boolean and will be _true_ if the shedding lasts 4 hours or longer.
102
102
 
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.
103
+ The second output shows `msg.stage` and `msg.schedule`, containing the latest information as retrieved from the API, with the lastUpdate field added. This output is mainly useful when debugging or writing your own functions and logic.
104
104
 
105
105
  ### Status
106
106
 
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-eskomsepush",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "description": "Node-RED interface for the Eskomsepush API",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -39,7 +39,7 @@
39
39
  test: { value: false },
40
40
  verbose: {value: false}
41
41
  },
42
- inputs:0,
42
+ inputs:1,
43
43
  outputs:2,
44
44
  icon: "eskomsepush.svg",
45
45
  label: function() {
@@ -126,7 +126,7 @@
126
126
  <ul id="list-areas"></ul>
127
127
 
128
128
  <p>
129
- Queries left: <span id="api_info">unknown/unknown</span><br />
129
+ Queries used: <span id="api_info">unknown/unknown</span><br />
130
130
  <input type="text" id="searchAreaText" placeholder="Area" disabled />
131
131
  <button id="searchAreaId">Search</button><br />
132
132
  </p>
@@ -159,60 +159,65 @@ see how many queries you have left.</p>
159
159
  <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>
160
160
  <p>Then you need to fill out which status to follow. This can be either <em>National</em> (eskom) or <em>Capetown</em>.</p>
161
161
  <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>
162
+ <p>The <em>verbose</em> checkbox will give some additional <tt>node.warn()</tt> messages, appearing in the debug tab. This is also useful when debugging.</p>
163
+
164
+ <h3 id=""inputs">Inputs</h3>
165
+
166
+ <p>
167
+ The input side is not needed in most cases. The node will output its status every ten minutes and won't update the information it gets from the API more
168
+ often. The input node however can be used to overrule this behaviour. When inserting a timestamp, the node will output the latest information it got and
169
+ re-calculate all fields.
170
+ </p>
171
+
172
+ <p>There are also special strings that can be injected as payload to force updates:
173
+ <ul>
174
+ <li><code>allowance</code> - for retrieving the latest API count values</li>
175
+ <li><code>stage</code> - for retrieving the latest active load shedding stage</li>
176
+ <li><code>area</code> - for retrieving the latest schedule information</li>
177
+ </ul>
178
+ </p>
179
+
180
+ <p>
181
+ So usually there is no reason to connect anything to the input. It is only needed if you want to have more control over the node.
182
+ </p>
162
183
 
163
184
  <h3 id="outputs">Outputs</h3>
164
185
 
165
186
  <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>
166
187
 
167
188
  <pre>
168
- {
169
- "payload": false,
170
- "LoadShedding": {
171
- "schedule": {
172
- "next": {
173
- "start": 1684267200000,
174
- "end": 1684276200000,
189
+ {
190
+ "payload": false,
191
+ "stage": "0",
192
+ "statusselect": "capetown",
193
+ "api": {
194
+ "count": 30,
195
+ "limit": 50
196
+ },
197
+ "calc": {
198
+ "sleeptime": "58",
199
+ "stage": "0",
200
+ "active": false,
201
+ "type": "event",
202
+ "next": {
175
203
  "type": "schedule",
204
+ "start": 1686830400000,
205
+ "end": 1686839400000,
176
206
  "duration": 9000,
177
207
  "islong": false
208
+ },
209
+ "secondstostatechange": 84912
178
210
  },
179
- "active": false
180
- },
181
- "event": {
182
- "next": {
183
- "start": 1684267200000,
184
- "end": 1684276200000
185
- },
186
- "active": false
187
- },
188
- "checked": "15:38",
189
- "next": {
190
- "start": 1684267200000,
191
- "end": 1684276200000,
192
- "type": "schedule",
193
- "duration": 9000,
194
- "islong": false
195
- },
196
- "active": false
197
- },
198
- "stage": "3",
199
- "statusselect": "capetown",
200
- "api": {
201
- "count": 45,
202
- "limit": 50,
203
- "lastStatusUpdate": "Tue May 16 2023 15:38:15 GMT+0200 (Central European Summer Time)",
204
- "lastScheduleUpdate": "Tue May 16 2023 15:38:15 GMT+0200 (Central European Summer Time)"
205
- },
206
- "_msgid": "dfa429a93a16472d"
207
- }
211
+ "_msgid": "9fa5d503c47f083e"
212
+ }
208
213
  </pre>
209
214
 
210
215
  <p>The <tt>start</tt> and <tt>end</tt> objects contain the time as unix timestamp in the Javascript format (milliseconds after the epoch),
211
- 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
216
+ while the <tt>duration</tt> is in seconds. The <code>msg.calc.next.islong</code> value is boolean and will be <em>true</em> if the
212
217
  shedding lasts 4 hours or longer.</p>
213
218
 
214
- <p>The second output shows <code>msg.stage</code> and <code>msg.schedule</code>, containing the latest information as retrieved from the API.
215
- This output is mainly useful when debugging or writing your own functions and logic.</p>
219
+ <p>The second output shows <code>msg.stage</code> and <code>msg.schedule</code>, containing the latest information as retrieved from the API,
220
+ with the <tt>lastUpdate</tt> field added. This output is mainly useful when debugging or writing your own functions and logic.</p>
216
221
 
217
222
  <h3 id="status">Status</h3>
218
223
 
@@ -18,10 +18,13 @@ module.exports = function (RED) {
18
18
  calc: {}
19
19
  }
20
20
 
21
- function getMinutesLeftInDay () {
22
- const currentDate = new Date()
23
- const tomorrow = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate() + 1)
24
- const timeDiff = tomorrow.getTime() - currentDate.getTime()
21
+ function getMinutesToAPIReset () {
22
+ const now = new Date()
23
+ const targetTime = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 2, 0, 0);
24
+ if (now > targetTime) {
25
+ targetTime.setDate(targetTime.getDate() + 1);
26
+ }
27
+ const timeDiff = targetTime - now;
25
28
  const minutesLeft = Math.floor(timeDiff / (1000 * 60))
26
29
 
27
30
  return minutesLeft
@@ -102,11 +105,11 @@ module.exports = function (RED) {
102
105
  })
103
106
  }
104
107
 
105
- function updateSheddingStatus (node) {
108
+ function updateSheddingStatus (node, msg) {
106
109
  const now = new Date()
107
110
 
108
111
  // Check allowance every ten minutes
109
- if (EskomSePushInfo.api.lastUpdate === null || (now.getTime() - EskomSePushInfo.api.lastUpdate.getTime()) > 600000) {
112
+ if ((msg && msg.payload === 'allowance' ) || EskomSePushInfo.api.lastUpdate === null || (now.getTime() - EskomSePushInfo.api.lastUpdate.getTime()) > 600000) {
110
113
  checkAllowance(node)
111
114
  }
112
115
 
@@ -122,19 +125,20 @@ module.exports = function (RED) {
122
125
  return
123
126
  }
124
127
 
125
- // Fetching actual information takes 2 calls, so calculate how long the day lasts and see how we
126
- // can divide the available calls over the day
128
+ // Fetching actual information takes 2 calls, so calculate how long until the next API count
129
+ // reset and divide the calls over the day. Wait at least 10 minutes between calls
127
130
  if ((EskomSePushInfo.api.info.allowance.limit - EskomSePushInfo.api.info.allowance.count) > 0) {
128
- EskomSePushInfo.calc.sleeptime = (getMinutesLeftInDay() / ((EskomSePushInfo.api.info.allowance.limit - EskomSePushInfo.api.info.allowance.count) / 2)).toFixed(0)
131
+ EskomSePushInfo.calc.sleeptime = (getMinutesToAPIReset() / ((EskomSePushInfo.api.info.allowance.limit - EskomSePushInfo.api.info.allowance.count) / 2)).toFixed(0)
132
+ if (EskomSePushInfo.calc.sleeptime < 10) { EskomSePushInfo.calc.sleeptime = 10 }
129
133
  } else {
130
134
  EskomSePushInfo.calc.sleeptime = 30
131
135
  }
132
136
 
133
- if (EskomSePushInfo.status.lastUpdate === null || (now.getTime() - EskomSePushInfo.status.lastUpdate) > (EskomSePushInfo.calc.sleeptime * 60000)) {
137
+ if (( msg && msg.payload === 'stage' ) || EskomSePushInfo.status.lastUpdate === null || (now.getTime() - EskomSePushInfo.status.lastUpdate) > (EskomSePushInfo.calc.sleeptime * 60000)) {
134
138
  checkStage(node)
135
139
  }
136
140
 
137
- if (EskomSePushInfo.area.lastUpdate === null || (now.getTime() - EskomSePushInfo.area.lastUpdate) > (EskomSePushInfo.calc.sleeptime * 60000)) {
141
+ if (( msg && msg.payload === 'area' ) || EskomSePushInfo.area.lastUpdate === null || (now.getTime() - EskomSePushInfo.area.lastUpdate) > (EskomSePushInfo.calc.sleeptime * 60000)) {
138
142
  checkArea(node)
139
143
  }
140
144
 
@@ -167,6 +171,8 @@ module.exports = function (RED) {
167
171
  if (EskomSePushInfo.area.info.events[0].note.match(/Stage (\d+)/i)) {
168
172
  EskomSePushInfo.calc.stage = EskomSePushInfo.area.info.events[0].note.match(/Stage (\d+)/i)[1]
169
173
  }
174
+ EskomSePushInfo.calc.start = EventStart
175
+ EskomSePushInfo.calc.end = EventEnd
170
176
  } else {
171
177
  EskomSePushInfo.calc.next = {
172
178
  type: 'event',
@@ -181,9 +187,12 @@ module.exports = function (RED) {
181
187
  // So not just like events, where they are in UTC with an offset
182
188
  let BreakLoop = false
183
189
  for (const dates of EskomSePushInfo.area.info.schedule.days) {
184
- for (const schedule of dates.stages[EskomSePushInfo.calc.stage]) {
190
+ for (const schedule of dates.stages[EskomSePushInfo.calc.stage-1]) {
185
191
  const ScheduleStart = Date.parse(dates.date + ' ' + schedule.split('-')[0])
186
- const ScheduleEnd = Date.parse(dates.date + ' ' + schedule.split('-')[1])
192
+ let ScheduleEnd = Date.parse(dates.date + ' ' + schedule.split('-')[1])
193
+ if (ScheduleEnd < ScheduleStart) {
194
+ ScheduleEnd += (24 * 60 * 60 * 1000)
195
+ }
187
196
  if (now < ScheduleEnd) {
188
197
  BreakLoop = true
189
198
  // This schedule is either active or will be next
@@ -239,18 +248,23 @@ module.exports = function (RED) {
239
248
  // And update the status
240
249
  let fill = 'green'
241
250
  let shape = 'ring'
242
- let statusText = 'Stage ' + EskomSePushInfo.calc.stage
251
+ let statusText = 'Stage ' + EskomSePushInfo.calc.stage + ': '
243
252
 
244
253
  if (EskomSePushInfo.calc.active) {
245
254
  fill = 'yellow'
246
255
  if (EskomSePushInfo.calc.type === 'event') {
247
256
  shape = 'dot'
248
257
  }
249
- statusText += ' - ' + new Date(EskomSePushInfo.calc.start).toLocaleTimeString()
250
- statusText += ' - ' + new Date(EskomSePushInfo.calc.end).toLocaleTimeString()
258
+ if ( EskomSePushInfo.calc.start ) {
259
+ statusText += new Date(EskomSePushInfo.calc.start).toLocaleTimeString([], {timeStyle: 'short'})
260
+ statusText += ' - ' + new Date(EskomSePushInfo.calc.end).toLocaleTimeString([], {timeStyle: 'short'})
261
+ }
251
262
  } else {
252
- statusText += ' - ' + new Date(EskomSePushInfo.calc.next.start).toLocaleTimeString()
253
- statusText += ' - ' + new Date(EskomSePushInfo.calc.next.end).toLocaleTimeString()
263
+ if (new Date(EskomSePushInfo.calc.next.start).getUTCDay() !== now.getUTCDate) {
264
+ statusText += new Date(EskomSePushInfo.calc.next.start).toLocaleString([], {weekday: 'short'}) + ' '
265
+ }
266
+ statusText += new Date(EskomSePushInfo.calc.next.start).toLocaleTimeString([], {timeStyle: 'short'})
267
+ statusText += ' - ' + new Date(EskomSePushInfo.calc.next.end).toLocaleTimeString([], {timeStyle: 'short'})
254
268
  }
255
269
 
256
270
  statusText += ' (API: ' + EskomSePushInfo.api.info.allowance.count + '/' + EskomSePushInfo.api.info.allowance.limit + ')'
@@ -270,6 +284,10 @@ module.exports = function (RED) {
270
284
  updateSheddingStatus(node)
271
285
  }, 60000)
272
286
 
287
+ node.on('input', function(msg) {
288
+ updateSheddingStatus(node, msg)
289
+ })
290
+
273
291
  node.on('close', function () {
274
292
  clearInterval(intervalId)
275
293
  })