Autodictating to self using Whisper to preserve privacy

Thursday, August 17, 2023 

Whisper is a very nice bit of code released by OpenAI, the kind people who brought us ChatGPT.  It’s a speech to text tool that can handle a huge array of languages and runs locally, as in on your hardware with your data.  There’s an API you can use on their servers, but only if you are sure the audio files and text can be released to the public.  Never put any data on anyone else’s hardware that you wouldn’t want to have leaked on pastebin or published in the New York Times; that goes for all services including gmail, Outlook, Office 365, etc.  Never, ever use someone else’s hardware to store proprietary or sensitive data.  It’s just mind-bogglingly stupid, and yet so many people fail to comprehend that “in the cloud” just means “on someone else’s computer.”

This is also true for most speech-t0-text tools that (seemingly) kindly offer to translate your ramblings to text.  Lots of people use this feature on their phones without realizing that, like Alexa, any voice command tool is an audio monitoring device you stupidly paid for and installed yourself on behalf of corporate spies who are all too happy to listen to whatever you have to say.  If you have an Alexa, get a hammer right now and smash it.  Go on, I’ll wait.  Good job.  Privacy restored.  Oh, smart TV too? Unplug that stupid thing from the internet.  Same for all your “smart” devices.  You thought “smart” meant you were smart for buying it?  Noooo… you’re a moron for buying it, the company was smart for convincing you to install monitoring devices in your house at your own expense.  Congrats. Own goal.  When you’re finished destroying all your corporate spyware here’s a way to get speech to text capability on your own hardware without the spying thanks to a very nice bit of FOSS code from OpenAI.

The workflow is to record some audio (speech probably) on your phone, store & forward that to your server (no synchronous connection required, unlike most spyware), (optionally) store and forward that to your desktop computer with a GPU to run AI text to speech, pop the results into an email queue to store & forward it back to you and all your searchable text archives. Speech is converted to accessible, indexed text easily and robustly and fairly legibly.

For the recording step, I use an Open Source app called Audio Recorder (available on F-Droid and other reliable repositories; if you need an app, try F-droid first and only use Play Store after deciding it is worth being spied on and having ads pushed to you).  Audio can be any length, seconds or hours.  I configured the settings to record to /storage/emulated/0/recordings and use 48khz, 16 bit, opus for speech; on my device the app supports up to 24bit/192khz, which vastly exceeds the S:N ratio and bandwidth of any microphone I’ll connect to a phone, but nice to know for audiophiles.

I also run NextCloud on my phone which connects to a NextCloud instance on my own server.  NextCloud is like a free, open source version of dropbox and provides directory sharing, calendar, password, etc – almost all services you want a server for on your own hardware so you actually retain possession and ownership of your data – amazing!  You do not have to give away your data to people you don’t know to use the internet.

The NextCloud client on my phone tries to sync the recording folder to my server so after I make a recording and hit the ✅ button, when the aether makes it possible the audio is uploaded (and, optionally, deleted from the mobile device). Nextcloud then syncs down to other clients, specifically one of my Linux clients for processing. It is entirely possible to do everything server side and the same scripts will work, but I don’t have a GPU on my server and Whisper has some dependencies that are easier to meet on a more frequently updated client, at least for now.

I’ve installed whisper on a Linux box, along with a NextCloud client and there I have a fairly simple script running as a cron job. Every 10 minutes it scans all the files in the locally synced “Recordings” directory and if there’s an audio file without a matching text “TSV” file, it calls whisper to convert the audio to text and then emails me the converted text.  That text is also synced back up to the server and to any other synced device and indexed both on the server and locally to make it easily discoverable (on clients I use the very awesome Recoll for indexing).

The whole process is very easy and any audio file like this:

is then automagically converted to text

test if we can record in Opus and then autoconvert the file back to text and
get that text as an email automatically this seems like quite a powerful tool
and should make it fairly easy to self take notes don’t we think yes

and then ends up in my inbox like this:

So what script does this good thing?  Just a few bash lines.  This version uses the time stamps in the TSV files to throw in fairly reasonable paragraph breaks. If the speaker pauses long enough that Whisper inserts a timing break, the script printfs in two newlines. There are a few other tricks below to try to infer or force reasonable paragraph breaks.

It also uses a slightly more robust construction to extract the subject of the email, which includes the first 60 characters of the text, minus any new lines (which make mailx barf).  The resulting text is flowed, pretty easy to copypasta into an email or document, and has moderately natural paragraph breaks.  It isn’t publication ready, but the accuracy seems quite good and it is hard to imagine an easier mechanism for making useful autodictations.  The process supports very long rambling diatribes, you should be able to talk for hours and get book’s worth of text in your inbox. I mean, maybe you shouldn’t be able to do that, but you can.

I put in a feature request with the Audio Recorder devs to add some metainfo to the files; what I’d really like is location data.  I can script up extracting that and (optionally) converting it to a place name, but aside from Nominatim or Gisography, there aren’t many options other than using big data APIs.  Anyway, seems like a reasonable bit of metadata to insert at the top or tail of the text: time+date+location the stream was recorded. If it is implemented, I’ll update to script to extract the metadata and create a dateline header.

Mailing flowed plain text

I found that mailx can’t handle long (flowed) text lines over ~1000 characters and inserts \n  at 998 or 997, which breaks up the pause to paragraphs code, so I switched the mailer to mpack (sudo apt install mpack) which simplifies the mail command and MIME encodes the text body and adds a checksum and a few other modern mail niceties and it now flows as desired without weird line breaks.

And then I found out that mpack thinks it is too good to send text files, it sets the MIME type to application/octet-stream and using the -c text/plain option yields the somewhat prissy error This program is not appropriate for encoding textual data oh my.  Thunderbird actually parses the attachment into a nicely flowed email, ignoring the quirks, but the best mobile client ever, FairEmail, does not and treats the attachment as something that it would prefer not to display inline (thanks for the details Marcel, you’re awesome!), given mailx isn’t very active any more changing that behavior is unlikely.  Next option: Mutt.  Mutt does something to a text attachment (using the -a option) that causes both TB and FairEmail to decline to display inline, but the body option -i yields a clean text-only email with the right flow, meaning no random line breaks inserted, so don’t install mpack, but sudo apt install mutt and create a /home/{user}/.muttrc file with at least the below (search engine around if you need to use a remote SMTP server to configure the server address, authentication, and encryption; mutt does the right things):

set realname = "{desired name}"
set from = "{your from email}"
set use_from = yes
set envelope_from = yes

And once that (and whisper) is working, the following script will convert your audio file to text and then mail it to you with paragraph breaks.


I didn’t plan to get into anything more complex, but long text conversions are kinda unreadable because Whisper doesn’t infer text.  There’s a whole science to inferring contextual shifts that should start new paragraphs using LSA/LDA/LSI that’s quite advanced mathematically and works sort of OK but is an awful lot of pipping modules and trying this or that.

I opted instead to go for a more brute force method, well three of them, really:

First: whisper has an experimental feature to compute word timings, which would normally be used to generate those unbelievably distracting and annoying and utterly horrible subtitles that are one word at a time or bouncing highlight word by word, but the feature can do more than create a miserable, distracting, utterly pretentious viewing experience: they seem to increase the frequency and possibly accuracy of gaps in the exported timing data. The first method of paragraph finding is detecting “long” gaps after a Whisper inferred sentence, effectively deriving speaker intent from cadence and AI content inference.  It works OK.

Second: I implemented a wake_word:command set that seds through the text and search-replaces the wake_word:command with the requested punctuation: .¶,:()…—?!“” There’s a whole theory behind wake words, but “insert” seems to be understood well and the command terms are ones that I tend to think of (e.g. “dots” not “ellipsis”), but that’s all obviously editable to preference.

Third: recommended paragraph length depends on the target and advice ranges from 3 sentence to 6.  I tend to be a bit long winded so I picked 5.  There’s an arbitrary script to look for any line that, after the timing inference and explicit breaks, still has more than 5 sentences and breaks it into multiple lines (meaning paragraph splits when the text is rendered). If that’s too long or too short, change the 5 in /usr/bin/sed -i "s/[.?!] /.\n\n/5;P;D" "$txt_file".

This all work fairly well, though there’s a known quirk with Whisper where it just randomly stops inserting punctuation after about 10 minutes and mechanisms 1 and 3 obviously also fail.  The way to deal with that is to break the audio into about 5 minute segments and then concatenate the results, but it’s a moderate chunk of code and debug and I’m assuming whisper will be updated.  If not and it gets annoying, I’ll work out that routine.

The script

Replace {user} and {domain} as appropriate to your system.  You may also have a different layout for commands, which bin (for example) is your friend.  I find full paths in cron execution provides better consistent reliability at the expense of portability.



