Skip to content

website_handler

Pragmatismo edited this page Mar 29, 2023 · 2 revisions

handler.py

The handler script is imported by host_webpage.py and used to respond to messages sent from the webpage, this can be used to enable buttons or controls on the webpage to perform actions on the pigrow such as running info modules, creating datawalls or activating devices. It's designed so that it can be upgraded and modified to use with custom webpages without having to rework host_webpage.py.

using the basic/handler.py

the basic handler has three basic functions;

info

info:<info module name>
info:boxname

this runs an info module from the info_module folder and returns the output.

log

log:<log name / path> 
log:selflog.txt

reads the last five entries of a log file from the pigrow/logs folder or from the full path of the file when supplied.

datwall

datawall:<preset>+<module>
datawall:basic_weekly
datawall:basic_weekly+basic_info

creates a datawall and returns the name of the file as found in the pigrow/graphs folder. When no module is supplied it creates the graphs listed in the preset and returns the name of the first graph created (datawall_graph_0.png)

Writing a webpage to use the handler

The index.html file in pigrow/basic/templates demonstrates basic sending and retrieving from the handler,

sending text

The following is a simple text input box and a button that sends the text to the handler,

<form id="text-form" onsubmit="event.preventDefault(); sendText(document.getElementById('text-input').value)">
  <input type="text" name="text_input" id="text-input" placeholder="Enter command">
  <button type="submit">Send</button>
</form>

it uses a function sendText that must also be included;

async function sendText(text) {
      const response = await fetch('/process_text', {
          method: 'POST',
          headers: {
              'Content-Type': 'application/json',
          },
          body: JSON.stringify({text: text}),
      });

      const result = await response.text();
      document.getElementById('response').innerText = result;

It simply sends the text then waits the response, to use datawalls it also has this which checks if the command starts with datawall: and if so it attempts to display the image that's name is returned.

      if (text.startsWith('datawall:')) {
          displayImage(result);
      }
  }

showing an image

This loads the image into the generated-image element on the webpage from /graphs/ which host_webpage.py uses as a link to the pigrow graphs folder

function displayImage(imagePath) {
      const imageElement = document.getElementById('generated-image');
      const imageUrl = `/graphs/${imagePath}?timestamp=${new Date().getTime()}`;
      imageElement.src = imageUrl;
      imageElement.style.display = 'block';
}

note the inclusion of ?timestamp= this is to ensure that it gets the new image from the pigrow folder rather than using the existing one in the cache.

Coding a new handler

host_webpage.py use of handler

The script used for hosting a webpages uses Flask to collect text strings sent from the webpage, this text is then sent to a function in the imported handler called call_command which takes the text string, parses it and returns the response as a text string, which may include the path to an image.

@app.route('/process_text', methods=['POST'])
def process_text():
   text = request.json['text']
    response = Handler.call_command(text)
    return response

the script also uses send_from_directory to allow use of files from the pigrow graphs folder.

@app.route('/graphs/<filename>')
def graphs_image(filename):
    graphs_folder = os.path.join(os.path.expanduser('~'), 'Pigrow/graphs/')
    return send_from_directory(graphs_folder, filename)

To load an image from the Pigrow graphs use the web path /graphs/ followed by the filename of the image.

basic/handler.py

The basic example handler is very simple, it has a class called Handler which contains call_command, this checks for a : and if present splits either side of it to create a key:value pair, if no : is present it uses the whole thing as the key - it then checks if that key is present in a list of commands, each command is associated with a function that performs the required action and returns a text string which is then passed back to the webpage.

To add a new command you can simply add an extra entry to the commands dict

commands = {
        "info": self.info_function,
        "log": self.showlog_function,
        "datawall": self.create_datawall
    }

if the command log: is sent to the handler it'll run the showlog_function, here's a simplified version;

def showlog_function(self, value):
    log_to_load = value
    cmd = "tail -n 5 " + log_to_load
    return self.run_local(cmd)

it just uses the function run_local to run a shell command on the pi and return the output.

note - the actual function will expand a filename to include the full path of the pigrow logs folder if no path is supplied.