ffmpeg: Width or Height not divisible by 2

If you’re trying to shrink a video by half along the lines of this, you may encounter an error about “width not divisible by 2” during processing, or you just might get an empty mp4 file:

INPUT_VIDEO='some-large-4k-video.mp4'
echo "Generating a ?x360 video from $INPUT_VIDEO"
ffmpeg -hide_banner -loglevel panic  -i "$INPUT_VIDEO" -vf scale=-1:360 -r 60000/1001 -preset slow -an -crf 23 -y -loglevel quiet -stats "$OUTPUT_FILENAME-360.mp4"

The problem happens when you get interesting video dimensions, such as a vertical video that might be 1080 wide by 1920 high.

When you try to downscale a 1080x1920 video to 360 high using -vf scale=-1:360 you end up trying to make a video that is 202.5 pixels wide, and that’s no good!

The simple fix here is to change -1 to -2 to tell ffmpeg to choose a number that’s divisible by 2 (instead of 1) so the end script might look like this:

INPUT_VIDEO='some-large-4k-video.mp4'
echo "Generating a ?x360 video from $INPUT_VIDEO"
ffmpeg -hide_banner -loglevel panic  -i "$INPUT_VIDEO" -vf scale=-2:360 -r 60000/1001 -preset slow -an -crf 23 -y -loglevel quiet -stats "$OUTPUT_FILENAME-360.mp4"
echo "Generating a ?x720 video from $INPUT_VIDEO"
ffmpeg -hide_banner -loglevel panic  -i "$INPUT_VIDEO" -vf scale=-2:720 -r 60000/1001 -preset slow -an -crf 23 -y -loglevel quiet -stats "$OUTPUT_FILENAME-720.mp4"
echo "Generating a ?x1080 video from $INPUT_VIDEO"
ffmpeg -hide_banner -loglevel panic  -i "$INPUT_VIDEO" -vf scale=-2:1080 -r 60000/1001 -preset slow -an -crf 23 -y -loglevel quiet -stats "$OUTPUT_FILENAME-1080.mp4"

How to embed Gutenberg on a stand alone plugin page

How’s this for cool.

It’s possible to embed Gutenberg into any backend plugin page, with a custom save callback. You can get access to the rendered Gutenberg HTML or raw blocks array to do fun things with.

Embedded Block Editor / Gutenberg in plugin admin page

This is the minimal amount of code to render a functional Gutenberg Block Editor in the backend:

   <div className="embed-gutenberg edit-post-visual-editor">
      <SlotFillProvider>
        <DropZoneProvider>
          <BlockEditorProvider
            value={blocks}
            onInput={onChange}
            onChange={onChange}
            className="embed-gutenberg__wrapper"
          >
            <div className="embed-gutenberg__editor">
              <BlockEditorKeyboardShortcuts/>
              <WritingFlow>
                <ObserveTyping>
                  <BlockList/>
                </ObserveTyping>
              </WritingFlow>
            </div>
            <div className="embed-gutenberg__sidebar">
              <BlockInspector/>
            </div>
            <Popover.Slot/>
          </BlockEditorProvider>
        </DropZoneProvider>
      </SlotFillProvider>
    </div>

The onChange callback can serialize the full html change from the Gutenberg block array using serialize() like this:

  let fullHtml = ''
  blocks.forEach(block => {
    const blockHtml = serialize(block);
    fullHtml = fullHtml + "\n" + blockHtml;
  })

And a default block array for Gutenberg might look something like this:

const defaultBlockArray = [{
  "clientId": "812f33be-af57-475e-9987-af1936fee811",
  "name": "core/paragraph",
  "isValid": true,
  "attributes": {"content": "This is the first Paragraph block", "dropCap": false},
  "innerBlocks": []
}, {
  "clientId": "1e4e538e-5b75-4c93-894f-9bad6aaea746",
  "name": "core/heading",
  "isValid": true,
  "attributes": {"content": "This is a header block", "level": 2},
  "innerBlocks": []
} ]

Sending Motion & Door open/close events to Slack using Node-RED

Sending slack alerts when doors open and close

This technique uses the following Hardware:

  • Sonoff RF Bridge ($14)
  • RaspberryPi Zero W ($10) + SD Card
  • 433MHz open/close magnetic sensor ($6)
  • 433MHz PIR sensor ($5)
  • Micro USB cables (to power Sonoff + Raspberry Pi)
  • USB to Serial adaptor (to flash Tasmota software onto the Sonoff)