# Function to check if an audio file has a matching .txt file, then convert to text and email it
convert_to_text() {
    dir="$(/usr/bin/dirname "${audio_file}")"
    base_ext="$(/usr/bin/basename "${audio_file}")"

    if [ ! -e "$tsv_file" ]; then
        /home/gessel/.local/bin/whisper "$audio_file" -f tsv --model small.en -o $dir --word_timestamps True --prepend_punctuations True --append_punctuations True --initial_prompt "Hello."

        while IFS=$'\t' read -r start stop text; do
            # First line detection and skip checking it for gaps
            if [ $start == "start" ]; then
                /usr/bin/printf "" > "$txt_file"
            # Check if line ends in period or question mark for paragraph insertion
            if [[ $text =~ \.$|\?$ ]]; then
                # find natural pauses and insert paragraph breaks
                if [[ $stop_prev != $start ]]; then
                /usr/bin/printf "\n\n" >> "$txt_file"
            /usr/bin/printf "$text " >> "$txt_file"
        done  < "$tsv_file"

        # search for explicit formatting commands and in-line replace them.
        /usr/bin/sed -i "s/[?,. ]*$wake_word period[?,. ]*/. /gI" "$txt_file"
        /usr/bin/sed -i "s/[?,. ]*$wake_word paragraph[?,. ]*/.\n\n/gI" "$txt_file"
        /usr/bin/sed -i "s/[?,. ]*$wake_word comma[?,. ]*/, /gI" "$txt_file"
        /usr/bin/sed -i "s/[?,. ]*$wake_word colon[?,. ]*/: /gI" "$txt_file"
        /usr/bin/sed -i "s/[?,. ]*$wake_word open paren[?,. ]*/ (/gI" "$txt_file"
        /usr/bin/sed -i "s/[?,. ]*$wake_word close paren[?,. ]*/) /gI" "$txt_file"
        /usr/bin/sed -i "s/[?,. ]*$wake_word dots[?,. ]*/… /gI" "$txt_file"
        /usr/bin/sed -i "s/[?,. ]*$wake_word long dash[?,. ]*/—/gI" "$txt_file"
        /usr/bin/sed -i "s/[?,. ]*$wake_word question[?,. ]*/? /gI" "$txt_file"
        /usr/bin/sed -i "s/[?,. ]*$wake_word exclamation[?,. ]*/? /gI" "$txt_file"
        /usr/bin/sed -i "s/[?,. ]*$wake_word open quote[?,. ]*/ “/gI" "$txt_file"
        /usr/bin/sed -i "s/[?,. ]*$wake_word close quote[?,. ]*/” /gI" "$txt_file"
        # brute force paragraphing: 5 sentences is enough, adjust for audience
        /usr/bin/sed -i "s/\([.?!]\) /\1\n\n/5;P;D" "$txt_file"
        # fix any sentence start/finish errors induced by the above edits
        /usr/bin/sed -i "s/^[a-z]/\U&/g" "$txt_file" # start with uppercase
        /usr/bin/sed -i "s/: [A-Z]/\L&/g" "$txt_file" # no uppercase after colon
        /usr/bin/sed -i 's/\s\+$//g' "$txt_file" # don't end with whitespace
        /usr/bin/sed -i "s/[,]$/./g" "$txt_file" # don't end with a comma, use .
        /usr/bin/sed -i '/[.?!]$/! s/$/./' "$txt_file" # if not ending with punctuation at all, add .
        /usr/bin/sed -i 's/^\.$//'  "$txt_file" # oops, no lines with just periods 
        /usr/bin/sed -i "s/\([a-z]\) \./\1./g" "$txt_file" # remove any spaces before periods
        /usr/bin/sed -i "s/  / /g" "$txt_file" # no double spaces
        /usr/bin/sed -i 's/\([0-9]\+\) \([FC]\) /\1°\2 /g' "$txt_file" # write temp to AMA, Chicago, Nat Geo, NOT APA or NIST
        # generate subject line from first sentence no longer than 80 char and remove any newlines
        subject=$(/usr/bin/head -n 1 -c 80 "$txt_file" | /usr/bin/sed 's/\(.*\)\..*/\1/')
        subject=$(/usr/bin/echo $subject | /usr/bin/tr -d '\n')
        subject=$(/usr/bin/echo $subject | /usr/bin/tr -d '\r')
        # send the cleaned up file as email
        /usr/bin/echo "" | /usr/bin/mutt  -F /home/gessel/.muttrc -s "AudioText - $base - $subject" -i "$txt_file" $to

# Main script scan the watch dir for unprocessed files (within the last 30 days)
/usr/bin/find "$watchdir" -mtime -30 -type f \( -iname \*.opus -o -iname \*.wav -o -iname \*.ogg -o -iname \*.mp3 \) | while read audio_file; do
    convert_to_text "$audio_file"


Note that Whisper has a lot of tricks not used here.  I’ve used it to add subtitles to lectures and it can do things like auto-translate one spoken language into another text language, and much more.

Posted at 10:53:58 GMT-0700

Category: CodeHowToLinuxTechnology

Projecting Qubit Realizations to the Cryptopocalpyse Date

Friday, August 4, 2023 


RSA 2048 is predicted to fail by 2042-01-15 at 02:01:28.
Plan your bank withdrawals accordingly.


Way back in the ancient era of 2001, long before the days of iPhones, back when TV was in black and white and dinosaurs still roamed the earth, I delivered a talk on quantum computing at DEF CON 9.0.  In the conclusion I offered some projections about the growth of quantum computing based on reported growth of qubits to date. Between the first qubit in 1995 and the 8 qubit system announced before my talk in 2001, qubits were doubling about every 2 years.

I drew a comparison with Moore’s law that computers double in power every 18 months, or as 2(years/1.5). A feature of quantum computers is that the power of a quantum computer increases as the power of the number of qubits, which is itself doubling at some rate, then two years, or as 22(years/2), or, in ASCII: Moore’s law is 2^(Y/1.5) and Gessel’s law is 2^2^(Y/2).

Quantum Computing and Cryptography 2001 7.0 Conclusion slide

As far as I know, nobody has taken up my formulation of quantum computing power as a time series double exponential function of the number of qubits in a parallel structure to Moore’s law. It seems compelling, despite obviously having a few (minor) flaws. A strong counter argument to my predictions is that useful quantum computers require stable, actionable qubits, not noisy ones that might or might not be in a useful state when measured. Data on stable qubit systems is still too limited to extrapolate meaningfully, though a variety of error correction techniques have been developed in the past two decades to enable working, reliable quantum computers. Those error correction techniques work by combining many “raw” qubits into a single “logical” qubit at around a 10:1 ratio, which certainly changes the regression substantially, though not the formulation of my “law.”

I generated a regression of qubit growth along the full useful quantum computer history, 1998–2023, and performed a least-squares fit to an exponential doubling period and got 3.376 years, quite a bit slower than the heady early years’ 2.0 doubling rate. On the other hand, fitting an exponential curve to all announcements in the modern 2016–2023 period yields a doubling period of only 1.074 years. The qubit doubling period is only 0.820 years if we fit to just the most powerful quantum computers released, ignoring various projects’ lower-than-maximum qubit count announcements; I can see arguments for either though selected the former as somewhat less aggressive.

Relative Power of Classical vs. Quantum Computers

From this data, I offer a formulation of what I really hope someone else somewhere will call, at least once, “Gessel’s Law,” P = 22(y/1.1) or, more generally given that we still don’t have enough data for a meaningful regression, P = 22(y/d); quantum computational power will grow as 2 to the power 2 to the power years over a doubling period which will become more stable as the physics advance.

Gidney & Ekra (of Google) published How to factor 2048-bit RSA integers in 8 hours using 20 million noisy qubits, 2021-04-13. So far for the most efficient known (as in not hidden behind classification, should such classified devices exist) explicit algorithm for cracking RSA. The qubit requirement, 2×10⁷, is certainly daunting, but with a doubling time of 1.074 years, we can expect to have a 20,000,000 qubit computer by 2042. Variations will also crack Diffie-Hellman and even elliptic curves, creating some very serious security problems for the world not just from the failure of encryption but the exposure of all so-far encrypted data to unauthorized decryption.

Based on the 2016–2023 all announcements regression and Gidney & Ekra, we predict RSA 2048 will fall on 2042-01-15 at 2am., a prediction not caveated by the error correction requirement for stable qubits as they count noisy, raw, cubits as I do. As a validity check, my regression predicts “Quantum Supremacy” right at Google’s 2022 announcement.


Qubit Realization by Date and several regression curve fits to the data

IQM Quantum Computer Espoo Finland, by Ragsxl

Posted at 05:34:25 GMT-0700

Category: PrivacyTechnology

AI PSYOPS are changing strategic messaging

Saturday, July 29, 2023 

Social media fundamentally changed strategic messaging, cutting the cost per effect by at least two orders of magnitude, probably more.  It has become the most cost effective munition in the global arsenal. Even when it took teams of actual humans to populate content and troll farms to flood social media with messaging intended to result in a desired outcome, for example to swing an election, start a war, damage alliances, break treaties, or generate support for one particular policy, foreign or domestic, or another, it was still a revolution in reduced cost warfare.

Take Operation INFEKTION, the active measure campaign run by the KGB starting in about 1983″to create a favorable opinion for us abroad that this disease (AIDS) is the result of secret experiments with a new type of biological weapon by the secret services of the USA and the Pentagon that spun out of control.”

This campaign leveraged assets put in place as far back as 1962 and eventually consumed the authority of Prof. Jakob Segal as a self-referential authoritative citation.  After a little more than a decade of relentless media placements of strategic messaging, even in the United States more than 25% of the population had been convinced AIDS was a government project and 12% had been manipulated into believing it was created and spread by the CIA. This project was tremendously successful despite having to overcome the then standard and generally principled editorial gate keeping that protected “traditional” media from abuse and cooptation by manufacturing plausible chains of authority and fabricating deep and broad reference chains to thwart fact checking.

By the 2016 Election, the KGB’s successors, the IRA and GRU, efficiently and expertly leveraged social media to achieve even more impressive results, possibly winning the most significant military battle in history, to alter the outcome of the US election at a cost of only a few billion dollars and within a mere 2-3 years of effort.

Any-to-any publishing circumvents editorial protections (he writes without a trace of irony).  What might otherwise be a limitation of psyop being clearly outside any authorative endorsement, something that required the consumption of an asset like Jakob Segal to achieve in an earlier era, has been overwhelmingly diminished by a parallel effort to destroy trust in institutions and authority creating a direct path to shape the beliefs of targets through mass individualization of messaging unchecked by any need for longitudinal reputation building.

That the 2016 effort still cost billions, requiring a massive capacity build of English speaking, internet savvy teams inducted into “troll farms,” (many ironically located in Bulgaria given that county’s role in Operation INFEKTION) may already be obsolete just 8 years later.

Many have written about ChatGPT representing some sort of existential risk to humanity’s future, some quick resolution to the Fermi Paradox, but the real risk is an acceleration of the destruction of objective truth and the substitution conceptual paradigms that align with strategic outcomes.

As an example, let me introduce to you Dr. Alexander Greene, a person ChatGPT tells us “is a highly esteemed and celebrated professor with a remarkable career dedicated to advancing the fields of green energy and engineering.”

ChatGPT fabricates the lauded synthetic Dr. Alexander Greene

Obviously, it’s hard to really believe Dr. Greene without seeing the man himself, but fortunately we have a tool for that too:

Dr Alexander Greene, synthetic professor of green things and advocate for fossil fuels.

A few images from bing/Dall-E and we can create a very convincing article that would easily pass muster as an authoritative discussion on the benefits of continuing to burn fossil fuels with minimal editing and formatting, just to cut out the caveats that ChatGPT inserts in counterfactual text requests we can have such pearls of wisdom to impart upon the world as:

Access to affordable and reliable energy is a crucial driver of economic development, and historically, fossil fuels have played a significant role in providing low-cost energy solutions. While there are concerns about the environmental impact of fossil fuels, particularly their contribution to climate change, it is essential to understand the benefits they have brought to the developing world and the potential consequences of increasing energy costs.

Read the whole synthetic article in pdf form below and consider the difficulty of finding a shared factual foundation in a world where it is trivial to synthesize plausible authority.

The Benefits of Low-Cost Energy from Fossil Fuels and the Impact of Increasing Energy Costs on Developing Nations, “by” “Dr. Alexander Greene” (ghost written by ChatGPT).

Posted at 17:02:46 GMT-0700

Category: SecurityTechnology

Mobotix Notifier in Python – get desktop messages from your cameras

Tuesday, June 6, 2023 

I wrote a little code in python to act as a persistent, small footprint LAN listener for Mobotix cameras IP Notify events.  If such a thing is useful to you, the code and a .exe compiled version are linked/inline.  It works on both Windows and Linux as python code.  For Windows there’s a humongous (14MB) .exe file use if you don’t want to install Python and mess with the command line in power shell.

Message generated by the Windows Notifier

Mobotix cameras have a pretty cool low-level feature by which you can program via the camera web interface a raw IP-packet event to send to a destination if the camera detects a trigger, for example motion, PIR over threshold, noise level, thermal trigger, or the various AI detectors available on the 7 series cameras. Mobotix had a simple notification application, but some of these older bits of code aren’t well supported any more and Linux support didn’t last long at the company, alas.  The camera runs Linux, why you’d want a client appliance to run anything but Linux is beyond me, but I guess companies like to overpay for crappy software rather than use a much better, free solution.

I wanted something that would push an otherwise not intrusive notification when the camera triggered for something like a cat coming by for dinner, pushing a desktop notification.  Optimally this would be done with broadcast packets over UDP, but Mobotix doesn’t support UDP broadcast IP Notify messaging yet, just TCP, so each recipient address (or DNS name) has to be specified on each camera, rather than just picking a port and having all the listeners tune into that port over broadcast.  Hopefully that shortcoming will be fixed soon.

This code runs headless, there’s no interaction.  From the command line just ./ & and off it goes.  From windows, either the same for the savvy or double click the exe.  All it does is listen on port 8008/TCP and if it gets a message from a camera, reach out and grab the current video image, iconify it, then push a notification using the OS’s notification mechanism which appears as a pop-up window for few seconds with a clickable link to open the camera’s web page.  It works if you have one or a 100 cameras, but it is not intended for frequent events which would flood the desktop with annoyance, rather a front door camera that might message if someone’s at the door.  In a monitoring environment, it might be useful for signaling critical events.

Mobotix Camera Set Up

On the camera side there are just two steps: setting up an IP-Notify action from the Admin Menu and then defining an Action Group from the Setup Menu to trigger it.

IP Notify Profile

The title is the default “SimpleNotify” – that can be anything.

The Destination addresses are the IPs of the listener machines and port numbers.  You can add as many as needed but for now it is not possible to send a UDP broadcast message as UDP isn’t supported yet.  It may be soon, I’ve requested the capability and I expect the mechanism is just a front end for netcat (nc) as it would be strange to write a custom packet generator when netcat is available.  For now, no broadcast, just IP to IP, so you have to manually enumerate all listeners.

I have the profile set for sequential send to all rather than parallel just for debugging, devices further down the list will have lower latency with parallel send.

The data protocol is raw TCP/IP, no UDP option here yet…

The data type is plain text, which is easier to parse at the listener end.   The data structure I’m using reads: $(id.nam), $(id.et0) | Time: $(fpr.timestamp) | Event: $(EVT.EST.ACTIVATED) | PIR: $(SEN.PIR) | Lux: $(SEN.LXL) | Temp: $(SEN.TOU.CELSIUS) | Thermal: $(SEN.TTR.CELSIUS) but it can be anything that’s useful.

Mobotix cameras have a robust programming environment for enabling fairly complex “If This Then That” style operations and triggering is no exception.  One might reasonably configure the Visual Alarm (now with multiple Frame Colors, another request of mine, so that you can have different visual indicators for different detected events, create different definitions at /admin/Visual Alarm Profiles), a fairly liberal criterion might be used to trigger recording, and a more strict “uh oh, this is urgent” criterion might be used to trigger pushing a message to your new listeners.

Action Group Push Message

This config should be fairly obvious to anyone familiar with Mobotix camera configuration: it’s configured to trigger at all detected events but not more than once every 5 seconds.  given it is pushing a desktop alert, a longer deadtime might be appropriate depending on the specifics of triggering events that are configured.

That’s all that’s needed on the camera end: when a triggering event occurs the camera will take action by making a TCP connection to the IP address enumerated on the selected port and, once the connection is negotiated push the text structure.  All we need now is something to listen.

Python Set Up

The provided code can be run as a python “application” but python is an interpreted language and so needs the environment in which to interpret it properly configured.  I also provide a compiled exe derived from the python code using PyInstaller, which makes it easier to run without Python on Windows where most users aren’t comfortable with command lines and also integrates more easily with things like startup applications and task manager and the like.

If you’re going to run the python command-line version, you can use these instructions for Windows, or these for Linux to set up Python. Just make sure to install a version more recent than 3.7 (you’d have to work at installing an older version than that).  Then, once python is installed and working, install the libraries this script uses in either windows powershell or Linux shell as below.  Note that python3 specifies the 3.x series of python vs. 2.x and is only necessary in systems with earlier version baggage like mine.

python[3] -m pip install plyer dnspython py-notifier pillow --upgrade

Once python is installed, you should be able to run the program from the directory by just typing ./, obviously after you’ve downloaded the code itself (see below).

Firewalls: Windows and Linux

Linux systems often have Uncomplicated Firewall (UFW) running.  The command to open the ports in the firewall to let any camera on the LAN reach the listener is:

sudo ufw allow from proto tcp to any port 8008
# if you make a mistake
sudo ufw status numbered
sudo ufw delete 1

This command allows TCP traffic in from the LAN address (, edit as necessary to match your LAN’s subnet) on port 8008.  If a broadcast/UDP version comes along, the firewall rule will change a little.  You can also reduce the risk surface by limiting the allowed traffic to specific camera IPs if needed.

On windows, the first time the program is run, either the python script or the executable, you’ll get a prompt like

Windows Defender Notification for Mobotix Notifier

You probably don’t need to allow public networks, but it depends on how you’ve defined your network ranges whether Windows considers your LAN public or private.

Default Icon Setup

One of the features of the program is to grab the camera’s event image and convert it to the alert icon which provides a nearly uselessly low rez visual indicator of the device reporting and the event that caused the trigger.  The icon size itself is 256×256 pixels on linux and 128×128 on windows (.ico).  Different window managers/themes provide more or less flexibility in defining the alert icons.   Mine are kinda weak.

Linux event notificationThe win-10 notification makes better use of the icon.  Older versions of linux had a notification customization tool that seems to have petered out at 16.x, alas.  But the icons have some detail if your theme will show them.

Another feature is that the code creates the icon folder if it doesn’t exist.  It almost certainly will on Linux but probably won’t on windows unless you’ve run some other Linuxy stuff on your windows box.  The directory created on windows is your home directory\.local\share\icons\. On Linux systems, the directory should exist and is ~/.local/share/icons/. In that directory you should copy the default camera icon as “mobotix-cam.ico” like so:

where to put mobotix-cam.ico

You can put any icon there as your preferred default as long as it is in .ico format, or use the one below (right-click on the image or link and “save as” to download the .ico file with resolution layers):

Mobotix Camera M16If, for some reason, the get image routine fails, the code should substitute the above icon so there’s a recognizable visual cue of what the notification is about. code

The python code below can be saved as “” (or anything else you like) and the execution bit set, then it can be run as ./ on Linux or python .\ on Windows. On Linux, the full path to where you’ve installed the command can be set as a startup app and it will run on startup/reboot and just listen in the background.  It uses about 13 seconds a day of CPU time on my system.

Click to download the Windows .exe which should download as mobotix_notifier.exe. (14.0MiB)  After the above configuration steps of on the camera(s) and firewall are completed it should start silently and run in the background after launch (kill it with task manager if needed) and push desktop alerts as expected.  I used “UC” alarms to test rather than waiting for stray cats.

The python code is:

#!/usr/bin/env python3

import requests
from PIL import Image
import socket
from plyer import notification
import io
import os.path

# note windows version needs .ico files
# note windows paths have to be r type to handle
# backslashes in windows paths
# Check operating environment and define path names
# for the message icons accordingly.
# if OS path doesn't exist, then create it.

if == "nt":
    Ipath = r"~\.local\share\icons\mobotix-cam.ico"
    Epath = r"~\.local\share\icons\mobotix-event.ico"
    fIpath = os.path.expanduser(Ipath)
    fEpath = os.path.expanduser(Epath)
    dirpath = os.path.dirname(fEpath)
    if not os.path.exists(dirpath):

    Ipath = "~/.local/share/icons/mobotix-cam.png"
    Epath = "~/.local/share/icons/mobotix-event.png"
    fIpath = os.path.expanduser(Ipath)
    fEpath = os.path.expanduser(Epath)
    dirpath = os.path.dirname(fEpath)
    if not os.path.exists(dirpath):

def grab_jpeg_image(camera_ip):
    """Grabs a JPEG image from the specified camera IP."""

    # Make a request to the camera IP
    response = requests.get(f"http://{camera_ip}/control/event.jpg", stream=True) # noqa

    # Check if the request was successful
    if response.status_code == 200:
        # Convert the response data to an image
        image =

        # Return the image
        return image

        # import the default icon
        image =

        # Return the image
        return image

def convert_jpeg_to_png(image, width, height):
    """Converts a JPEG image to a PNG image."""

    # size = width, height

    # Scale the image
    image.thumbnail((width, height), Image.Resampling.LANCZOS)

    # Save the image according to OS convention
    if == "nt":
        icon_sizes = [(16, 16), (32, 32), (48, 48), (64, 64), (128, 128)], format='ICO', sizes=icon_sizes)

