From 4272bdb78598f67639719fdcf074927cea8cc9b1 Mon Sep 17 00:00:00 2001 From: David HERNANDEZ Date: Fri, 13 May 2022 01:01:55 +0200 Subject: [PATCH] Feat - Preparing a new release following revamping of emhass core module --- emhass/CHANGELOG.md | 24 +- emhass/DOCS.md | 28 ++ emhass/Dockerfile | 5 +- emhass/app_server.py | 224 ----------- emhass/config.yml | 2 +- emhass/config_emhass.json | 55 --- emhass/config_emhass.yaml | 63 +++ emhass/requirements.txt | 7 +- emhass/rootfs/etc/services.d/emhass/run | 2 +- emhass/static/style.css | 505 ------------------------ emhass/templates/index.html | 68 ---- 11 files changed, 120 insertions(+), 863 deletions(-) delete mode 100644 emhass/app_server.py delete mode 100644 emhass/config_emhass.json create mode 100644 emhass/config_emhass.yaml delete mode 100644 emhass/static/style.css delete mode 100644 emhass/templates/index.html diff --git a/emhass/CHANGELOG.md b/emhass/CHANGELOG.md index f0b9875..1d13609 100644 --- a/emhass/CHANGELOG.md +++ b/emhass/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.2.0] - 2022-05-13 +### Improvement +- New version of this add-on following revamping of emhass core module and release v0.3.0 +- Moved the webserver to the core emhass module for easier development. +- Added Model Predictive Control optimization. + ## [0.1.42] - 2022-05-05 ### Fix - Fixed issue on correct defferable load total energy computation, following emhass v0.2.14. @@ -154,4 +160,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [0.1.24]: https://github.com/davidusb-geek/emhass-add-on/releases/tag/v0.1.24 [0.1.25]: https://github.com/davidusb-geek/emhass-add-on/releases/tag/v0.1.25 [0.1.26]: https://github.com/davidusb-geek/emhass-add-on/releases/tag/v0.1.26 -[0.1.27]: https://github.com/davidusb-geek/emhass-add-on/releases/tag/v0.1.27 \ No newline at end of file +[0.1.27]: https://github.com/davidusb-geek/emhass-add-on/releases/tag/v0.1.27 +[0.1.28]: https://github.com/davidusb-geek/emhass-add-on/releases/tag/v0.1.28 +[0.1.29]: https://github.com/davidusb-geek/emhass-add-on/releases/tag/v0.1.29 +[0.1.30]: https://github.com/davidusb-geek/emhass-add-on/releases/tag/v0.1.30 +[0.1.31]: https://github.com/davidusb-geek/emhass-add-on/releases/tag/v0.1.31 +[0.1.32]: https://github.com/davidusb-geek/emhass-add-on/releases/tag/v0.1.32 +[0.1.33]: https://github.com/davidusb-geek/emhass-add-on/releases/tag/v0.1.33 +[0.1.34]: https://github.com/davidusb-geek/emhass-add-on/releases/tag/v0.1.34 +[0.1.35]: https://github.com/davidusb-geek/emhass-add-on/releases/tag/v0.1.35 +[0.1.36]: https://github.com/davidusb-geek/emhass-add-on/releases/tag/v0.1.36 +[0.1.37]: https://github.com/davidusb-geek/emhass-add-on/releases/tag/v0.1.37 +[0.1.38]: https://github.com/davidusb-geek/emhass-add-on/releases/tag/v0.1.38 +[0.1.39]: https://github.com/davidusb-geek/emhass-add-on/releases/tag/v0.1.39 +[0.1.40]: https://github.com/davidusb-geek/emhass-add-on/releases/tag/v0.1.40 +[0.1.41]: https://github.com/davidusb-geek/emhass-add-on/releases/tag/v0.1.41 +[0.1.42]: https://github.com/davidusb-geek/emhass-add-on/releases/tag/v0.1.42 +[0.2.0]: https://github.com/davidusb-geek/emhass-add-on/releases/tag/v0.2.0 \ No newline at end of file diff --git a/emhass/DOCS.md b/emhass/DOCS.md index ce2463a..0fe41c0 100644 --- a/emhass/DOCS.md +++ b/emhass/DOCS.md @@ -187,6 +187,34 @@ The possible dictionnary keys to pass data are: - `prod_price_forecast` for the PV production selling price forecast. +### A naive Model Predictive Controller + +A MPC controller was introduced in v0.3.0 of the core emhass moule. This an informal/naive representation of a MPC controller. + +A MPC controller performs the following actions: + +- Set the prediction horizon and receiding horizon parameters. +- Perform an optimization on the prediction horizon. +- Apply the first element of the obtained optimized control variables. +- Repeat at a relatively high frequency, ex: 5 min. + +This is the receiding horizon principle. + +When applyin this controller, the following `runtimeparams` should be defined: + +- `prediction_horizon` for the MPC prediction horizon. Fix this at at least 5 times the optimization time step. + +- `soc_init` for the initial value of the battery SOC for the current iteration of the MPC. + +- `soc_final` for the final value of the battery SOC for the current iteration of the MPC. + +- `def_total_hours` for the list of deferrable loads functioning hours. These values can decrease as the day advances to take into account receidding horizon daily energy objectives for each deferrable load. + +A correct call for a MPC optimization should look like: + +``` +curl -i -H "Content-Type: application/json" -X POST -d '{"pv_power_forecast":[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], "prediction_horizon":10, "soc_init":0.5,"soc_final":0.6,"def_total_hours":[1,3]}' http://localhost:5000/action/naive-mpc-optim +``` ## Disclaimer diff --git a/emhass/Dockerfile b/emhass/Dockerfile index af3ef91..faff7f2 100644 --- a/emhass/Dockerfile +++ b/emhass/Dockerfile @@ -49,10 +49,7 @@ RUN apt-get update \ WORKDIR / # copy contents -COPY app_server.py /usr/src -COPY config_emhass.json /usr/src -COPY templates /usr/src/templates -COPY static /usr/src/static +COPY config_emhass.yaml /usr/src COPY data /usr/src/data COPY rootfs / diff --git a/emhass/app_server.py b/emhass/app_server.py deleted file mode 100644 index 366e308..0000000 --- a/emhass/app_server.py +++ /dev/null @@ -1,224 +0,0 @@ -#!/usr/bin/python - -from flask import Flask, render_template, make_response, request, redirect, url_for -from jinja2 import Environment, FileSystemLoader -from requests import get -# from waitress import serve -# from paste.translogger import TransLogger -from pathlib import Path -import os, json, argparse, pickle -from datetime import datetime, timedelta -import pandas as pd -import plotly.express as px -from emhass.command_line import set_input_data_dict -from emhass.command_line import perfect_forecast_optim, dayahead_forecast_optim, naive_mpc_optim -from emhass.command_line import publish_data -from emhass.utils import get_root - -def get_injection_dict(df, plot_size = 1366): - # Create plots - fig = px.line(df, title='Systems powers and optimization cost results', - template='seaborn', width=plot_size, height=0.75*plot_size) - fig.update_traces(line_shape="vh") - # Get full path to image - image_path_0 = fig.to_html(full_html=False, default_width='75%') - # The tables - table1 = df.reset_index().to_html(classes='mystyle', index=False) - # The dict of plots - injection_dict = {} - injection_dict['title'] = '