And the following free and/or open source Software:

  • Tasmota (think of this like a mini operating system that will run on the Sonoff RF Bridge, it lets us easily send and receive 433MHz signals via MQTT)
  • Raspbian Buster Lite, this is the operating system we’ll put on the SD card that will run on the Raspberry Pi Zero W
  • Node-RED is the web based tool that lets us easily wire together events that come from MQTT and pipe those events to other devices or services (i.e. MQTT message to Slack message). This software will run on the Raspberry Pi.
  • Mosquitto is a MQTT broker/server that will run on the Raspberry Pi, the Sonoff will send all received 433MHz messages to the MQTT broker. Node-RED is also connected to the MQTT broker so that’s how it sees the 433MHz events.
  • esptool is a command line program used to flash the new Tasmota software onto the Sonoff RF Bridge. We’ll use this once in the initial Tasmota setup. There’s other options for flashing such as via Visual Studio code or Arduino but I find esptool easiest.

Here are the steps covered in detail below:

  1. Flashing Tasmota to the Sonoff RF Bridge (i.e. open it up, connect some wires, use esptool to flash Tasmota to the onboard ESP8266)
  2. Setting up the Raspberry Pi with Node-RED + mosquitto mqtt
  3. Configuring Tasmota to connect to Wifi and send events to our Pi MQTT server
  4. Configuring Node-RED to “do things” when it receives events via MQTT (i.e. post to slash)

Lets get started!

Hardware used in this setup

Flashing Tasmota to the Sonoff RF Bridge:

I’m assuming your running this howto from Linux. Windows and Mac users will need some adjustments around how software is installed and port names etc..

First install esptool ( pip install esptool ) on your laptop. We’ll use this tool to write the Tasmota firmware over Serial to the Sonoff.

Next, open up the Sonoff. Use a small flat screwdriver to remove the sticky feet covering the 4 screws, then undo them like this:

Remove the screws from the back.

Then make sure all power is off and open it up, you’ll see the board sitting inside:

Board inside the Sonoff RF Bridge

Now pull the board out gently, it should pop out with little force:

Inside the Sonoff RF Bridge. The big white thing in the middle is a light that glows when the unit is on, the springey looking things are the antenna.

Now we’re going to bent the light up gently so we can access the main switch, and the serial TX/RX pins easier. Bend up gently like this:

Bending the sonoff light up

Now connect the USB serial cables to the Sonoff, like so (basically just make sure it’s 3v and not 5v and make sure the TX goes to RX, and RX to TX):

USB Serial 3V      =>  Sonoff 3V
USB Serial Ground  =>  Sonoff Ground
USB Serial TX      =>  Sonoff RX
USB Serial RX      =>  Sonoff TX

Then turn the main switch OFF, it should be labelled as OFF on latest Sonoff devices (or just switch it close to the light endpoints). It looks a bit like this:

Connecting the TX/RX serial to Sonoff ready for flashing. With the main switch off.

Now download the latest tasmota.bin firmware file from https://github.com/arendst/Tasmota/releases to your laptop ready to use shortly.

First we’re going to backup the factory Sonoff firmware:

  1. Hold down the button on the side of the Sonoff
  2. Plug the USB Serial adaptor (with the 4 serial cables connected to the Sonoff) into your laptop
  3. Let go of the button after a few seconds. The Sonoff will now be in “flash ready mode”
  4. Make a backup of the firmware with esptool read_flash feature:
$ sudo chown dtbaker: /dev/ttyUSB0  # if your user doesn't have tty access, use this hack to get it quickly and avoid permission denied errors. 
$ esptool.py --port /dev/ttyUSB0 read_flash 0x00000 0x100000 fwbackup.bin
esptool.py v2.8
Serial port /dev/ttyUSB0
Connecting....
Detecting chip type... ESP8266
Chip is ESP8285
Features: WiFi, Embedded Flash
Crystal is 26MHz
MAC: 60:01:94:e4:b9:63
Uploading stub...
Running stub...
Stub running...
1048576 (100 %)
1048576 (100 %)
Read 1048576 bytes at 0x0 in 95.4 seconds (88.0 kbit/s)...
Hard resetting via RTS pin...
$ ls -lh fwbackup.bin 
-rw-rw-r-- 1 dtbaker dtbaker 1.0M Feb  5 20:19 fwbackup.bin

Now we’re going to erase the Sonoff flash, so it’s ready for writing:

$ esptool.py --port /dev/ttyUSB0 erase_flash
esptool.py v2.8
Serial port /dev/ttyUSB0
Connecting....
Detecting chip type... ESP8266
Chip is ESP8285
Features: WiFi, Embedded Flash
Crystal is 26MHz
MAC: 60:01:94:e4:b9:63
Uploading stub...
Running stub...
Stub running...
Erasing flash (this may take a while)...
Chip erase completed successfully in 3.1s
Hard resetting via RTS pin...

Now it’s finally time to write the Tasmota.bin file to the Sonoff that you downloaded before:

$ esptool.py --port /dev/ttyUSB0 write_flash -fs 1MB -fm dout 0x0 
Downloads/tasmota.bin 
esptool.py v2.8
Serial port /dev/ttyUSB0
Connecting....
Detecting chip type... ESP8266
Chip is ESP8285
Features: WiFi, Embedded Flash
Crystal is 26MHz
MAC: 60:01:94:e4:b9:63
Uploading stub...
Running stub...
Stub running...
Configuring flash size...
Compressed 580480 bytes to 399923...
Wrote 580480 bytes (399923 compressed) at 0x00000000 in 35.3 seconds (effective 131.4 kbit/s)...
Hash of data verified.
Leaving...
Hard resetting via RTS pin...

Done!

Now unplug the USB serial adaptor, switch the main board switch from OFF to ON again, and gently push the main light back down into its original place.

While the board is still out of the housing you can plug in a Micro USB power cable to make sure it’s all working before re-assembling everything, just incase you have to flash it again for some reason.

Once it powers up, grab your phone and see if a tasmota-X wifi network is available:

Tasmota has successfully started on the Sonoff

I prefer to use my phone for this initial setup so my laptop still has internet for Googling things etc.. Connect your phone to the tasmota wifi network and visit http://192.168.4.1 then go to the Configure > Wifi section like this:

The Wifi configuration area of the Tasmota interface, as viewed from a phone

Enter your wifi ssid and password into the first two boxes then scroll down to press save. Tasmota will reboot and after 30 seconds it’ll come back up connected to your main Wifi, so you can now access this interface easier from your laptop.

Disconnect / forget the network from your phone, you wont need it again now it’s on main wifi.

Now that Tasmota is on the main wifi you have to find its IP address, as assigned by DHCP. The easiest way to do this is to have a look in your router for the list of connected devices, it should be named tasmota. For me it came through as 172.16.5.93 but it’ll be different for everyone.

So open up the tasmota interface in a web browser from your laptop again:
http://PUT-YOUR-TASMOTA-IP-HERE/

You’ll see this nice interface:

Default Tasmota interface via Web configuration tool

The first thing we need to do is change the overall “mode” of the device from “Basic Module” over to “Sonoff Bridge”. To do this go to Configure Module and choose Sonoff Bridge from the list:

Press save and wait 30 seconds for it to restart. Now refresh the main web interface again and it should look like this, confirming that the device is now in “Bridge” mode:

Sonoff main UI in bridge mode

Configure the Raspberry Pi Zero W with Node-RED and Mosquitto MQTT

Install raspbian buster light onto the Raspberry Pi W SD Card. This is beyond the scope of this blog post. Have a look at here for the download link and some instructions. Using the “light” version of raspbian is fine and will work better on th e low powered Pi Zero: https://www.raspberrypi.org/downloads/raspbian/

Once you’ve installed the Raspberry Pi, configured Wifi and enabled SSH, you can now SSH into the Pi from your laptop and install some software, like so:

$ ssh pi@ip-address-of-pizero-here
Password: raspberry 
$ sudo apt-get update
  .... takes a few minutes
$ sudo apt-get install mosquitto mosquitto-clients nodejs nodered
  .... takes a few minutes
$ sudo systemctl enable nodered.service   # so nodered starts on boot
$ sudo reboot

The first Pi reboot will take a while as Node-RED does its thing and starts up. Check back in a few minutes. Once it’s fully booted and Node-RED has started you can access the UI via a web browser at: http://ip-address-of-pi-here:1880

You should see a blank canvas ready for you to wire up the MQTT to Slack flow.

Connect Tasmota to our new MQTT broker:

Now that we have an MQTT broker installed on the Raspberry Pi (above) we can jump back into our Tasmota web UI and configure it to send all 433MHz events over MQTT to the Pi.

Go to Configure > MQTT and put in the IP address of the Raspberry Pi, and choose a topic that makes sense for your application, like so:

Configure MQTT from Sonoff

Now we can confirm the MQTT broker is receiving 433MHz events by checking the raw MQTT messages using mosquitto_sub command, like this:

$ sudo apt-get install mosquitto-clients
$ mosquitto_sub -h ip-address-of-pi-here -t "#" -v

doorSensors/RESULT {"Time":"2020-02-05T12:20:35","RfReceived":{"Sync":14060,"Low":480,"High":1380,"Data":"3E780E","RfKey":"None"}}
doorSensors/RESULT {"Time":"2020-02-05T12:20:45","RfReceived":{"Sync":14060,"Low":480,"High":1380,"Data":"3E780A","RfKey":"None"}}

If you get a Error: Connection refused message from the mosquitto_sub command ti means the IP address is wrong or the mosquitto server isn’t running on the Raspberry Pi. SSH into the Pi and fix things up.

Setup Node-RED to post messages to Slack

This is the fun part:

An example flow on how to process messages from MQTT and send to Slack

You’ll want to use these Node-RED nodes in the flow;

  • MQTT in (to read messages from the doorSensors/RESULTS topic from MQTT)
  • JSON block (to convert the mqtt string into a JSON object for the next step)
  • Function Block (to split out the device ID and open/close action from MQTT)
  • Count block (to optionally limit how many “motion” alerts to send per minute, if you’re in an area with lots of motion it can be a bit annoying having a constant stream of messages come through, we just need 1 alert)
  • Switch Block (to easily rename device IDs to friendly names like “Front Door”)
  • Function Block (to build out a JSON object that Slack can consume)
  • Slack-web-out Block (to post the message to slack)
Reading MQTT messages into Node-RED
Simple string to JSON conversion
This is the function block to decide the type of motion event and split some strings up
Any messages coming down this line will be restricted to 1 per 5 minutes
Rename our 5 character IDs to friendly names for slack
Build up some JSON that can be sent to slack

And finally we post to slack using the slack-web-out node. You’ll have to install the slack package through Node-RED menu -> “Manage Pallete” -> “Install” -> slack.

And you’ll want a legacy slack API token to configure this node, from here.

Finally send the message to Slack
The messages coming through to Slack from Node-RED

3D printing a case to hold the Sonoff and Raspberry Pi Zero

I wanted a quick case for the Sonoff + Pi duo so they can stick together for this project. The easiest way to do this was using OpenSCAD/ OpenJScad.

So I went over to https://openjscad.org/ and typed out the following quick bit of code, after measuring the size of the sonoff/pi slots:

var wallWidth = 2; // width of walls
var sonoffWidth = 63; // width of the sonoff unit
var sonoffDepth = 21; // depth of the sonoff unit
var raspberryPiZeroWidth = 66;
var raspberryPiZeroDepth = 2;
var unitWidth = raspberryPiZeroWidth + (wallWidth * 2);
var unitDepth = wallWidth + sonoffDepth + wallWidth + raspberryPiZeroDepth + wallWidth;
var unitHeight = 30;
function sonoffHolder() {
  return difference(
    // This is our main hunk of plastic rectangle, we'll cut things out of this:
    cube({size: [unitWidth, unitDepth, unitHeight]}),
    // These are the bits we cut out of the block above:
    union(
      // We move our next cut over into the center of the sonoff cut area:
      translate([(unitWidth - sonoffWidth) / 2, wallWidth, wallWidth],
        // This is the area the sonoff will slot into
        cube({size: [sonoffWidth, sonoffDepth, unitHeight - wallWidth]})
      ),
      translate([(unitWidth - raspberryPiZeroWidth) / 2, wallWidth + sonoffDepth + wallWidth, wallWidth],
        // This is the area the raspberry pi will slot into
        cube({size: [raspberryPiZeroWidth, raspberryPiZeroDepth, unitHeight - wallWidth]})
      )
    )
  );
}
function main() {
  return sonoffHolder();
}

Which produced this beauty:

Next step was to download the STL file and throw it into a Slicer like so:

Slicing the STL into GCODE

Then print:

Then assemble:

Now it’s time to put the sensors up!

Use the included double sided tape, or some glue, or screw them in. Test the sensors have enough range to reach wherever your Sonoff/Tasmota is located. You may have to move the Sonoff around to get the best reception for all your sensors.

Get creative!

