emhass 0.8.1__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.
@@ -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
- <!-- action button elements section -->
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
- <!-- alert box element (for json parse issue) -->
55
- <div style="display: none;" id="alert" class="alert">
56
- <div>
57
- <span onclick="this.parentElement.parentElement.style.display='none';">&times;</span>
58
- <p id="alert-text"></p>
59
- </div>
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';">&times;</span>
49
+ <p id="alert-text"></p>
60
50
  </div>
61
- <!-- -->
62
- <br><br><br>
63
- <!-- dynamic table/diagram elements section -->
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>&#x2713;</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>&#x292C;</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>
@@ -1,3 +1,4 @@
1
+ <link rel="stylesheet" type="text/css" href="{{ basename }}/static/style.css">
1
2
  {% for plot in injection_dict %} <!-- diagrams/tables elements will be added here -->
2
3
  <div class="table_div">
3
4
  {{injection_dict[plot]}}
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,25 +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
- #Obtain url and key from ENV or ARG
212
- hass_url = os.getenv("EMHASS_URL", default=args.url)
213
- key = os.getenv("SUPERVISOR_TOKEN", default=args.key)
214
- key = os.getenv("EMHASS_KEY", key)
215
- #If url or key is None, Set as empty string to reduce NoneType errors bellow
216
- if key is None: key = ""
217
- if hass_url is None: hass_url = ""
222
+ DATA_PATH = os.getenv("DATA_PATH", default="/app/data/")
218
223
  # Read options info
219
224
  if options_json.exists():
220
225
  with options_json.open('r') as data:
221
226
  options = json.load(data)
222
227
  else:
223
228
  app.logger.error("options.json does not exists")
224
- DATA_PATH = os.getenv("DATA_PATH", default="/app/data/")
225
229
  else:
226
230
  if use_options:
227
231
  OPTIONS_PATH = os.getenv('OPTIONS_PATH', default="/app/options.json")
@@ -233,9 +237,12 @@ if __name__ == "__main__":
233
237
  else:
234
238
  app.logger.error("options.json does not exists")
235
239
  else:
236
- options = None
237
- CONFIG_PATH = os.getenv("CONFIG_PATH", default="/app/config_emhass.yaml")
238
- DATA_PATH = os.getenv("DATA_PATH", default="/app/data/")
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);
239
246
 
240
247
  config_path = Path(CONFIG_PATH)
241
248
  data_path = Path(DATA_PATH)
@@ -249,7 +256,7 @@ if __name__ == "__main__":
249
256
  plant_conf = config['plant_conf']
250
257
  else:
251
258
  app.logger.error("Unable to open the default configuration yaml file")
252
- app.logger.info("Failed config_path: "+str(config_path))
259
+ raise Exception("Failed to open config file, config_path: "+str(config_path))
253
260
 
254
261
  params = {}
255
262
  params['retrieve_hass_conf'] = retrieve_hass_conf
@@ -325,17 +332,54 @@ if __name__ == "__main__":
325
332
  else: #If addon is false
326
333
  costfun = os.getenv('LOCAL_COSTFUN', default='profit')
327
334
  logging_level = os.getenv('LOGGING_LEVEL', default='INFO')
328
- with open(os.getenv('SECRETS_PATH', default='/app/secrets_emhass.yaml'), 'r') as file:
329
- params_secrets = yaml.load(file, Loader=yaml.FullLoader)
330
- hass_url = params_secrets['hass_url']
331
-
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")
332
373
  # Build params
333
374
  if use_options:
334
375
  params = build_params(params, params_secrets, options, 1, app.logger)
335
376
  else:
336
377
  params = build_params(params, params_secrets, options, args.addon, app.logger)
337
- with open(str(data_path / 'params.pkl'), "wb") as fid:
338
- pickle.dump((config_path, params), fid)
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))
339
383
 
340
384
  # Define logger
341
385
  #stream logger