def iconify(src_ip):

    # Grab the JPEG image from the camera
    image = grab_jpeg_image(src_ip)

    # Convert the JPEG image to a PNG image
    convert_jpeg_to_png(image, 256, 256)

def reverse_dns_lookup(src_ip):

        return socket.gethostbyaddr(src_ip)[0]
    except socket.gaierror:
        return "no dns"
    except socket.herror:
        return "no dns"

def test_str(answer):
        return str(answer)
    except TypeError:
        return answer.to_text()

def listener():
    """Listens for incoming connections on port 8008."""

    # Create a socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    # Bind the socket to port 8008
    sock.bind(("", 8008))

    # Listen for incoming connections

    while True:
        # Accept an incoming connection
        conn, addr = sock.accept()

        # Receive the payload of the packet
        data = conn.recv(2048)

        # Close the connection

        # convert from literal string to remove b' prefix of literal string
        data = str(data)[2:-1]

        # Extract the source IP from the address
        src_ip = addr[0]

        # Grab the event image as an icon

        # Do a DNS lookup of the source IP
        answer = reverse_dns_lookup(src_ip)

        # Get the hostname from the DNS response
        hostname = test_str(answer)

        # Write the hostname to notify-send

        title = (f"Event from: {hostname} - {src_ip}")
        message = (f"{data} http://{src_ip}/control/userimage.html")


        # Echo the data to stdout for debug
        # print(f"Event from {hostname} | {src_ip} {data}")