Now that you have the basic setup with Sonoff, Tasmota, MQTT and Node-RED you can start to get creative.

  • Maybe graph open, closed and motion events in Grafana?
  • Send push notifications to your phone if you’re not home?
  • Control other 433MHz appliances and switches?
  • Turn a light on when a gate or door is opened?
  • Send an alert if a door is left open too long?

Monitoring Solar Panels with NodeRed + InfluxDB + Grafana

This post will guide you through how to monitor your solar panels using some free open source tools running on your local network.

Required technical level: Moderate to Advanced experience with programming/networking/sql/docker. This certainly isn’t a point and click solution.

Here’s a summary of the setup:

  • Solar System:
    • 18 x REC 350W panels
    • 18 x Enphase IQ 7+ Micro-Inverters: Each panel gets an inverter, allows per-panel monitoring, no DC power loss over long runs as you get AC straight from the panels, and no central inverter.
    • 1 x Envoy S-Metered: This is the brains that communicates with all the panels and manages input/output. It’s connected to your wifi/ethernet. We will talk to local Envoy API endpoints to read realtime power usage, individual solar panel status and solar generation data.
  • Software:
    • NodeRED: This is a cool piece of software that lets you build a program by dragging blocks around and connecting them with lines. It’s in a nice web interface and doesn’t take long to get use to. I like NodeRED because you can always just drag a “function” block onto the screen and write whatever you like in JavaScript, so you’re not limited to the drag & drop blocks it provides.
    • InfluxDB: This is a special kind of database where we store all our solar data in a time based format. InfluxDB is really good at certain time based queries and makes graphing and computing things easy.
    • Grafana: This is the pretty graphing dashboard that we’ll run to highlight various parts of our solar usage. It will read the data straight from InfluxDB and we can configure graphs and values to display in each dashboard card.
    • Unraid (optional): This is a really cool operating system that runs off a USB drive. I’ve got it running on an old computer with 4 hard drives. Unraid has very nice harddrive / redundancy management, and an even nicer “docker” app repository. The above 3 pieces of software can all be installed within Unraid and be up and running in minutes. Setting up Unraid can be quite involved, so do some research before decided to go down that path. The software above can run fine elsewhere on your local network, even on a Raspberry Pi sitting on your shelf.

How does it work?

Basically Node-RED is configured to reads values directly from the Enphase solar system API every few seconds. This happens over the local network. No internet connection is required. Node-RED does some basic calculations on the data and then writes that data to InfluxDB.

What we end up with is a nice historical record of solar generation, house power usage, individual panel output, how much we’re exporting to the grid and how much we’re pulling from the grid.

Once all the data is in InfluxDB you can configure Grafana to display pretty graphs based on that data. You can also query InfluxDB directly to determine current power usage / daily usage in order to decide on some home automation tasks (e.g. is there enough Solar generation to turn the aircon on automatically?)

I’ll break all these steps down into sections below:

The Enphase Envoy API:

The Envoy S-Metered is connected to your local network (either via WiFi or an Ethernet cable). It has some handy “API” endpoints that you can query to get all sorts of interesting data.

How do you find these API endpoints? The easiest way is to login to the Envoy web dashboard and view the Chrome network inspector. You can see all the API requests that are used to generate the built in Envoy dashboard. But to save you from that hassle I’ve listed some of them below:

Assuming 10.30.0.14 is the IP address of your Envoy, try these endpoints:

