iobroker.apg-info 0.1.21 → 0.1.22
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/CHANGELOG_OLD.md +3 -0
- package/README.md +4 -3
- package/admin/jsonConfig.json +286 -263
- package/io-package.json +29 -27
- package/main.js +90 -172
- package/package.json +2 -2
package/CHANGELOG_OLD.md
CHANGED
package/README.md
CHANGED
|
@@ -34,6 +34,10 @@ For more details check https://transparency.entsoe.eu/content/static_content/Sta
|
|
|
34
34
|
Placeholder for the next version (at the beginning of the line):
|
|
35
35
|
### __WORK IN PROGRESS__
|
|
36
36
|
-->
|
|
37
|
+
### 0.1.22 (2025-10-21)
|
|
38
|
+
* (HGlab01) Implement retry mechanism for API calls
|
|
39
|
+
* (HGlab01) add turn on/off for peak hours and market prices
|
|
40
|
+
|
|
37
41
|
### 0.1.21 (2025-10-13)
|
|
38
42
|
* (HGlab01) Support quater-hourly tarifs
|
|
39
43
|
* (HGlab01) Bump iobroker-jsonexplorer to 0.2.1
|
|
@@ -49,9 +53,6 @@ For more details check https://transparency.entsoe.eu/content/static_content/Sta
|
|
|
49
53
|
* (HGlab01) Log finetuning
|
|
50
54
|
* (HGlab01) Bump axios to 1.10.0
|
|
51
55
|
|
|
52
|
-
### 0.1.17 (2025-06-03)
|
|
53
|
-
* (HGlab01) Add retry mechanism for Entsoe
|
|
54
|
-
|
|
55
56
|
## License
|
|
56
57
|
MIT License
|
|
57
58
|
|
package/admin/jsonConfig.json
CHANGED
|
@@ -1,263 +1,286 @@
|
|
|
1
|
-
{
|
|
2
|
-
"i18n": false,
|
|
3
|
-
"type": "tabs",
|
|
4
|
-
"tabsStyle": {
|
|
5
|
-
"width": "calc(100% - 100px)"
|
|
6
|
-
},
|
|
7
|
-
"items": {
|
|
8
|
-
"General": {
|
|
9
|
-
"type": "panel",
|
|
10
|
-
"label": "General",
|
|
11
|
-
"items": {
|
|
12
|
-
"threshold": {
|
|
13
|
-
"type": "number",
|
|
14
|
-
"label": {
|
|
15
|
-
"en": "Threshold market price (€ct/kWh)",
|
|
16
|
-
"de": "Schwellenmarktpreis (€ct/kWh)",
|
|
17
|
-
"ru": "Порог рыночная цена (€ct/kWh)",
|
|
18
|
-
"pt": "Preço de mercado limiar (€ct/kWh)",
|
|
19
|
-
"nl": "Dreig de marktprijs",
|
|
20
|
-
"fr": "Prix du marché seuil (€ct/kWh)",
|
|
21
|
-
"it": "Prezzo di mercato (€ct/kWh)",
|
|
22
|
-
"es": "Precio del mercado de propiedad (€ct/kWh)",
|
|
23
|
-
"pl": "Cena rynkowa (ok",
|
|
24
|
-
"uk": "Потрібна ціна ринку (€ct/kWh)",
|
|
25
|
-
"zh-cn": "市场价格(ct/kWh)"
|
|
26
|
-
},
|
|
27
|
-
"min": 0,
|
|
28
|
-
"step": 0.1,
|
|
29
|
-
"newLine": true,
|
|
30
|
-
"xs": 12,
|
|
31
|
-
"sm": 12,
|
|
32
|
-
"md":
|
|
33
|
-
"lg":
|
|
34
|
-
"xl":
|
|
35
|
-
},
|
|
36
|
-
"country": {
|
|
37
|
-
"type": "select",
|
|
38
|
-
"options": [
|
|
39
|
-
{
|
|
40
|
-
"label": {
|
|
41
|
-
"en": "Swiss",
|
|
42
|
-
"de": "Schweiz",
|
|
43
|
-
"ru": "Swiss",
|
|
44
|
-
"pt": "Swiss",
|
|
45
|
-
"nl": "Swiss",
|
|
46
|
-
"fr": "Swiss",
|
|
47
|
-
"it": "Swiss",
|
|
48
|
-
"es": "Swiss",
|
|
49
|
-
"pl": "Swiss",
|
|
50
|
-
"uk": "Swiss",
|
|
51
|
-
"zh-cn": "Swiss"
|
|
52
|
-
},
|
|
53
|
-
"value": "ch"
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
"label": {
|
|
57
|
-
"en": "Austria",
|
|
58
|
-
"de": "Österreich",
|
|
59
|
-
"ru": "Austria",
|
|
60
|
-
"pt": "Austria",
|
|
61
|
-
"nl": "Austria",
|
|
62
|
-
"fr": "Austria",
|
|
63
|
-
"it": "Austria",
|
|
64
|
-
"es": "Austria",
|
|
65
|
-
"pl": "Austria",
|
|
66
|
-
"uk": "Austria",
|
|
67
|
-
"zh-cn": "Austria"
|
|
68
|
-
},
|
|
69
|
-
"value": "at"
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
"label": {
|
|
73
|
-
"en": "Germany",
|
|
74
|
-
"de": "Deutschland",
|
|
75
|
-
"ru": "Germany",
|
|
76
|
-
"pt": "Germany",
|
|
77
|
-
"nl": "Germany",
|
|
78
|
-
"fr": "Germany",
|
|
79
|
-
"it": "Germany",
|
|
80
|
-
"es": "Germany",
|
|
81
|
-
"pl": "Germany",
|
|
82
|
-
"uk": "Germany",
|
|
83
|
-
"zh-cn": "Germany"
|
|
84
|
-
},
|
|
85
|
-
"value": "de"
|
|
86
|
-
}
|
|
87
|
-
],
|
|
88
|
-
"label": {
|
|
89
|
-
"en": "Country of the market",
|
|
90
|
-
"de": "Land der Strombörse"
|
|
91
|
-
},
|
|
92
|
-
"xs": 12,
|
|
93
|
-
"sm": 12,
|
|
94
|
-
"md": 6,
|
|
95
|
-
"lg": 4,
|
|
96
|
-
"xl": 4
|
|
97
|
-
},
|
|
98
|
-
"forecast": {
|
|
99
|
-
"
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
"
|
|
116
|
-
"
|
|
117
|
-
"
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
"
|
|
122
|
-
"
|
|
123
|
-
"
|
|
124
|
-
"
|
|
125
|
-
"
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
"
|
|
129
|
-
"
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
"
|
|
139
|
-
"
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
"
|
|
148
|
-
"
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
"
|
|
152
|
-
"
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
"
|
|
161
|
-
"
|
|
162
|
-
"
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
"
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
"
|
|
174
|
-
"
|
|
175
|
-
"
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
"
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
"
|
|
187
|
-
"
|
|
188
|
-
"
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
"
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
"
|
|
197
|
-
"
|
|
198
|
-
"
|
|
199
|
-
"
|
|
200
|
-
"
|
|
201
|
-
"
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
"
|
|
206
|
-
|
|
207
|
-
"de": "
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
"
|
|
211
|
-
"
|
|
212
|
-
"
|
|
213
|
-
"
|
|
214
|
-
"
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
"
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
"
|
|
234
|
-
"
|
|
235
|
-
"
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
"
|
|
239
|
-
"
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
"
|
|
248
|
-
"
|
|
249
|
-
"
|
|
250
|
-
|
|
251
|
-
"en": "Token
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
"xs": 12,
|
|
255
|
-
"sm": 12,
|
|
256
|
-
"md":
|
|
257
|
-
"lg":
|
|
258
|
-
"xl":
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
1
|
+
{
|
|
2
|
+
"i18n": false,
|
|
3
|
+
"type": "tabs",
|
|
4
|
+
"tabsStyle": {
|
|
5
|
+
"width": "calc(100% - 100px)"
|
|
6
|
+
},
|
|
7
|
+
"items": {
|
|
8
|
+
"General": {
|
|
9
|
+
"type": "panel",
|
|
10
|
+
"label": "General",
|
|
11
|
+
"items": {
|
|
12
|
+
"threshold": {
|
|
13
|
+
"type": "number",
|
|
14
|
+
"label": {
|
|
15
|
+
"en": "Threshold market price (€ct/kWh)",
|
|
16
|
+
"de": "Schwellenmarktpreis (€ct/kWh)",
|
|
17
|
+
"ru": "Порог рыночная цена (€ct/kWh)",
|
|
18
|
+
"pt": "Preço de mercado limiar (€ct/kWh)",
|
|
19
|
+
"nl": "Dreig de marktprijs",
|
|
20
|
+
"fr": "Prix du marché seuil (€ct/kWh)",
|
|
21
|
+
"it": "Prezzo di mercato (€ct/kWh)",
|
|
22
|
+
"es": "Precio del mercado de propiedad (€ct/kWh)",
|
|
23
|
+
"pl": "Cena rynkowa (ok",
|
|
24
|
+
"uk": "Потрібна ціна ринку (€ct/kWh)",
|
|
25
|
+
"zh-cn": "市场价格(ct/kWh)"
|
|
26
|
+
},
|
|
27
|
+
"min": 0,
|
|
28
|
+
"step": 0.1,
|
|
29
|
+
"newLine": true,
|
|
30
|
+
"xs": 12,
|
|
31
|
+
"sm": 12,
|
|
32
|
+
"md": 3,
|
|
33
|
+
"lg": 2,
|
|
34
|
+
"xl": 2
|
|
35
|
+
},
|
|
36
|
+
"country": {
|
|
37
|
+
"type": "select",
|
|
38
|
+
"options": [
|
|
39
|
+
{
|
|
40
|
+
"label": {
|
|
41
|
+
"en": "Swiss",
|
|
42
|
+
"de": "Schweiz",
|
|
43
|
+
"ru": "Swiss",
|
|
44
|
+
"pt": "Swiss",
|
|
45
|
+
"nl": "Swiss",
|
|
46
|
+
"fr": "Swiss",
|
|
47
|
+
"it": "Swiss",
|
|
48
|
+
"es": "Swiss",
|
|
49
|
+
"pl": "Swiss",
|
|
50
|
+
"uk": "Swiss",
|
|
51
|
+
"zh-cn": "Swiss"
|
|
52
|
+
},
|
|
53
|
+
"value": "ch"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"label": {
|
|
57
|
+
"en": "Austria",
|
|
58
|
+
"de": "Österreich",
|
|
59
|
+
"ru": "Austria",
|
|
60
|
+
"pt": "Austria",
|
|
61
|
+
"nl": "Austria",
|
|
62
|
+
"fr": "Austria",
|
|
63
|
+
"it": "Austria",
|
|
64
|
+
"es": "Austria",
|
|
65
|
+
"pl": "Austria",
|
|
66
|
+
"uk": "Austria",
|
|
67
|
+
"zh-cn": "Austria"
|
|
68
|
+
},
|
|
69
|
+
"value": "at"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"label": {
|
|
73
|
+
"en": "Germany",
|
|
74
|
+
"de": "Deutschland",
|
|
75
|
+
"ru": "Germany",
|
|
76
|
+
"pt": "Germany",
|
|
77
|
+
"nl": "Germany",
|
|
78
|
+
"fr": "Germany",
|
|
79
|
+
"it": "Germany",
|
|
80
|
+
"es": "Germany",
|
|
81
|
+
"pl": "Germany",
|
|
82
|
+
"uk": "Germany",
|
|
83
|
+
"zh-cn": "Germany"
|
|
84
|
+
},
|
|
85
|
+
"value": "de"
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
"label": {
|
|
89
|
+
"en": "Country of the market",
|
|
90
|
+
"de": "Land der Strombörse"
|
|
91
|
+
},
|
|
92
|
+
"xs": 12,
|
|
93
|
+
"sm": 12,
|
|
94
|
+
"md": 6,
|
|
95
|
+
"lg": 4,
|
|
96
|
+
"xl": 4
|
|
97
|
+
},
|
|
98
|
+
"forecast": {
|
|
99
|
+
"newLine": true,
|
|
100
|
+
"type": "checkbox",
|
|
101
|
+
"label": "Forecast based on 10.15 auction (works in AT and DE)",
|
|
102
|
+
"default": false,
|
|
103
|
+
"xs": 12,
|
|
104
|
+
"sm": 12,
|
|
105
|
+
"md": 6,
|
|
106
|
+
"lg": 4,
|
|
107
|
+
"xl": 4
|
|
108
|
+
},
|
|
109
|
+
"peakHours": {
|
|
110
|
+
"newLine": true,
|
|
111
|
+
"type": "checkbox",
|
|
112
|
+
"label": "Check peak hours (works for Austria)",
|
|
113
|
+
"default": false,
|
|
114
|
+
"xs": 12,
|
|
115
|
+
"sm": 12,
|
|
116
|
+
"md": 6,
|
|
117
|
+
"lg": 4,
|
|
118
|
+
"xl": 4
|
|
119
|
+
},
|
|
120
|
+
"marketPrices": {
|
|
121
|
+
"newLine": true,
|
|
122
|
+
"type": "checkbox",
|
|
123
|
+
"label": "Check market prices",
|
|
124
|
+
"default": false,
|
|
125
|
+
"xs": 12,
|
|
126
|
+
"sm": 12,
|
|
127
|
+
"md": 6,
|
|
128
|
+
"lg": 4,
|
|
129
|
+
"xl": 4
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
"Calculation": {
|
|
134
|
+
"type": "panel",
|
|
135
|
+
"label": "Calculation",
|
|
136
|
+
"items": {
|
|
137
|
+
"calculate": {
|
|
138
|
+
"newLine": true,
|
|
139
|
+
"type": "checkbox",
|
|
140
|
+
"label": {
|
|
141
|
+
"en": "Activate price calculation",
|
|
142
|
+
"de": "Preiskalkulation aktivieren"
|
|
143
|
+
},
|
|
144
|
+
"xs": 12,
|
|
145
|
+
"sm": 12,
|
|
146
|
+
"md": 6,
|
|
147
|
+
"lg": 4,
|
|
148
|
+
"xl": 4
|
|
149
|
+
},
|
|
150
|
+
"feeAbsolute": {
|
|
151
|
+
"newLine": true,
|
|
152
|
+
"type": "number",
|
|
153
|
+
"label": {
|
|
154
|
+
"en": "Provider fee absolute (€ct/kWh)",
|
|
155
|
+
"de": "Provider Gebühr Absolut (€ct/kWh)"
|
|
156
|
+
},
|
|
157
|
+
"hidden": "!data.calculate",
|
|
158
|
+
"xs": 12,
|
|
159
|
+
"sm": 12,
|
|
160
|
+
"md": 3,
|
|
161
|
+
"lg": 2,
|
|
162
|
+
"xl": 2
|
|
163
|
+
},
|
|
164
|
+
"feeRelative": {
|
|
165
|
+
"type": "number",
|
|
166
|
+
"label": {
|
|
167
|
+
"en": "Provider fee (%)",
|
|
168
|
+
"de": "Provider Gebühr (%)"
|
|
169
|
+
},
|
|
170
|
+
"hidden": "!data.calculate",
|
|
171
|
+
"xs": 12,
|
|
172
|
+
"sm": 12,
|
|
173
|
+
"md": 3,
|
|
174
|
+
"lg": 2,
|
|
175
|
+
"xl": 2
|
|
176
|
+
},
|
|
177
|
+
"charges": {
|
|
178
|
+
"type": "number",
|
|
179
|
+
"label": {
|
|
180
|
+
"en": "other taxes (%)",
|
|
181
|
+
"de": "weitere Abgaben (%)"
|
|
182
|
+
},
|
|
183
|
+
"hidden": "!data.calculate",
|
|
184
|
+
"xs": 12,
|
|
185
|
+
"sm": 12,
|
|
186
|
+
"md": 3,
|
|
187
|
+
"lg": 2,
|
|
188
|
+
"xl": 2
|
|
189
|
+
},
|
|
190
|
+
"gridCosts": {
|
|
191
|
+
"type": "number",
|
|
192
|
+
"label": {
|
|
193
|
+
"en": "grid costs (€ct/kWh)",
|
|
194
|
+
"de": "Netzentgelte (€ct/kWh)"
|
|
195
|
+
},
|
|
196
|
+
"hidden": "!data.calculate",
|
|
197
|
+
"xs": 12,
|
|
198
|
+
"sm": 12,
|
|
199
|
+
"md": 3,
|
|
200
|
+
"lg": 2,
|
|
201
|
+
"xl": 2
|
|
202
|
+
},
|
|
203
|
+
"vat": {
|
|
204
|
+
"type": "number",
|
|
205
|
+
"label": {
|
|
206
|
+
"en": "VAT (%)",
|
|
207
|
+
"de": "USt (%)"
|
|
208
|
+
},
|
|
209
|
+
"hidden": "!data.calculate",
|
|
210
|
+
"xs": 12,
|
|
211
|
+
"sm": 12,
|
|
212
|
+
"md": 3,
|
|
213
|
+
"lg": 2,
|
|
214
|
+
"xl": 2
|
|
215
|
+
},
|
|
216
|
+
"_text0": {
|
|
217
|
+
"type": "staticText",
|
|
218
|
+
"text": "",
|
|
219
|
+
"newLine": true,
|
|
220
|
+
"hidden": "!data.calculate",
|
|
221
|
+
"xs": 12,
|
|
222
|
+
"sm": 12,
|
|
223
|
+
"md": 12,
|
|
224
|
+
"lg": 12,
|
|
225
|
+
"xl": 12
|
|
226
|
+
},
|
|
227
|
+
"_text1": {
|
|
228
|
+
"type": "staticText",
|
|
229
|
+
"text": {
|
|
230
|
+
"de": "Calculation: <br> provider = tradePrice * Abs(tradePrice * providerFee) + feeAbsolute; <br> charges = (tradePrice + provider) * otherTaxes; <br> vat = (tradePrice + provider + charges + gridCosts) * vat; <br> price = tradePrice + provider + charges + gridCosts + vat;",
|
|
231
|
+
"en": "Calculation: <br> provider = tradePrice * tradePrice * providerFee + feeAbsolute; <br> charges = (tradePrice + provider) * otherTaxes; <br> vat = (tradePrice + provider + charges + gridCosts) * vat; <br> price = tradePrice + provider + charges + gridCosts + vat;"
|
|
232
|
+
},
|
|
233
|
+
"newLine": true,
|
|
234
|
+
"hidden": "!data.calculate",
|
|
235
|
+
"xs": 12,
|
|
236
|
+
"sm": 12,
|
|
237
|
+
"md": 12,
|
|
238
|
+
"lg": 12,
|
|
239
|
+
"xl": 12
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
"EntsoeToken": {
|
|
244
|
+
"type": "panel",
|
|
245
|
+
"label": "Entsoe Token",
|
|
246
|
+
"items": {
|
|
247
|
+
"tokenEncrypted": {
|
|
248
|
+
"newLine": true,
|
|
249
|
+
"type": "text",
|
|
250
|
+
"label": {
|
|
251
|
+
"en": "Token",
|
|
252
|
+
"de": "Token"
|
|
253
|
+
},
|
|
254
|
+
"xs": 12,
|
|
255
|
+
"sm": 12,
|
|
256
|
+
"md": 6,
|
|
257
|
+
"lg": 4,
|
|
258
|
+
"xl": 4
|
|
259
|
+
},
|
|
260
|
+
"_text0": {
|
|
261
|
+
"type": "staticText",
|
|
262
|
+
"text": "",
|
|
263
|
+
"newLine": true,
|
|
264
|
+
"xs": 12,
|
|
265
|
+
"sm": 12,
|
|
266
|
+
"md": 12,
|
|
267
|
+
"lg": 12,
|
|
268
|
+
"xl": 12
|
|
269
|
+
},
|
|
270
|
+
"_text1": {
|
|
271
|
+
"type": "staticText",
|
|
272
|
+
"text": {
|
|
273
|
+
"de": "Token is only needed for Swiss market. How to request a token is described in the readme.",
|
|
274
|
+
"en": "Token is only needed for Swiss market. How to request a token is described in the readme."
|
|
275
|
+
},
|
|
276
|
+
"newLine": true,
|
|
277
|
+
"xs": 12,
|
|
278
|
+
"sm": 12,
|
|
279
|
+
"md": 12,
|
|
280
|
+
"lg": 12,
|
|
281
|
+
"xl": 12
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "apg-info",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.22",
|
|
5
5
|
"news": {
|
|
6
|
+
"0.1.22-alpha.0": {
|
|
7
|
+
"en": "Implement retry mechanism for API calls\nadd turn on/off for peak hours and market prices",
|
|
8
|
+
"de": "Implementieren Retry-Mechanismus für API-Anrufe\nan-/ausschalten für spitzenzeiten und marktpreise",
|
|
9
|
+
"ru": "Внедрение механизма retry для вызовов API\nдобавить включение / выключение в часы пик и рыночные цены",
|
|
10
|
+
"pt": "Implementar mecanismo de repetição para chamadas de API\nadicionar ligar/desligar para horas de pico e preços de mercado",
|
|
11
|
+
"nl": "Implementeer opnieuw proberen mechanisme voor API-oproepen\nhet toevoegen van aan/uit voor piekuren en marktprijzen",
|
|
12
|
+
"fr": "Mettre en place un mécanisme de ré-essai pour les appels API\najouter la mise en marche/arrêt pour les heures de pointe et les prix du marché",
|
|
13
|
+
"it": "Meccanismo di implementazione per le chiamate API\naggiungere turn on/off per ore di punta e prezzi di mercato",
|
|
14
|
+
"es": "Implementar el mecanismo de reingreso para llamadas API\nañadir encendido / apagado para horas pico y precios del mercado",
|
|
15
|
+
"pl": "Wdrożenie mechanizmu ponownego testowania połączeń API\ndodać włączenie / wyłączenie dla godzin szczytu i cen rynkowych",
|
|
16
|
+
"uk": "Впровадження механізму птиху для викликів API\nдодайте зворотній зв’язок за попередні години та ринкові ціни",
|
|
17
|
+
"zh-cn": "执行 API 调用重试机制\n增加高峰时数和市场价格的开启/关闭"
|
|
18
|
+
},
|
|
19
|
+
"0.1.22": {
|
|
20
|
+
"en": "Implement retry mechanism for API calls\nadd turn on/off for peak hours and market prices",
|
|
21
|
+
"de": "Implementieren Retry-Mechanismus für API-Anrufe\nan-/ausschalten für spitzenzeiten und marktpreise",
|
|
22
|
+
"ru": "Внедрение механизма retry для вызовов API\nдобавить включение / выключение в часы пик и рыночные цены",
|
|
23
|
+
"pt": "Implementar mecanismo de repetição para chamadas de API\nadicionar ligar/desligar para horas de pico e preços de mercado",
|
|
24
|
+
"nl": "Implementeer opnieuw proberen mechanisme voor API-oproepen\nhet toevoegen van aan/uit voor piekuren en marktprijzen",
|
|
25
|
+
"fr": "Mettre en place un mécanisme de ré-essai pour les appels API\najouter la mise en marche/arrêt pour les heures de pointe et les prix du marché",
|
|
26
|
+
"it": "Meccanismo di implementazione per le chiamate API\naggiungere turn on/off per ore di punta e prezzi di mercato",
|
|
27
|
+
"es": "Implementar el mecanismo de reingreso para llamadas API\nañadir encendido / apagado para horas pico y precios del mercado",
|
|
28
|
+
"pl": "Wdrożenie mechanizmu ponownego testowania połączeń API\ndodać włączenie / wyłączenie dla godzin szczytu i cen rynkowych",
|
|
29
|
+
"uk": "Впровадження механізму птиху для викликів API\nдодайте зворотній зв’язок за попередні години та ринкові ціни",
|
|
30
|
+
"zh-cn": "执行 API 调用重试机制\n增加高峰时数和市场价格的开启/关闭"
|
|
31
|
+
},
|
|
6
32
|
"0.1.21": {
|
|
7
33
|
"en": "Support quater-hourly tarifs\nBump iobroker-jsonexplorer to 0.2.1",
|
|
8
34
|
"de": "Vier-Stunden-Preise unterstützen\nBump iobroker-jsonexplorer auf 0,2.1",
|
|
@@ -67,32 +93,6 @@
|
|
|
67
93
|
"pl": "prekursor json- explorer v0.2 gotowość\nAksykony bumpy do 1.12.2",
|
|
68
94
|
"uk": "prepeare json-explorer v0.2 готовність\nБампер аксіос до 1.12.2",
|
|
69
95
|
"zh-cn": "预选json-探索器 v0.2 准备状态\n横轴改为1.12.2"
|
|
70
|
-
},
|
|
71
|
-
"0.1.20-alpha.0": {
|
|
72
|
-
"en": "prepeare json-explorer v0.2 readiness\nBump axios to 1.12.2",
|
|
73
|
-
"de": "prepeare json-explorer v0.2 bereitschaft\nBump axios zu 1.12.2",
|
|
74
|
-
"ru": "предварительная готовность json-explorer v0.2\nАксиос накачки до 1.12.2",
|
|
75
|
-
"pt": "prepeare json-explorer v0.2 pronto\nBump axios a 1.12.2",
|
|
76
|
-
"nl": "prepeare json-explorer v0.2 gereedheid\nBump axios naar 1.12.2",
|
|
77
|
-
"fr": "prepeare json-explorer v0.2 préparation\nAxios de pompe au 1.12.2",
|
|
78
|
-
"it": "prepeare json-explorer v0.2 prontezza\nAssio di bump a 1.12.2",
|
|
79
|
-
"es": "prepeare json-explorer v0.2 preparedness\nAxios de bomba a 1.12.2",
|
|
80
|
-
"pl": "prekursor json- explorer v0.2 gotowość\nAksykony bumpy do 1.12.2",
|
|
81
|
-
"uk": "prepeare json-explorer v0.2 готовність\nБампер аксіос до 1.12.2",
|
|
82
|
-
"zh-cn": "预选json-探索器 v0.2 准备状态\n横轴改为1.12.2"
|
|
83
|
-
},
|
|
84
|
-
"0.1.19": {
|
|
85
|
-
"en": "use encrypted token in config",
|
|
86
|
-
"de": "verwendung verschlüsselter token in config",
|
|
87
|
-
"ru": "использовать зашифрованный токен в конфигурации",
|
|
88
|
-
"pt": "usar o token criptografado na configuração",
|
|
89
|
-
"nl": "gebruik versleuteld token in config",
|
|
90
|
-
"fr": "utiliser un jeton chiffré dans config",
|
|
91
|
-
"it": "utilizzare token crittografato in configurazione",
|
|
92
|
-
"es": "uso encriptado token en config",
|
|
93
|
-
"pl": "użyj szyfrowanego symbolu w konfigu",
|
|
94
|
-
"uk": "використовуйте зашифрований токен в конфігурації",
|
|
95
|
-
"zh-cn": "在配置中使用加密的令牌"
|
|
96
96
|
}
|
|
97
97
|
},
|
|
98
98
|
"titleLang": {
|
|
@@ -184,6 +184,8 @@
|
|
|
184
184
|
"threshold": 10,
|
|
185
185
|
"country": "at",
|
|
186
186
|
"forecast": false,
|
|
187
|
+
"peakHours": true,
|
|
188
|
+
"marketPrices": true,
|
|
187
189
|
"calculate": false,
|
|
188
190
|
"feeAbsolute": 1.49,
|
|
189
191
|
"feeRelative": 0,
|
package/main.js
CHANGED
|
@@ -10,9 +10,10 @@ const { version } = require('./package.json');
|
|
|
10
10
|
|
|
11
11
|
// Constants
|
|
12
12
|
const MAX_DELAY = 25000; //25000
|
|
13
|
+
const API_TIMEOUT = 20000; //20000
|
|
13
14
|
|
|
14
15
|
// @ts-expect-error axios.create is ok
|
|
15
|
-
const axiosInstance = axios.create({ timeout:
|
|
16
|
+
const axiosInstance = axios.create({ timeout: API_TIMEOUT });
|
|
16
17
|
|
|
17
18
|
class ApgInfo extends utils.Adapter {
|
|
18
19
|
/**
|
|
@@ -62,6 +63,8 @@ class ApgInfo extends utils.Adapter {
|
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
this.calculate = this.config.calculate ?? false;
|
|
66
|
+
this.peakHours = this.config.peakHours ?? false;
|
|
67
|
+
this.marketPrices = this.config.marketPrices ?? false;
|
|
65
68
|
|
|
66
69
|
if (this.calculate == true) {
|
|
67
70
|
this.feeAbsolute = this.config.feeAbsolute ?? 0;
|
|
@@ -114,8 +117,8 @@ class ApgInfo extends utils.Adapter {
|
|
|
114
117
|
this.log.info(`Delay execution by ${callApiDelay}ms to better spread API calls`);
|
|
115
118
|
await jsonExplorer.sleep(callApiDelay);
|
|
116
119
|
await jsonExplorer.setLastStartTime();
|
|
117
|
-
|
|
118
|
-
const resultMarketPrice = await this.executeMarketPrice(country, forecast);
|
|
120
|
+
|
|
121
|
+
const [resultPeakHours, resultMarketPrice] = await Promise.all([this.executeRequestPeakHours(), this.executeMarketPrice(country, forecast)]);
|
|
119
122
|
|
|
120
123
|
if (resultPeakHours == 'error' || resultMarketPrice == 'error') {
|
|
121
124
|
this.terminate ? this.terminate(utils.EXIT_CODES.UNCAUGHT_EXCEPTION) : process.exit(0);
|
|
@@ -139,22 +142,47 @@ class ApgInfo extends utils.Adapter {
|
|
|
139
142
|
}
|
|
140
143
|
}
|
|
141
144
|
|
|
142
|
-
/*
|
|
143
145
|
/**
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
* @param {
|
|
146
|
+
* Makes an API call with retry logic.
|
|
147
|
+
*
|
|
148
|
+
* @param {string} uri The URI to call.
|
|
149
|
+
* @param {string} methodName The name of the calling method for logging.
|
|
150
|
+
* @param {(response: any) => any} processResponse A function to process the successful response.
|
|
151
|
+
* @returns {Promise<any>} The processed response or null on final server error.
|
|
147
152
|
*/
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
153
|
+
async _apiCallWithRetry(uri, methodName, processResponse) {
|
|
154
|
+
let attempts = 0;
|
|
155
|
+
const maxAttempts = 3;
|
|
156
|
+
let delay = 10 * 1000; // 10 seconds
|
|
157
|
+
|
|
158
|
+
while (attempts < maxAttempts) {
|
|
159
|
+
try {
|
|
160
|
+
const response = await axiosInstance.get(uri);
|
|
161
|
+
if (response?.data == null) {
|
|
162
|
+
throw new Error(`Respone empty for URL ${uri} with status code ${response.status}`);
|
|
163
|
+
}
|
|
164
|
+
this.log.debug(`Response in ${methodName}(): [${response.status}] ${JSON.stringify(response.data)}`);
|
|
165
|
+
console.log(`Response in ${methodName}(): [${response.status}] ${JSON.stringify(response.data)}`);
|
|
166
|
+
return processResponse(response);
|
|
167
|
+
} catch (error) {
|
|
168
|
+
attempts++;
|
|
169
|
+
if (attempts >= maxAttempts) {
|
|
170
|
+
// @ts-expect-error response may exist
|
|
171
|
+
const errorMessage = error.response?.data ? `with response ${JSON.stringify(error.response.data)}` : '';
|
|
172
|
+
this.log.error(`Error in ${methodName}() attempt ${attempts}/${maxAttempts}: ${error} ${errorMessage}`);
|
|
173
|
+
console.error(`Error in ${methodName}() attempt ${attempts}/${maxAttempts}: ${error} ${errorMessage}`);
|
|
174
|
+
// @ts-expect-error response may exist
|
|
175
|
+
if (error.response?.status >= 500) {
|
|
176
|
+
return null; // On final attempt for server errors, resolve with null
|
|
177
|
+
}
|
|
178
|
+
throw error; // Otherwise rethrow
|
|
179
|
+
}
|
|
180
|
+
this.log.info(`Retrying in ${delay / 1000}s for ${methodName}...`);
|
|
181
|
+
await jsonExplorer.sleep(delay);
|
|
182
|
+
delay *= 2; // Exponential backoff (10s, 20s)
|
|
183
|
+
}
|
|
156
184
|
}
|
|
157
|
-
}
|
|
185
|
+
}
|
|
158
186
|
|
|
159
187
|
/**
|
|
160
188
|
* Retrieves peak hours from REST-API
|
|
@@ -163,33 +191,7 @@ class ApgInfo extends utils.Adapter {
|
|
|
163
191
|
let uri = `https://awareness.cloud.apg.at/api/v1/PeakHourStatus`;
|
|
164
192
|
this.log.debug(`API-Call ${uri}`);
|
|
165
193
|
console.log(`API-Call ${uri}`);
|
|
166
|
-
return
|
|
167
|
-
axiosInstance
|
|
168
|
-
.get(uri)
|
|
169
|
-
.then(response => {
|
|
170
|
-
if (!response || !response.data) {
|
|
171
|
-
throw new Error(`getDataPeakHours(): Respone empty for URL ${uri} with status code ${response.status}`);
|
|
172
|
-
} else {
|
|
173
|
-
this.log.debug(`Response in getDataPeakHours(): [${response.status}] ${JSON.stringify(response.data)}`);
|
|
174
|
-
console.log(`Response in getDataPeakHours(): [${response.status}] ${JSON.stringify(response.data)}`);
|
|
175
|
-
resolve(response?.data ?? null);
|
|
176
|
-
}
|
|
177
|
-
})
|
|
178
|
-
.catch(error => {
|
|
179
|
-
if (error?.response?.data) {
|
|
180
|
-
console.error(`Error in getDataPeakHours(): ${error} with response ${JSON.stringify(error.response.data)}`);
|
|
181
|
-
this.log.error(`Error to get peak hours ${error} with response ${JSON.stringify(error.response.data)}`);
|
|
182
|
-
} else {
|
|
183
|
-
console.error(`Error in getDataPeakHours(): ${error}`);
|
|
184
|
-
this.log.error(`Error to get peak hours ${error}`);
|
|
185
|
-
}
|
|
186
|
-
if (error?.response?.status >= 500) {
|
|
187
|
-
resolve(null);
|
|
188
|
-
} else {
|
|
189
|
-
reject(error);
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
});
|
|
194
|
+
return this._apiCallWithRetry(uri, 'getDataPeakHours', response => response?.data ?? null);
|
|
193
195
|
}
|
|
194
196
|
|
|
195
197
|
/**
|
|
@@ -198,7 +200,7 @@ class ApgInfo extends utils.Adapter {
|
|
|
198
200
|
* @param {boolean} tomorrow true means it is the next day, false means today
|
|
199
201
|
* @param {string} country country of the market
|
|
200
202
|
*/
|
|
201
|
-
async
|
|
203
|
+
async getDataExaa(tomorrow, country) {
|
|
202
204
|
let day = cleanDate(new Date());
|
|
203
205
|
if (tomorrow) {
|
|
204
206
|
day = addDays(day, 1);
|
|
@@ -209,33 +211,7 @@ class ApgInfo extends utils.Adapter {
|
|
|
209
211
|
this.log.debug(`API-Call ${uri}`);
|
|
210
212
|
console.log(`API-Call ${uri}`);
|
|
211
213
|
|
|
212
|
-
return
|
|
213
|
-
axiosInstance
|
|
214
|
-
.get(uri)
|
|
215
|
-
.then(response => {
|
|
216
|
-
if (!response || !response.data) {
|
|
217
|
-
throw new Error(`getDataDayAheadExaa(): Respone empty for URL ${uri} with status code ${response.status}`);
|
|
218
|
-
} else {
|
|
219
|
-
this.log.debug(`Response in getDataDayAheadExaa(): [${response.status}] ${JSON.stringify(response.data)}`);
|
|
220
|
-
console.log(`Response in getDataDayAheadExaa(): [${response.status}] ${JSON.stringify(response.data)}`);
|
|
221
|
-
resolve(response?.data?.data ?? null);
|
|
222
|
-
}
|
|
223
|
-
})
|
|
224
|
-
.catch(error => {
|
|
225
|
-
if (error?.response?.data) {
|
|
226
|
-
console.error(`Error in getDataDayAheadExaa(): ${error} with response ${JSON.stringify(error.response.data)}`);
|
|
227
|
-
this.log.error(`Error to get market price (Exaa) ${error} with response ${JSON.stringify(error.response.data)}`);
|
|
228
|
-
} else {
|
|
229
|
-
console.error(`Error in getDataDayAheadExaa(): ${error}`);
|
|
230
|
-
this.log.error(`Error to get market price (Exaa) ${error}`);
|
|
231
|
-
}
|
|
232
|
-
if (error?.response?.status >= 500) {
|
|
233
|
-
resolve(null);
|
|
234
|
-
} else {
|
|
235
|
-
reject(error);
|
|
236
|
-
}
|
|
237
|
-
});
|
|
238
|
-
});
|
|
214
|
+
return this._apiCallWithRetry(uri, 'getDataExaa', response => response?.data?.data ?? null);
|
|
239
215
|
}
|
|
240
216
|
|
|
241
217
|
/**
|
|
@@ -243,7 +219,7 @@ class ApgInfo extends utils.Adapter {
|
|
|
243
219
|
*
|
|
244
220
|
* @param {string} country country of the market
|
|
245
221
|
*/
|
|
246
|
-
async
|
|
222
|
+
async getDataExaa1015(country) {
|
|
247
223
|
country = country.toUpperCase();
|
|
248
224
|
const day = addDays(cleanDate(new Date()), 1);
|
|
249
225
|
|
|
@@ -252,36 +228,11 @@ class ApgInfo extends utils.Adapter {
|
|
|
252
228
|
this.log.debug(`API-Call ${uri}`);
|
|
253
229
|
console.log(`API-Call ${uri}`);
|
|
254
230
|
|
|
255
|
-
return
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
throw new Error(`getDataDayAheadExaa1015(): Respone empty for URL ${uri} with status code ${response.status}`);
|
|
261
|
-
} else {
|
|
262
|
-
this.log.debug(`Response in getDataDayAheadExaa1015(): [${response.status}] ${JSON.stringify(response.data)}`);
|
|
263
|
-
console.log(`Response in getDataDayAheadExaa1015(): [${response.status}] ${JSON.stringify(response.data)}`);
|
|
264
|
-
if (country == 'AT') {
|
|
265
|
-
resolve(response?.data?.AT?.price ?? null);
|
|
266
|
-
} else {
|
|
267
|
-
resolve(response?.data?.DE?.price ?? null);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
})
|
|
271
|
-
.catch(error => {
|
|
272
|
-
if (error?.response?.data) {
|
|
273
|
-
console.error(`Error in getDataDayAheadExaa1015(): ${error} with response ${JSON.stringify(error.response.data)}`);
|
|
274
|
-
this.log.error(`Error to get market price (Exaa1015) ${error} with response ${JSON.stringify(error.response.data)}`);
|
|
275
|
-
} else {
|
|
276
|
-
console.error(`Error in getDataDayAheadExaa1015(): ${error}`);
|
|
277
|
-
this.log.error(`Error to get market price (Exaa1015) ${error}`);
|
|
278
|
-
}
|
|
279
|
-
if (error?.response?.status >= 500) {
|
|
280
|
-
resolve(null);
|
|
281
|
-
} else {
|
|
282
|
-
reject(error);
|
|
283
|
-
}
|
|
284
|
-
});
|
|
231
|
+
return this._apiCallWithRetry(uri, 'getDataExaa1015', response => {
|
|
232
|
+
if (country === 'AT') {
|
|
233
|
+
return response?.data?.AT?.price ?? null;
|
|
234
|
+
}
|
|
235
|
+
return response?.data?.DE?.price ?? null;
|
|
285
236
|
});
|
|
286
237
|
}
|
|
287
238
|
|
|
@@ -291,7 +242,7 @@ class ApgInfo extends utils.Adapter {
|
|
|
291
242
|
* @param {boolean} tomorrow true means it is the next day, false means today
|
|
292
243
|
* @param {string} country country of the market
|
|
293
244
|
*/
|
|
294
|
-
async
|
|
245
|
+
async getDataAwattar(tomorrow, country) {
|
|
295
246
|
const day0 = cleanDate(new Date());
|
|
296
247
|
let start = 0;
|
|
297
248
|
let end = 0;
|
|
@@ -313,33 +264,7 @@ class ApgInfo extends utils.Adapter {
|
|
|
313
264
|
}
|
|
314
265
|
this.log.debug(`API-Call ${uri}`);
|
|
315
266
|
console.log(`API-Call ${uri}`);
|
|
316
|
-
return
|
|
317
|
-
axiosInstance
|
|
318
|
-
.get(uri)
|
|
319
|
-
.then(response => {
|
|
320
|
-
if (!response || !response.data) {
|
|
321
|
-
throw new Error(`getDataDayAheadAwattar(): Respone empty for URL ${uri} with status code ${response.status}`);
|
|
322
|
-
} else {
|
|
323
|
-
this.log.debug(`Response in getDataDayAheadAwattar(): [${response.status}] ${JSON.stringify(response.data)}`);
|
|
324
|
-
console.log(`Response in getDataDayAheadAwattar(): [${response.status}] ${JSON.stringify(response.data)}`);
|
|
325
|
-
resolve(response.data);
|
|
326
|
-
}
|
|
327
|
-
})
|
|
328
|
-
.catch(error => {
|
|
329
|
-
if (error?.response?.data) {
|
|
330
|
-
console.error(`Error in getDataDayAheadAwattar(): ${error} with response ${JSON.stringify(error.response.data)}`);
|
|
331
|
-
this.log.error(`Error to get market price (Awattar) ${error} with response ${JSON.stringify(error.response.data)}`);
|
|
332
|
-
} else {
|
|
333
|
-
console.error(`Error in getDataDayAheadAwattar(): ${error}`);
|
|
334
|
-
this.log.error(`Error to get market price (Awattar) ${error}`);
|
|
335
|
-
}
|
|
336
|
-
if (error?.response?.status >= 500) {
|
|
337
|
-
resolve(null);
|
|
338
|
-
} else {
|
|
339
|
-
reject(error);
|
|
340
|
-
}
|
|
341
|
-
});
|
|
342
|
-
});
|
|
267
|
+
return this._apiCallWithRetry(uri, 'getDataAwattar', response => response.data);
|
|
343
268
|
}
|
|
344
269
|
|
|
345
270
|
/**
|
|
@@ -348,7 +273,7 @@ class ApgInfo extends utils.Adapter {
|
|
|
348
273
|
* @param {boolean} tomorrow means it is the next day, false means today
|
|
349
274
|
* @param {string} country country of the market
|
|
350
275
|
*/
|
|
351
|
-
async
|
|
276
|
+
async getDataEntsoe(tomorrow, country) {
|
|
352
277
|
const url = 'https://web-api.tp.entsoe.eu/api?documentType=A44';
|
|
353
278
|
const securityToken = this.token;
|
|
354
279
|
|
|
@@ -381,29 +306,9 @@ class ApgInfo extends utils.Adapter {
|
|
|
381
306
|
this.log.debug(`API-Call ${uri}`);
|
|
382
307
|
console.log(`API-Call ${uri}`);
|
|
383
308
|
|
|
384
|
-
return
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
.then(response => {
|
|
388
|
-
if (!response || !response.data) {
|
|
389
|
-
throw new Error(`getDataDayAheadEntsoe(): Respone empty for URL ${uri} with status code ${response.status}`);
|
|
390
|
-
} else {
|
|
391
|
-
this.log.debug(`Response in getDataDayAheadEntsoe(): [${response.status}] ${JSON.stringify(response.data)}`);
|
|
392
|
-
console.log(`Response in getDataDayAheadEntsoe(): [${response.status}] ${JSON.stringify(response.data)}`);
|
|
393
|
-
let result = xml2js(response.data);
|
|
394
|
-
resolve(result?.Publication_MarketDocument ?? null);
|
|
395
|
-
}
|
|
396
|
-
})
|
|
397
|
-
.catch(error => {
|
|
398
|
-
if (error?.response?.data) {
|
|
399
|
-
console.error(`Error in getDataDayAheadEntsoe(): ${error} with response ${JSON.stringify(error.response.data)}`);
|
|
400
|
-
this.log.warn(`Error to get market price (Entsoe) ${error} with response ${JSON.stringify(error.response.data)}`);
|
|
401
|
-
} else {
|
|
402
|
-
console.error(`Error in getDataDayAheadEntsoe(): ${error}`);
|
|
403
|
-
this.log.warn(`Error to get market price (Entsoe) ${error}`);
|
|
404
|
-
}
|
|
405
|
-
reject(error);
|
|
406
|
-
});
|
|
309
|
+
return this._apiCallWithRetry(uri, 'getDataEntsoe', response => {
|
|
310
|
+
const result = response?.data == null ? null : xml2js(response.data);
|
|
311
|
+
return result?.Publication_MarketDocument ?? null;
|
|
407
312
|
});
|
|
408
313
|
}
|
|
409
314
|
|
|
@@ -414,6 +319,13 @@ class ApgInfo extends utils.Adapter {
|
|
|
414
319
|
* @param {boolean} forecast also checks 10.15 auction for next day
|
|
415
320
|
*/
|
|
416
321
|
async executeMarketPrice(country, forecast) {
|
|
322
|
+
if (this.marketPrices == false) {
|
|
323
|
+
const statesToDelete = await this.getStatesAsync(`marketprice*`);
|
|
324
|
+
for (const idS in statesToDelete) {
|
|
325
|
+
await this.delObjectAsync(idS);
|
|
326
|
+
}
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
417
329
|
this.log.debug('Execute market price retrieval');
|
|
418
330
|
let source1 = null;
|
|
419
331
|
const configTraversJsonFalse = { replaceName: true, replaceID: true, level: 3, validateAttribute: false };
|
|
@@ -510,9 +422,6 @@ class ApgInfo extends utils.Adapter {
|
|
|
510
422
|
jDay1BelowThresholdq.numberOfSlots = days1Belowq;
|
|
511
423
|
jDay1AboveThresholdq.numberOfSlots = days1Aboveq;
|
|
512
424
|
|
|
513
|
-
await this.createCharts(arrAll0, arrAll1, source1, false);
|
|
514
|
-
await this.createCharts(arrAll0q, arrAll1q, null, true);
|
|
515
|
-
|
|
516
425
|
await jsonExplorer.traverseJson(jDay0, 'marketprice.today', configTraversJsonFalse);
|
|
517
426
|
await jsonExplorer.traverseJson(jDay0BelowThreshold, 'marketprice.belowThreshold.today', configTraversJsonFalse);
|
|
518
427
|
await jsonExplorer.traverseJson(jDay0AboveThreshold, 'marketprice.aboveThreshold.today', configTraversJsonFalse);
|
|
@@ -527,6 +436,12 @@ class ApgInfo extends utils.Adapter {
|
|
|
527
436
|
await jsonExplorer.traverseJson(jDay1BelowThresholdq, 'marketprice_quarter_hourly.belowThreshold.tomorrow', configTraversJsonFalse);
|
|
528
437
|
await jsonExplorer.traverseJson(jDay1AboveThresholdq, 'marketprice_quarter_hourly.aboveThreshold.tomorrow', configTraversJsonFalse);
|
|
529
438
|
|
|
439
|
+
//copy objets to use this for charts later
|
|
440
|
+
const arrAll0Copy = structuredClone(arrAll0);
|
|
441
|
+
const arrAll1Copy = structuredClone(arrAll1);
|
|
442
|
+
const arrAll0qCopy = structuredClone(arrAll0q);
|
|
443
|
+
const arrAll1qCopy = structuredClone(arrAll1q);
|
|
444
|
+
|
|
530
445
|
//now it is time to sort by prcie
|
|
531
446
|
arrBelow0.sort(compareSecondColumn);
|
|
532
447
|
arrBelow1.sort(compareSecondColumn);
|
|
@@ -679,6 +594,9 @@ class ApgInfo extends utils.Adapter {
|
|
|
679
594
|
await jsonExplorer.stateSetCreate('marketprice_quarter_hourly.today.average', 'average', price0Avgq, false);
|
|
680
595
|
await jsonExplorer.stateSetCreate('marketprice_quarter_hourly.tomorrow.average', 'average', price1Avgq, false);
|
|
681
596
|
|
|
597
|
+
await this.createCharts(arrAll0Copy, arrAll1Copy, source1, false);
|
|
598
|
+
await this.createCharts(arrAll0qCopy, arrAll1qCopy, null, true);
|
|
599
|
+
|
|
682
600
|
await jsonExplorer.checkExpire('marketprice.*');
|
|
683
601
|
await jsonExplorer.checkExpire('marketprice_quarter_hourly.*');
|
|
684
602
|
await jsonExplorer.deleteObjectsWithNull('marketprice.*Threshold.*');
|
|
@@ -758,14 +676,13 @@ class ApgInfo extends utils.Adapter {
|
|
|
758
676
|
async _getAndProcessMarketData(country, forecast) {
|
|
759
677
|
let prices0Awattar, prices1Awattar, prices0Exaa, prices1Exaa, prices1Exaa1015;
|
|
760
678
|
|
|
761
|
-
const eXaaToday = await this.
|
|
762
|
-
const eXaaTomorrow = await this.getDataDayAheadExaa(true, country);
|
|
679
|
+
const [eXaaToday, eXaaTomorrow] = await Promise.all([this.getDataExaa(false, country), this.getDataExaa(true, country)]);
|
|
763
680
|
|
|
764
|
-
//check for provider for
|
|
681
|
+
//check for provider for today
|
|
765
682
|
prices0Exaa = eXaaToday?.h ?? null;
|
|
766
683
|
if (prices0Exaa == null) {
|
|
767
684
|
this.log.info(`No market data from Exaa for today, let's try Awattar`);
|
|
768
|
-
prices0Awattar = await this.
|
|
685
|
+
prices0Awattar = await this.getDataAwattar(false, country);
|
|
769
686
|
if (prices0Awattar?.data?.[0]) {
|
|
770
687
|
this.log.info('Todays market data from Awattar available');
|
|
771
688
|
this.log.debug(`Todays market data result from Awattar is: ${JSON.stringify(prices0Awattar)}`);
|
|
@@ -780,14 +697,14 @@ class ApgInfo extends utils.Adapter {
|
|
|
780
697
|
prices1Exaa = eXaaTomorrow?.h ?? null;
|
|
781
698
|
if (prices1Exaa == null) {
|
|
782
699
|
this.log.info(`No market data from Exaa for tomorrow, let's try Awattar`);
|
|
783
|
-
prices1Awattar = await this.
|
|
700
|
+
prices1Awattar = await this.getDataAwattar(true, country);
|
|
784
701
|
if (prices1Awattar?.data?.[0]) {
|
|
785
702
|
this.log.info('Tomorrows market data from Awattar available');
|
|
786
703
|
this.log.debug(`Tomorrow market data result from Awattar is: ${JSON.stringify(prices1Awattar)}`);
|
|
787
704
|
} else {
|
|
788
705
|
if (forecast) {
|
|
789
706
|
this.log.info('No market data from Awattar for tomorrow , last chance Exaa 10.15 auction!');
|
|
790
|
-
const eXaa1015 = await this.
|
|
707
|
+
const eXaa1015 = await this.getDataExaa1015(country);
|
|
791
708
|
prices1Exaa1015 = eXaa1015;
|
|
792
709
|
if (prices1Exaa1015) {
|
|
793
710
|
this.log.info('Market data from Exaa 10.15 auction available');
|
|
@@ -851,19 +768,13 @@ class ApgInfo extends utils.Adapter {
|
|
|
851
768
|
let prices0Entsoe, prices1Entsoe;
|
|
852
769
|
|
|
853
770
|
try {
|
|
854
|
-
[prices0Entsoe, prices1Entsoe] = await Promise.all([
|
|
855
|
-
this.getDataDayAheadEntsoe(false, country),
|
|
856
|
-
this.getDataDayAheadEntsoe(true, country),
|
|
857
|
-
]);
|
|
771
|
+
[prices0Entsoe, prices1Entsoe] = await Promise.all([this.getDataEntsoe(false, country), this.getDataEntsoe(true, country)]);
|
|
858
772
|
} catch (error) {
|
|
859
773
|
if (String(error).includes('read ECONNRESET') || String(error).includes('timeout') || String(error).includes('socket hang up')) {
|
|
860
774
|
this.log.info(`Entsoe request failed. Let's wait 3 minutes and try again...`);
|
|
861
775
|
await jsonExplorer.sleep(3 * 60 * 1000);
|
|
862
776
|
this.log.info(`OK! Let's try again now!`);
|
|
863
|
-
[prices0Entsoe, prices1Entsoe] = await Promise.all([
|
|
864
|
-
this.getDataDayAheadEntsoe(false, country),
|
|
865
|
-
this.getDataDayAheadEntsoe(true, country),
|
|
866
|
-
]);
|
|
777
|
+
[prices0Entsoe, prices1Entsoe] = await Promise.all([this.getDataEntsoe(false, country), this.getDataEntsoe(true, country)]);
|
|
867
778
|
} else {
|
|
868
779
|
throw error;
|
|
869
780
|
}
|
|
@@ -1008,6 +919,13 @@ class ApgInfo extends utils.Adapter {
|
|
|
1008
919
|
* Handles json-object and creates states for peak hours
|
|
1009
920
|
*/
|
|
1010
921
|
async executeRequestPeakHours() {
|
|
922
|
+
if (this.peakHours == false) {
|
|
923
|
+
const statesToDelete = await this.getStatesAsync(`peakTime.*`);
|
|
924
|
+
for (const idS in statesToDelete) {
|
|
925
|
+
await this.delObjectAsync(idS);
|
|
926
|
+
}
|
|
927
|
+
return null;
|
|
928
|
+
}
|
|
1011
929
|
try {
|
|
1012
930
|
let result = await this.getDataPeakHours();
|
|
1013
931
|
this.log.debug(`Peak hour result is: ${JSON.stringify(result)}`);
|
|
@@ -1098,7 +1016,7 @@ class ApgInfo extends utils.Adapter {
|
|
|
1098
1016
|
}
|
|
1099
1017
|
}*/
|
|
1100
1018
|
} catch (error) {
|
|
1101
|
-
let eMsg = `Error in ExecuteRequestPeakHours(): ${error}
|
|
1019
|
+
let eMsg = `Error in ExecuteRequestPeakHours(): ${error}`;
|
|
1102
1020
|
this.log.error(eMsg);
|
|
1103
1021
|
console.error(eMsg);
|
|
1104
1022
|
this.sendSentry(error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.apg-info",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.22",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "HGlab01",
|
|
6
6
|
"email": "myiobrokeradapters@gmail.com"
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"@iobroker/eslint-config": "^2.2.0",
|
|
51
51
|
"@iobroker/testing": "^5.1.1",
|
|
52
52
|
"@types/gulp": "^4.0.17",
|
|
53
|
-
"@types/node": "^24.
|
|
53
|
+
"@types/node": "^24.8.1",
|
|
54
54
|
"@types/proxyquire": "^1.3.31",
|
|
55
55
|
"proxyquire": "^2.1.3"
|
|
56
56
|
},
|