emhass 0.8.0__py3-none-any.whl → 0.8.2__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.
@@ -0,0 +1,425 @@
1
+ //on page reload get saved data
2
+ window.onload = async function () {
3
+ pageSelected = await loadBasicOrAdvanced();
4
+
5
+ //add listener for basic and advanced html switch
6
+ document.getElementById("basicOrAdvanced").addEventListener("click", () => SwitchBasicOrAdvanced());
7
+ };
8
+
9
+ //add listeners to buttons (based on page)
10
+ function loadButtons(page) {
11
+ switch (page) {
12
+ case "advanced":
13
+ [
14
+ "dayahead-optim",
15
+ "forecast-model-fit",
16
+ "forecast-model-predict",
17
+ "forecast-model-tune",
18
+ "perfect-optim",
19
+ "publish-data",
20
+ "naive-mpc-optim"
21
+ ].forEach((id) =>
22
+ document.getElementById(id).addEventListener("click", () => formAction(id, "advanced"))
23
+ );
24
+ ["input-plus", "input-minus"].forEach((id) =>
25
+ document.getElementById(id).addEventListener("click", () => dictInputs(id))
26
+ );
27
+ document.getElementById("input-select").addEventListener("change", () => getSavedData());
28
+ document.getElementById("input-clear").addEventListener("click", () => ClearInputData());
29
+ break;
30
+ case "basic":
31
+ document.getElementById("dayahead-optim-basic").addEventListener("click", () => formAction("dayahead-optim", "basic"));
32
+ break;
33
+ }
34
+ }
35
+
36
+ //on check present basic or advanced html inside form element
37
+ async function loadBasicOrAdvanced(RequestedPage) {
38
+ let basicFile = "basic.html";
39
+ let advencedFile = "advanced.html";
40
+ var formContainer = document.getElementById("TabSelection"); //container element to house basic or advanced data
41
+ //first check any function arg
42
+ if (arguments.length == 1) {
43
+ switch (RequestedPage) {
44
+ case "basic":
45
+ htmlData = await getHTMLData(basicFile);
46
+ formContainer.innerHTML = htmlData;
47
+ loadButtons("basic"); //load buttons based on basic or advanced
48
+ if (testStorage()) { localStorage.setItem("TabSelection", "basic") } //remember mode (save to localStorage)
49
+ return "basic"; //return basic to get saved data
50
+ case "advanced":
51
+ htmlData = await getHTMLData(advencedFile);
52
+ formContainer.innerHTML = htmlData;
53
+ loadButtons("advanced");
54
+ if (testStorage()) { localStorage.setItem("TabSelection", "advanced") }
55
+ getSavedData();
56
+ return "advanced";
57
+ default:
58
+ htmlData = await getHTMLData(advencedFile);
59
+ formContainer.innerHTML = htmlData;
60
+ loadButtons("advanced");
61
+ getSavedData();
62
+ return "advanced";
63
+ }
64
+ }
65
+ //then check localStorage
66
+ if (testStorage()) {
67
+ if (localStorage.getItem("TabSelection") !== null && localStorage.getItem("TabSelection") === "advanced") { //if advance
68
+ htmlData = await getHTMLData(advencedFile);
69
+ formContainer.innerHTML = htmlData;
70
+ loadButtons("advanced");
71
+ getSavedData();
72
+ return "advanced";
73
+ }
74
+ else { //else run basic (first time)
75
+ htmlData = await getHTMLData(basicFile);
76
+ formContainer.innerHTML = htmlData;
77
+ loadButtons("basic");
78
+ return "basic";
79
+ }
80
+ } else {
81
+ //if localStorage not supported, set to advanced page
82
+ htmlData = await getHTMLData(advencedFile);
83
+ formContainer.innerHTML = htmlData;
84
+ loadButtons("advanced");
85
+ return "advanced";
86
+ }
87
+ }
88
+
89
+ //on button press, check current displayed page data and switch
90
+ function SwitchBasicOrAdvanced() {
91
+ var formContainerChildID = document.getElementById("TabSelection").firstElementChild.id
92
+ if (formContainerChildID === 'basic') {
93
+ loadBasicOrAdvanced("advanced")
94
+ }
95
+ else {
96
+ loadBasicOrAdvanced("basic")
97
+ }
98
+ }
99
+
100
+ //set current url
101
+ function getHTMLURL() {
102
+ var currentUrl
103
+ if (window.location) {
104
+ currentUrl = window.location.href; //get current url to append
105
+ }
106
+ else { currentUrl = "" }
107
+ return currentUrl
108
+ }
109
+
110
+ //get html data from basic.html or advanced.html
111
+ async function getHTMLData(htmlFile) {
112
+ const response = await fetch(getHTMLURL() + `static/` + htmlFile);
113
+ blob = await response.blob(); //get data blob
114
+ htmlTemplateData = await new Response(blob).text(); //obtain html from blob
115
+ return await htmlTemplateData;
116
+ }
117
+
118
+ //function pushing data via post, triggered by button action
119
+ async function formAction(action, page) {
120
+
121
+ if (page !== "basic") { //dont try to get input data in basic mode
122
+ var data = inputToJson(page);
123
+ }
124
+ else { var data = {} } //send no data
125
+
126
+ if (data !== 0) { //don't run if there is an error in the input (box/list) Json data
127
+ showChangeStatus("loading", {}); // show loading div for status
128
+ const response = await fetch(getHTMLURL() + `action/${action}`, {
129
+ //fetch data from webserver.py
130
+ method: "POST",
131
+ headers: {
132
+ "Content-Type": "application/json",
133
+ },
134
+ body: JSON.stringify(data), //note that post can only send data via strings
135
+ });
136
+ if (response.status == 201) {
137
+ showChangeStatus(response.status, {});
138
+ if (page !== "basic") {
139
+ saveStorage(); //save to storage if successful
140
+ }
141
+ return true
142
+ } //if successful
143
+ else {
144
+ showChangeStatus(response.status, await response.json());
145
+ return false
146
+ } // else get Log data from response
147
+ } else {
148
+ showChangeStatus("remove"); //replace loading, show tick or cross with none
149
+ return false
150
+ }
151
+ }
152
+
153
+ //function in control of status icons of post above
154
+ async function showChangeStatus(status, logJson) {
155
+ var loading = document.getElementById("loader"); //element showing statuses
156
+ if (status === "remove") {
157
+ //remove all
158
+ loading.innerHTML = "";
159
+ loading.classList.remove("loading");
160
+ } else if (status === "loading") {
161
+ //show loading logo
162
+ loading.innerHTML = "";
163
+ loading.classList.add("loading"); //append class with loading animation styling
164
+ } else if (status === 201) {
165
+ //if status is 201, then show a tick
166
+ loading.classList.remove("loading");
167
+ loading.innerHTML = `<p class=tick>&#x2713;</p>`;
168
+ getTemplate(); //get updated templates
169
+ } else {
170
+ //then show a cross
171
+ loading.classList.remove("loading");
172
+ loading.innerHTML = `<p class=cross>&#x292C;</p>`; //show cross icon to indicate an error
173
+ if (logJson.length != 0) {
174
+ document.getElementById("alert-text").textContent =
175
+ "\r\n\u2022 " + logJson.join("\r\n\u2022 "); //show received log data in alert box
176
+ document.getElementById("alert").style.display = "block";
177
+ document.getElementById("alert").style.textAlign = "left";
178
+ }
179
+ }
180
+ }
181
+
182
+ //get rendered html template with containing new table data
183
+ async function getTemplate() {
184
+ //fetch data from webserver.py
185
+ let htmlTemplateData = "";
186
+ response = await fetch(getHTMLURL() + `template/table-template`, {
187
+ method: "GET",
188
+ });
189
+ blob = await response.blob(); //get data blob
190
+ htmlTemplateData = await new Response(blob).text(); //obtain html from blob
191
+ templateDiv = document.getElementById("template"); //get template container element to override
192
+ templateDiv.innerHTML = htmlTemplateData; //override container inner html with new data
193
+ var scripts = Array.from(templateDiv.getElementsByTagName("script")); //replace script tags manually
194
+ for (const script of scripts) {
195
+ var TempScript = document.createElement("script");
196
+ TempScript.innerHTML = script.innerHTML;
197
+ script.parentElement.appendChild(TempScript);
198
+ }
199
+ }
200
+
201
+ //test localStorage support
202
+ function testStorage() {
203
+ try {
204
+ localStorage.setItem("test", { test: "123" });
205
+ localStorage.removeItem("test");
206
+ return true;
207
+ } catch (error) {
208
+ return false;
209
+ }
210
+ return false;
211
+ }
212
+
213
+ //function gets saved data (if any)
214
+ function getSavedData() {
215
+ dictInputs(); //check selected current (List or Box) is correct
216
+ if (testStorage()) {
217
+ //if local storage exists and works
218
+ let selectElement = document.getElementById("input-select"); // select button element
219
+ var input_container = document.getElementById("input-container"); // container div containing all dynamic input elements (Box/List)
220
+ if (
221
+ localStorage.getItem("input_container_content") &&
222
+ localStorage.getItem("input_container_content") !== "{}"
223
+ ) {
224
+ //If items already stored in local storage, then override default
225
+ if (selectElement.value == "Box") {
226
+ //if Box is selected, show saved json data into box
227
+ document.getElementById("text-area").value = localStorage.getItem(
228
+ "input_container_content"
229
+ );
230
+ }
231
+ if (selectElement.value == "List") {
232
+ //if List is selected, show saved json data into box
233
+ storedJson = JSON.parse(
234
+ localStorage.getItem("input_container_content")
235
+ );
236
+ if (Object.keys(storedJson).length > 0) {
237
+ input_container.innerHTML = "";
238
+ i = 1;
239
+ for (const ikey in storedJson) {
240
+ input_container.appendChild(
241
+ createInputListDiv(ikey, JSON.stringify(storedJson[ikey]))
242
+ ); //call function to present each key as an list div element (with saved values)
243
+ }
244
+ }
245
+ }
246
+ }
247
+ }
248
+ }
249
+
250
+ //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)
251
+ function saveStorage() {
252
+ var data = JSON.stringify(inputToJson());
253
+ if (testStorage() && data != "{}") {
254
+ //don't bother saving if empty and/or storage don't exist
255
+ localStorage.setItem("input_container_content", data);
256
+ }
257
+ }
258
+
259
+ //function gets values from input-list/text-area(from input-box) elements and return json dict object
260
+ function inputToJson() {
261
+ var input_container = document.getElementById("input-container"); //container
262
+ let inputListArr = document.getElementsByClassName("input-list"); //list
263
+ let inputTextArea = document.getElementById("text-area"); //box
264
+ let input_container_child = null;
265
+ input_container_child = input_container.firstElementChild; //work out which element is first inside container div
266
+ var jsonReturnData = {};
267
+
268
+ if (input_container_child == null) {
269
+ //if no elements in container then return empty
270
+ return jsonReturnData;
271
+ }
272
+ //if List return box json
273
+ if (
274
+ input_container_child.className == "input-list" &&
275
+ inputListArr.length > 0
276
+ ) {
277
+ //if list is first and if list is greater then 0, otherwise give empty dict
278
+
279
+ let jsonTempData = "{";
280
+ for (let i = 0; i < inputListArr.length; i++) {
281
+ let key = inputListArr[i].getElementsByClassName("input-key")[0].value;
282
+ var value =
283
+ inputListArr[i].getElementsByClassName("input-value")[0].value;
284
+ //curate a string with list elements to parse into json later
285
+ if (key !== "") {
286
+ //key must not be empty
287
+ if (i !== 0) {
288
+ jsonTempData = jsonTempData.concat(",");
289
+ } //add comma before every parameter, exuding the first
290
+ jsonTempData = jsonTempData.concat('"' + key + '":' + value);
291
+ }
292
+ }
293
+ jsonTempData = jsonTempData.concat("}");
294
+ try {
295
+ jsonReturnData = JSON.parse(jsonTempData);
296
+ } catch (error) {
297
+ //if json error, show in alert box
298
+ document.getElementById("alert-text").textContent =
299
+ "\r\n" +
300
+ error +
301
+ "\r\n" +
302
+ "JSON Error: String values may not be wrapped in quotes";
303
+ document.getElementById("alert").style.display = "block";
304
+ document.getElementById("alert").style.textAlign = "center";
305
+ return 0;
306
+ }
307
+ }
308
+ //if Box return box json
309
+ if (
310
+ input_container_child.className == "input-box" &&
311
+ inputTextArea.value != ""
312
+ ) {
313
+ //if Box is first and text is not empty, otherwise give empty dict
314
+ try {
315
+ jsonReturnData = JSON.parse(inputTextArea.value);
316
+ } catch (error) {
317
+ //if json error, show in alert box
318
+ document.getElementById("alert-text").textContent = "\r\n" + error;
319
+ document.getElementById("alert").style.display = "block";
320
+ return 0;
321
+ }
322
+ }
323
+ return jsonReturnData;
324
+ }
325
+
326
+ //function creates input list div element (and pass it values if given)
327
+ function createInputListDiv(ikey, ivalue) {
328
+ let div = document.createElement("div");
329
+ div.className = "input-list";
330
+ div.innerHTML = `
331
+ <input class="input-key" type="text" placeholder="pv_power_forecast" >
332
+ <p>:</p>
333
+ <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]" >
334
+ `;
335
+
336
+ if (ikey && ivalue) {
337
+ //if value and key is provided (from local storage) then add as elements values
338
+ div.getElementsByClassName("input-key")[0].value = String(ikey);
339
+ div.getElementsByClassName("input-value")[0].value = String(ivalue);
340
+ }
341
+
342
+ return div;
343
+ }
344
+
345
+ //function assigned to control (add and remove) input (Box and List) elements
346
+ function dictInputs(action) {
347
+ var input_container = document.getElementById("input-container"); // container div containing all dynamic input elements
348
+ let selectElement = document.getElementById("input-select"); // select button
349
+ let input_container_child = null;
350
+ let input_container_child_name = null;
351
+ if (input_container.children.length > 0) {
352
+ 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))
353
+ input_container_child_name = input_container.firstElementChild.className;
354
+ }
355
+ //if list is selected, remove text-area (from Box) element and replace (with input-list)
356
+ if (selectElement.value == "List") {
357
+ if (action == "input-plus" || input_container_child_name == "input-box") {
358
+ //if plus button pressed, or Box element exists
359
+ if (input_container_child_name == "input-box") {
360
+ input_container_child.remove();
361
+ }
362
+ input_container.appendChild(createInputListDiv(false, false)); //call to createInputListDiv function to craft input-list element (with no values) and append inside container element
363
+ }
364
+ if (action == "input-minus") {
365
+ //minus button pressed, remove input-list element
366
+ if (input_container.children.length > 0) {
367
+ let inputListArr = document.getElementsByClassName("input-list");
368
+ let obj = inputListArr.item(inputListArr.length - 1);
369
+ obj.innerHTML = "";
370
+ obj.remove();
371
+ }
372
+ }
373
+ }
374
+ //if box is selected, remove input-list elements and replace (with text-area)
375
+ if (selectElement.value == "Box") {
376
+ if (
377
+ input_container_child_name == "input-list" ||
378
+ input_container_child === null
379
+ ) {
380
+ // if input list exists or no Box element
381
+ input_container.innerHTML = ""; //remove input-list list elements via erasing container innerHTML
382
+ let div = document.createElement("div"); //add input-box element
383
+ div.className = "input-box";
384
+ div.innerHTML = `
385
+ <textarea id="text-area" rows="30" placeholder="{}"></textarea>
386
+ `;
387
+ input_container.appendChild(div); //append inside of container element
388
+ }
389
+ }
390
+ }
391
+
392
+ //clear stored input data from localStorage (if any), clear input elements
393
+ async function ClearInputData(id) {
394
+ if (
395
+ testStorage() &&
396
+ localStorage.getItem("input_container_content") !== null
397
+ ) {
398
+ localStorage.setItem("input_container_content", "{}");
399
+ }
400
+ ClearInputElements();
401
+ }
402
+
403
+ //clear input elements
404
+ async function ClearInputElements() {
405
+ let selectElement = document.getElementById("input-select");
406
+ var input_container = document.getElementById("input-container");
407
+ if (selectElement.value == "Box") {
408
+ document.getElementById("text-area").value = "{}";
409
+ }
410
+ if (selectElement.value == "List") {
411
+ input_container.innerHTML = "";
412
+ }
413
+ }
414
+
415
+ // //Run day ahead, then publish actions
416
+ // async function DayheadOptimPublish() {
417
+ // response = await formAction("dayahead-optim", "basic")
418
+ // if (response) { //if successful publish data
419
+ // formAction("publish-data", "basic")
420
+ // }
421
+ //}
422
+
423
+
424
+
425
+
emhass/static/style.css CHANGED
@@ -150,6 +150,15 @@ a {
150
150
  text-decoration: none;
151
151
  }