http://10.30.0.14/production.json – current consumption and production data:
{
   "production": [
     {
       "type": "inverters",
       "activeCount": 18,
       "readingTime": 1580639250,
       "wNow": 0,
       "whLifetime": 7062574
     },
     {
       "type": "eim",
       "activeCount": 1,
       "measurementType": "production",
       "readingTime": 1580639747,
       "wNow": -0.846,
       "whLifetime": 7047408.938,
       "varhLeadLifetime": 311108.038,
       "varhLagLifetime": 828596.594,
       "vahLifetime": 7906976.196,
       "rmsCurrent": 1.228,
       "rmsVoltage": 246.489,
       "reactPwr": 299.641,
       "apprntPwr": 302.769,
       "pwrFactor": 0,
       "whToday": 42939.938,
       "whLastSevenDays": 245381.938,
       "vahToday": 46195.196,
       "varhLeadToday": 3546.038,
       "varhLagToday": 2608.594
     }
   ],
   "consumption": [
     {
       "type": "eim",
       "activeCount": 1,
       "measurementType": "total-consumption",
       "readingTime": 1580639747,
       "wNow": 3484.957,
       "whLifetime": 6812381.751,
       "varhLeadLifetime": 2232700.619,
       "varhLagLifetime": 1052652.057,
       "vahLifetime": 7903318.012,
       "rmsCurrent": 15.917,
       "rmsVoltage": 246.502,
       "reactPwr": -870.018,
       "apprntPwr": 3923.522,
       "pwrFactor": 0.89,
       "whToday": 67028.751,
       "whLastSevenDays": 412962.751,
       "vahToday": 35580.012,
       "varhLeadToday": 9815.619,
       "varhLagToday": 5360.057
     },
     {
       "type": "eim",
       "activeCount": 1,
       "measurementType": "net-consumption",
       "readingTime": 1580639747,
       "wNow": 3485.803,
       "whLifetime": 3241750.723,
       "varhLeadLifetime": 1921592.581,
       "varhLagLifetime": 224055.463,
       "vahLifetime": 7903318.012,
       "rmsCurrent": 14.689,
       "rmsVoltage": 246.516,
       "reactPwr": -570.377,
       "apprntPwr": 3620.717,
       "pwrFactor": 0.97,
       "whToday": 0
     }
   ],
   "storage": []
 }
http://10.30.0.14/api/v1/production/inverters – Current individual panel data
Note: this endpoint is protected with authenentication. Use the last 6 digits of the Envoy serial number as password, example:
curl -m 10 --digest -u "envoy:054786" http://10.30.0.14/api/v1/production/inverters
{
     "serialNumber": "121921415",
     "lastReportDate": 158062595,
     "devType": 1,
     "lastReportWatts": -1,
     "maxReportWatts": 286
   },
   {
     "serialNumber": "121923489",
     "lastReportDate": 1580632601,
     "devType": 1,
     "lastReportWatts": -1,
     "maxReportWatts": 286
   },
   {
     "serialNumber": "12191458",
     "lastReportDate": 158063252,
     "devType": 1,
     "lastReportWatts": -1,
     "maxReportWatts": 285
   },
   {
     "serialNumber": "121921029",
     "lastReportDate": 158063298,
     "devType": 1,
     "lastReportWatts": -1,
     "maxReportWatts": 285
   },
   {
     "serialNumber": "121921138",
     "lastReportDate": 158063204,
     "devType": 1,
     "lastReportWatts": -1,
     "maxReportWatts": 285
   },
   {
     "serialNumber": "121921119",
     "lastReportDate": 158063615,
     "devType": 1,
     "lastReportWatts": -1,
     "maxReportWatts": 285
   },
   {
     "serialNumber": "121921534",
     "lastReportDate": 158063212,
     "devType": 1,
     "lastReportWatts": -1,
     "maxReportWatts": 286
   },
   {
     "serialNumber": "121921155",
     "lastReportDate": 158062619,
     "devType": 1,
     "lastReportWatts": -1,
     "maxReportWatts": 286
   },
   {
     "serialNumber": "121911022",
     "lastReportDate": 15806257,
     "devType": 1,
     "lastReportWatts": 1,
     "maxReportWatts": 286
   },
   {
     "serialNumber": "121926272",
     "lastReportDate": 158063614,
     "devType": 1,
     "lastReportWatts": -1,
     "maxReportWatts": 286
   },
   {
     "serialNumber": "121917527",
     "lastReportDate": 158063607,
     "devType": 1,
     "lastReportWatts": -1,
     "maxReportWatts": 286
   },
   {
     "serialNumber": "121115993",
     "lastReportDate": 158062616,
     "devType": 1,
     "lastReportWatts": -1,
     "maxReportWatts": 286
   },
   {
     "serialNumber": "121116476",
     "lastReportDate": 158063610,
     "devType": 1,
     "lastReportWatts": -1,
     "maxReportWatts": 285
   },
   {
     "serialNumber": "121117665",
     "lastReportDate": 158063592,
     "devType": 1,
     "lastReportWatts": -1,
     "maxReportWatts": 286
   },
   {
     "serialNumber": "121913427",
     "lastReportDate": 158062600,
     "devType": 1,
     "lastReportWatts": -1,
     "maxReportWatts": 285
   },
   {
     "serialNumber": "121115085",
     "lastReportDate": 158063206,
     "devType": 1,
     "lastReportWatts": -1,
     "maxReportWatts": 286
   },
   {
     "serialNumber": "121916266",
     "lastReportDate": 158032602,
     "devType": 1,
     "lastReportWatts": -1,
     "maxReportWatts": 285
   },
   {
     "serialNumber": "121117194",
     "lastReportDate": 158062609,
     "devType": 1,
     "lastReportWatts": -1,
     "maxReportWatts": 286
   }
 ]
