Sunday, 31 October 2021

Bravia, Bravia, wherefore art thou Bravia

Just a quick post that might help anyone with a Sony Bravia X70G/X70xxG family LCD TV.

We just upgraded our 12-year old 40" Bravia to the KD-55X7000G model, which I got almost-new for half price, because I can't resist a bargain. It's not a very high-end TV, but it's got Netflix etc and it puts out a good picture. The operating system is described as "Linux", and it's functional* and snappy, which is all I really want. As a reluctant "Smart TV" purchaser, I'd rather a "dumb" smart TV than one that shows me advertisements.

One of the ways it is unacceptably dumb though, is doing DHCP while it's doing something else. Yes, a TV capable of showing 4K content at 60Hz (that's 4096x2160 * 60 = 530,841,600 pixels per second if you're counting) can't handle obtaining a new DHCP lease - an exchange of 2 simple UDP datagrams over the network.

This was manifesting itself very annoyingly during Netflix sessions. The pattern would be: we'd turn on the TV, then get sidetracked for a bit, then finally settle down and watch a 22-minute episode of "Superstore" on Netflix. All fine. Then, reliably as clockwork, if we decided to watch a second episode, at some point during the show we'd get a "can't access Netflix servers" error, which was completely unrecoverable. Even a swift reboot of the TV wouldn't be enough - leaving it off for about five minutes seemed to be required to flush out the problem.

It turned out that I hadn't explicitly added the TV's MAC address to my dnsmasq configuration, so it was being given an IP address with the default lease time of one hour. Now dnsmasq is pretty smart and won't assign a different IP address to a device once it's seen it (unless it absolutely has to) so it wasn't even anything as nasty as the TV getting a different IP. No, it was simply asking to renew the lease. Obviously the straw that broke the camel's back.

So the "solution" I'm using (and has been flawless so far) is to:

  1. give the TV a static IP address; and
  2. give it a 24 hour lease time
24 hours is more than enough time to cover any foreseeable Netflix binge and the TV will do a DHCPDISCOVER on boot the next time anyway, so we're covered nicely.

(*) Oh there is one other thing - the TV unfathomably doesn't seem to be able to set its clock from the network - I'm looking at fixing that soon, fingers crossed...

Saturday, 25 September 2021

Automating heating vents with openHAB, esp8266 and LEGO - Part 2; Hardware implementation

In the first part of this series I outlined what I'm trying to build - a smart vent on the cheap - so now it's time to build it! Here's what I'm working with - these are "period-style heating registers" as available from my local warehouse-style hardware store. A decorative "vintage" metal plate (scratched to hell) holds a rectangular plastic frame with two pivoting slats sitting in the airflow. A simple plastic slider protrudes through a slot in the metal plate for user control of slat angle.

In the grand tradition of absolutely-ridiculous first hardware versions (check out Mouse v1.0!), I've built this proof-of-concept out of LEGO Technic. In an excellent coincidence, the width of the vent is a perfect fit for the crab-claw-like clamping mechanism I've created, which is fortunate because it requires quite a decent bit of force to move the slider. This gizmo is heavily overbuilt using my best "LEGO Masters" techniques and doesn't flex, warp or bend one bit once it's in position. I'm using an "XL" LEGO Power Functions motor with a worm drive PLUS some extra gear reduction to make sure that:

  • I have the torque to move the slider
  • The slats won't move unless I want them to (one of the best features of worm-drives); and
  • The transition from shut-to-open (or vice versa) takes a while
It might be counterintuitive, but since this solution has no feedback (i.e. to tell it when the slats are truly open or shut) then timing is all I have. Moving everything slowly gives me the best chance of stopping any movement before any hardware limits get exceeded (and expensive Danish plastic starts snapping).

Here it is all mounted up. It sits up about 5cm above the normal vent height, which is obviously less than ideal, but should be fine as the whole assembly sits under a sofa-bed which has copious amounts of space underneath it. The dual pinions (to spread the torque and keep everything level) drive the rack left or right, and the slider is "captured" between the red elements and opens or shuts the slats.

The remainder of the hardware is pretty simple - a butchered LEGO Power Functions cable connects the motor to a standard L293D H-bridge, and thence to the "embedded computer" part of the solution, which I'll talk about next...

