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.
- emhass/forecast.py +3 -3
- emhass/optimization.py +3 -0
- emhass/retrieve_hass.py +1 -1
- emhass/static/advanced.html +32 -0
- emhass/static/basic.html +12 -0
- emhass/static/img/feather-sprite.svg +1 -0
- emhass/static/script.js +425 -0
- emhass/static/style.css +132 -23
- emhass/templates/index.html +36 -340
- emhass/templates/template.html +1 -0
- emhass/web_server.py +66 -23
- {emhass-0.8.0.dist-info → emhass-0.8.2.dist-info}/METADATA +37 -25
- emhass-0.8.2.dist-info/RECORD +23 -0
- emhass-0.8.0.dist-info/RECORD +0 -19
- {emhass-0.8.0.dist-info → emhass-0.8.2.dist-info}/LICENSE +0 -0
- {emhass-0.8.0.dist-info → emhass-0.8.2.dist-info}/WHEEL +0 -0
- {emhass-0.8.0.dist-info → emhass-0.8.2.dist-info}/entry_points.txt +0 -0
- {emhass-0.8.0.dist-info → emhass-0.8.2.dist-info}/top_level.txt +0 -0
emhass/templates/index.html
CHANGED
@@ -6,61 +6,52 @@
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7
7
|
<link rel="stylesheet" type="text/css" href="{{ basename }}/static/style.css">
|
8
8
|
<link rel="icon" href="{{ basename }}/static/img/emhass_logo_short.svg">
|
9
|
+
<script src="{{ basename }}/static/script.js"></script>
|
9
10
|
</head>
|
10
11
|
|
11
12
|
<body style="margin: auto; align-items:center; text-align:center;">
|
13
|
+
|
12
14
|
<div>
|
15
|
+
<!-- Icons by feather https://github.com/feathericons/feather -->
|
16
|
+
<div id="top-links">
|
17
|
+
<!-- advanced or basic page switch -->
|
18
|
+
<a id="basicOrAdvanced" style="margin-right: .7em; cursor: pointer; z-index: 1">
|
19
|
+
<svg class="feather">
|
20
|
+
<use href="{{ basename }}/static/img/feather-sprite.svg#tool" />
|
21
|
+
</svg>
|
22
|
+
</a>
|
23
|
+
<a href="https://emhass.readthedocs.io/en/latest/">
|
24
|
+
<svg class="feather">
|
25
|
+
<use href="{{ basename }}/static/img/feather-sprite.svg#book" />
|
26
|
+
</svg>
|
27
|
+
</a>
|
28
|
+
<a href="https://github.com/davidusb-geek/emhass">
|
29
|
+
<svg style="margin-right: 0px;" class="feather">
|
30
|
+
<use href="{{ basename }}/static/img/feather-sprite.svg#git-branch" />
|
31
|
+
</svg>
|
32
|
+
</a>
|
33
|
+
</div>
|
34
|
+
<!-- Title -->
|
13
35
|
<img src="{{ basename }}/static/img/emhass_icon.png" alt="">
|
14
36
|
<h2>EMHASS: Energy Management Optimization for Home Assistant</h2>
|
15
37
|
</div>
|
38
|
+
|
39
|
+
|
16
40
|
<div class="center">
|
17
|
-
<div class="form">
|
18
|
-
<!--
|
19
|
-
<div class="loading-div">
|
20
|
-
<h4>Use the buttons below to manually launch different optimization tasks</h4>
|
21
|
-
<div id=loader></div> <!-- status dynamic element -->
|
22
|
-
</div>
|
23
|
-
<button type="button" id="perfect-optim" class="button button1">Perfect Optimization</button>
|
24
|
-
<button type="button" id="dayahead-optim" class="button button2">Day-ahead Optimization</button>
|
25
|
-
<button type="button" id="advanced-ml" class="button button3">Advanced ML Tasks</button>
|
26
|
-
<!--ml toggled div button -->
|
27
|
-
<h4>Use the button below to publish the optimized variables at the current timestamp</h4>
|
28
|
-
<button type="button" id="publish-data" class="button button1">Publish Optimization Results</button>
|
29
|
-
<div id="mlDiv" style="display: none;"> <!-- dynamic hidden toggled div for ml -->
|
30
|
-
<h4>Use the buttons below to fit, predict and tune a machine learning model for testing</h4>
|
31
|
-
<button type="button" id="forecast-model-fit" class="button button1">ML forecast model fit</button>
|
32
|
-
<button type="button" id="forecast-model-predict" class="button button2">ML forecast model
|
33
|
-
predict</button>
|
34
|
-
<button type="button" id="forecast-model-tune" class="button button3">ML forecast model tune</button>
|
35
|
-
</div>
|
36
|
-
<!-- -->
|
37
|
-
<!--dynamic input elements section -->
|
38
|
-
<h4>Input Runtime Parameters</h4>
|
39
|
-
<div class="input-button-container">
|
40
|
-
<div class="input-buttons">
|
41
|
-
<button type="button" id="input-plus">+</button>
|
42
|
-
<button type="button" id="input-minus">-</button>
|
43
|
-
<select name="Select" id="input-select">
|
44
|
-
<option value="List" selected>List</option>
|
45
|
-
<option value="Box">Box</option>
|
46
|
-
</select>
|
47
|
-
<button type="button" id="input-clear">Clear</button>
|
48
|
-
</div>
|
49
|
-
</div>
|
50
|
-
<div id="input-container"> <!-- (Box/List) dynamic input elements will be added here -->
|
51
|
-
</div>
|
52
|
-
<!-- -->
|
41
|
+
<div id=TabSelection class="form">
|
42
|
+
<!-- Basic and Advance pages will be loaded here -->
|
53
43
|
</div>
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
44
|
+
</div>
|
45
|
+
<!-- alert box element (for json parse issue) -->
|
46
|
+
<div style="display: none;" id="alert" class="alert">
|
47
|
+
<div>
|
48
|
+
<span onclick="this.parentElement.parentElement.style.display='none';">×</span>
|
49
|
+
<p id="alert-text"></p>
|
60
50
|
</div>
|
61
|
-
|
62
|
-
|
63
|
-
|
51
|
+
</div>
|
52
|
+
<!-- -->
|
53
|
+
<br>
|
54
|
+
<!-- dynamic table/diagram elements section -->
|
64
55
|
</div>
|
65
56
|
<div id="template"> <!-- table/diagram container element -->
|
66
57
|
{% for plot in injection_dict %} <!-- diagrams/tables elements will be added here -->
|
@@ -76,299 +67,4 @@
|
|
76
67
|
</footer>
|
77
68
|
</body>
|
78
69
|
|
79
|
-
|
80
|
-
|
81
|
-
<script>
|
82
|
-
|
83
|
-
//on page reload get saved data
|
84
|
-
window.onload = async function () {
|
85
|
-
getSavedData()
|
86
|
-
};
|
87
|
-
|
88
|
-
//function pushing data via post, triggered by button action
|
89
|
-
async function formAction(action) {
|
90
|
-
var data = inputToJson()
|
91
|
-
if (data !== 0) { //don't run if there is an error in the input (box/list) Json data
|
92
|
-
showChangeStatus("loading", {}) // show loading div for status
|
93
|
-
const response = await fetch(`{{ basename }}/action/${action}`, { //fetch data from webserver.py
|
94
|
-
method: "POST",
|
95
|
-
headers: {
|
96
|
-
"Content-Type": "application/json",
|
97
|
-
},
|
98
|
-
body: JSON.stringify(data), //note that post can only send data via strings
|
99
|
-
})
|
100
|
-
if (response.status == 201) {
|
101
|
-
showChangeStatus(response.status, {})
|
102
|
-
saveStorage() //save to storage if successful
|
103
|
-
} //if successful
|
104
|
-
else { showChangeStatus(response.status, await response.json()) } // else get Log data from response
|
105
|
-
}
|
106
|
-
else {
|
107
|
-
showChangeStatus("remove") //replace loading, show tick or cross with none
|
108
|
-
}
|
109
|
-
}
|
110
|
-
|
111
|
-
//function in control of status icons of post above
|
112
|
-
async function showChangeStatus(status, logJson) {
|
113
|
-
var loading = document.getElementById("loader") //element showing statuses
|
114
|
-
if (status === "remove") { //remove all
|
115
|
-
loading.innerHTML = "";
|
116
|
-
loading.classList.remove("loading");
|
117
|
-
}
|
118
|
-
else if (status === "loading") { //show loading logo
|
119
|
-
loading.innerHTML = "";
|
120
|
-
loading.classList.add("loading"); //append class with loading animation styling
|
121
|
-
}
|
122
|
-
else if (status === 201) { //if status is 201, then show a tick
|
123
|
-
loading.classList.remove("loading")
|
124
|
-
loading.innerHTML = `<p class=tick>✓</p>`
|
125
|
-
getTemplate() //get updated templates
|
126
|
-
|
127
|
-
}
|
128
|
-
else { //then show a cross
|
129
|
-
loading.classList.remove("loading")
|
130
|
-
loading.innerHTML = `<p class=cross>⤬</p>` //show cross icon to indicate an error
|
131
|
-
if (logJson.length != 0) {
|
132
|
-
document.getElementById("alert-text").textContent = "\r\n\u2022 " + logJson.join("\r\n\u2022 ") //show received log data in alert box
|
133
|
-
document.getElementById("alert").style.display = "block";
|
134
|
-
document.getElementById("alert").style.textAlign = "left";
|
135
|
-
}
|
136
|
-
}
|
137
|
-
}
|
138
|
-
|
139
|
-
//get rendered html template with containing new table data
|
140
|
-
async function getTemplate() { //fetch data from webserver.py
|
141
|
-
let htmlTemplateData = ""
|
142
|
-
response = await fetch(`{{ basename }}/template/table-template`, {
|
143
|
-
method: "GET"
|
144
|
-
})
|
145
|
-
blob = await response.blob() //get data blob
|
146
|
-
htmlTemplateData = await new Response(blob).text() //obtain html from blob
|
147
|
-
templateDiv = document.getElementById("template") //get template container element to override
|
148
|
-
templateDiv.innerHTML = htmlTemplateData //override container inner html with new data
|
149
|
-
var scripts = Array.from(templateDiv.getElementsByTagName('script')); //replace script tags manually
|
150
|
-
for (const script of scripts) {
|
151
|
-
var TempScript = document.createElement("script");
|
152
|
-
TempScript.innerHTML = script.innerHTML
|
153
|
-
script.parentElement.appendChild(TempScript)
|
154
|
-
}
|
155
|
-
}
|
156
|
-
|
157
|
-
|
158
|
-
//test localStorage support
|
159
|
-
function testStorage() {
|
160
|
-
try {
|
161
|
-
localStorage.setItem("test", { "test": "123" })
|
162
|
-
localStorage.removeItem("test")
|
163
|
-
return true
|
164
|
-
}
|
165
|
-
catch (error) {
|
166
|
-
return false
|
167
|
-
}
|
168
|
-
return false
|
169
|
-
}
|
170
|
-
|
171
|
-
|
172
|
-
//function gets saved data (if any)
|
173
|
-
function getSavedData() {
|
174
|
-
|
175
|
-
dictInputs() //check selected current (List or Box) is correct
|
176
|
-
if (testStorage()) { //if local storage exists and works
|
177
|
-
let selectElement = document.getElementById('input-select') // select button element
|
178
|
-
var input_container = document.getElementById('input-container'); // container div containing all dynamic input elements (Box/List)
|
179
|
-
if (localStorage.getItem("input_container_content") && localStorage.getItem("input_container_content") !== "{}" ) { //If items already stored in local storage, then override default
|
180
|
-
if (selectElement.value == "Box") { //if Box is selected, show saved json data into box
|
181
|
-
document.getElementById("text-area").value = localStorage.getItem("input_container_content");
|
182
|
-
}
|
183
|
-
if (selectElement.value == "List") { //if List is selected, show saved json data into box
|
184
|
-
storedJson = JSON.parse(localStorage.getItem("input_container_content"));
|
185
|
-
if (Object.keys(storedJson).length > 0) {
|
186
|
-
input_container.innerHTML = "";
|
187
|
-
i = 1;
|
188
|
-
for (const ikey in storedJson) {
|
189
|
-
input_container.appendChild(createInputListDiv(ikey, JSON.stringify(storedJson[ikey]))); //call function to present each key as an list div element (with saved values)
|
190
|
-
}
|
191
|
-
}
|
192
|
-
}
|
193
|
-
}
|
194
|
-
}
|
195
|
-
}
|
196
|
-
|
197
|
-
//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)
|
198
|
-
function saveStorage() {
|
199
|
-
var data = JSON.stringify(inputToJson())
|
200
|
-
if (testStorage() && data != "{}") { //don't bother saving if empty and/or storage don't exist
|
201
|
-
localStorage.setItem("input_container_content", data);
|
202
|
-
}
|
203
|
-
}
|
204
|
-
|
205
|
-
//function gets values from input-list/text-area(from input-box) elements and return json dict object
|
206
|
-
function inputToJson() {
|
207
|
-
var input_container = document.getElementById('input-container'); //container
|
208
|
-
let inputListArr = document.getElementsByClassName('input-list'); //list
|
209
|
-
let inputTextArea = document.getElementById('text-area'); //box
|
210
|
-
let input_container_child = null
|
211
|
-
input_container_child = input_container.firstElementChild; //work out which element is first inside container div
|
212
|
-
var jsonReturnData = {};
|
213
|
-
|
214
|
-
if (input_container_child == null) { //if no elements in container then return empty
|
215
|
-
return (jsonReturnData)
|
216
|
-
};
|
217
|
-
//if List return box json
|
218
|
-
if (input_container_child.className == "input-list" && inputListArr.length > 0) { //if list is first and if list is greater then 0, otherwise give empty dict
|
219
|
-
|
220
|
-
let jsonTempData = "{";
|
221
|
-
for (let i = 0; i < inputListArr.length; i++) {
|
222
|
-
let key = inputListArr[i].getElementsByClassName('input-key')[0].value;
|
223
|
-
var value = inputListArr[i].getElementsByClassName('input-value')[0].value;
|
224
|
-
//curate a string with list elements to parse into json later
|
225
|
-
if (key !== "") { //key must not be empty
|
226
|
-
if (i !== 0) { jsonTempData = jsonTempData.concat(","); } //add comma before every parameter, exuding the first
|
227
|
-
jsonTempData = jsonTempData.concat('"' + key + '":' + value);
|
228
|
-
}
|
229
|
-
}
|
230
|
-
jsonTempData = jsonTempData.concat("}");
|
231
|
-
try {
|
232
|
-
jsonReturnData = JSON.parse(jsonTempData);
|
233
|
-
}
|
234
|
-
catch (error) { //if json error, show in alert box
|
235
|
-
document.getElementById("alert-text").textContent = "\r\n" + error + "\r\n" + "JSON Error: String values may not be wrapped in quotes"
|
236
|
-
document.getElementById("alert").style.display = "block";
|
237
|
-
document.getElementById("alert").style.textAlign = "center";
|
238
|
-
return (0)
|
239
|
-
}
|
240
|
-
}
|
241
|
-
//if Box return box json
|
242
|
-
if (input_container_child.className == "input-box" && inputTextArea.value != "") { //if Box is first and text is not empty, otherwise give empty dict
|
243
|
-
try {
|
244
|
-
jsonReturnData = JSON.parse(inputTextArea.value);
|
245
|
-
}
|
246
|
-
catch (error) { //if json error, show in alert box
|
247
|
-
document.getElementById("alert-text").textContent = "\r\n" + error
|
248
|
-
document.getElementById("alert").style.display = "block";
|
249
|
-
return (0)
|
250
|
-
}
|
251
|
-
}
|
252
|
-
return (jsonReturnData)
|
253
|
-
}
|
254
|
-
|
255
|
-
//function creates input list div element (and pass it values if given)
|
256
|
-
function createInputListDiv(ikey, ivalue) {
|
257
|
-
let div = document.createElement('div');
|
258
|
-
div.className = 'input-list';
|
259
|
-
div.innerHTML = `
|
260
|
-
<input class="input-key" type="text" placeholder="pv_power_forecast" >
|
261
|
-
<p>:</p>
|
262
|
-
<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]" >
|
263
|
-
`;
|
264
|
-
|
265
|
-
if (ikey && ivalue) { //if value and key is provided (from local storage) then add as elements values
|
266
|
-
div.getElementsByClassName("input-key")[0].value = String(ikey);
|
267
|
-
div.getElementsByClassName("input-value")[0].value = String(ivalue);
|
268
|
-
}
|
269
|
-
|
270
|
-
return (div);
|
271
|
-
}
|
272
|
-
|
273
|
-
//function assigned to control (add and remove) input (Box and List) elements
|
274
|
-
function dictInputs(action) {
|
275
|
-
var input_container = document.getElementById('input-container') // container div containing all dynamic input elements
|
276
|
-
let selectElement = document.getElementById('input-select') // select button
|
277
|
-
let input_container_child = null
|
278
|
-
let input_container_child_name = null
|
279
|
-
if (input_container.children.length > 0) {
|
280
|
-
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))
|
281
|
-
input_container_child_name = input_container.firstElementChild.className
|
282
|
-
};
|
283
|
-
//if list is selected, remove text-area (from Box) element and replace (with input-list)
|
284
|
-
if (selectElement.value == "List") {
|
285
|
-
if (action == "input-plus" || input_container_child_name == "input-box") { //if plus button pressed, or Box element exists
|
286
|
-
if (input_container_child_name == "input-box") {
|
287
|
-
input_container_child.remove();
|
288
|
-
}
|
289
|
-
input_container.appendChild(createInputListDiv(false, false)); //call to createInputListDiv function to craft input-list element (with no values) and append inside container element
|
290
|
-
};
|
291
|
-
if (action == "input-minus") { //minus button pressed, remove input-list element
|
292
|
-
if (input_container.children.length > 0) {
|
293
|
-
let inputListArr = document.getElementsByClassName('input-list');
|
294
|
-
let obj = inputListArr.item(inputListArr.length - 1);
|
295
|
-
obj.innerHTML = '';
|
296
|
-
obj.remove();
|
297
|
-
}
|
298
|
-
};
|
299
|
-
}
|
300
|
-
//if box is selected, remove input-list elements and replace (with text-area)
|
301
|
-
if (selectElement.value == "Box") {
|
302
|
-
if (input_container_child_name == "input-list" || input_container_child === null) { // if input list exists or no Box element
|
303
|
-
input_container.innerHTML = ""; //remove input-list list elements via erasing container innerHTML
|
304
|
-
let div = document.createElement('div'); //add input-box element
|
305
|
-
div.className = "input-box";
|
306
|
-
div.innerHTML = `
|
307
|
-
<textarea id="text-area" rows="30" placeholder="{}"></textarea>
|
308
|
-
`;
|
309
|
-
input_container.appendChild(div); //append inside of container element
|
310
|
-
}
|
311
|
-
};
|
312
|
-
}
|
313
|
-
|
314
|
-
|
315
|
-
//hide ml buttons behind Advanced ML tasks button
|
316
|
-
function advancedButtons(id) {
|
317
|
-
mldiv = document.getElementById("mlDiv")
|
318
|
-
if (mldiv.style.display == "none") {
|
319
|
-
mldiv.style.display = "block"
|
320
|
-
}
|
321
|
-
else {
|
322
|
-
mldiv.style.display = "none"
|
323
|
-
}
|
324
|
-
}
|
325
|
-
|
326
|
-
//clear stored input data from localStorage (if any), clear input elements
|
327
|
-
async function ClearInputData(id) {
|
328
|
-
if (testStorage() && localStorage.getItem("input_container_content") !== null) {
|
329
|
-
localStorage.setItem("input_container_content", "{}")
|
330
|
-
}
|
331
|
-
ClearInputElements();
|
332
|
-
|
333
|
-
}
|
334
|
-
|
335
|
-
//clear input elements
|
336
|
-
async function ClearInputElements() {
|
337
|
-
let selectElement = document.getElementById('input-select')
|
338
|
-
var input_container = document.getElementById('input-container');
|
339
|
-
if (selectElement.value == "Box") {
|
340
|
-
document.getElementById("text-area").value = "{}";
|
341
|
-
}
|
342
|
-
if (selectElement.value == "List") {
|
343
|
-
input_container.innerHTML = "";
|
344
|
-
}
|
345
|
-
|
346
|
-
}
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
//add listeners to buttons
|
351
|
-
[
|
352
|
-
"dayahead-optim",
|
353
|
-
"forecast-model-fit",
|
354
|
-
"forecast-model-predict",
|
355
|
-
"forecast-model-tune",
|
356
|
-
"perfect-optim",
|
357
|
-
"publish-data",
|
358
|
-
].forEach((id) => document.getElementById(id).addEventListener('click', () => formAction(id)));
|
359
|
-
[
|
360
|
-
"advanced-ml",
|
361
|
-
].forEach((id) => document.getElementById(id).addEventListener('click', () => advancedButtons(id)));
|
362
|
-
[
|
363
|
-
"input-plus",
|
364
|
-
"input-minus",
|
365
|
-
].forEach((id) => document.getElementById(id).addEventListener('click', () => dictInputs(id)));
|
366
|
-
[
|
367
|
-
"input-select"
|
368
|
-
].forEach((id) => document.getElementById(id).addEventListener('change', () => getSavedData(id)));
|
369
|
-
[
|
370
|
-
"input-clear"
|
371
|
-
].forEach((id) => document.getElementById(id).addEventListener('click', () => ClearInputData(id)));
|
372
|
-
</script>
|
373
|
-
|
374
70
|
</html>
|
emhass/templates/template.html
CHANGED
emhass/web_server.py
CHANGED
@@ -9,7 +9,6 @@ from importlib.metadata import version, PackageNotFoundError
|
|
9
9
|
from pathlib import Path
|
10
10
|
import os, json, argparse, pickle, yaml, logging, re
|
11
11
|
from distutils.util import strtobool
|
12
|
-
import pandas as pd
|
13
12
|
|
14
13
|
from emhass.command_line import set_input_data_dict
|
15
14
|
from emhass.command_line import perfect_forecast_optim, dayahead_forecast_optim, naive_mpc_optim
|
@@ -97,6 +96,8 @@ def action_call(action_name):
|
|
97
96
|
config_path, params = pickle.load(fid)
|
98
97
|
runtimeparams = request.get_json(force=True)
|
99
98
|
params = json.dumps(params)
|
99
|
+
if runtimeparams is not None and runtimeparams != '{}':
|
100
|
+
app.logger.info("Passed runtime parameters: " + str(runtimeparams))
|
100
101
|
runtimeparams = json.dumps(runtimeparams)
|
101
102
|
ActionStr = " >> Setting input data dict"
|
102
103
|
app.logger.info(ActionStr)
|
@@ -203,26 +204,28 @@ if __name__ == "__main__":
|
|
203
204
|
args = parser.parse_args()
|
204
205
|
|
205
206
|
use_options = os.getenv('USE_OPTIONS', default=False)
|
207
|
+
|
208
|
+
#Obtain url and key from ENV or ARG (if any)
|
209
|
+
hass_url = os.getenv("EMHASS_URL", default=args.url)
|
210
|
+
key = os.getenv("SUPERVISOR_TOKEN", default=args.key)
|
211
|
+
if hass_url != "http://supervisor/core/api":
|
212
|
+
key = os.getenv("EMHASS_KEY", key)
|
213
|
+
#If url or key is None, Set as empty string to reduce NoneType errors bellow
|
214
|
+
if key is None: key = ""
|
215
|
+
if hass_url is None: hass_url = ""
|
216
|
+
|
206
217
|
# Define the paths
|
207
218
|
if args.addon==1:
|
208
219
|
OPTIONS_PATH = os.getenv('OPTIONS_PATH', default="/app/options.json")
|
209
220
|
options_json = Path(OPTIONS_PATH)
|
210
221
|
CONFIG_PATH = os.getenv("CONFIG_PATH", default="/app/config_emhass.yaml")
|
211
|
-
|
212
|
-
hass_url = os.getenv("EMHASS_URL", default=args.url)
|
213
|
-
key = os.getenv("SUPERVISOR_TOKEN", default=args.key)
|
214
|
-
if hass_url != "http://supervisor/core/api":
|
215
|
-
key = os.getenv("EMHASS_KEY", key)
|
216
|
-
#If url or key is None, Set as empty string to reduce NoneType errors bellow
|
217
|
-
if key is None: key = ""
|
218
|
-
if hass_url is None: hass_url = ""
|
222
|
+
DATA_PATH = os.getenv("DATA_PATH", default="/app/data/")
|
219
223
|
# Read options info
|
220
224
|
if options_json.exists():
|
221
225
|
with options_json.open('r') as data:
|
222
226
|
options = json.load(data)
|
223
227
|
else:
|
224
228
|
app.logger.error("options.json does not exists")
|
225
|
-
DATA_PATH = os.getenv("DATA_PATH", default="/app/data/")
|
226
229
|
else:
|
227
230
|
if use_options:
|
228
231
|
OPTIONS_PATH = os.getenv('OPTIONS_PATH', default="/app/options.json")
|
@@ -234,9 +237,12 @@ if __name__ == "__main__":
|
|
234
237
|
else:
|
235
238
|
app.logger.error("options.json does not exists")
|
236
239
|
else:
|
237
|
-
options = None
|
238
|
-
|
239
|
-
|
240
|
+
options = None
|
241
|
+
|
242
|
+
#if data path specified by options.json
|
243
|
+
if options_json.exists():
|
244
|
+
if options.get('data_path', None) != None and options.get('data_path', None) != "default":
|
245
|
+
DATA_PATH = options.get('data_path', None);
|
240
246
|
|
241
247
|
config_path = Path(CONFIG_PATH)
|
242
248
|
data_path = Path(DATA_PATH)
|
@@ -250,7 +256,7 @@ if __name__ == "__main__":
|
|
250
256
|
plant_conf = config['plant_conf']
|
251
257
|
else:
|
252
258
|
app.logger.error("Unable to open the default configuration yaml file")
|
253
|
-
|
259
|
+
raise Exception("Failed to open config file, config_path: "+str(config_path))
|
254
260
|
|
255
261
|
params = {}
|
256
262
|
params['retrieve_hass_conf'] = retrieve_hass_conf
|
@@ -271,11 +277,11 @@ if __name__ == "__main__":
|
|
271
277
|
# Some data from options
|
272
278
|
logging_level = options.get('logging_level','INFO')
|
273
279
|
url_from_options = options.get('hass_url', 'empty')
|
274
|
-
if url_from_options == 'empty' or url_from_options == '':
|
275
|
-
url =
|
280
|
+
if url_from_options == 'empty' or url_from_options == '' or url_from_options == "http://supervisor/core/api":
|
281
|
+
url = "http://supervisor/core/api/config"
|
276
282
|
else:
|
277
283
|
hass_url = url_from_options
|
278
|
-
url = hass_url+"
|
284
|
+
url = hass_url+"api/config"
|
279
285
|
token_from_options = options.get('long_lived_token', 'empty')
|
280
286
|
if token_from_options == 'empty' or token_from_options == '':
|
281
287
|
long_lived_token = key
|
@@ -326,17 +332,54 @@ if __name__ == "__main__":
|
|
326
332
|
else: #If addon is false
|
327
333
|
costfun = os.getenv('LOCAL_COSTFUN', default='profit')
|
328
334
|
logging_level = os.getenv('LOGGING_LEVEL', default='INFO')
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
335
|
+
if Path(os.getenv('SECRETS_PATH', default='/app/secrets_emhass.yaml')).is_file():
|
336
|
+
with open(os.getenv('SECRETS_PATH', default='/app/secrets_emhass.yaml'), 'r') as file:
|
337
|
+
params_secrets = yaml.load(file, Loader=yaml.FullLoader)
|
338
|
+
#Check if URL and KEY are provided by file. If not attempt using values from ARG/ENV
|
339
|
+
if params_secrets.get("hass_url", "empty") == "empty" or params_secrets['hass_url'] == "":
|
340
|
+
app.logger.info("No specified Home Assistant URL in secrets_emhass.yaml. Attempting to get from ARG/ENV")
|
341
|
+
if hass_url != "":
|
342
|
+
params_secrets['hass_url'] = hass_url
|
343
|
+
else:
|
344
|
+
app.logger.error("Can not find Home Assistant URL from secrets_emhass.yaml or ARG/ENV")
|
345
|
+
raise Exception("Can not find Home Assistant URL from secrets_emhass.yaml or ARG/ENV")
|
346
|
+
else:
|
347
|
+
hass_url = params_secrets['hass_url']
|
348
|
+
if params_secrets.get("long_lived_token", "empty") == "empty" or params_secrets['long_lived_token'] == "":
|
349
|
+
app.logger.info("No specified Home Assistant KEY in secrets_emhass.yaml. Attempting to get from ARG/ENV")
|
350
|
+
if key != "":
|
351
|
+
params_secrets['long_lived_token'] = key
|
352
|
+
else:
|
353
|
+
app.logger.error("Can not find Home Assistant KEY from secrets_emhass.yaml or ARG/ENV")
|
354
|
+
raise Exception("Can not find Home Assistant KEY from secrets_emhass.yaml or ARG/ENV")
|
355
|
+
else: #If no secrets file try args, else set some defaults
|
356
|
+
app.logger.info("Failed to find secrets_emhass.yaml in directory:" + os.getenv('SECRETS_PATH', default='/app/secrets_emhass.yaml') )
|
357
|
+
app.logger.info("Attempting to use secrets from arguments or environment variables")
|
358
|
+
params_secrets = {}
|
359
|
+
params_secrets['time_zone'] = os.getenv("TIME_ZONE", default="Europe/Paris")
|
360
|
+
params_secrets['lat'] = float(os.getenv("LAT", default="45.83"))
|
361
|
+
params_secrets['lon'] = float(os.getenv("LON", default="6.86"))
|
362
|
+
params_secrets['alt'] = float(os.getenv("ALT", default="4807.8"))
|
363
|
+
if hass_url != "":
|
364
|
+
params_secrets['hass_url'] = hass_url
|
365
|
+
else: #If cant find secrets_emhass and passed url ENV/ARG, then send error
|
366
|
+
app.logger.error("No specified Home Assistant URL")
|
367
|
+
raise Exception("Can not find Home Assistant URL from secrets_emhass.yaml or ARG/ENV")
|
368
|
+
if key != "":
|
369
|
+
params_secrets['long_lived_token'] = key
|
370
|
+
else: #If cant find secrets_emhass and passed key ENV/ARG, then send error
|
371
|
+
app.logger.error("No specified Home Assistant KEY")
|
372
|
+
raise Exception("Can not find Home Assistant KEY from secrets_emhass.yaml or ARG/ENV")
|
333
373
|
# Build params
|
334
374
|
if use_options:
|
335
375
|
params = build_params(params, params_secrets, options, 1, app.logger)
|
336
376
|
else:
|
337
377
|
params = build_params(params, params_secrets, options, args.addon, app.logger)
|
338
|
-
|
339
|
-
|
378
|
+
if os.path.exists(str(data_path)):
|
379
|
+
with open(str(data_path / 'params.pkl'), "wb") as fid:
|
380
|
+
pickle.dump((config_path, params), fid)
|
381
|
+
else:
|
382
|
+
raise Exception("missing: " + str(data_path))
|
340
383
|
|
341
384
|
# Define logger
|
342
385
|
#stream logger
|