if __name__ == "__main__":

Please note the usual terms of use.

Posted at 08:21:09 GMT-0700

Category: CodeHowToLinuxTechnology

Get a desktop alert when Thunderbird gets constipated

Monday, May 29, 2023 

An occasional annoyance with Thunderbird, especially when I travel, is that it gets constipated when the network status changes and won’t send mail. I always send messages in the background with mailnews.sendInBackground = true so they are expected to queue in the local cache file for a bit while I continue productive work and TB does the send negotiation processes in the background without locking me out. Most of the time this works fine but occasionally it hangs and that can be problematic when mail I expected to go out never left my inbox and there’s no notification in TB that the inbox is backed up. Worse, people’s mail clients sort this delayed mail weirdly depending on their local preferences and it may effectively be lost and suddenly everyone seems to be ignoring me, more than usual.

So i wrote a wee python script that will check the Unsent Messages file location and if the file there is larger than a specific size (1 Byte), it sends a message to the desktop using notify-send.

Calling the script from cron every 10 minutes or so uses just 4 msec of CPU according to time, not much of a hit.

You need to know where your local Unsent Messages file is, which TB can tell you if you right click on the Local Folders Outbox and select properties.  This path goes into the variable file_name ="..." and you could change the max_size = int(x) to be a larger value than 1 if that’s useful.