EMHASS optimization results

' - injection_dict['subsubtitle2'] = '

Plotting latest optimization results

' - injection_dict['figure_0'] = image_path_0 - injection_dict['subsubtitle1'] = '

Last run optimization results table

' - injection_dict['table1'] = table1 - return injection_dict - -def build_params(params, options): - # Updating variables in retrieve_hass_conf - params['retrieve_hass_conf'][0]['freq'] = options['optimization_time_step'] - params['retrieve_hass_conf'][1]['days_to_retrieve'] = options['historic_days_to_retrieve'] - params['retrieve_hass_conf'][2]['var_PV'] = options['sensor_power_photovoltaics'] - params['retrieve_hass_conf'][3]['var_load'] = options['sensor_power_load_no_var_loads'] - params['retrieve_hass_conf'][6]['var_replace_zero'] = [options['sensor_power_photovoltaics']] - params['retrieve_hass_conf'][7]['var_interp'] = [options['sensor_power_photovoltaics'], options['sensor_power_load_no_var_loads']] - # Updating variables in optim_conf - params['optim_conf'][0]['set_use_battery'] = options['set_use_battery'] - params['optim_conf'][2]['num_def_loads'] = options['number_of_deferrable_loads'] - params['optim_conf'][3]['P_deferrable_nom'] = [i['nominal_power_of_deferrable_loads'] for i in options['list_nominal_power_of_deferrable_loads']] - params['optim_conf'][4]['def_total_hours'] = [i['operating_hours_of_each_deferrable_load'] for i in options['list_operating_hours_of_each_deferrable_load']] - params['optim_conf'][5]['treat_def_as_semi_cont'] = [i['treat_deferrable_load_as_semi_cont'] for i in options['list_treat_deferrable_load_as_semi_cont']] - params['optim_conf'][6]['set_def_constant'] = [False for i in range(len(params['optim_conf'][3]['P_deferrable_nom']))] - start_hours_list = [i['peak_hours_periods_start_hours'] for i in options['list_peak_hours_periods_start_hours']] - end_hours_list = [i['peak_hours_periods_end_hours'] for i in options['list_peak_hours_periods_end_hours']] - num_peak_hours = len(start_hours_list) - list_hp_periods_list = [{'period_hp_'+str(i+1):[{'start':start_hours_list[i]},{'end':end_hours_list[i]}]} for i in range(num_peak_hours)] - params['optim_conf'][10]['list_hp_periods'] = list_hp_periods_list - params['optim_conf'][11]['load_cost_hp'] = options['load_peak_hours_cost'] - params['optim_conf'][12]['load_cost_hc'] = options['load_offpeak_hours_cost'] - params['optim_conf'][14]['prod_sell_price'] = options['photovoltaic_production_sell_price'] - params['optim_conf'][15]['set_total_pv_sell'] = options['set_total_pv_sell'] - # Updating variables in plant_conf - params['plant_conf'][0]['P_grid_max'] = options['maximum_power_from_grid'] - params['plant_conf'][1]['module_model'] = [i['pv_module_model'] for i in options['list_pv_module_model']] - params['plant_conf'][2]['inverter_model'] = [i['pv_inverter_model'] for i in options['list_pv_inverter_model']] - params['plant_conf'][3]['surface_tilt'] = [i['surface_tilt'] for i in options['list_surface_tilt']] - params['plant_conf'][4]['surface_azimuth'] = [i['surface_azimuth'] for i in options['list_surface_azimuth']] - params['plant_conf'][5]['modules_per_string'] = [i['modules_per_string'] for i in options['list_modules_per_string']] - params['plant_conf'][6]['strings_per_inverter'] = [i['strings_per_inverter'] for i in options['list_strings_per_inverter']] - params['plant_conf'][7]['Pd_max'] = options['battery_discharge_power_max'] - params['plant_conf'][8]['Pc_max'] = options['battery_charge_power_max'] - params['plant_conf'][9]['eta_disch'] = options['battery_discharge_efficiency'] - params['plant_conf'][10]['eta_ch'] = options['battery_charge_efficiency'] - params['plant_conf'][11]['Enom'] = options['battery_nominal_energy_capacity'] - params['plant_conf'][12]['SOCmin'] = options['battery_minimum_state_of_charge'] - params['plant_conf'][13]['SOCmax'] = options['battery_maximum_state_of_charge'] - params['plant_conf'][14]['SOCtarget'] = options['battery_target_state_of_charge'] - # The params dict - params['params_secrets'] = params_secrets - params['passed_data'] = {'pv_power_forecast':None,'load_power_forecast':None,'load_cost_forecast':None,'prod_price_forecast':None} - return params - -# Define the Flask instance -app = Flask(__name__, static_url_path='/static') - -# Define the paths -OPTIONS_PATH = "/data/options.json" -options_json = Path(OPTIONS_PATH) -CONFIG_PATH = "/usr/src/config_emhass.json" -config_path = Path(CONFIG_PATH) -base_path = str(config_path.parent) - -# Read options info -if options_json.exists(): - with options_json.open('r') as data: - options = json.load(data) -else: - app.logger.error("options.json does not exists") - -# Read example config file -if config_path.exists(): - with config_path.open('r') as data: - config = json.load(data) - retrieve_hass_conf = config['retrieve_hass_conf'] - optim_conf = config['optim_conf'] - plant_conf = config['plant_conf'] -else: - app.logger.error("config_emhass.json does not exists") - -params = {} -params['retrieve_hass_conf'] = retrieve_hass_conf -params['optim_conf'] = optim_conf -params['plant_conf'] = plant_conf -with open(base_path+'/data/params.pkl', "wb") as fid: - pickle.dump(params, fid) - -# The cost function -costfun = options['costfun'] - -# Initialize this global dict -opt_res = pd.read_csv(base_path+'/data/opt_res_dayahead_latest.csv', index_col='timestamp') -injection_dict = get_injection_dict(opt_res) -with open(base_path+'/data/injection_dict.pkl', "wb") as fid: - pickle.dump(injection_dict, fid) - -@app.route('/') -def index(): - app.logger.info("EMHASS server online, serving index.html...") - # Load HTML template - file_loader = FileSystemLoader(base_path+'/templates') - env = Environment(loader=file_loader) - template = env.get_template('index.html') - # Load cache dict - with open(base_path+'/data/injection_dict.pkl', "rb") as fid: - injection_dict = pickle.load(fid) - if injection_dict is None: - return make_response(template.render(injection_dict={})) - else: - return make_response(template.render(injection_dict=injection_dict)) - -@app.route('/action/', methods=['POST']) -def action_call(action_name): - with open(base_path+'/data/params.pkl', "rb") as fid: - params = pickle.load(fid) - runtimeparams = request.get_json(force=True) - params = json.dumps(params) - input_data_dict = set_input_data_dict(config_path, base_path, costfun, - params, runtimeparams, action_name, app.logger) - if action_name == 'publish-data': - app.logger.info("Publishing data...") - _ = publish_data(input_data_dict, app.logger) - msg = f'EMHASS >> Action publish-data executed... \n' - return make_response(msg, 201) - elif action_name == 'perfect-optim': - app.logger.info("Performing perfect optimization...") - opt_res = perfect_forecast_optim(input_data_dict, app.logger) - injection_dict = get_injection_dict(opt_res) - with open(base_path+'/data/injection_dict.pkl', "wb") as fid: - pickle.dump(injection_dict, fid) - msg = f'EMHASS >> Action perfect-optim executed... \n' - return make_response(msg, 201) - elif action_name == 'dayahead-optim': - app.logger.info("Performing dayahead optimization...") - opt_res = dayahead_forecast_optim(input_data_dict, app.logger) - injection_dict = get_injection_dict(opt_res) - with open(base_path+'/data/injection_dict.pkl', "wb") as fid: - pickle.dump(injection_dict, fid) - msg = f'EMHASS >> Action dayahead-optim executed... \n' - return make_response(msg, 201) - elif action_name == 'naive-mpc-optim': - app.logger.info("Performing naive MPC optimization...") - opt_res = naive_mpc_optim(input_data_dict, app.logger) - injection_dict = get_injection_dict(opt_res) - with open(base_path+'/data/injection_dict.pkl', "wb") as fid: - pickle.dump(injection_dict, fid) - msg = f'EMHASS >> Action naive-mpc-optim executed... \n' - return make_response(msg, 201) - else: - app.logger.error("ERROR: passed action is not valid") - msg = f'EMHASS >> ERROR: Passed action is not valid... \n' - return make_response(msg, 400) - -if __name__ == "__main__": - # Parsing arguments - parser = argparse.ArgumentParser() - parser.add_argument('--url', type=str, help='The hass instance url') - parser.add_argument('--key', type=str, help='Your access key') - args = parser.parse_args() - web_ui_url = options['web_ui_url'] - url_from_options = options['hass_url'] - if url_from_options == 'empty': - hass_url = args.url - url = hass_url+"/config" - else: - hass_url = url_from_options - url = hass_url+"/api/config" - token_from_options = options['long_lived_token'] - if token_from_options == 'empty': - long_lived_token = args.key - else: - long_lived_token = token_from_options - headers = { - "Authorization": "Bearer " + long_lived_token, - "content-type": "application/json" - } - response = get(url, headers=headers) - config_hass = response.json() - params_secrets = { - 'hass_url': hass_url, - 'long_lived_token': long_lived_token, - 'time_zone': config_hass['time_zone'], - 'lat': config_hass['latitude'], - 'lon': config_hass['longitude'], - 'alt': config_hass['elevation'] - } - # Build params - with open(base_path+'/data/params.pkl', "rb") as fid: - params = pickle.load(fid) - params = build_params(params, options) - with open(base_path+'/data/params.pkl', "wb") as fid: - pickle.dump(params, fid) - - # Launch server - port = int(os.environ.get('PORT', 5000)) - app.run(debug=False, host=web_ui_url, port=port) - #serve(TransLogger(app, setup_console_handler=True), host=web_ui_url, port=port) \ No newline at end of file diff --git a/emhass/config.yml b/emhass/config.yml index 6c0d168..0b1ff79 100644 --- a/emhass/config.yml +++ b/emhass/config.yml @@ -1,7 +1,7 @@ name: EMHASS description: Energy Management for Home Assistant url: https://github.com/davidusb-geek/emhass -version: 0.1.42 +version: 0.2.0 slug: emhass arch: - aarch64 diff --git a/emhass/config_emhass.json b/emhass/config_emhass.json deleted file mode 100644 index 3aab2d3..0000000 --- a/emhass/config_emhass.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "retrieve_hass_conf": - [ - {"freq": 30}, - {"days_to_retrieve": 2}, - {"var_PV": "sensor.power_photovoltaics"}, - {"var_load": "sensor.power_load_no_var_loads"}, - {"load_negative": false}, - {"set_zero_min": true}, - {"var_replace_zero": ["sensor.power_photovoltaics"]}, - {"var_interp": ["sensor.power_photovoltaics", "sensor.power_load_no_var_loads"]} - ], - "optim_conf": - [ - {"set_use_battery": false}, - {"delta_forecast": 1}, - {"num_def_loads": 2}, - {"P_deferrable_nom": [3000, 750]}, - {"def_total_hours": [5, 8]}, - {"treat_def_as_semi_cont": [true, true]}, - {"set_def_constant": [false, false]}, - {"weather_forecast_method": "scrapper"}, - {"load_forecast_method": "naive"}, - {"load_cost_forecast_method": "hp_hc_periods"}, - {"list_hp_periods": - [ - {"period_hp_1": [{"start": "02:54"}, {"end": "15:24"}]}, - {"period_hp_2": [{"start": "17:24"}, {"end": "20:24"}]} - ] - }, - {"load_cost_hp": 0.1907}, - {"load_cost_hc": 0.1419}, - {"prod_price_forecast_method": "constant"}, - {"prod_sell_price": 0.065}, - {"set_total_pv_sell": false} - ], - "plant_conf": - [ - {"P_grid_max": 9000}, - {"module_model": "CSUN_Eurasia_Energy_Systems_Industry_and_Trade_CSUN295_60M"}, - {"inverter_model": "Fronius_International_GmbH__Fronius_Primo_5_0_1_208_240__240V_"}, - {"surface_tilt": 30}, - {"surface_azimuth": 205}, - {"modules_per_string": 16}, - {"strings_per_inverter": 1}, - {"Pd_max": 1000}, - {"Pc_max": 1000}, - {"eta_disch": 0.95}, - {"eta_ch": 0.95}, - {"Enom": 5000}, - {"SOCmin": 0.3}, - {"SOCmax": 0.9}, - {"SOCtarget": 0.6} - ] -} diff --git a/emhass/config_emhass.yaml b/emhass/config_emhass.yaml new file mode 100644 index 0000000..30a964a --- /dev/null +++ b/emhass/config_emhass.yaml @@ -0,0 +1,63 @@ +# Configuration file for EMHASS + +retrieve_hass_conf: + - freq: 30 # The time step to resample retrieved data from hass in minutes + - days_to_retrieve: 8 # We will retrieve data from now and up to days_to_retrieve days + - var_PV: 'sensor.power_photovoltaics' # Photovoltaic produced power sensor in Watts + - var_load: 'sensor.power_load_no_var_loads' # Household power consumption sensor in Watts (deferrable loads should be substracted) + - load_negative: False # Set to True if the retrived load variable is negative by convention + - set_zero_min: True # A special treatment for a minimum value saturation to zero. Values below zero are replaced by nans + - var_replace_zero: # A list of retrived variables that we would want to replace nans with zeros + - 'sensor.power_photovoltaics' + - var_interp: # A list of retrived variables that we would want to interpolate nan values using linear interpolation + - 'sensor.power_photovoltaics' + - 'sensor.power_load_no_var_loads' + +optim_conf: + - set_use_battery: False # consider a battery storage + - delta_forecast: 1 # days + - num_def_loads: 2 + - P_deferrable_nom: # Watts + - 3000.0 + - 750.0 + - def_total_hours: # hours + - 5 + - 8 + - treat_def_as_semi_cont: # treat this variable as semi continuous + - True + - True + - set_def_constant: # set as a constant fixed value variable with just one startup for each 24h + - False + - False + - weather_forecast_method: 'scrapper' # options are 'scrapper' and 'csv' + - load_forecast_method: 'naive' # options are 'csv' to load a custom load forecast from a CSV file or 'naive' for a persistance model + - load_cost_forecast_method: 'hp_hc_periods' # options are 'hp_hc_periods' for peak and non-peak hours contracts and 'csv' to load custom cost from CSV file + - list_hp_periods: # list of different tariff periods (only needed if load_cost_forecast_method='hp_hc_periods') + - period_hp_1: + - start: '02:54' + - end: '15:24' + - period_hp_2: + - start: '17:24' + - end: '20:24' + - load_cost_hp: 0.1907 # peak hours load cost in €/kWh (only needed if load_cost_forecast_method='hp_hc_periods') + - load_cost_hc: 0.1419 # non-peak hours load cost in €/kWh (only needed if load_cost_forecast_method='hp_hc_periods') + - prod_price_forecast_method: 'constant' # options are 'constant' for constant fixed value or 'csv' to load custom price forecast from a CSV file + - prod_sell_price: 0.065 # power production selling price in €/kWh (only needed if prod_price_forecast_method='constant') + - set_total_pv_sell: False # consider that all PV power is injected to the grid (self-consumption with total sell) + +plant_conf: + - P_grid_max: 9000 # The maximum power that can be supplied by the utility grid in Watts + - module_model: 'CSUN_Eurasia_Energy_Systems_Industry_and_Trade_CSUN295_60M' # The PV module model + - inverter_model: 'Fronius_International_GmbH__Fronius_Primo_5_0_1_208_240__240V_' # The PV inverter model + - surface_tilt: 30 # The tilt angle of your solar panels + - surface_azimuth: 205 # The azimuth angle of your PV installation + - modules_per_string: 16 # The number of modules per string + - strings_per_inverter: 1 # The number of used strings per inverter + - Pd_max: 1000 # If your system has a battery (set_use_battery=True), the maximum discharge power in Watts + - Pc_max: 1000 # If your system has a battery (set_use_battery=True), the maximum charge power in Watts + - eta_disch: 0.95 # If your system has a battery (set_use_battery=True), the discharge efficiency + - eta_ch: 0.95 # If your system has a battery (set_use_battery=True), the charge efficiency + - Enom: 5000 # If your system has a battery (set_use_battery=True), the total capacity of the battery stack in Wh + - SOCmin: 0.3 # If your system has a battery (set_use_battery=True), the minimun allowable battery state of charge + - SOCmax: 0.9 # If your system has a battery (set_use_battery=True), the minimun allowable battery state of charge + - SOCtarget: 0.6 # If your system has a battery (set_use_battery=True), the desired battery state of charge at the end of each optimization cycle diff --git a/emhass/requirements.txt b/emhass/requirements.txt index 97b7eaa..de4cf5c 100644 --- a/emhass/requirements.txt +++ b/emhass/requirements.txt @@ -12,8 +12,7 @@ pyyaml>=5.4.1 netcdf4>=1.5.4 tables>=3.6.1 flask>=2.0.3 -#waitress>=2.1.1 -#Paste>=3.5.0 +waitress>=2.1.1 plotly>=5.6.0 -emhass==0.2.14 -#git+https://github.com/davidusb-geek/emhass.git@0f67c27 \ No newline at end of file +emhass==0.3.0 +#git+https://github.com/davidusb-geek/emhass.git@759c458 \ No newline at end of file diff --git a/emhass/rootfs/etc/services.d/emhass/run b/emhass/rootfs/etc/services.d/emhass/run index 757bb81..e28b023 100644 --- a/emhass/rootfs/etc/services.d/emhass/run +++ b/emhass/rootfs/etc/services.d/emhass/run @@ -3,4 +3,4 @@ # Start EMHASS service # ============================================================================== -exec python3 /usr/src/app_server.py --url "http://supervisor/core/api" --key "$SUPERVISOR_TOKEN" +exec python3 -m emhass.web_server --url "http://supervisor/core/api" --key "$SUPERVISOR_TOKEN" --add_on "True" diff --git a/emhass/static/style.css b/emhass/static/style.css deleted file mode 100644 index 30789b5..0000000 --- a/emhass/static/style.css +++ /dev/null @@ -1,505 +0,0 @@ -/*! style.css v1.0.0 (modified version) | ISC License | https://github.com/ungoldman/style.css */ -html { - color: #303030; - background-color: white; - box-sizing: border-box; - font-family: -apple-system, BlinkMacSystemFont, "avenir next", avenir, "segoe ui", "fira sans", roboto, noto, "droid sans", "liberation sans", "lucida grande", "helvetica neue", helvetica, "franklin gothic medium", "century gothic", cantarell, oxygen, ubuntu, sans-serif; - font-size: calc(14px + 0.25vw); - line-height: 1.55; - -webkit-font-kerning: normal; - font-kerning: normal; - text-rendering: optimizeLegibility; - -webkit-font-feature-settings: "kern", "liga" 1, "calt" 0; - font-feature-settings: "kern", "liga" 1, "calt" 0; - -ms-text-size-adjust: 100%; - -webkit-text-size-adjust: 100%; -} - -body { - margin: 0; -} - -article, -aside, -footer, -header, -nav, -section, -figcaption, -figure, -main { - display: block; -} - -*, -*::before, -*::after { - box-sizing: inherit; -} - -p, -blockquote, -ul, -ol, -dl, -table, -pre { - margin-top: 0; - margin-bottom: 1.25em; -} - -small { - font-size: 80%; -} - -h1, -h2, -h3, -h4, -h5, -h6 { - font-weight: 500; - line-height: 1.25em; - margin-bottom: 1.25rem; - margin-top: 2rem; - position: relative; -} - -h1 small, -h2 small, -h3 small, -h4 small, -h5 small, -h6 small { - color: #777; - font-size: 0.7em; - font-weight: 300; -} - -h1 code, -h2 code, -h3 code, -h4 code, -h5 code, -h6 code { - font-size: 0.9em; -} - -h1 { - font-size: 2.75em; -} - -h2 { - font-size: 2.25em; -} - -h3 { - font-size: 1.75em; -} - -h4 { - font-size: 1.5em; -} - -h5 { - font-size: 1.25em; -} - -h6 { - font-size: 1.15em; - color: #575757; -} - -p { - letter-spacing: -0.01em; -} - -a { - background-color: transparent; - -webkit-text-decoration-skip: objects; - color: #0074d9; - text-decoration: none; -} - -a:active, a:hover { - outline-width: 0; - outline: 0; -} - -a:active, a:focus, a:hover { - text-decoration: underline; -} - -ul, -ol { - padding: 0; - padding-left: 2em; -} - -ul ol, -ol ol { - list-style-type: lower-roman; -} - -ul ul, -ul ol, -ol ul, -ol ol { - margin-top: 0; - margin-bottom: 0; -} - -ul ul ol, -ul ol ol, -ol ul ol, -ol ol ol { - list-style-type: lower-alpha; -} - -li > p { - margin-top: 1em; -} - -blockquote { - margin: 0 0 1rem; - padding: 0 1rem; - color: #7d7d7d; - border-left: 4px solid #d6d6d6; -} - -blockquote > :first-child { - margin-top: 0; -} - -blockquote > :last-child { - margin-bottom: 0; -} - -b, -strong { - font-weight: inherit; - font-weight: 600; -} - -mark { - background-color: #ff0; - color: #000; -} - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -code, -pre, -kbd, -samp { - font-family: menlo, inconsolata, consolas, "fira mono", "noto mono", "droid sans mono", "liberation mono", "dejavu sans mono", "ubuntu mono", monaco, "courier new", monospace; - font-size: 90%; -} - -pre, code { - background-color: #f7f7f7; - border-radius: 3px; -} - -pre { - overflow: auto; - word-wrap: normal; - padding: 1em; - line-height: 1.45; -} - -pre code { - background: transparent; - display: inline; - padding: 0; - line-height: inherit; - word-wrap: normal; -} - -pre code::before, pre code::after { - content: normal; -} - -pre > code { - border: 0; - font-size: 1em; - white-space: pre; - word-break: normal; -} - -code { - padding: 0.2em 0; - margin: 0; -} - -code::before, code::after { - letter-spacing: -0.2em; - content: '\00a0'; -} - -kbd { - background-color: #e6e6e6; - background-image: linear-gradient(#fafafa, #e6e6e6); - background-repeat: repeat-x; - border: 1px solid #d6d6d6; - border-radius: 2px; - box-shadow: 0 1px 0 #d6d6d6; - color: #303030; - display: inline-block; - line-height: 0.95em; - margin: 0 1px; - padding: 5px 5px 1px; -} - -td, -th { - padding: 0; -} - -hr { - box-sizing: content-box; - overflow: visible; - background: transparent; - height: 4px; - padding: 0; - margin: 1em 0; - background-color: #e7e7e7; - border: 0 none; -} - -hr::before { - display: table; - content: ''; -} - -hr::after { - display: table; - clear: both; - content: ''; -} - -img { - border-style: none; - border: 0; - max-width: 100%; -} - -svg:not(:root) { - overflow: hidden; -} - -figure { - margin: 1em 0; -} - -figure img { - background: white; - border: 1px solid #c7c7c7; - padding: 0.25em; -} - -figcaption { - font-style: italic; - font-size: 0.75em; - font-weight: 200; - margin: 0; -} - -abbr[title] { - border-bottom: none; - text-decoration: underline; - text-decoration: underline dotted; -} - -dfn { - font-style: italic; -} - -dd { - margin-left: 0; -} - -dl { - padding: 0; -} - -dl dt { - padding: 0; - margin-top: 1em; - font-size: 1em; - font-style: italic; - font-weight: 600; -} - -dl dd { - padding: 0 1em; - margin-bottom: 1.25em; -} - -audio, -video { - display: inline-block; -} - -audio:not([controls]) { - display: none; - height: 0; -} - -input { - margin: 0; -} - -button, -input, -optgroup, -select, -textarea { - font-family: sans-serif; - font-size: 100%; - line-height: 1.15; - margin: 0; -} - -button, -input { - overflow: visible; -} - -button, -select { - text-transform: none; -} - -button, -html [type="button"], -[type="reset"], -[type="submit"] { - -webkit-appearance: button; -} - -button::-moz-focus-inner, -[type="button"]::-moz-focus-inner, -[type="reset"]::-moz-focus-inner, -[type="submit"]::-moz-focus-inner { - border-style: none; - padding: 0; -} - -button:-moz-focusring, -[type="button"]:-moz-focusring, -[type="reset"]:-moz-focusring, -[type="submit"]:-moz-focusring { - outline: 1px dotted ButtonText; -} - -fieldset { - border: 1px solid #c0c0c0; - margin: 0 2px; - padding: 0.35em 0.625em 0.75em; -} - -legend { - color: inherit; - display: table; - max-width: 100%; - padding: 0; - white-space: normal; -} - -progress { - display: inline-block; - vertical-align: baseline; -} - -textarea { - overflow: auto; -} - -[type="checkbox"], -[type="radio"] { - padding: 0; -} - -[type="number"]::-webkit-inner-spin-button, -[type="number"]::-webkit-outer-spin-button { - height: auto; -} - -[type="search"] { - -webkit-appearance: textfield; - outline-offset: -2px; -} - -[type="search"]::-webkit-search-cancel-button, -[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} - -[disabled] { - cursor: default; -} - -::-webkit-file-upload-button { - -webkit-appearance: button; - font: inherit; -} - -details, -menu { - display: block; -} - -summary { - display: list-item; -} - -canvas { - display: inline-block; -} - -template { - display: none; -} - -[hidden] { - display: none; -} - -/* Special style for DataFrmae tables */ -/* includes alternating gray and white with on-hover color */ - -.mystyle { - font-size: 11pt; - font-family: Arial; - border-collapse: collapse; - border: 1px solid silver; - -} - -.mystyle td, th { - padding: 5px; -} - -.mystyle tr:nth-child(even) { - background: #E0E0E0; -} - -.mystyle tr:hover { - background: silver; - cursor: pointer; -} \ No newline at end of file diff --git a/emhass/templates/index.html b/emhass/templates/index.html deleted file mode 100644 index 4e1f893..0000000 --- a/emhass/templates/index.html +++ /dev/null @@ -1,68 +0,0 @@ - - - - - EMHASS: Energy Management Optimization for Home Assistant - - - -
-

EMHASS: Energy Management Optimization for Home Assistant

-

Basic optimization control

-
-
-
-
- - -
-
-

-
-
- {% for plot in injection_dict %} -
- {{injection_dict[plot]}} -
- {% endfor %} -
-
-

© MIT License | Copyright (c) 2021-2022 David HERNANDEZ

-
-
- - -