152
152
 
153
+ a:hover {
154
+ color: #251fe3;
155
+ }
156
+
157
+ a:active {
158
+ color: #070f71;
159
+ }
160
+
161
+
153
162
  a:active,
154
163
  a:hover {
155
164
  outline-width: 0;
@@ -407,6 +416,7 @@ h2 {
407
416
  color: #111111;
408
417
  }
409
418
 
419
+
410
420
  button:hover,
411
421
  .alert:hover,
412
422
  select:hover {
@@ -414,6 +424,10 @@ select:hover {
414
424
  background-color: #e1e1e1;
415
425
  }
416
426
 
427
+ button:active {
428
+ background-color: #d7d7db;
429
+ }
430
+
417
431
  button,
418
432
  input,
419
433
  optgroup,
@@ -430,15 +444,6 @@ input {
430
444
  overflow: visible;
431
445
  }
432
446
 
433
- button,
434
- .alert,
435
- select {
436
- text-transform: none;
437
- border-width: 1px;
438
- border-style: solid;
439
- box-shadow: #3d3d3d 3px 3px;
440
- ;
441
- }
442
447
 
443
448
  button,
444
449
  html [type="button"],
@@ -535,6 +540,21 @@ template {
535
540
  display: none;
536
541
  }
537
542
 
543
+ /* feather icons css
544
+ https://github.com/feathericons/feather */
545
+ .feather {
546
+ width: 27px;
547
+ height: 27px;
548
+ stroke: currentColor;
549
+ stroke-width: 2;
550
+ stroke-linecap: round;
551
+ stroke-linejoin: round;
552
+ fill: none;
553
+ margin-right: 12px;
554
+ filter: drop-shadow( #282928 .4px .5px);
555
+ }
556
+
557
+
538
558
  /* -------------- */
539
559
 
540
560
  /* EMHASS added style */
@@ -543,11 +563,45 @@ button,
543
563
  .main-svg,
544
564
  .mystyle,
545
565
  .alert,
566
+ .info,
546
567
  table {
547
568
  border-radius: 7px;
548
569
  /* overflow: visible; */
549
570
  }
550
571
 
572
+ button,
573
+ .alert,
574
+ .info,
575
+ select {
576
+ text-transform: none;
577
+ border-width: 1px;
578
+ border-style: solid;
579
+ box-shadow: #3d3d3d 3px 3px;
580
+ }
581
+
582
+
583
+ .alert, .info {
584
+ max-width: 50%;
585
+ }
586
+
587
+ .info {
588
+ margin: 0 auto;
589
+ margin-top: 2em;
590
+ padding-top: 1em;
591
+ }
592
+
593
+ h2 {
594
+ margin-bottom: .3em;
595
+ }
596
+
597
+ .table_div h4{
598
+ margin-top: .5em;
599
+ }
600
+
601
+ .info p {
602
+ line-height: 2em;
603
+ }
604
+
551
605
  table {
552
606
  max-width: 50%;
553
607
  }
@@ -634,6 +688,13 @@ th {
634
688
  cursor: pointer;
635
689
  }
636
690
 
691
+ #top-links {
692
+ display: flex;
693
+ position: absolute;
694
+ right: 13px;
695
+ top: 13px;
696
+ }
697
+
637
698
  /* Post task status styling */
638
699
  .loading-div {
639
700
  display: flex;
@@ -727,10 +788,24 @@ th {
727
788
  }
728
789
  }
729
790
 
791
+ /* Basic and Advanced fade transitions */
792
+ .TabSelection, #advance, #basic, button, select, .info {
793
+ animation-name: fadeInOpacity;
794
+ animation-iteration-count: 1;
795
+ animation-timing-function: ease-in-out;
796
+ animation-duration: .3s;
797
+ }
798
+
799
+ .input-list, .input-box {
800
+ animation-name: fadeInOpacity;
801
+ animation-iteration-count: 1;
802
+ animation-timing-function: ease-in-out;
803
+ animation-duration: .1s;
804
+ }
805
+
730
806
  /* Alert box styling */
731
807
  .alert {
732
808
  display: box;
733
- max-width: fit-content;
734
809
  overflow-x: auto;
735
810
  align-items: center;
736
811
  align-self: center;
@@ -862,6 +937,10 @@ th {
862
937
  .layer-below {
863
938
  display: none !important;
864
939
  }
940
+
941
+ .info, .alert {
942
+ max-width: 100%;
943
+ }
865
944
  }
866
945
 
867
946
  /* Dark Theme Support */
@@ -872,13 +951,20 @@ th {
872
951
  }
873
952
 
874
953
  img,
875
- figure {
876
- -webkit-filter: invert(90%);
877
- filter: invert(90%);
954
+ figure, svg.main-svg{
955
+ -webkit-filter: invert(.82);
956
+ filter: invert(.82);
957
+ }
958
+
959
+ figure, svg.main-svg{
960
+ border-color: #181818;
961
+ border-style: solid;
962
+ border-width: 1px;
878
963
  }
879
964
 
880
965
  button,
881
966
  .alert,
967
+ .info,
882
968
  select {
883
969
  color: #e1e1e1;
884
970
  background-color: #282928;
@@ -892,22 +978,36 @@ th {
892
978
  background-color: #3d3d3d;
893
979
  }
894
980
 
981
+ button:active {
982
+ background-color: #111111;
983
+ }
984
+
895
985
  h2,
896
986
  kbd,
897
987
  a {
898
988
  background-color: #111111;
899
989
  color: #ccf3ff;
900
- border: rgb(71, 71, 71);
990
+ border: #3f3f3f;
991
+ }
992
+
993
+ a:hover {
994
+ color: #d2e8f0;
901
995
  }
902
996
 
903
- .modebar-btn {
904
- background-color: #e1e1e1;
997
+
998
+ a:active {
999
+ color: #aaccd6;
905
1000
  }
906
1001
 
907
- .main-svg svg,
908
- legend {
909
- background-color: rgb(73, 73, 73, 5%);
910
- background: rgb(73, 73, 73, 5%);
1002
+ .modebar-btn svg path {
1003
+ fill: #111 !important;
1004
+ }
1005
+ .modebar-btn svg {
1006
+ filter:invert(100%) sepia(64%) saturate(2%) hue-rotate(294deg) brightness(85%) contrast(93%) !important
1007
+ }
1008
+ .modebar-btn--logo svg{
1009
+ filter: None !important;
1010
+ /* filter: invert(100%) saturate(100%) brightness(87%) contrast(100%) !important */
911
1011
  }
912
1012
 
913
1013
  .g-gtitle,
@@ -938,9 +1038,18 @@ th {
938
1038
  background-color: #3f3f3f;
939
1039
  }
940
1040
 
941
- .mystyle,
942
- .main-svg {
943
- box-shadow: 0 0 0 1px silver;
1041
+ tr:hover td,
1042
+ th, td:first-child, td:last-child
1043
+ {
1044
+ border-radius: 7px
1045
+ }
1046
+
1047
+ .modebar-group{
1048
+ background-color: #0000 !important;
1049
+ }
1050
+
1051
+ .modebar-btn{
1052
+ background: #3f3f3f;
944
1053
  }
945
1054
 
946
1055
  .input-list input,