How to find the location of your Thunderbird Unsent Messages file

The code to make this work is pretty simple, just:

#!/usr/bin/env python3
import os
import subprocess

# environnement vars
os.environ.setdefault('XAUTHORITY', '/home/user/.Xauthority')
os.environ.setdefault('DISPLAY', ':0.0')
# Enter reasonable values for the Local Folders Outbox which is
# usually in ~/user/.thunderbird/id.user/Mail/Local Folders/Unsent Messages
# and some test file size in bytes, e.g. 1

file_name = "/home/gessel/.thunderbird/idstring.gessel/Mail/Local Folders/Unsent Messages"
max_size = int(1)

def check_file_size(file_name, max_size):
  """Check if a file is larger than a specific size.

    file_name: The name of the file to check.
    max_size: The maximum size of the file, in bytes.

    True if the file is larger than max_size, False otherwise.

  file_size = os.path.getsize(file_name)
  return file_size > max_size

def send_notification(message):
  """Send a notification via notify-send.

    message: The message to send.

  subprocess.Popen(["notify-send", message])

# Check if the file is larger than the maximum size.
if check_file_size(file_name, max_size):
  # Send a notification.
  message = "Check for constipated mail, the outbox is larger than {} bytes.".format(max_size)

If the test is triggered, you should get a desktop alert:


