emhass 0.12.4__py3-none-any.whl → 0.12.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
emhass/web_server.py DELETED
@@ -1,756 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
-
4
- import argparse
5
- import json
6
- import logging
7
- import os
8
- import pickle
9
- import re
10
- import threading
11
- from importlib.metadata import PackageNotFoundError, version
12
- from pathlib import Path
13
- from typing import Optional
14
-
15
- import yaml
16
- from flask import Flask, make_response, request
17
- from flask import logging as log
18
- from jinja2 import Environment, PackageLoader
19
- from waitress import serve
20
-
21
- from emhass.command_line import (
22
- continual_publish,
23
- dayahead_forecast_optim,
24
- forecast_model_fit,
25
- forecast_model_predict,
26
- forecast_model_tune,
27
- naive_mpc_optim,
28
- perfect_forecast_optim,
29
- publish_data,
30
- regressor_model_fit,
31
- regressor_model_predict,
32
- set_input_data_dict,
33
- weather_forecast_cache,
34
- )
35
- from emhass.utils import (
36
- build_config,
37
- build_legacy_config_params,
38
- build_params,
39
- build_secrets,
40
- get_injection_dict,
41
- get_injection_dict_forecast_model_fit,
42
- get_injection_dict_forecast_model_tune,
43
- param_to_config,
44
- )
45
-
46
- # Define the Flask instance
47
- app = Flask(__name__)
48
-
49
- emhass_conf = {}
50
- entity_path = Path
51
- params_secrets = {}
52
- continual_publish_thread = []
53
- injection_dict = {}
54
-
55
-
56
- def create_app(settings_override=None):
57
- """
58
- Create a Flask application.
59
- :param settings_override: Override settings
60
- :return: Flask app
61
- """
62
- global app
63
- gunicorn_logger = logging.getLogger("gunicorn.error")
64
- app.logger.handlers = gunicorn_logger.handlers
65
- app.logger.setLevel(logging.INFO)
66
- main()
67
- return app
68
-
69
-
70
- def checkFileLog(refString=None) -> bool:
71
- """
72
- Check logfile for error, anything after string match if provided.
73
-
74
- :param refString: String to reduce log area to check for errors. Use to reduce log to check anything after string match (ie. an action).
75
- :type refString: str
76
- :return: Boolean return if error was found in logs
77
- :rtype: bool
78
-
79
- """
80
- if refString is not None:
81
- logArray = grabLog(
82
- refString
83
- ) # grab reduced log array (everything after string match)
84
- else:
85
- if (emhass_conf["data_path"] / "actionLogs.txt").exists():
86
- with open(str(emhass_conf["data_path"] / "actionLogs.txt"), "r") as fp:
87
- logArray = fp.readlines()
88
- else:
89
- app.logger.debug("Unable to obtain actionLogs.txt")
90
- for logString in logArray:
91
- if logString.split(" ", 1)[0] == "ERROR":
92
- return True
93
- return False
94
-
95
-
96
- def grabLog(refString) -> list:
97
- """
98
- Find string in logs, append all lines after into list to return.
99
-
100
- :param refString: String used to string match log.
101
- :type refString: str
102
- :return: List of lines in log after string match.
103
- :rtype: list
104
-
105
- """
106
- isFound = []
107
- output = []
108
- if (emhass_conf["data_path"] / "actionLogs.txt").exists():
109
- with open(str(emhass_conf["data_path"] / "actionLogs.txt"), "r") as fp:
110
- logArray = fp.readlines()
111
- # Find all string matches, log key (line Number) in isFound
112
- for x in range(len(logArray) - 1):
113
- if re.search(refString, logArray[x]):
114
- isFound.append(x)
115
- if len(isFound) != 0:
116
- # Use last item in isFound to extract action logs
117
- for x in range(isFound[-1], len(logArray)):
118
- output.append(logArray[x])
119
- return output
120
-
121
-
122
- # Clear the log file
123
- def clearFileLog():
124
- """
125
- Clear the contents of the log file (actionLogs.txt)
126
-
127
- """
128
- if (emhass_conf["data_path"] / "actionLogs.txt").exists():
129
- with open(str(emhass_conf["data_path"] / "actionLogs.txt"), "w") as fp:
130
- fp.truncate()
131
-
132
-
133
- @app.route("/")
134
- @app.route("/index")
135
- def index():
136
- """
137
- Render initial index page and serve to web server.
138
- Appends plot tables saved from previous optimization into index.html, then serves.
139
-
140
- """
141
- app.logger.info("EMHASS server online, serving index.html...")
142
- # Load HTML template
143
- file_loader = PackageLoader("emhass", "templates")
144
- env = Environment(loader=file_loader)
145
- # check if index.html exists
146
- if "index.html" not in env.list_templates():
147
- app.logger.error("Unable to find index.html in emhass module")
148
- return make_response(["ERROR: unable to find index.html in emhass module"], 404)
149
- template = env.get_template("index.html")
150
- # Load cached dict (if exists), to present generated plot tables
151
- if (emhass_conf["data_path"] / "injection_dict.pkl").exists():
152
- with open(str(emhass_conf["data_path"] / "injection_dict.pkl"), "rb") as fid:
153
- injection_dict = pickle.load(fid)
154
- else:
155
- app.logger.info(
156
- "The data container dictionary is empty... Please launch an optimization task"
157
- )
158
- injection_dict = {}
159
-
160
- # replace {{basename}} in html template html with path root
161
- # basename = request.headers.get("X-Ingress-Path", "")
162
- # return make_response(template.render(injection_dict=injection_dict, basename=basename))
163
-
164
- return make_response(template.render(injection_dict=injection_dict))
165
-
166
-
167
- @app.route("/configuration")
168
- def configuration():
169
- """
170
- Configuration page actions:
171
- Render and serve configuration page html
172
-
173
- """
174
- app.logger.info("serving configuration.html...")
175
- # get params
176
- if (emhass_conf["data_path"] / "params.pkl").exists():
177
- with open(str(emhass_conf["data_path"] / "params.pkl"), "rb") as fid:
178
- emhass_conf["config_path"], params = pickle.load(fid)
179
- # Load HTML template
180
- file_loader = PackageLoader("emhass", "templates")
181
- env = Environment(loader=file_loader)
182
- # check if configuration.html exists
183
- if "configuration.html" not in env.list_templates():
184
- app.logger.error("Unable to find configuration.html in emhass module")
185
- return make_response(
186
- ["ERROR: unable to find configuration.html in emhass module"], 404
187
- )
188
- template = env.get_template("configuration.html")
189
- return make_response(template.render(config=params))
190
-
191
-
192
- @app.route("/template", methods=["GET"])
193
- def template_action():
194
- """
195
- template page actions:
196
- Render and serve template html
197
-
198
- """
199
- app.logger.info(" >> Sending rendered template table data")
200
- file_loader = PackageLoader("emhass", "templates")
201
- env = Environment(loader=file_loader)
202
- # Check if template.html exists
203
- if "template.html" not in env.list_templates():
204
- app.logger.error("Unable to find template.html in emhass module")
205
- return make_response(
206
- ["WARNING: unable to find template.html in emhass module"], 404
207
- )
208
- template = env.get_template("template.html")
209
- if (emhass_conf["data_path"] / "injection_dict.pkl").exists():
210
- with open(str(emhass_conf["data_path"] / "injection_dict.pkl"), "rb") as fid:
211
- injection_dict = pickle.load(fid)
212
- else:
213
- app.logger.warning("Unable to obtain plot data from injection_dict.pkl")
214
- app.logger.warning("Try running an launch an optimization task")
215
- injection_dict = {}
216
- return make_response(template.render(injection_dict=injection_dict))
217
-
218
-
219
- @app.route("/get-config", methods=["GET"])
220
- def parameter_get():
221
- """
222
- Get request action that builds, formats and sends config as json (config.json format)
223
-
224
- """
225
- app.logger.debug("Obtaining current saved parameters as config")
226
- # Build config from all possible sources (inc. legacy yaml config)
227
- config = build_config(
228
- emhass_conf,
229
- app.logger,
230
- emhass_conf["defaults_path"],
231
- emhass_conf["config_path"],
232
- emhass_conf["legacy_config_path"],
233
- )
234
- if type(config) is bool and not config:
235
- return make_response(["failed to retrieve default config file"], 500)
236
- # Format parameters in config with params (converting legacy json parameters from options.json if any)
237
- params = build_params(emhass_conf, {}, config, app.logger)
238
- if type(params) is bool and not params:
239
- return make_response(["Unable to obtain associations file"], 500)
240
- # Covert formatted parameters from params back into config.json format
241
- return_config = param_to_config(params, app.logger)
242
- # Send config
243
- return make_response(return_config, 201)
244
-
245
-
246
- # Get default Config
247
- @app.route("/get-config/defaults", methods=["GET"])
248
- def config_get():
249
- """
250
- Get request action, retrieves and sends default configuration
251
-
252
- """
253
- app.logger.debug("Obtaining default parameters")
254
- # Build config, passing only default file
255
- config = build_config(emhass_conf, app.logger, emhass_conf["defaults_path"])
256
- if type(config) is bool and not config:
257
- return make_response(["failed to retrieve default config file"], 500)
258
- # Format parameters in config with params
259
- params = build_params(emhass_conf, {}, config, app.logger)
260
- if type(params) is bool and not params:
261
- return make_response(["Unable to obtain associations file"], 500)
262
- # Covert formatted parameters from params back into config.json format
263
- return_config = param_to_config(params, app.logger)
264
- # Send params
265
- return make_response(return_config, 201)
266
-
267
-
268
- # Get YAML-to-JSON config
269
- @app.route("/get-json", methods=["POST"])
270
- def json_convert():
271
- """
272
- Post request action, receives yaml config (config_emhass.yaml or EMHASS-Add-on config page) and converts to config json format.
273
-
274
- """
275
- app.logger.info("Attempting to convert YAML to JSON")
276
- data = request.get_data()
277
- yaml_config = yaml.safe_load(data)
278
-
279
- # If filed to Parse YAML
280
- if yaml_config is None:
281
- return make_response(["failed to Parse YAML from data"], 400)
282
- # Test YAML is legacy config format (from config_emhass.yaml)
283
- test_legacy_config = build_legacy_config_params(
284
- emhass_conf, yaml_config, app.logger
285
- )
286
- if test_legacy_config:
287
- yaml_config = test_legacy_config
288
- # Format YAML to params (format params. check if params match legacy option.json format)
289
- params = build_params(emhass_conf, {}, yaml_config, app.logger)
290
- if type(params) is bool and not params:
291
- return make_response(["Unable to obtain associations file"], 500)
292
- # Covert formatted parameters from params back into config.json format
293
- config = param_to_config(params, app.logger)
294
- # convert json to str
295
- config = json.dumps(config)
296
-
297
- # Send params
298
- return make_response(config, 201)
299
-
300
-
301
- @app.route("/set-config", methods=["POST"])
302
- def parameter_set():
303
- """
304
- Receive JSON config, and save config to file (config.json and param.pkl)
305
-
306
- """
307
- config = {}
308
- if not emhass_conf["defaults_path"]:
309
- return make_response(["Unable to Obtain defaults_path from emhass_conf"], 500)
310
- if not emhass_conf["config_path"]:
311
- return make_response(["Unable to Obtain config_path from emhass_conf"], 500)
312
-
313
- # Load defaults as a reference point (for sorting) and a base to override
314
- if (
315
- os.path.exists(emhass_conf["defaults_path"])
316
- and Path(emhass_conf["defaults_path"]).is_file()
317
- ):
318
- with emhass_conf["defaults_path"].open("r") as data:
319
- config = json.load(data)
320
- else:
321
- app.logger.warning(
322
- "Unable to obtain default config. only parameters passed from request will be saved to config.json"
323
- )
324
-
325
- # Retrieve sent config json
326
- request_data = request.get_json(force=True)
327
-
328
- # check if data is empty
329
- if len(request_data) == 0:
330
- return make_response(["failed to retrieve config json"], 400)
331
-
332
- # Format config by converting to params (format params. check if params match legacy option.json format. If so format)
333
- params = build_params(emhass_conf, params_secrets, request_data, app.logger)
334
- if type(params) is bool and not params:
335
- return make_response(["Unable to obtain associations file"], 500)
336
-
337
- # Covert formatted parameters from params back into config.json format.
338
- # Overwrite existing default parameters in config
339
- config.update(param_to_config(params, app.logger))
340
-
341
- # Save config to config.json
342
- if os.path.exists(emhass_conf["config_path"].parent):
343
- with emhass_conf["config_path"].open("w") as f:
344
- json.dump(config, f, indent=4)
345
- else:
346
- return make_response(["Unable to save config file"], 500)
347
- request_data
348
-
349
- # Save params with updated config
350
- if os.path.exists(emhass_conf["data_path"]):
351
- with open(str(emhass_conf["data_path"] / "params.pkl"), "wb") as fid:
352
- pickle.dump(
353
- (
354
- emhass_conf["config_path"],
355
- build_params(emhass_conf, params_secrets, config, app.logger),
356
- ),
357
- fid,
358
- )
359
- else:
360
- return make_response(["Unable to save params file, missing data_path"], 500)
361
-
362
- app.logger.info("Saved parameters from webserver")
363
- return make_response({}, 201)
364
-
365
-
366
- @app.route("/action/<action_name>", methods=["POST"])
367
- def action_call(action_name):
368
- """
369
- Receive Post action, run action according to passed slug(action_name) (e.g. /action/publish-data)
370
-
371
- :param action_name: Slug/Action string corresponding to which action to take
372
- :type action_name: String
373
-
374
- """
375
- global continual_publish_thread
376
- global injection_dict
377
-
378
- # Setting up parameters
379
- # Params
380
- ActionStr = " >> Obtaining params: "
381
- app.logger.info(ActionStr)
382
- if (emhass_conf["data_path"] / "params.pkl").exists():
383
- with open(str(emhass_conf["data_path"] / "params.pkl"), "rb") as fid:
384
- emhass_conf["config_path"], params = pickle.load(fid)
385
- # Set local costfun variable
386
- if params.get("optim_conf", None) is not None:
387
- costfun = params["optim_conf"].get("costfun", "profit")
388
- params = json.dumps(params)
389
- else:
390
- app.logger.error("Unable to find params.pkl file")
391
- return make_response(grabLog(ActionStr), 400)
392
- # Runtime
393
- runtimeparams = request.get_json(force=True, silent=True)
394
- if runtimeparams is not None:
395
- if runtimeparams != "{}":
396
- app.logger.info("Passed runtime parameters: " + str(runtimeparams))
397
- else:
398
- app.logger.warning("Unable to parse runtime parameters")
399
- runtimeparams = {}
400
- runtimeparams = json.dumps(runtimeparams)
401
-
402
- # weather-forecast-cache (check before set_input_data_dict)
403
- if action_name == "weather-forecast-cache":
404
- ActionStr = " >> Performing weather forecast, try to caching result"
405
- app.logger.info(ActionStr)
406
- weather_forecast_cache(emhass_conf, params, runtimeparams, app.logger)
407
- msg = "EMHASS >> Weather Forecast has run and results possibly cached... \n"
408
- if not checkFileLog(ActionStr):
409
- return make_response(msg, 201)
410
- return make_response(grabLog(ActionStr), 400)
411
-
412
- ActionStr = " >> Setting input data dict"
413
- app.logger.info(ActionStr)
414
- input_data_dict = set_input_data_dict(
415
- emhass_conf, costfun, params, runtimeparams, action_name, app.logger
416
- )
417
- if not input_data_dict:
418
- return make_response(grabLog(ActionStr), 400)
419
-
420
- # If continual_publish is True, start thread with loop function
421
- if len(continual_publish_thread) == 0 and input_data_dict["retrieve_hass_conf"].get(
422
- "continual_publish", False
423
- ):
424
- # Start Thread
425
- continualLoop = threading.Thread(
426
- name="continual_publish",
427
- target=continual_publish,
428
- args=[input_data_dict, entity_path, app.logger],
429
- )
430
- continualLoop.start()
431
- continual_publish_thread.append(continualLoop)
432
-
433
- # Run action based on POST request
434
- # If error in log when running action, return actions log (list) as response. (Using ActionStr as a reference of the action start in the log)
435
- # publish-data
436
- if action_name == "publish-data":
437
- ActionStr = " >> Publishing data..."
438
- app.logger.info(ActionStr)
439
- _ = publish_data(input_data_dict, app.logger)
440
- msg = "EMHASS >> Action publish-data executed... \n"
441
- if not checkFileLog(ActionStr):
442
- return make_response(msg, 201)
443
- return make_response(grabLog(ActionStr), 400)
444
- # perfect-optim
445
- elif action_name == "perfect-optim":
446
- ActionStr = " >> Performing perfect optimization..."
447
- app.logger.info(ActionStr)
448
- opt_res = perfect_forecast_optim(input_data_dict, app.logger)
449
- injection_dict = get_injection_dict(opt_res)
450
- with open(str(emhass_conf["data_path"] / "injection_dict.pkl"), "wb") as fid:
451
- pickle.dump(injection_dict, fid)
452
- msg = "EMHASS >> Action perfect-optim executed... \n"
453
- if not checkFileLog(ActionStr):
454
- return make_response(msg, 201)
455
- return make_response(grabLog(ActionStr), 400)
456
- # dayahead-optim
457
- elif action_name == "dayahead-optim":
458
- ActionStr = " >> Performing dayahead optimization..."
459
- app.logger.info(ActionStr)
460
- opt_res = dayahead_forecast_optim(input_data_dict, app.logger)
461
- injection_dict = get_injection_dict(opt_res)
462
- with open(str(emhass_conf["data_path"] / "injection_dict.pkl"), "wb") as fid:
463
- pickle.dump(injection_dict, fid)
464
- msg = "EMHASS >> Action dayahead-optim executed... \n"
465
- if not checkFileLog(ActionStr):
466
- return make_response(msg, 201)
467
- return make_response(grabLog(ActionStr), 400)
468
- # naive-mpc-optim
469
- elif action_name == "naive-mpc-optim":
470
- ActionStr = " >> Performing naive MPC optimization..."
471
- app.logger.info(ActionStr)
472
- opt_res = naive_mpc_optim(input_data_dict, app.logger)
473
- injection_dict = get_injection_dict(opt_res)
474
- with open(str(emhass_conf["data_path"] / "injection_dict.pkl"), "wb") as fid:
475
- pickle.dump(injection_dict, fid)
476
- msg = "EMHASS >> Action naive-mpc-optim executed... \n"
477
- if not checkFileLog(ActionStr):
478
- return make_response(msg, 201)
479
- return make_response(grabLog(ActionStr), 400)
480
- # forecast-model-fit
481
- elif action_name == "forecast-model-fit":
482
- ActionStr = " >> Performing a machine learning forecast model fit..."
483
- app.logger.info(ActionStr)
484
- df_fit_pred, _, mlf = forecast_model_fit(input_data_dict, app.logger)
485
- injection_dict = get_injection_dict_forecast_model_fit(df_fit_pred, mlf)
486
- with open(str(emhass_conf["data_path"] / "injection_dict.pkl"), "wb") as fid:
487
- pickle.dump(injection_dict, fid)
488
- msg = "EMHASS >> Action forecast-model-fit executed... \n"
489
- if not checkFileLog(ActionStr):
490
- return make_response(msg, 201)
491
- return make_response(grabLog(ActionStr), 400)
492
- # forecast-model-predict
493
- elif action_name == "forecast-model-predict":
494
- ActionStr = " >> Performing a machine learning forecast model predict..."
495
- app.logger.info(ActionStr)
496
- df_pred = forecast_model_predict(input_data_dict, app.logger)
497
- if df_pred is None:
498
- return make_response(grabLog(ActionStr), 400)
499
- table1 = df_pred.reset_index().to_html(classes="mystyle", index=False)
500
- injection_dict = {}
501
- injection_dict["title"] = (
502
- "<h2>Custom machine learning forecast model predict</h2>"
503
- )
504
- injection_dict["subsubtitle0"] = (
505
- "<h4>Performed a prediction using a pre-trained model</h4>"
506
- )
507
- injection_dict["table1"] = table1
508
- with open(str(emhass_conf["data_path"] / "injection_dict.pkl"), "wb") as fid:
509
- pickle.dump(injection_dict, fid)
510
- msg = "EMHASS >> Action forecast-model-predict executed... \n"
511
- if not checkFileLog(ActionStr):
512
- return make_response(msg, 201)
513
- return make_response(grabLog(ActionStr), 400)
514
- # forecast-model-tune
515
- elif action_name == "forecast-model-tune":
516
- ActionStr = " >> Performing a machine learning forecast model tune..."
517
- app.logger.info(ActionStr)
518
- df_pred_optim, mlf = forecast_model_tune(input_data_dict, app.logger)
519
- if df_pred_optim is None or mlf is None:
520
- return make_response(grabLog(ActionStr), 400)
521
- injection_dict = get_injection_dict_forecast_model_tune(df_pred_optim, mlf)
522
- with open(str(emhass_conf["data_path"] / "injection_dict.pkl"), "wb") as fid:
523
- pickle.dump(injection_dict, fid)
524
- msg = "EMHASS >> Action forecast-model-tune executed... \n"
525
- if not checkFileLog(ActionStr):
526
- return make_response(msg, 201)
527
- return make_response(grabLog(ActionStr), 400)
528
- # regressor-model-fit
529
- elif action_name == "regressor-model-fit":
530
- ActionStr = " >> Performing a machine learning regressor fit..."
531
- app.logger.info(ActionStr)
532
- regressor_model_fit(input_data_dict, app.logger)
533
- msg = "EMHASS >> Action regressor-model-fit executed... \n"
534
- if not checkFileLog(ActionStr):
535
- return make_response(msg, 201)
536
- return make_response(grabLog(ActionStr), 400)
537
- # regressor-model-predict
538
- elif action_name == "regressor-model-predict":
539
- ActionStr = " >> Performing a machine learning regressor predict..."
540
- app.logger.info(ActionStr)
541
- regressor_model_predict(input_data_dict, app.logger)
542
- msg = "EMHASS >> Action regressor-model-predict executed... \n"
543
- if not checkFileLog(ActionStr):
544
- return make_response(msg, 201)
545
- return make_response(grabLog(ActionStr), 400)
546
- # Else return error
547
- else:
548
- app.logger.error("ERROR: passed action is not valid")
549
- msg = "EMHASS >> ERROR: Passed action is not valid... \n"
550
- return make_response(msg, 400)
551
-
552
-
553
- def main(
554
- args: Optional[dict] = None,
555
- ):
556
- global continual_publish_thread
557
- global emhass_conf
558
- global entity_path
559
- global injection_dict
560
- global app
561
- # Pre formatted config parameters
562
- config = {}
563
- # Secrets
564
- global params_secrets
565
- # Built parameters (formatted config + secrets)
566
- params = None
567
-
568
- # Find env's, not not set defaults
569
- DATA_PATH = os.getenv("DATA_PATH", default="/app/data/")
570
- ROOT_PATH = os.getenv("ROOT_PATH", default=str(Path(__file__).parent))
571
- CONFIG_PATH = os.getenv("CONFIG_PATH", default="/share/config.json")
572
- OPTIONS_PATH = os.getenv("OPTIONS_PATH", default="/data/options.json")
573
- DEFAULTS_PATH = os.getenv(
574
- "DEFAULTS_PATH", default=ROOT_PATH + "/data/config_defaults.json"
575
- )
576
- ASSOCIATIONS_PATH = os.getenv(
577
- "ASSOCIATIONS_PATH", default=ROOT_PATH + "/data/associations.csv"
578
- )
579
- LEGACY_CONFIG_PATH = os.getenv(
580
- "LEGACY_CONFIG_PATH", default="/app/config_emhass.yaml"
581
- )
582
-
583
- # Define the paths
584
- config_path = Path(CONFIG_PATH)
585
- options_path = Path(OPTIONS_PATH)
586
- defaults_path = Path(DEFAULTS_PATH)
587
- associations_path = Path(ASSOCIATIONS_PATH)
588
- legacy_config_path = Path(LEGACY_CONFIG_PATH)
589
- data_path = Path(DATA_PATH)
590
- root_path = Path(ROOT_PATH)
591
- # Add paths to emhass_conf
592
- emhass_conf["config_path"] = config_path
593
- emhass_conf["options_path"] = options_path
594
- emhass_conf["defaults_path"] = defaults_path
595
- emhass_conf["associations_path"] = associations_path
596
- emhass_conf["legacy_config_path"] = legacy_config_path
597
- emhass_conf["data_path"] = data_path
598
- emhass_conf["root_path"] = root_path
599
-
600
- # Combine parameters from configuration sources (if exists)
601
- config.update(
602
- build_config(
603
- emhass_conf, app.logger, defaults_path, config_path, legacy_config_path
604
- )
605
- )
606
- if type(config) is bool and not config:
607
- raise Exception("Failed to find default config")
608
-
609
- # Set local variables
610
- costfun = os.getenv("LOCAL_COSTFUN", config.get("costfun", "profit"))
611
- logging_level = os.getenv("LOGGING_LEVEL", config.get("logging_level", "INFO"))
612
- # Temporary set logging level if debug
613
- if logging_level == "DEBUG":
614
- app.logger.setLevel(logging.DEBUG)
615
-
616
- ## Secrets
617
- # Argument
618
- argument = {}
619
- no_response = False
620
- if args is not None:
621
- if args.get("url", None):
622
- argument["url"] = args["url"]
623
- if args.get("key", None):
624
- argument["key"] = args["key"]
625
- if args.get("no_response", None):
626
- no_response = args["no_response"]
627
- # Combine secrets from ENV, Arguments/ARG, Secrets file (secrets_emhass.yaml), options (options.json from addon configuration file) and/or Home Assistant Standalone API (if exist)
628
- emhass_conf, secrets = build_secrets(
629
- emhass_conf,
630
- app.logger,
631
- argument,
632
- options_path,
633
- os.getenv("SECRETS_PATH", default="/app/secrets_emhass.yaml"),
634
- bool(no_response),
635
- )
636
- params_secrets.update(secrets)
637
-
638
- server_ip = params_secrets.get("server_ip", "0.0.0.0")
639
-
640
- # Check if data path exists
641
- if not os.path.isdir(emhass_conf["data_path"]):
642
- app.logger.warning("Unable to find data_path: " + str(emhass_conf["data_path"]))
643
- if os.path.isdir(Path("/app/data/")):
644
- emhass_conf["data_path"] = Path("/app/data/")
645
- else:
646
- Path(root_path / "data/").mkdir(parents=True, exist_ok=True)
647
- emhass_conf["data_path"] = root_path / "data/"
648
- app.logger.info("data_path has been set to " + str(emhass_conf["data_path"]))
649
-
650
- # Initialize this global dict
651
- if (emhass_conf["data_path"] / "injection_dict.pkl").exists():
652
- with open(str(emhass_conf["data_path"] / "injection_dict.pkl"), "rb") as fid:
653
- injection_dict = pickle.load(fid)
654
- else:
655
- injection_dict = None
656
-
657
- # Build params from config and param_secrets (migrate params to correct config catagories), save result to params.pkl
658
- params = build_params(emhass_conf, params_secrets, config, app.logger)
659
- if type(params) is bool:
660
- raise Exception("A error has occurred while building params")
661
- # Update params with local variables
662
- params["optim_conf"]["costfun"] = costfun
663
- params["optim_conf"]["logging_level"] = logging_level
664
-
665
- # Save params to file for later reference
666
- if os.path.exists(str(emhass_conf["data_path"])):
667
- with open(str(emhass_conf["data_path"] / "params.pkl"), "wb") as fid:
668
- pickle.dump((config_path, params), fid)
669
- else:
670
- raise Exception("missing: " + str(emhass_conf["data_path"]))
671
-
672
- # Define loggers
673
- formatter = logging.Formatter(
674
- "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
675
- )
676
- log.default_handler.setFormatter(formatter)
677
- # Action file logger
678
- fileLogger = logging.FileHandler(str(emhass_conf["data_path"] / "actionLogs.txt"))
679
- formatter = logging.Formatter("%(levelname)s - %(name)s - %(message)s")
680
- fileLogger.setFormatter(formatter) # add format to Handler
681
- if logging_level == "DEBUG":
682
- app.logger.setLevel(logging.DEBUG)
683
- fileLogger.setLevel(logging.DEBUG)
684
- elif logging_level == "INFO":
685
- app.logger.setLevel(logging.INFO)
686
- fileLogger.setLevel(logging.INFO)
687
- elif logging_level == "WARNING":
688
- app.logger.setLevel(logging.WARNING)
689
- fileLogger.setLevel(logging.WARNING)
690
- elif logging_level == "ERROR":
691
- app.logger.setLevel(logging.ERROR)
692
- fileLogger.setLevel(logging.ERROR)
693
- else:
694
- app.logger.setLevel(logging.DEBUG)
695
- fileLogger.setLevel(logging.DEBUG)
696
- app.logger.propagate = False
697
- app.logger.addHandler(fileLogger)
698
- # Clear Action File logger file, ready for new instance
699
- clearFileLog()
700
-
701
- # If entity_path exists, remove any entity/metadata files
702
- entity_path = emhass_conf["data_path"] / "entities"
703
- if os.path.exists(entity_path):
704
- entity_pathContents = os.listdir(entity_path)
705
- if len(entity_pathContents) > 0:
706
- for entity in entity_pathContents:
707
- os.remove(entity_path / entity)
708
-
709
- # Initialise continual publish thread list
710
- continual_publish_thread = []
711
-
712
- # Launch server
713
- port = int(os.environ.get("PORT", 5000))
714
- app.logger.info(
715
- "Launching the emhass webserver at: http://" + server_ip + ":" + str(port)
716
- )
717
- app.logger.info(
718
- "Home Assistant data fetch will be performed using url: "
719
- + params_secrets["hass_url"]
720
- )
721
- app.logger.info("The data path is: " + str(emhass_conf["data_path"]))
722
- app.logger.info("The logging is: " + str(logging_level))
723
- try:
724
- app.logger.info("Using core emhass version: " + version("emhass"))
725
- except PackageNotFoundError:
726
- app.logger.info("Using development emhass version")
727
-
728
- return server_ip, port
729
-
730
-
731
- if __name__ == "__main__":
732
- # Parsing arguments
733
- parser = argparse.ArgumentParser()
734
- parser.add_argument(
735
- "--url",
736
- type=str,
737
- help="The URL to your Home Assistant instance, ex the external_url in your hass configuration",
738
- )
739
- parser.add_argument(
740
- "--key",
741
- type=str,
742
- help="Your access key. If using EMHASS in standalone this should be a Long-Lived Access Token",
743
- )
744
- parser.add_argument(
745
- "--no_response",
746
- type=bool,
747
- default=False,
748
- help="This is set if json response errors occur",
749
- )
750
- args = parser.parse_args()
751
-
752
- server_ip, port = main(vars(args))
753
- os.environ["IP"] = str(server_ip)
754
- os.environ["PORT"] = str(port)
755
-
756
- serve(app, host=server_ip, port=port, threads=8)