We’re working on the 2.0 re-write of our Full Site Builder called StylePress.
Follow along at github.com/dtbaker/stylepress or check out the Full Site Builder Website
We’re working on the 2.0 re-write of our Full Site Builder called StylePress.
Follow along at github.com/dtbaker/stylepress or check out the Full Site Builder Website
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’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.
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": []
} ]
This technique uses the following Hardware:
And the following free and/or open source Software:
Here are the steps covered in detail below:
Lets get started!
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:
Then make sure all power is off and open it up, you’ll see the board sitting inside:
Now pull the board out gently, it should pop out with little force:
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:
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:
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:
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:
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:
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:
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:
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.
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:
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.
This is the fun part:
You’ll want to use these Node-RED nodes in the flow;
doorSensors/RESULTS
topic from MQTT)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.
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:
Then print:
Then assemble:
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.
Now that you have the basic setup with Sonoff, Tasmota, MQTT and Node-RED you can start to get creative.
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:
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 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:
{ "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": [] }
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 } ]
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
}
}
}
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.
The above screenshot has 3 main flows going on:
todo..
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.
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:
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:
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.
To install a Template Kit into your WordPress website please use the free Template Kit Import plugin.
Template Kits can be downloaded from a number of places:
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.
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
😀
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: