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