http://10.30.0.14/stream/meter – HTTP stream of instant usage / generation data
This is a fun one! It's a stream of instant values coming straight out of the Envoy. Turn on a light or the oven? You'll see values change in this stream instantly! Get some clouds go over the solar panels? Production numbers drop off instantly. It's really cool.
You'll need the installer password for the Envoy. You could ask your installer or google around for the way to find this out yourself. 
Getting access to this HTTP stream in Node-RED is a little tricky, and I certainly wouldn't recommend storing EVERY single value received in InfluxDB. But I've found this endpoint has been good to get instant values out of the Inverter when I want to do some home automation such as dialing back the aircon or pool pump if it gets cloudy.
Here's the output from this HTTP stream:
{
   "production": {
     "ph-a": {
       "p": 0,
       "q": 302.684,
       "s": 305.658,
       "v": 248.005,
       "i": 1.233,
       "pf": 0,
       "f": 50
     },
     "ph-b": {
       "p": 0,
       "q": 0,
       "s": 0,
       "v": 0,
       "i": 0,
       "pf": 0,
       "f": 0
     },
     "ph-c": {
       "p": 0,
       "q": 0,
       "s": 0,
       "v": 0,
       "i": 0,
       "pf": 0,
       "f": 0
     }
   },
   "net-consumption": {
     "ph-a": {
       "p": 1838.766,
       "q": -608.615,
       "s": 2144.261,
       "v": 248.097,
       "i": 8.647,
       "pf": 0.85,
       "f": 50
     },
     "ph-b": {
       "p": 0,
       "q": 0,
       "s": 0,
       "v": 0,
       "i": 0,
       "pf": 0,
       "f": 0
     },
     "ph-c": {
       "p": 0,
       "q": 0,
       "s": 0,
       "v": 0,
       "i": 0,
       "pf": 0,
       "f": 0
     }
   },
   "total-consumption": {
     "ph-a": {
       "p": 1838.766,
       "q": -911.299,
       "s": 2450.762,
       "v": 248.051,
       "i": 9.88,
       "pf": 0.75,
       "f": 50
     },
     "ph-b": {
       "p": 0,
       "q": 0,
       "s": 0,
       "v": 0,
       "i": 0,
       "pf": 0,
       "f": 0
     },
     "ph-c": {
       "p": 0,
       "q": 0,
       "s": 0,
       "v": 0,
       "i": 0,
       "pf": 0,
       "f": 0
     }
   }
 }

Node-RED setup:

This is the fun part! I’m going to assume some familiarity with Node-RED here. But if you’ve never used it just jump in and you’ll find it surprising intuitive.

Screenshot of Node-RED connected to Solar system

The above screenshot has 3 main flows going on:

  1. Processing the Envoy HTTP stream every few seconds.
  2. Processing the full details/overall production data every minute.
  3. Processing the individual panel data every 5 minutes.

todo..

Elementor Template Kits

You may be wondering: “What is an Elementor Template Kit?

A Template Kit contains pretty visual layouts that are compatible with your page builder. It may contain a nicely designed Home Page, a Contact page, or some Testimonial Blocks that you can drag & drop onto any page. Each of these pre-made templates are fully customizable in the editor.

A Template Kit can be an alternative to a WordPress theme. First you install a free page builder plugin (for example, Elementor) and then you can install a pre-designed Elementor Template Kit. From there you can design your entire website in an intuitive drag & drop editor.

What’s inside a Template Kit?

A Template Kit is a ZIP file that contains a number of Templates. The Templates within the Kit are all similar in design and style.

A Template Kit may contain several Templates, such as:

  • Home Page
  • Contact Page
  • Profile Block
  • Portfolio Block
  • Contact Form
  • Product Listing
  • Decorated Image Block
  • Testimonial Block
This is an example of a “Template” within a “Template Kit”. This template lets you display any image in a pretty flower frame with some text on the right hand side. Everything is drag & drop within the Elementor page builder.

How do you use a Template Kit?