Sunday, 29 August 2021

Switching to git switch

A recent OS upgrade resulted in my Git version being bumped up to 3.x, an interesting feature of which is the new git switch command, which replaces part of the heavily overloaded git checkout functionality with something much clearer.

Now, to switch to branch foo you can just invoke git switch foo

And to create a new branch, instead of git checkout -b newbranch,
it's git switch -c newbranch

These are small changes, to be sure, but worthwhile in bringing more focus to individual commands, and relieving some of the incredibly heavy lifting being done by the flags passed to git checkout, which hopefully now can be my "tool of last resort" as it usually leaves me stranded in "detached head hell" ... my fault, not Git's!

I've also updated my gcm alias/script to use git switch, so accordingly it's now gsm.

Sunday, 25 July 2021

Automating heating vents with openHAB, esp8266 and LEGO - Part 1; rationale

It's winter here in Melbourne, and it's a cold one. Combined with the fact that everyone is spending a lot more time at home than before, it's time to start optimising for comfort and efficiency...

I've shared my house's floorplan before on this blog, but this time here it is overlaid with the "schema" of the gas central-heating system, which sends hot air through underfloor ducts into the house through eight vents (or "registers" if you prefer) - shown as red squares:

Now some houses *might* have "zones" implemented, where certain areas of the house are on a physically separated section of ducting and can be addressed and controlled individually. This house is not one of those. I've shown the two *notional* zones we'd probably *like* to have in orange (living spaces) and green (sleeping areas). If you're wondering, we've been advised that for technical reasons related to our heating unit (aka furnace) and available space under the house, a zoned system is not practicable. In any case, it would probably be a bit coarse-grained anyway, as these days I'm working pretty-much 5-days-a-week at home, from the study - the room at the bottom-left of the floorplan.

As such, I would like to be able to control the specific vent in my study, opening and closing it as needed so that it's warm to work in, particularly in the mornings, but also not wasting warm air that is better off being routed to elsewhere in the house in the evenings and on weekends. Also, if the temperature in the study is warm enough, I'd like the vent to shut itself off. It sounds like the height of laziness, but it happens that this vent is located underneath a large couch, so it's actually a major pain to adjust it by hand.

Off-the-shelf "smart vent" solutions have been available for a number of years, from Flair and Keen but they are Not Cheap, don't have any openHAB binding support, don't have stock available and/or don't ship to me in Australia. So it's a roll-your-own situation...

Thursday, 24 June 2021

How do I find all the HTML elements that aren't getting my preferred font?

A quickie script that saved a lot of manually combing through the DOM

Someone noticed that certain elements in our React app were not getting the desired font-face, instead getting plain-old Arial. I wanted to be able to programmatically sniff them out in the browser, so here's what I came up with, mainly thanks to an answer on Stack Overflow for finding all elements on a page, and the MDN documentation for the getComputedStyle function which browsers helpfully expose.

Whack this in your browser's Javascript console and you should be able to hover on any element that is listed to see it on the page:

  // Start where React attaches to the DOM
  const reactRoot = document.getElementById("root"); 
  
  // Get all elements below that
  const kids = reactRoot.getElementsByTagName("*"); 
  
  for (var e of kids) { 
    if (window.getComputedStyle(e)["font-family"] === "Arial") { 
      console.log(e); // Allows it to be hovered in console
    }
  }
  

In case you were wondering, the culprit here was a button that didn't have its font-family set - and Chrome (perhaps others) will use its default (user-agent stylesheet) font style for that in preference to what you have set on the body, which you might be forgiven for assuming gets cascaded down.

Sunday, 30 May 2021

Using the Velleman K8055 USB Experiment board with OpenHAB 3.x

The venerable Velleman K8055 USB Experimenter's Board is a neat way to interface a modern computer with a selection of analog and digital I/O ports. Unfortunately, support for using it within the OpenHAB home-automation framework (where it seems like a natural fit for tinkering) has fallen by the wayside - it had a proper binding in OpenHAB v1, and kinda-sorta still worked in OpenHAB v2, but it's a non-starter in 2021 with OpenHAB v3.x.

