davidusb-geek / emhass

emhass: Energy Management for Home Assistant, is a Python module designed to optimize your home energy interfacing with Home Assistant.
MIT License
260 stars 51 forks source link

Flask dynamically reload diagram after action (button press) #186

Closed GeoDerp closed 4 months ago

GeoDerp commented 5 months ago

I can see this being done either via python as a response (to completing the task) or via JavaScript to refresh after X time after button press. I believe, it would be nice to have the latest data be presented on the webpage after pressing the respective button in the EMHASS webpage. The implementation of this statically would be the easiest. Telling the whole page to refresh. Ideally I would think the best method would be to refresh the diagram div's/sections dynamically on new data.

davidusb-geek commented 5 months ago

That would be nice. While you are at this take also a look at this if you can: https://github.com/users/davidusb-geek/projects/1?pane=issue&itemId=52141646

GeoDerp commented 4 months ago

Had a 2 second look at this but I believe if we can redraw the {{injection_dict[plot]}} loop on every change (ex. On file change or function return) or X seconds we might be able to the diagram section to dynamically refresh. 🤷‍♂️

davidusb-geek commented 4 months ago

Ok, I think should be the best option, but how to redraw on every change on web_server.py ?

GeoDerp commented 4 months ago

That I don't know. We might be able to passively scan for changes to the file. (Or trigger from button function response) However I haven't observed the code enough to see if redrawing the loop dynamically is even possible.

GeoDerp commented 4 months ago

I'm wondering if, just before the response of a /action button, we call a function to rerun this?

    basename = request.headers.get("X-Ingress-Path", "")
    return make_response(template.render(injection_dict=injection_dict, basename=basename))

If this response is even responsible for drawing the diagrams. Only has a look at the code for a few mins so far.

Different thought, I think I may have seen an example of the diagrams being presented in a separate html that was appended to the index. Then you could in theory refresh only that portion of the html. 🤷‍♂️

GeoDerp commented 4 months ago

maybe something like this? https://stackoverflow.com/a/16881055

After action button response, we run a HTTP fetch/get to get the latest HTML of the graphs and replace (using JavaScript) Similar to the idea of https://github.com/davidusb-geek/emhass/issues/191 but we will be passing html (from the template render) then json

Supper ruff very broken example index.html

    async function getTemplate(action) { //return html
            let htmlTemplateData = ""
            response = await fetch(`{{ basename }}/action/getTemplate`, {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                },
                body: {},
            }).then(blob = await response.blob()).then(htmlTemplateData = await new Response(blob).text()) 
           return(htmlTemplateData)
        } 

 //create function that calls getTemplate and replaces div with diagrams

webserver..py

@app.route('/action/<action_name>', methods=['POST'])
def action_call(action_name):
    if action_name == 'getTemplate':
        # Load HTML template
        file_loader = PackageLoader('emhass', 'templates')
        env = Environment(loader=file_loader)
        template = env.get_template('template.html') #html with just template
        # Load cache dict
        if (data_path / 'injection_dict.pkl').exists():
            with open(str(data_path / 'injection_dict.pkl'), "rb") as fid:
                injection_dict = pickle.load(fid)
        else:
            app.logger.warning("The data container dictionary is empty... Please launch an optimization task")
            injection_dict={}
        basename = request.headers.get("X-Ingress-Path", "")    
        return make_response(template.render(injection_dict=injection_dict, basename=basename))

template.html

    {% for plot in injection_dict %} <!-- diagrams/tables elements will be added here -->
    <div class="table_div">
        {{injection_dict[plot]}}
    </div>
    {% endfor %}
GeoDerp commented 4 months ago

I think this can work. Just needs time

GeoDerp commented 4 months ago

Should be able to get a function version of this tomorrow

davidusb-geek commented 4 months ago

Is this solved with the new version?

GeoDerp commented 4 months ago

Sure is 👍