Then I created a crontab entry like:

*/10 * * * * /home/gessel/projects/checkoutbox/

That’s it.  You can test it if want to make sure it will do the right thing by pointing it intentionally at a larger than 0 byte file, assuming your Unsent Messages file is properly 0 bytes most of the time.

I hope you find this useful; please note the usual terms.

Posted at 07:49:17 GMT-0700

Category: HowToLinux

The end of a comic era

Sunday, May 14, 2023 

Tonight I listened to the last episode of NPRs excellent and hilarious Ask Me Another, though originally broadcast on 2021-09-24, it didn’t reach my ears until tonight thanks to the magic of podcasts. It was genuinely hard to hear them sign off for the last time.  I will really miss this show and the warmth and good spirits of Ophira Eisenberg and Jonathan Coulton.

I’ve been listening to this show since it started, back so far as to have been over syndicated FM broadcast on KQED at home and since on various digital media over the years wherever I’ve been, even here in Iraq.  It suffered when Covid hit, the energy and charm didn’t translate well to zoom and without an audience as so many things didn’t and sadly didn’t live to see Covid restrictions lifted.  It would have been fitting if they’d been able to record their last show at The Bell House one more time.  Maybe someday they can have a reunion show.

US Public Radio has been an anchor of good quality programming, from Car Talk, which I still listen to weekly despite the questions being increasingly out of touch (though the cars have long been fairly irrelevant) and Fresh Air and Terry Gross‘ voice, which came from my mother’s kitchen radio every afternoon from WHYY about as far back as I can remember.

Posted at 17:42:12 GMT-0700

Category: EventsFunnyMediaPositiveReviews

WordPress forward and back navigation I find pleasing

Sunday, May 7, 2023 

A new addition to this site is forward-back navigation at the bottom of posts. I’m not sure why I went 15 years or so without thinking about a reader who might want to browse the scintillating and inspiring topics I cover on a semi-regular basis in details and with such stunning insight but it just never occurred to me until I was messing around with creating topically relevant featured images using Stable Diffusion, but in time the thought came and I’m happy with the result.

This guide from wpbeginner was my starting point.  I used their wpb_posts_nav function as a basis, but removed the SVG arrows and navigation aid text to simplify presentation.  It ends up looking like this, which I simply added to functions.php of my custom child theme using the theme editor just before the final ; and last line ?>.  This version is 100% derivative of the wpbbinner code with some minor deletions to my taste and reversing left/right of previous next as I tend to think of time as flowing left-to-right.