If you're running your OpenHAB (and a connected K8055) off a Raspberry Pi however, you're in luck. Simply head over to Github where some outstanding humans have done all the hard work to get your K8055 working again. It's all nicely-documented, so go ahead and give it a try. Come back here when you've got the k8055 command-line program running and making the LEDs go on and off; I'll wait.

Right. Let's get it cooking with OpenHAB, but without going through the hoops of building a new binding. Instead, we'll harness the power of OpenHAB's exec binding, and use OpenHAB's built-in state management to get persistent control of the K8055's outputs. What do I mean by that? Well, the k8055 program is completely stateless; whenever you tell it to set the digital outputs to 147 (i.e. 8, 5, 2 and 1 HIGH), it does just that, ignoring how bits 7, 6, 4 and 3 were set before - they're going to be LOW. It doesn't OR them or mask them with the current state. In fact, it can't even tell you the current state, only what it should be AFTER executing your instructions...

On your Pi, head to ${OPENHAB_CONF}, and add:

items/velleman.items
Group:Number:SUM gVellemanOutputs "Velleman output sum"
Number VellemanD1 (gVellemanOutputs)
Number VellemanD2 (gVellemanOutputs)
Number VellemanD3 (gVellemanOutputs)
Number VellemanD4 (gVellemanOutputs)
Number VellemanD5 (gVellemanOutputs)
Number VellemanD6 (gVellemanOutputs)
Number VellemanD7 (gVellemanOutputs)
Number VellemanD8 (gVellemanOutputs)

Number VellemanA1
Number VellemanA2