To use a Template Kit you do not need to know any WordPress coding, or any web development coding skills, and you do not need to purchase a WordPress theme. Here are some steps to use a Template Kit on WordPress:

  1. Install WordPress
  2. Install the free WordPress theme Hello Elementor.
  3. Install the free WordPress plugin Elementor.
  4. Install the free WordPress plugin Template Kit Import.
  5. Download a Template Kit (zip file) to your desktop.
    • You can purchase Template Kits through ThemeForest.net.
    • You can download Template Kits as part of your Envato Elements paid subscription.
    • You can download free Template Kits from the Elementor Library.
  6. Navigate to Tools > Template Kit in WordPress.
  7. Upload your Template Kit zip file to WordPress.
  8. Choose which of the pre-made template layouts you would like to add to your website.
  9. Edit

Template Kits can be much more configurable than a standard WordPress theme. You have fine grained control over every element on the page. Don’t like some text in the template? No worries, simply delete it. Need the column to be wider? Just drag and drop it wider. Need to change all the colours to suit your brand? Everything is configurable in the editor.

This is how easy it is to change Template Kit settings. Every element on the page is configurable via the point + click interface. No code required.

To install a Template Kit into your WordPress website please use the free Template Kit Import plugin.

Where can I find Template Kits?

Template Kits can be downloaded from a number of places:

  • You can purchase Template Kits through ThemeForest.net.
  • You can download Template Kits as part of your Envato Elements paid subscription.
  • You can download free Template Kits from the Elementor Library.
Example screenshot from a popular Template Kit on ThemeForest.net

Load data into Google Spreadsheet from PHP

Lets say you have a PHP script that returns a value, based on a query string:

mysite.com/query.php?string=blah

Getting the result of that PHP script into Google Sheets is rather easy. Open Tools > Script Editor in Google Sheets and create a function like this:

function getMyQueryValue(value) {
   var response = UrlFetchApp.fetch("https://mysite.com.com/query.php?string=" + value);
   Logger.log(response.getContentText());
   return JSON.parse(response);
}

Now in Google Sheets simply type =getMyQueryValue(A1) and it will pass the value of A1 over to your PHP script and return the result into your Google Sheet!


There’s also a really cool 1 liner way to inject data into Google Sheet if the remote host returns XML/HTML.. Lets pretend get-xml.php?query=2020-01-01 returns this content:

<result>
  <value>12</value>
</result>

Then in Google Sheets you simply have to use the built in importxml function like this:

=VALUE(REGEXREPLACE(IMPORTXML("https://mysite.com/get-xml.php?query=" & A1 , "//value"),"\D+", ""))

Read more about IMPORTXML here.

Linux: Monitor what is using your webcam

If your curious what programs are attempting to read your Linux webcam then it’s possible to use the auditctl and ausearch tools to see what process is using the webcam.

First install the auditd package:

sudo apt-get install auditd

Then run auditctl to start monitoring the /dev/video0 device for access:

sudo /sbin/auditctl -w /dev/video0 -p war

Then after a little while use the ausearch tool to see which processes have tried to access /dev/video0 :

sudo /sbin/ausearch -f /dev/video0

The results will look something like this:

time->Sat Feb  1 17:50:29 2020
 type=PROCTITLE msg=audit(1580543429.661:123): proctitle=63617400234
 type=PATH msg=audit(1580543429.661:123): item=0 name="/dev/video0" inode=3413842 dev=fd:00 mode=0100664 ouid=1000 ogid=1001 rdev=00:00 nametype=NORMAL cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0 cap_frootid=0
 type=CWD msg=audit(1580543429.661:123): cwd="/home/dtbaker"
 type=SYSCALL msg=audit(1580543429.661:123): arch=c000003e syscall=257 success=yes exit=3 a0=ffffff9c a1=7ffc0b5422d6 a2=0 a3=0 items=1 ppid=8910 pid=10436 auid=1000 uid=1000 gid=1001 euid=1000 suid=1000 fsuid=1000 egid=1001 sgid=1001 fsgid=1001 tty=pts8 ses=173 comm="cat" exe="/usr/bin/cat" key=(null)

This shows that the command cat was used to try access /dev/video0 😀

Fix “orphan image” in unraid docker

This can happen if you’re manually messing around with the location of docker configuration files on the hard drives.

If the orphan image is from a Community Applications installed docker image, simply follow these steps:

  1. Go to the App menu
  2. Choose Previous Apps on the left hand side
  3. Click the reinstall icon on the app associated with the orphan image
  4. Confirm the data/config directories match those that already exist on unraid
  5. Run the reinstall
  6. The broken docker app will be back up and running with all the saved config and data available