Progressive Web Apps

I saw a presentation on PWAs a few weeks ago and wanted to try making one because for a developer this is something of a Holy Grail. It offers the opportunity to develop an App once, in the Web Browser, and run it ‘everywhere’. Clearly there are potential performance issues etc. but in general, it is a great opportunity.  There are a few gotchas:

  1. YOU MUST SERVE THE PWA OVER HTTPS (NOTE THE S in HTTPS). Yes you need to have a secure web connection and therefore you need to acquire a certificate for your site.
  2. Your manifest file MUST contain the correct properties. There is documentation on the web for this.
  3. Different browsers handle this process differently. I strongly suggest using Google Chrome for now.  When the app is loaded, on the desktop you can choose “Install QR Code PWA”, which will install it as a Chrome App.

And in Chrome for Android you should be prompted to install it if you wish.

This will add the app to the Home Screen and also to the Apps Menu. The app is now available fully offline. FireFox does not quite behave the same way and only adds the app to the home screen and the icon also has a little FireFox icon over it.

If you notice that your manifest file is not being loaded, even though it is referenced in your html <HEAD> section, then you should check that you are using HTTPS for your connection.

There are several places where you MUST use an absolute path for the files you need. Please check the demo and look at the loaded files.

Now when you open the PWA it will look like a native Android app. There will be no URL etc.

 

Loading a non WordPress HTML page into WordPress

An existing page from some time ago (before WP) needed loading into a page in WordPress. While there are several ways to accomplish this I wanted the result to appear seamless. ie. No borders or scrolling required.

The source HTML contains references to a relative directory ‘vietnam_files’. It was necessary to alter the path to this directory as this reloading changes the path, thinking they must be beneath the WP root. This is where the use of innerHTML.replace() comes in. It substitutes the full path for the relative one. Here is the result.

This code was the result:-

<iframe style="height:0px; width:0px; visibility: hidden" id="SourceObject" type="text/html" src="/~ianm/vietnam.html" onload="iframeReady()"></iframe>
<div id="LocalDiv"></div>

<script>
iframeReady = function() {
  LocalDiv.innerHTML = sourceObject.contentDocument.getElementById("VietnamContent").innerHTML.replace(/vietnam_files/g,'/~ianm/vietnam_files');
}
var sourceObject = document.getElementById('SourceObject');
var localDiv = document.getElementById("LocalDiv")
</script>

 

Button to shutdown the Raspberry Pi 3B+

The 3B+ behaves a little differently. The LEDs are, by default, controlled by the kernel. There are device drivers for them. This is a new script to enable an added momentary action button to control the LEDs and shutdown the Pi 3B+

#!/usr/bin/env python3
from gpiozero import Button
from signal import pause
import warnings, os, sys

# Script to process a button press on the GPIO of a Raspberry Pi 3B+
# and alter the LEDs of the device according to:
#
# Press and release, toggle the LEDs off, or default, depending.
# Press and hold, for 6 seconds, shutdown the Pi.
# Note the Pi 3B+ is controlled from kernel devices, not direct from the GPIO
# unless you wish to.
#
# Add to /etc/rc.local
# For example
#
# ~pi/shutdown_pi.py 21 6 1  

offGPIO = int(sys.argv[1]) if len(sys.argv) >= 2 else 21
offtime = int(sys.argv[2]) if len(sys.argv) >= 3 else 6
defaultOff = True if len(sys.argv) >= 4 else False

mintime=1

powerLedDev = "/sys/class/leds/led1/"
activityLedDev = "/sys/class/leds/led0/"

powerLedDefAction = "default-on"
activityLedDefAction = "mmc0"

powerLedNewAction = powerLedDefAction
activityLedNewAction = activityLedDefAction

def ledSet(led, item, value):
    f = open(led + item, "w+")
    f.write(value)
    f.close()

def LEDsOff():
    ledSet(powerLedDev, "trigger", "none")
    ledSet(activityLedDev, "trigger", "none")

def when_held(b):
    p = b.pressed_time

    #timer_val = "{0:.0f}".format(500/p)
    timer_val = int(500/p)
    os.system("echo timer > " + powerLedDev + "trigger")

    ledSet(powerLedDev, "delay_on", str(timer_val))
    ledSet(powerLedDev, "delay_off", str(timer_val))

    if p > offtime:
        ledSet(powerLedDev, "trigger", "none")
        ledSet(activityLedDev, "trigger", activityLedDefAction)
        os.system("/sbin/shutdown -h now")