// Arguments to be placed for '%2$s' in command line
String VellemanOutputArgs {channel="exec:command:setoutputs:input"}
things/velleman.things
Thing exec:command:setoutputs [command="/usr/local/bin/k8055 %2$s", interval=0, autorun=true]
misc/exec.whitelist
/usr/local/bin/k8055 %2$s
(In OpenHAB 3, for security, you've got to allow-list all the commands that exec can run)

Now we can add a stanza to our sitemap, to get some UI controls:

sitemaps/default.sitemap
...
Frame label="Velleman Outputs" {
  Switch item=VellemanD1 label="Digital 1" mappings=[0="OFF",1="ON"]
  Switch item=VellemanD2 label="Digital 2" mappings=[0="OFF",2="ON"]
  Switch item=VellemanD3 label="Digital 3" mappings=[0="OFF",4="ON"]
  Switch item=VellemanD4 label="Digital 4" mappings=[0="OFF",8="ON"]
  Switch item=VellemanD5 label="Digital 5" mappings=[0="OFF",16="ON"]
  Switch item=VellemanD6 label="Digital 6" mappings=[0="OFF",32="ON"]
  Switch item=VellemanD7 label="Digital 7" mappings=[0="OFF",64="ON"]
  Switch item=VellemanD8 label="Digital 8" mappings=[0="OFF",128="ON"]
  Slider item=VellemanA1 label="Analog 1" minValue=0 maxValue=255
  Slider item=VellemanA2 label="Analog 2" minValue=0 maxValue=255
}
...

This gives us all the controls for all of the outputs the Velleman K8055 supports:

Now we're ready to write a rule that ties everything together and makes it work persistently:

rules/velleman.rules
rule "Velleman Hardware Sync"
when
   Item gVellemanDigitals changed or Member of gVellemanAnalogs changed
then
  val dState = if (gVellemanDigitals.state == NULL) "" else "-d:" + gVellemanDigitals.state
  val a1State = if (VellemanA1.state == NULL) "" else "-a1:" + VellemanA1.state
  val a2State = if (VellemanA2.state == NULL) "" else "-a2:" + VellemanA2.state

    val formattedCommand = dState + " " + a1State + " " + a2State
   // logInfo("velleman.rules", formattedCommand) // Diagnostics if needed
    VellemanOutputArgs.sendCommand(formattedCommand)
end

So the neat "tricks" here I think are:

  • Baking the bit-twiddling logic into each Switch - some might object to putting values like 32 or 128 directly into the sitemap. I figure, you're going to have repetitive code up here in the UI, may as well extract some value out of it if it makes the logic in the rules simpler ... and it really does
  • Using the Group:Number:SUM derived group state to generate the final output byte - the other part of the solution that keeps the rule really clean - OpenHAB itself recalculates the sum of all the switch values that belong to the group gVellemanOutputs
  • Using Member of gVellemanAnalogs to reduce repetition in the when clause on the analog outputs - it's only a little thing, but I like it
It's also refreshed my memory on how powerful OpenHAB Item Groups (as opposed to the confusingly-similar Sitemap Groups) can be. When I consider how many lines of rules code my initial attempt was, and how readable the final result is, I think they are a massive win.

Sunday, 25 April 2021

Computers I Have Known - Part 3.11 (For Workgroups)

(This is Part 3 of my Computers I Have Known series)

The 1990s lurched into existence and my Dad again was pivotal in my computing education. As a secondhand bookseller, he had a need to catalog his ever-increasing stock and also write a monthly column for a trade journal. Through school friends I'd become fairly familiar with the PC and Windows 3.0 had finally made it friendly enough that I figured my Dad would be able to point-and-click his way around.

I'd also become acquainted with the Intel 286/386/486 PC landscape through the magic of the Melbourne (Australia) Age newspaper's "Green Guide", ostensibly the TV guide, but more interestingly a hotbed of small computer shops frantically advertising for your beige-box business, dropping prices a handful of dollars week-on-week. Sadly there seems to be virtually no online record available of this weekly bonanza (that probably had equivalents in almost every major city) - but trust me, in an effectively pre-internet era, it was the nearest thing to a Google search for "Clone PC". We ended up with a very solid upper-mid-end choice - a MicroArts 486-DX33 mini-tower, with 4 megabytes of RAM, 1Mb Super-VGA card on its VESA Local Bus, double-speed CD-ROM drive, a whopping (if memory serves correctly) 340Mb Western Digital hard drive, 15" Viewsonic CRT and a superb HP LaserJet 4L desktop laser printer. The only thing better at the time would have been the DX2-66MHz version, which commanded a large premium for its mind-bogglingly fast CPU, but actually talked to everything else at the same speed as the 33MHz brother I'd selected.

I set the machine up to go straight to Windows on boot (the old WIN at the bottom of AUTOEXEC.BAT ... ah memories) and suitably butchered the Windows 3.0 Program Manager to offer my Dad precisely two things to click on - Write (the primitive yet more-than-sufficient-for-his-needs default word processor), and the proprietary book-cataloguing software which was the standard in the trade.

And with that, he was away. That PC had an extraordinary innings of some 15(!) years, gradually gaining a fax/modem, scanner, network card, more RAM, ticking up to Windows 3.11 (surely the most significant operating system point-release in history) and transitioning from "only computer" to "front desk computer" and finally "back office computer". The Internet came along, and my Dad moved his catalogue of books online through increasingly-sophisticated mechanisms, the first of which was a hand-rolled series of templated HTML pages coded by yours truly, which would get "mail merged" with the output of the proprietary book catalog system, and the resulting family of pages FTPed up to his shop website. People would scroll through the listings, which were by-category only (Art, Music, History, etc) and email him with their order. If they were local, they'd come in and pick them up, but he'd box and ship orders internationally on a regular basis. His "secure credit card option" (his words) was getting customers to send him their credit card number in several separate emails; it was ... a simpler time ...

Yep, my Dad, the luddite, was selling books on the web in 1992 - two years before there was an amazon.com. But I digress.

Pictures of my Dad's website, coded by yours truly, from the earliest available Wayback Machine scrape in December 1998. The Wayback Machine and the Internet Archive are an incredible resource - please donate to them if you can!

Every byte of the above (hilariously-primitive) website was honed (on Notepad or Paintbrush) and uploaded (with WS_FTP) from that 486. This machine was the last box I could confidently say I knew like the back of my hand. From its carefully-tuned BIOS settings through its lovingly-crafted CONFIG.SYS and up into Windows from Character Map to Write, I knew this thing inside-out and tended to it like a cherished pet - which was probably how it managed to survive well into the Pentium 4 era before making its way to the great Beige Box in the sky ...