emhass 0.10.6__py3-none-any.whl → 0.15.5__py3-none-any.whl
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.
- emhass/command_line.py +1827 -735
- emhass/connection_manager.py +108 -0
- emhass/data/associations.csv +98 -0
- emhass/data/cec_inverters.pbz2 +0 -0
- emhass/data/cec_modules.pbz2 +0 -0
- emhass/data/config_defaults.json +120 -0
- emhass/forecast.py +1482 -622
- emhass/img/emhass_icon.png +0 -0
- emhass/machine_learning_forecaster.py +565 -212
- emhass/machine_learning_regressor.py +162 -122
- emhass/optimization.py +1724 -590
- emhass/retrieve_hass.py +1104 -248
- emhass/static/advanced.html +9 -1
- emhass/static/basic.html +4 -2
- emhass/static/configuration_list.html +48 -0
- emhass/static/configuration_script.js +956 -0
- emhass/static/data/param_definitions.json +592 -0
- emhass/static/script.js +377 -322
- emhass/static/style.css +270 -13
- emhass/templates/configuration.html +77 -0
- emhass/templates/index.html +23 -14
- emhass/templates/template.html +4 -5
- emhass/utils.py +1797 -428
- emhass/web_server.py +850 -448
- emhass/websocket_client.py +224 -0
- emhass-0.15.5.dist-info/METADATA +164 -0
- emhass-0.15.5.dist-info/RECORD +34 -0
- {emhass-0.10.6.dist-info → emhass-0.15.5.dist-info}/WHEEL +1 -2
- emhass-0.15.5.dist-info/entry_points.txt +2 -0
- emhass-0.10.6.dist-info/METADATA +0 -622
- emhass-0.10.6.dist-info/RECORD +0 -26
- emhass-0.10.6.dist-info/entry_points.txt +0 -2
- emhass-0.10.6.dist-info/top_level.txt +0 -1
- {emhass-0.10.6.dist-info → emhass-0.15.5.dist-info/licenses}/LICENSE +0 -0
emhass/static/script.js
CHANGED
|
@@ -1,419 +1,474 @@
|
|
|
1
|
+
//configuration for dynamically processing index page
|
|
2
|
+
//loads either the basic or advance html
|
|
3
|
+
|
|
4
|
+
//used static files
|
|
5
|
+
//advanced.html : template html for displaying all the actions + runtime parameter input
|
|
6
|
+
//basic.html : template html for displaying a minimal view of actions
|
|
7
|
+
|
|
1
8
|
//on page reload get saved data
|
|
2
9
|
window.onload = async function () {
|
|
10
|
+
await loadBasicOrAdvanced();
|
|
3
11
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
12
|
+
//add listener for basic and advanced html switch
|
|
13
|
+
document
|
|
14
|
+
.getElementById("basicOrAdvanced")
|
|
15
|
+
.addEventListener("click", () => SwitchBasicOrAdvanced());
|
|
8
16
|
};
|
|
9
17
|
|
|
10
18
|
//add listeners to buttons (based on page)
|
|
11
|
-
function loadButtons(page) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
19
|
+
async function loadButtons(page) {
|
|
20
|
+
switch (page) {
|
|
21
|
+
case "advanced":
|
|
22
|
+
[
|
|
23
|
+
"dayahead-optim",
|
|
24
|
+
"forecast-model-fit",
|
|
25
|
+
"forecast-model-predict",
|
|
26
|
+
"forecast-model-tune",
|
|
27
|
+
"regressor-model-fit",
|
|
28
|
+
"regressor-model-predict",
|
|
29
|
+
"export-influxdb-to-csv",
|
|
30
|
+
"perfect-optim",
|
|
31
|
+
"publish-data",
|
|
32
|
+
"naive-mpc-optim",
|
|
33
|
+
].forEach((id) =>
|
|
34
|
+
document
|
|
35
|
+
.getElementById(id)
|
|
36
|
+
.addEventListener("click", () => formAction(id, "advanced"))
|
|
37
|
+
);
|
|
38
|
+
["input-plus", "input-minus"].forEach((id) =>
|
|
39
|
+
document
|
|
40
|
+
.getElementById(id)
|
|
41
|
+
.addEventListener("click", () => dictInputs(id))
|
|
42
|
+
);
|
|
43
|
+
document
|
|
44
|
+
.getElementById("input-select")
|
|
45
|
+
.addEventListener("change", () => getSavedData());
|
|
46
|
+
document
|
|
47
|
+
.getElementById("input-clear")
|
|
48
|
+
.addEventListener("click", () => ClearInputData());
|
|
49
|
+
|
|
50
|
+
// Check if InfluxDB is enabled and show/hide export section
|
|
51
|
+
await checkInfluxDBAndShowExport();
|
|
52
|
+
break;
|
|
53
|
+
case "basic":
|
|
54
|
+
document
|
|
55
|
+
.getElementById("dayahead-optim-basic")
|
|
56
|
+
.addEventListener("click", () => formAction("dayahead-optim", "basic"));
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
37
59
|
}
|
|
38
60
|
|
|
39
61
|
//on check present basic or advanced html inside form element
|
|
40
62
|
async function loadBasicOrAdvanced(RequestedPage) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
formContainer.innerHTML = htmlData;
|
|
63
|
-
loadButtons("advanced");
|
|
64
|
-
getSavedData();
|
|
65
|
-
return "advanced";
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
//then check localStorage
|
|
69
|
-
if (testStorage()) {
|
|
70
|
-
if (localStorage.getItem("TabSelection") !== null && localStorage.getItem("TabSelection") === "advanced") { //if advance
|
|
71
|
-
htmlData = await getHTMLData(advencedFile);
|
|
72
|
-
formContainer.innerHTML = htmlData;
|
|
73
|
-
loadButtons("advanced");
|
|
74
|
-
getSavedData();
|
|
75
|
-
return "advanced";
|
|
76
|
-
}
|
|
77
|
-
else { //else run basic (first time)
|
|
78
|
-
htmlData = await getHTMLData(basicFile);
|
|
79
|
-
formContainer.innerHTML = htmlData;
|
|
80
|
-
loadButtons("basic");
|
|
81
|
-
return "basic";
|
|
63
|
+
let basicFile = "basic.html";
|
|
64
|
+
let advencedFile = "advanced.html";
|
|
65
|
+
let formContainer = document.getElementById("TabSelection"); //container element to house basic or advanced data
|
|
66
|
+
let htmlData
|
|
67
|
+
//first check any function arg
|
|
68
|
+
if (arguments.length == 1) {
|
|
69
|
+
switch (RequestedPage) {
|
|
70
|
+
case "basic":
|
|
71
|
+
htmlData = await getHTMLData(basicFile);
|
|
72
|
+
formContainer.innerHTML = htmlData;
|
|
73
|
+
loadButtons("basic"); //load buttons based on basic or advanced
|
|
74
|
+
if (testStorage()) {
|
|
75
|
+
localStorage.setItem("TabSelection", "basic");
|
|
76
|
+
} //remember mode (save to localStorage)
|
|
77
|
+
return "basic"; //return basic to get saved data
|
|
78
|
+
case "advanced":
|
|
79
|
+
htmlData = await getHTMLData(advencedFile);
|
|
80
|
+
formContainer.innerHTML = htmlData;
|
|
81
|
+
loadButtons("advanced");
|
|
82
|
+
if (testStorage()) {
|
|
83
|
+
localStorage.setItem("TabSelection", "advanced");
|
|
82
84
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
+
getSavedData();
|
|
86
|
+
return "advanced";
|
|
87
|
+
default:
|
|
85
88
|
htmlData = await getHTMLData(advencedFile);
|
|
86
89
|
formContainer.innerHTML = htmlData;
|
|
87
90
|
loadButtons("advanced");
|
|
91
|
+
getSavedData();
|
|
88
92
|
return "advanced";
|
|
89
93
|
}
|
|
94
|
+
}
|
|
95
|
+
//then check localStorage
|
|
96
|
+
if (testStorage()) {
|
|
97
|
+
if (
|
|
98
|
+
localStorage.getItem("TabSelection") !== null &&
|
|
99
|
+
localStorage.getItem("TabSelection") === "advanced"
|
|
100
|
+
) {
|
|
101
|
+
//if advance
|
|
102
|
+
let htmlData = await getHTMLData(advencedFile);
|
|
103
|
+
formContainer.innerHTML = htmlData;
|
|
104
|
+
loadButtons("advanced");
|
|
105
|
+
getSavedData();
|
|
106
|
+
return "advanced";
|
|
107
|
+
} else {
|
|
108
|
+
//else run basic (first time)
|
|
109
|
+
htmlData = await getHTMLData(basicFile);
|
|
110
|
+
formContainer.innerHTML = htmlData;
|
|
111
|
+
loadButtons("basic");
|
|
112
|
+
return "basic";
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
//if localStorage not supported, set to advanced page
|
|
116
|
+
htmlData = await getHTMLData(advencedFile);
|
|
117
|
+
formContainer.innerHTML = htmlData;
|
|
118
|
+
loadButtons("advanced");
|
|
119
|
+
return "advanced";
|
|
120
|
+
}
|
|
90
121
|
}
|
|
91
122
|
|
|
92
123
|
//on button press, check current displayed page data and switch
|
|
93
124
|
function SwitchBasicOrAdvanced() {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
125
|
+
let formContainerChildID =
|
|
126
|
+
document.getElementById("TabSelection").firstElementChild.id;
|
|
127
|
+
if (formContainerChildID === "basic") {
|
|
128
|
+
loadBasicOrAdvanced("advanced");
|
|
129
|
+
} else {
|
|
130
|
+
loadBasicOrAdvanced("basic");
|
|
131
|
+
}
|
|
101
132
|
}
|
|
102
133
|
|
|
103
|
-
|
|
104
134
|
//get html data from basic.html or advanced.html
|
|
105
135
|
async function getHTMLData(htmlFile) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
136
|
+
const response = await fetch(`static/` + htmlFile);
|
|
137
|
+
let blob = await response.blob(); //get data blob
|
|
138
|
+
let htmlTemplateData = await new Response(blob).text(); //obtain html from blob
|
|
139
|
+
return htmlTemplateData;
|
|
110
140
|
}
|
|
111
141
|
|
|
112
142
|
//function pushing data via post, triggered by button action
|
|
113
143
|
async function formAction(action, page) {
|
|
144
|
+
let data = {}
|
|
145
|
+
if (page !== "basic") {
|
|
146
|
+
//dont try to get input data in basic mode
|
|
147
|
+
data = inputToJson();
|
|
148
|
+
} else {
|
|
149
|
+
data = {};
|
|
150
|
+
} //send no data
|
|
114
151
|
|
|
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
|
-
showChangeStatus("remove"); //replace loading, show tick or cross with none
|
|
143
|
-
return false
|
|
144
|
-
}
|
|
152
|
+
if (data !== 0) {
|
|
153
|
+
//don't run if there is an error in the input (box/list) Json data
|
|
154
|
+
showChangeStatus("loading", {}); // show loading div for status
|
|
155
|
+
const response = await fetch(`action/` + action, {
|
|
156
|
+
//fetch data from webserver.py
|
|
157
|
+
method: "POST",
|
|
158
|
+
headers: {
|
|
159
|
+
"Content-Type": "application/json",
|
|
160
|
+
'Transfer-Encoding': 'chunked'
|
|
161
|
+
},
|
|
162
|
+
body: JSON.stringify(data), //note that post can only send data via strings
|
|
163
|
+
});
|
|
164
|
+
if (response.status == 201) {
|
|
165
|
+
showChangeStatus(response.status, {});
|
|
166
|
+
if (page !== "basic") {
|
|
167
|
+
saveStorage(); //save to storage if successful
|
|
168
|
+
}
|
|
169
|
+
return true;
|
|
170
|
+
} //if successful
|
|
171
|
+
else {
|
|
172
|
+
showChangeStatus(response.status, await response.json());
|
|
173
|
+
return false;
|
|
174
|
+
} // else get Log data from response
|
|
175
|
+
} else {
|
|
176
|
+
showChangeStatus("remove"); //replace loading, show tick or cross with none
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
145
179
|
}
|
|
146
180
|
|
|
147
181
|
//function in control of status icons of post above
|
|
148
182
|
async function showChangeStatus(status, logJson) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
183
|
+
let loading = document.getElementById("loader"); //element showing statuses
|
|
184
|
+
if (status === "remove") {
|
|
185
|
+
//remove all
|
|
186
|
+
loading.innerHTML = "";
|
|
187
|
+
loading.classList.remove("loading");
|
|
188
|
+
} else if (status === "loading") {
|
|
189
|
+
//show loading logo
|
|
190
|
+
loading.innerHTML = "";
|
|
191
|
+
loading.classList.add("loading"); //append class with loading animation styling
|
|
192
|
+
} else if (status === 201) {
|
|
193
|
+
//if status is 201, then show a tick
|
|
194
|
+
loading.classList.remove("loading");
|
|
195
|
+
loading.innerHTML = `<p class=tick>✓</p>`;
|
|
196
|
+
getTemplate(); //get updated templates
|
|
197
|
+
} else {
|
|
198
|
+
//then show a cross
|
|
199
|
+
loading.classList.remove("loading");
|
|
200
|
+
loading.innerHTML = `<p class=cross>⤬</p>`; //show cross icon to indicate an error
|
|
201
|
+
if (logJson.length != 0 && document.getElementById("alert-text") !== null) {
|
|
202
|
+
document.getElementById("alert-text").textContent =
|
|
203
|
+
"\r\n\u2022 " + logJson.join("\r\n\u2022 "); //show received log data in alert box
|
|
204
|
+
document.getElementById("alert").style.display = "block";
|
|
205
|
+
document.getElementById("alert").style.textAlign = "left";
|
|
173
206
|
}
|
|
207
|
+
}
|
|
174
208
|
}
|
|
175
209
|
|
|
176
210
|
//get rendered html template with containing new table data
|
|
177
211
|
async function getTemplate() {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
212
|
+
//fetch data from webserver.py
|
|
213
|
+
let htmlTemplateData = "";
|
|
214
|
+
let response = await fetch(`template`, {
|
|
215
|
+
method: "GET",
|
|
216
|
+
});
|
|
217
|
+
let blob = await response.blob(); //get data blob
|
|
218
|
+
htmlTemplateData = await new Response(blob).text(); //obtain html from blob
|
|
219
|
+
let templateDiv = document.getElementById("template"); //get template container element to override
|
|
220
|
+
templateDiv.innerHTML = htmlTemplateData; //override container inner html with new data
|
|
221
|
+
let scripts = Array.from(templateDiv.getElementsByTagName("script")); //replace script tags manually
|
|
222
|
+
for (const script of scripts) {
|
|
223
|
+
let TempScript = document.createElement("script");
|
|
224
|
+
TempScript.innerHTML = script.innerHTML;
|
|
225
|
+
script.parentElement.appendChild(TempScript);
|
|
226
|
+
}
|
|
193
227
|
}
|
|
194
228
|
|
|
195
229
|
//test localStorage support
|
|
196
230
|
function testStorage() {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
return false;
|
|
203
|
-
}
|
|
231
|
+
try {
|
|
232
|
+
localStorage.setItem("test", { test: "123" });
|
|
233
|
+
localStorage.removeItem("test");
|
|
234
|
+
return true;
|
|
235
|
+
} catch (error) {
|
|
204
236
|
return false;
|
|
237
|
+
}
|
|
205
238
|
}
|
|
206
239
|
|
|
207
240
|
//function gets saved data (if any)
|
|
208
241
|
function getSavedData() {
|
|
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
|
-
}
|
|
242
|
+
dictInputs(); //check selected current (List or Box) is correct
|
|
243
|
+
if (testStorage()) {
|
|
244
|
+
//if local storage exists and works
|
|
245
|
+
let selectElement = document.getElementById("input-select"); // select button element
|
|
246
|
+
let input_container = document.getElementById("input-container"); // container div containing all dynamic input elements (Box/List)
|
|
247
|
+
if (
|
|
248
|
+
localStorage.getItem("input_container_content") &&
|
|
249
|
+
localStorage.getItem("input_container_content") !== "{}"
|
|
250
|
+
) {
|
|
251
|
+
//If items already stored in local storage, then override default
|
|
252
|
+
if (selectElement.value == "Box") {
|
|
253
|
+
//if Box is selected, show saved json data into box
|
|
254
|
+
document.getElementById("text-area").value = localStorage.getItem(
|
|
255
|
+
"input_container_content"
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
if (selectElement.value == "List") {
|
|
259
|
+
//if List is selected, show saved json data into box
|
|
260
|
+
let storedJson = JSON.parse(
|
|
261
|
+
localStorage.getItem("input_container_content")
|
|
262
|
+
);
|
|
263
|
+
if (Object.keys(storedJson).length > 0) {
|
|
264
|
+
input_container.innerHTML = "";
|
|
265
|
+
for (const ikey in storedJson) {
|
|
266
|
+
input_container.appendChild(
|
|
267
|
+
createInputListDiv(ikey, JSON.stringify(storedJson[ikey]))
|
|
268
|
+
); //call function to present each key as an list div element (with saved values)
|
|
269
|
+
}
|
|
240
270
|
}
|
|
271
|
+
}
|
|
241
272
|
}
|
|
273
|
+
}
|
|
242
274
|
}
|
|
243
275
|
|
|
244
276
|
//using localStorage, store json data from input-list(List)/text-area(from input-box) elements for saved state save on page refresh (will save state on successful post)
|
|
245
277
|
function saveStorage() {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
278
|
+
let data = JSON.stringify(inputToJson());
|
|
279
|
+
if (testStorage() && data != "{}") {
|
|
280
|
+
//don't bother saving if empty and/or storage don't exist
|
|
281
|
+
localStorage.setItem("input_container_content", data);
|
|
282
|
+
}
|
|
251
283
|
}
|
|
252
284
|
|
|
253
285
|
//function gets values from input-list/text-area(from input-box) elements and return json dict object
|
|
254
286
|
function inputToJson() {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
287
|
+
let input_container = document.getElementById("input-container"); //container
|
|
288
|
+
let inputListArr = document.getElementsByClassName("input-list"); //list
|
|
289
|
+
let inputTextArea = document.getElementById("text-area"); //box
|
|
290
|
+
let input_container_child = null;
|
|
291
|
+
input_container_child = input_container.firstElementChild; //work out which element is first inside container div
|
|
292
|
+
let jsonReturnData = {};
|
|
261
293
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
294
|
+
if (input_container_child == null) {
|
|
295
|
+
//if no elements in container then return empty
|
|
296
|
+
return jsonReturnData;
|
|
297
|
+
}
|
|
298
|
+
//if List return box json
|
|
299
|
+
if (
|
|
300
|
+
input_container_child.className == "input-list" &&
|
|
301
|
+
inputListArr.length > 0
|
|
302
|
+
) {
|
|
303
|
+
//if list is first and if list is greater then 0, otherwise give empty dict
|
|
272
304
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
}
|
|
287
|
-
jsonTempData = jsonTempData.concat("}");
|
|
288
|
-
try {
|
|
289
|
-
jsonReturnData = JSON.parse(jsonTempData);
|
|
290
|
-
} catch (error) {
|
|
291
|
-
//if json error, show in alert box
|
|
292
|
-
document.getElementById("alert-text").textContent =
|
|
293
|
-
"\r\n" +
|
|
294
|
-
error +
|
|
295
|
-
"\r\n" +
|
|
296
|
-
"JSON Error: String values may not be wrapped in quotes";
|
|
297
|
-
document.getElementById("alert").style.display = "block";
|
|
298
|
-
document.getElementById("alert").style.textAlign = "center";
|
|
299
|
-
return 0;
|
|
300
|
-
}
|
|
305
|
+
let jsonTempData = "{";
|
|
306
|
+
for (let i = 0; i < inputListArr.length; i++) {
|
|
307
|
+
let key = inputListArr[i].getElementsByClassName("input-key")[0].value;
|
|
308
|
+
let value =
|
|
309
|
+
inputListArr[i].getElementsByClassName("input-value")[0].value;
|
|
310
|
+
//curate a string with list elements to parse into json later
|
|
311
|
+
if (key !== "") {
|
|
312
|
+
//key must not be empty
|
|
313
|
+
if (i !== 0) {
|
|
314
|
+
jsonTempData = jsonTempData.concat(",");
|
|
315
|
+
} //add comma before every parameter, exuding the first
|
|
316
|
+
jsonTempData = jsonTempData.concat('"' + key + '":' + value);
|
|
317
|
+
}
|
|
301
318
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
}
|
|
319
|
+
jsonTempData = jsonTempData.concat("}");
|
|
320
|
+
try {
|
|
321
|
+
jsonReturnData = JSON.parse(jsonTempData);
|
|
322
|
+
} catch (error) {
|
|
323
|
+
//if json error, show in alert box
|
|
324
|
+
document.getElementById("alert-text").textContent =
|
|
325
|
+
"\r\n" +
|
|
326
|
+
error +
|
|
327
|
+
"\r\n" +
|
|
328
|
+
"JSON Error: String values may not be wrapped in quotes";
|
|
329
|
+
document.getElementById("alert").style.display = "block";
|
|
330
|
+
document.getElementById("alert").style.textAlign = "center";
|
|
331
|
+
return 0;
|
|
316
332
|
}
|
|
317
|
-
|
|
333
|
+
}
|
|
334
|
+
//if Box return box json
|
|
335
|
+
if (
|
|
336
|
+
input_container_child.className == "input-box" &&
|
|
337
|
+
inputTextArea.value != ""
|
|
338
|
+
) {
|
|
339
|
+
//if Box is first and text is not empty, otherwise give empty dict
|
|
340
|
+
try {
|
|
341
|
+
jsonReturnData = JSON.parse(inputTextArea.value);
|
|
342
|
+
} catch (error) {
|
|
343
|
+
//if json error, show in alert box
|
|
344
|
+
document.getElementById("alert-text").textContent = "\r\n" + error;
|
|
345
|
+
document.getElementById("alert").style.display = "block";
|
|
346
|
+
return 0;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return jsonReturnData;
|
|
318
350
|
}
|
|
319
351
|
|
|
320
352
|
//function creates input list div element (and pass it values if given)
|
|
321
353
|
function createInputListDiv(ikey, ivalue) {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
354
|
+
let div = document.createElement("div");
|
|
355
|
+
div.className = "input-list";
|
|
356
|
+
div.innerHTML = `
|
|
325
357
|
<input class="input-key" type="text" placeholder="pv_power_forecast" >
|
|
326
358
|
<p>:</p>
|
|
327
359
|
<input class="input-value" type="text" placeholder="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 141.22, 246.18, 513.5, 753.27, 1049.89, 1797.93, 1697.3, 3078.93, 1164.33, 1046.68, 1559.1, 2091.26, 1556.76, 1166.73, 1516.63, 1391.13, 1720.13, 820.75, 804.41, 251.63, 79.25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" >
|
|
328
360
|
`;
|
|
329
361
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
362
|
+
if (ikey && ivalue) {
|
|
363
|
+
//if value and key is provided (from local storage) then add as elements values
|
|
364
|
+
div.getElementsByClassName("input-key")[0].value = String(ikey);
|
|
365
|
+
div.getElementsByClassName("input-value")[0].value = String(ivalue);
|
|
366
|
+
}
|
|
335
367
|
|
|
336
|
-
|
|
368
|
+
return div;
|
|
337
369
|
}
|
|
338
370
|
|
|
339
371
|
//function assigned to control (add and remove) input (Box and List) elements
|
|
340
372
|
function dictInputs(action) {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
373
|
+
let input_container = document.getElementById("input-container"); // container div containing all dynamic input elements
|
|
374
|
+
let selectElement = document.getElementById("input-select"); // select button
|
|
375
|
+
let input_container_child = null;
|
|
376
|
+
let input_container_child_name = null;
|
|
377
|
+
if (input_container.children.length > 0) {
|
|
378
|
+
input_container_child = input_container.firstElementChild; // figure out what is the first element inside of container (ie: "text-area" (input-box) or "input-list" (list))
|
|
379
|
+
input_container_child_name = input_container.firstElementChild.className;
|
|
380
|
+
}
|
|
381
|
+
//if list is selected, remove text-area (from Box) element and replace (with input-list)
|
|
382
|
+
if (selectElement.value == "List") {
|
|
383
|
+
if (action == "input-plus" || input_container_child_name == "input-box") {
|
|
384
|
+
//if plus button pressed, or Box element exists
|
|
385
|
+
if (input_container_child_name == "input-box") {
|
|
386
|
+
input_container_child.remove();
|
|
387
|
+
}
|
|
388
|
+
input_container.appendChild(createInputListDiv(false, false)); //call to createInputListDiv function to craft input-list element (with no values) and append inside container element
|
|
348
389
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
}
|
|
358
|
-
if (action == "input-minus") {
|
|
359
|
-
//minus button pressed, remove input-list element
|
|
360
|
-
if (input_container.children.length > 0) {
|
|
361
|
-
let inputListArr = document.getElementsByClassName("input-list");
|
|
362
|
-
let obj = inputListArr.item(inputListArr.length - 1);
|
|
363
|
-
obj.innerHTML = "";
|
|
364
|
-
obj.remove();
|
|
365
|
-
}
|
|
366
|
-
}
|
|
390
|
+
if (action == "input-minus") {
|
|
391
|
+
//minus button pressed, remove input-list element
|
|
392
|
+
if (input_container.children.length > 0) {
|
|
393
|
+
let inputListArr = document.getElementsByClassName("input-list");
|
|
394
|
+
let obj = inputListArr.item(inputListArr.length - 1);
|
|
395
|
+
obj.innerHTML = "";
|
|
396
|
+
obj.remove();
|
|
397
|
+
}
|
|
367
398
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
399
|
+
}
|
|
400
|
+
//if box is selected, remove input-list elements and replace (with text-area)
|
|
401
|
+
if (selectElement.value == "Box") {
|
|
402
|
+
if (
|
|
403
|
+
input_container_child_name == "input-list" ||
|
|
404
|
+
input_container_child === null
|
|
405
|
+
) {
|
|
406
|
+
// if input list exists or no Box element
|
|
407
|
+
input_container.innerHTML = ""; //remove input-list list elements via erasing container innerHTML
|
|
408
|
+
let div = document.createElement("div"); //add input-box element
|
|
409
|
+
div.className = "input-box";
|
|
410
|
+
div.innerHTML = `
|
|
379
411
|
<textarea id="text-area" rows="30" placeholder="{}"></textarea>
|
|
380
412
|
`;
|
|
381
|
-
|
|
382
|
-
}
|
|
413
|
+
input_container.appendChild(div); //append inside of container element
|
|
383
414
|
}
|
|
415
|
+
}
|
|
384
416
|
}
|
|
385
417
|
|
|
386
418
|
//clear stored input data from localStorage (if any), clear input elements
|
|
387
419
|
async function ClearInputData(id) {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
420
|
+
if (
|
|
421
|
+
testStorage() &&
|
|
422
|
+
localStorage.getItem("input_container_content") !== null
|
|
423
|
+
) {
|
|
424
|
+
localStorage.setItem("input_container_content", "{}");
|
|
425
|
+
}
|
|
426
|
+
ClearInputElements();
|
|
395
427
|
}
|
|
396
428
|
|
|
397
429
|
//clear input elements
|
|
398
430
|
async function ClearInputElements() {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
431
|
+
let selectElement = document.getElementById("input-select");
|
|
432
|
+
let input_container = document.getElementById("input-container");
|
|
433
|
+
if (selectElement.value == "Box") {
|
|
434
|
+
document.getElementById("text-area").value = "{}";
|
|
435
|
+
}
|
|
436
|
+
if (selectElement.value == "List") {
|
|
437
|
+
input_container.innerHTML = "";
|
|
438
|
+
}
|
|
407
439
|
}
|
|
408
440
|
|
|
409
441
|
// //Run day ahead, then publish actions
|
|
410
442
|
// async function DayheadOptimPublish() {
|
|
411
443
|
// response = await formAction("dayahead-optim", "basic")
|
|
412
|
-
// if (response) { //if successful publish data
|
|
444
|
+
// if (response) { //if successful publish data
|
|
413
445
|
// formAction("publish-data", "basic")
|
|
414
446
|
// }
|
|
415
447
|
//}
|
|
416
448
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
449
|
+
// Check if InfluxDB is configured and show/hide export section
|
|
450
|
+
async function checkInfluxDBAndShowExport() {
|
|
451
|
+
try {
|
|
452
|
+
const response = await fetch("/get-config");
|
|
453
|
+
if (response.ok) {
|
|
454
|
+
const config = await response.json();
|
|
455
|
+
const exportSection = document.getElementById("export-influxdb-section");
|
|
456
|
+
if (exportSection) {
|
|
457
|
+
// Show export if InfluxDB is configured (has host and port)
|
|
458
|
+
// Users can export from InfluxDB even if use_influxdb is false for optimization
|
|
459
|
+
const isInfluxDBConfigured =
|
|
460
|
+
config.influxdb_host &&
|
|
461
|
+
config.influxdb_host !== "" &&
|
|
462
|
+
config.influxdb_port;
|
|
463
|
+
|
|
464
|
+
if (isInfluxDBConfigured) {
|
|
465
|
+
exportSection.style.display = "block";
|
|
466
|
+
} else {
|
|
467
|
+
exportSection.style.display = "none";
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
} catch (error) {
|
|
472
|
+
console.error("Error checking InfluxDB configuration:", error);
|
|
473
|
+
}
|
|
474
|
+
}
|