def when_pressed():
    global powerLedNewAction
    global activityLedNewAction

    if powerLedNewAction == powerLedDefAction:
        powerLedNewAction = "none"
        activityLedNewAction = "none"
    else:
        powerLedNewAction = powerLedDefAction
        activityLedNewAction = activityLedDefAction

    LEDsOff()

def when_released():
    ledSet(powerLedDev, "trigger", powerLedNewAction)
    ledSet(activityLedDev, "trigger", activityLedNewAction)

with warnings.catch_warnings():
    warnings.simplefilter("ignore")

if defaultOff:
    LEDsOff()

btn = Button(offGPIO, hold_time=mintime, hold_repeat=True)
btn.when_held = when_held
btn.when_pressed = when_pressed
btn.when_released = when_released
pause()

 

Requirement Analysis – Intro

Background

It has been my experience, working in the software field for many years that the understanding of Requirements Analysis leaves a lot to be desired even by those who profess to understand them. The concept, it seems, is too simple, making it tough for many to grasp the actual finer nuances.

Introduction

The process of solving any problem involves identifying the problem and what goal this problem is preventing you from reaching. Putting this concept straight into the world of software engineering is too much of a leap. Most software developers (engineers, coders, hackers etc. depending on capability and training) will automatically begin to bring to bear their existing processes and really miss the whole thing. So let’s begin with something else:-

The Issue of the Dripping Gutter.

The Snow Blower.

Follow Up.

 

Raspberry Pi SD Card Corruption

There are stories on the web about this issue and I have experienced it myself now. Twice.

The issue seems to occur when the device is powered off when accessing the SD Card. Exactly what the damage is is not clear though. The Pi boots and displays many file system errors. The first time this occurred, after the usual fsck attempts, I decided to build the machine again on another SD Card. The second time it occurred I tried something new. I used my Linux machine to dd the SD Card content to another identical SD Card. There were no read errors and no write errors on the first SDCard which was from the first build.

My assumption is that at some point on the file system a low voltage event has occurred that has set a low value in one of the flash nodes. Research required on my part to understand the flash storage and access circuitry but it seems to work fine when read and written to from my laptop and via a USB flash adapter.

So the flash just needed to be completely read and rewritten.

So I have now added a shutdown button. This will shutdown the Pi when the button is depressed for six seconds. There are many examples of how this is done on the web but none apply to the Pi 3B+.

Using a switch over pins 39 and 40 of the GPIO is called GPIO 21.

LEDS to indicate activity such as flashing can be controlled via system commands to echo values to /sys/class/leds/led1/trigger and delay_on and delay_off when trigger is set to timer.

Add Login/Logout item to top menu bar in WordPress

Add this code to your theme’s functions.php file.

add_filter('wp_nav_menu_items', 'add_login_logout_link', 10, 2);
function add_login_logout_link($items, $args) {
        ob_start();

        wp_loginout('index.php');
        $loginoutlink = ob_get_contents();
        ob_end_clean();
        if ($args->theme_location == 'top')
                $items .= '<li>'. $loginoutlink . '</li>';
    return $items;
}

 

Auto sub content in WordPress

Adding the headings of direct children of the page to the page automatically. Just add the code below to the end of the content-page.php file in your theme directory.

<?php
        $mypages = get_pages( array( 'parent' => $post->ID, 
            'sort_column' => 'post_date', 
            'sort_order' => 'desc' ) ); 
        foreach( $mypages as $page ) {
                $content = $page->post_content;
                if ( ! $content ) // Check for empty page                
                continue;
                $content = apply_filters( 'the_content', $content );
?>
                <h2><a href="<?php echo get_page_link( $page->ID );?>"> <?php echo $page->post_title; ?></a></h2>
                <div class="entry">
                        <?php// echo $content; ?>
                </div>
<?php
}
?>

 

Experiments in Web Publishing

After creating the desired router and configuration, it was time to move on to the Web Server. In the past Drupal was the Web Server of choice but it was time to look at other options and compare. WordPress came out on top. This became a lesson in installing and configuring WordPress

OpenWRT

Last week there was a need for a new feature on the main office router. This feature was not available. It seemed time to explore new options. The end result was an journey through the maze of the Open Source WiFi firmware world. The center of the maze turned out to be OpenWRT or LEDE.

Installation of OpenWRT went surprising well and configuration fairly easy. Options used included USB storage, sftp server and sqm (QoS). There will be a project page on this.