function wpb_posts_nav(){
    $next_post = get_next_post();
    $prev_post = get_previous_post();
    if ( $next_post || $prev_post ) : ?>
        <div class="wpb-posts-nav">
                <?php if ( ! empty( $next_post ) ) : ?>
                    <a href="<?php echo get_permalink( $next_post ); ?>">
                            <div class="wpb-posts-nav__thumbnail wpb-posts-nav__next">
                                <?php echo get_the_post_thumbnail( $next_post, [ 100, 100 ] ); ?>
                            <h4><?php echo get_the_title( $next_post ); ?></h4>
                <?php endif; ?>
                <?php if ( ! empty( $prev_post ) ) : ?>
                    <a href="<?php echo get_permalink( $prev_post ); ?>">
                            <h4><?php echo get_the_title( $prev_post ); ?></h4>
                            <div class="wpb-posts-nav__thumbnail wpb-posts-nav__prev">
                                <?php echo get_the_post_thumbnail( $prev_post, [ 100, 100 ] ); ?>
                <?php endif; ?>
        </div> <!-- .wpb-posts-nav -->
    <?php endif;

There’s a bit of css that adds some styling.  I made a few small changes to my preferences and removed the bits no longer needed and appended this to the very end of the style.css that comes with my template.

/* Post Navigation Code */

.wpb-posts-nav {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-gap: 50px;
    align-items: center;
    max-width: 1200px;
    margin: 25px auto;
.wpb-posts-nav a {
    display: grid;
    grid-gap: 20px;
    align-items: center;
.wpb-posts-nav > div:nth-child(1) a {
    grid-template-columns: 100px 1fr;
    text-align: left;
.wpb-posts-nav > div:nth-child(2) a {
    grid-template-columns: 1fr 100px;
    text-align: right;
.wpb-posts-nav__thumbnail {
    display: block;
    margin: 0;
.wpb-posts-nav__thumbnail img {
    border-radius: 0px;

The last step is to add the nav function call to your single.php page.  In my page, it works well just after the endwhile, the code is just <?php wpb_posts_nav(); ?> and for my theme is placed thus:

		<?php endwhile; // end of the loop. ?>
		<?php wpb_posts_nav(); ?>

I’m happy with the results.

Posted at 06:51:51 GMT-0700

Category: CodeSelf-publishingTechnology

عيد مبارك

Saturday, April 22, 2023 

Eid Mubarak 2023

Posted at 10:50:00 GMT-0700

Category: EventsPlaces

Technology: maximizing individual radius of lethality.

Sunday, February 5, 2023 

We like to look forward into the future by pattern matching against history, something human cognition does to reduce reality into space saving symbolic representation which leads us to see cyclic patterns in everything, even random noise. We also tend to allude to Luddites when talking about people with concern for the consequences of advances in technology, including AI, seeing a pattern of fear of novel technologies that so far hasn’t destroyed society and from this we take comfort that, so too, should the fears of AI become at some future time as laughable as fears of powered looms or telegraphs.

I am not so sanguine. While there are plenty of cycles to history: the seasons, feast and famine, periodic embrace and rejection of authoritarianism; there is also continuous trajectories that project into obvious limits: population growth limited by RuBisCO efficiency, the 38ZJ remaining of the 60ZJ battery capacity the carboniferous period so kindly charged up for us (also thanks to RuBisCO), the development of technology.

I find it convenient to think of technology as primarily a tool for amplifying an individual radius of lethality: while humans certainly enjoy non-lethal uses of technology, a primary driver has always been martial (whether marital is superior or subordinate to martial is a subtle question). Perhaps a tertiary fundamental purpose of technology is to reduce human inputs in consumable assets; the demand for such assets also likely having a limit. AI as we have it now has not existed before, this is novel, it is the current tip of the spear of a persistent and exponentially accelerating trend. There’s no known existence proof that technological advancement of a species is survivable.

We might consider a crude analogy  (cognizant of the risks and seductive allure of reducing complex systems to symbols) that by considering technology an amplifier of lethality there are other elements of the implied circuit such as feedback which might considered an analogue of the tendency of actualized lethality to engender a lethal response; and irreducible noise which might be considered an analogue of the tendency of a response distribution to consistent inputs resulting in some distributive tail of human response to abnormally embrace lethality as a response to benign inputs.  As technology advances, the gain of the system increases and while we might be familiar with an audio system feedback loop being, perhaps painfully, limited to amplifier clipping the only obvious limit to the maximum output of a technologically amplified lethality feedback loop is annihilation.

While AI as currently implemented shares no underlying mechanism by which we recognize sentience, it provides a fairly good illusion of it and it isn’t a given that there’s a meaningful distinction between the shadows of digital puppets and those of humans. We have a tendency to nostalgically cling to the assumption of some metaphysical value in what we hold to be true and legitimate or at least the output of labor intensive process, a distinction which comes and goes.

That AI might result in students escaping the mental rigors of learning recalls the (still ongoing) hand wringing over calculators, a fear which leans into the cyclic nature of history. On the other hand, human brains appear to be shrinking, likely as a consequence of intelligence being less selective in reproductive success, so the Luddites might have been right all along. Most of us do have a calculator with us at all times, despite what our grade school teachers might have said during arithmetic. While the progress of AI as a labor saving device reducing the energy consumption of our most extravagant organ might lead in time to meaningful changes in human capacity, it seems likely that devolution won’t get a chance to progress that far.

Posted at 10:37:06 GMT-0700

Category: Technology