The Millhouse Group Blog
Software Development in the 21st Century
Sunday 20 October 2024
Google Home Minis (1st Gen) bricked
As of this month, I have two out of my fleet of three *Google Home Mini (Gen 1)*s out of commission. Neither will boot; one slows a lonely green LED when the reset button is hit, the other nothing at all (and pulls massive power from USB while it boots).
There is a [truly MASSIVE thread](https://www.googlenestcommunity.com/t5/Speakers-and-Displays/HOME-mini-UNRESPONSIVE-thread-quot-Google-home-mini-4-dots-stuck-problem/m-p/558790/page/209876543210) about this over on the Google Nest forum. The *TL;DR* is this: if you contact Google about this issue, they will stall and give you the runaround. Eventually if you persist, they will get you to confirm the purchase date of the device(s), at which point they will either send you a new device or close the conversation with __"[it is|they are] out of warranty"__.
The speculation on the forums is that Google are remote-bricking these devices as they reach their warranty period. This truly saddens me but given the well-documented Google anti-enthusiasm for long-term product support, it makes perfect sense. These devices acheived their aim of massive market penetration and have become almost-indispensable around my house, with the excellent Spotify and Chromecast integrations being used multiple times per day. The very low purchase price (indeed *$free* in one case) made it a no-brainer to dot (hah!) them around the place.
The only working *Home Mini* left is a little newer. How much longer will it survive? I can't imagine there is any way to block it from receiving that remote kill-code from the mothership without completely nerfing its internet access, so it's just a ticking time bomb now.
There's also a highly visible lack of supply for the obvious replacement, the *Nest Mini (Gen 2)*. I suspect Google is letting these evaporate without replacement, so they can introduce a *Gen 3* model, considerably more expensive, for all the people who they've locked into the Google Nest/Home ecosystem and who now, funnily enough, need to replace a fleet of devices. Damn.
Draw your own conclusions about how Google's tracking with _"Don't Be Evil"_ at this point, as tonnes of still-useful electronics make their way into landfill.
Sunday 8 September 2024
UnSAFe at any speed
I first encountered the [Scaled Agile Framework for enterprise](https://scaledagileframework.com/), (from here-on referred to as SAFe) in 2014, when the large enterprise I was working for decided it was what was needed in order to ship solutions faster. (*Spoiler:* it wasn't, it didn't help in the slightest, it made us considerably slower, at considerable expense). I'll let you peruse their website at your leisure, but before you go, remember the tenets of the [Agile Manifesto](https://agilemanifesto.org/) (emphasis mine):
- Individuals and interactions over *processes* and tools
- Working software over comprehensive *documentation*
- Customer collaboration over contract negotiation
- Responding to change over *following a plan*
Now look at this, the ***SAFe 6.0*** I-don't-even-know-what-to-call-it diagram:
All I see is prescriptive *processes*, *documentation* and *plans*. You don't "do" agile development by signing up for expensive certifications that basically allow you to continue to ship 4-times-a-year but call yourself an agile workplace when you're recruiting. You also won't fool *anyone* which half a clue during the recruitment process.
Just one more example. This is grabbed from a PDF from [Accenture](https://sai2.wpengine.com/wp-content/uploads/delightful-downloads/2020/01/Key_Accenture_Learning_on_Scaled_and_Distributed_Agile_August-18-for-SAFe.pdf), who are [fully on-board with SAFe](https://scaledagile.com/case_study/accenture/), I suspect because:
- Acronym
- Includes the word Agile
- You can get certified for it (at great expense)
- It seems to make sense if you don't look at it too closely
- It actually makes you go slower than before (more billable hour$)
Ready? Here we go. This is how easy it is to "integrate" agile and waterfall:
So simple! It's super-cool that they like, don't have any interdependencies whatsoever! So clean!
Saturday 31 August 2024
Tesla Model 3 problems that the BYD Seal fixes
As stated at the time, after [having a Tesla Model 3 for 4 months](https://blog.themillhousegroup.com/2024/03/tesla-model-3-2022-standard-range-rwd.html), I won't be buying one. But what I may well get (or at least, lease), is the [*BYD Seal*](https://www.byd.com/eu/car/seal) instead - here's why:
| Criteria | Tesla Model 3 2024 RWD Standard Range | BYD Seal Premium 2024 |
| --------- | ----------- | -------- |
| Door handles | Aero, but annoying and fiddly | Aero, but pop out when needed |
| Instrument binnacle | None | Yes, 10.25" panel |
| HUD | No | Yes |
| Indicator stalks | None | Yes, normal |
| Rear entertainment screen | Yes | No |
| Roof | Glass | Glass, silver plated, shade insert |
| Supercharger access | Yes | Yes |
| Manufactured in | China | China |
| Apple CarPlay | No | Yes, wireless |
| Qi charging pads | 1 | 2 |
| 0-100km/h | 6.1 sec | 5.9 sec |
| V2L | No | Yes |
Yep, it fixes just about everything I disliked about the (pre-Highland) Model 3, plus what they "improved" in the Highland refresh, and it costs less to boot. Nice.
Sunday 28 July 2024
Next.js Server Actions - how do I send complex data
[Next.js](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations#forms) has some extraordinary integration between front- and back-ends. It takes some thinking about, but the thing that stumped me the longest was getting relatively complex objects (i.e. not just simple strings) across the "Server Actions" boundary.
## The Rules
There are lots of [rules](https://react.dev/reference/rsc/use-server#serializable-parameters-and-return-values) about __what__ you can send. Basically, it has to be serializable, which makes sense. But *how* do you send these things?
Unfortunately, most of the documentation limits itself to [trivially-simple](https://react.dev/reference/rsc/use-server#server-actions-in-forms) examples; like [a single simple string](https://react.dev/reference/react-dom/components/form#handle-form-submission-with-a-server-action)
My specific use case was trying to get an object that contained an array of very simple objects across the boundary:
```
type DTO = {
id: number;
foo: string;
people: Person[];
}
type Person = {
name: string;
age: number;
}
const dataToSend: DTO = {
id: 123,
foo: "bar",
people: [
{
name: "Tom",
age: 12,
},
{
name: "Dick",
age: 45,
},
{
name: "Harry",
age: 78,
},
]
];
```
While the order of the objects within the array was unimportant, it was crucial that the elements of the object could be rehydrated (for want of a better expression) correctly on the server side - i.e. with the `Harry`-is-`78` connection intact, guaranteed.
## On the client (version 1)
This should not be too hard, I thought to myself. I mean, sending form contents was pretty-much the original "interactive internet" mechanism; `/cgi-bin` and all that old stuff. I found a useful page that explained a [decent mechanism](https://mattstauffer.com/blog/a-little-trick-for-grouping-fields-in-an-html-form/) to serialize arrays, which I implemented like this in my React form:
```
{dto.people.map((p, i) => (
<>
>
))}
```
I went down that rabbit hole for far too long, before realising ... I *can* just send a string - a **JSON** string!
## Client, version 2
This new version feels much more React-idiomatic, and does a lot less grubbing around in `form` minutae. It uses the `useFormState` hook as well as a `useState` to allow the client to snappily update while the user is doing data-entry. Then when it's submission-time, everything gets shipped off in the everything-old-is-new-again Server Actions kinda way (various validation and button-enablement niceties removed for simplicity):
```
"use client"
const initialState = {
message: ''
}
export const SelectionForm = ({ dto: DTO}) => {
const [state, formAction] = useFormState(createOrUpdateServerAction, initialState);
const [inFlightPeople, setInFlightPeople] = useState(dto.people);
const onPeopleChanged = (newPeople: Person[]) => {
setInFlightPeople(newPeople);
}
return (
)
}
```
## Server action
In case you're wondering what the server-side of this looks like:
```
"use server"
export const createOrUpdateServerAction = async (prevState: any, formData: FormData) => {
const id = parseInt(formData.get("id"), 10);
const foo = formData.get("foo");
const peopleString = formData.get("people");
const people = JSON.parse(peopleString) as Person[];
// etc
```
.. Yes, you could just `JSON.stringify()` your whole object and send that instead of using `hidden` form fields. But I kinda like seeing them make their way over the network old-skool. Maybe that's just me :-)
Saturday 15 June 2024
Home-Cooked and Delicious
I've just read a wonderful article by [Maggie Appleton](https://maggieappleton.com/about) calling out what she terms ["home-cooked" software](https://maggieappleton.com/home-cooked-software) and the "barefoot" developers who work on such things.
It really made me want to crack back into publishing *scratch-my-own-itch* software; I've done a [little](https://github.com/themillhousegroup/mondrian) [of](https://github.com/themillhousegroup/scoup) [it](https://github.com/themillhousegroup/react-chromakeyed-image) in the past but a lot of it remains unfinished, either due to being "good enough" or, in probably my most public flame-out, the [Broadlink binding](https://github.com/themillhousegroup/broadlink-openhab-binding) for [openHAB](https://github.com/themillhousegroup/openhab2-addons), being ground down by a PR process (to get the binding integrated into the main codebase) that took so long that it became too onerous for me to continue with it.
I've got a few things brewing in my Github that aren't quite ready for public consumption yet, but in the meantime, almost as a counterpoint to my [post at the start of this year](https://blog.themillhousegroup.com/2024/01/my-apps-2024.html) where I shared my totally-*un*-niche list of uncontroversial, mainstream tools I use regularly, here are a couple of **super-barefoot**, totally home-cooked bits of software that excellent people have crafted, that happen to intersect with my niches of interest.
I should preface this short list with some background. As keen longtime readers might have [deduced](https://blog.themillhousegroup.com/2022/04/automating-heating-vents-with-openhab.html), I am a lover of Danish plastic. Yep, I'm an AFOL, an Adult Fan Of LEGO. I'm actually such a fan that I have *two* distinct collections; my personal stash of umpteen pieces, which is gradually being formed into a rendition of a railway running through a mythical French town in the 1980s:
As a side-effect of picking the "good bits" out of the many LEGO sets I've been gifted or bought, I've ended up with quite a number (over 11,000 at last count) less desirable (to me) LEGO pieces, and so those are in my "for sale" collection, available for perusal at both the major online LEGO marketplaces, [BrickLink](https://store.bricklink.com/trainfan#/shop) and [BrickOwl](https://trainfan.brickowl.com/). Which brings me to the first bit of home-cooked software.
### BrickSync
[BrickSync](http://www.bricksync.net/) does just what you'd expect inventory-management software to do in this instance; it keeps my inventory list in sync between the two marketplaces. Given that there are currently around 18,000 stores in BrickLink and 2,500 in BrickOwl (it's much younger, you can clearly see it in the UI), you would think that there would be, **at most** a worldwide market of 2,500 customers for this application. And yet here, completely free (although a donation is of course welcomed) is an open-source product that works extremely well, **just for us**.
It's a little rough around the edges, works in a weird *kinda-CLI, kinda-DOS-app* way and unfortunately *requires* to be run on an x86 platform (so no Raspberry Pi, but luckily I have an Intel NUC that fits the bill) but it deserves a shout-out for getting the job done. Something I've just added to my todo list is to download the [source](https://github.com/ZZJHONS/Bricksync) and make it much more like a traditional Unix CLI program, so that I can schedule synchronization runs with `cron` instead of having to leave a terminal window open while SSHed into my NUC.
### Brickognize
Closely-related to the first item. Historically, one of the most painful tasks when weeding out parts from my personal collection into my online for-sale inventory, has been **classification**. BrickLink has been around for a *very* long time, and being the only LEGO marketplace has led to certain naming conventions chosen by the original developer having "stuck". For example, *Light Bluish Gray* is *the* term used in the community, despite being completely different to the official LEGO colour name: *Medium Stone Gray*. Similarly, Bricklink groups similar parts into "categories" in ways that are sometimes difficult to follow. This made finding the right colour, category and part number for the given lump of ABS sitting in front of you quite the challenge. Until now.
[Brickognize](https://brickognize.com/) lets you take a picture (e.g. with your phone) of a LEGO part and will give you the most likely parts (including their BrickLink part number) that it corresponds to. There's obviously a very well-trained AI/LLM under there because in my experience (and I've used it probably a hundred times by now) it gets the part exactly right 98% of the time, and if it's not right, it's *very very* close.
And again, it's free. It even generously shares an [API](https://api.brickognize.com/docs) so you could build it into a workflow to streamline inventory additions yet further. Awesome.
Barefoot developers, you inspire and delight me. Thankyou.
Labels:
ai,
dev,
lego,
markdown-enabled,
nuc,
raspberrypi,
software
Tuesday 21 May 2024
Facepalm: Vanity email, insanity-email
Wow. Long time no [facepalm](https://blog.themillhousegroup.com/search?q=facepalm). Guess I must be in the right job!
This was a good one though.
So at work when a new customer signs up, one of the *many* things we do is create an [Auth0](https://auth0.com/) account for them. It's really just a "shell", with nothing of any value in it, but it gives them a stable identity to build other stuff off.
To create such a shell account we just need their email address, and we conjure up a [random UUID](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID) to use as their password. This has worked flawlessly for *tens of thousands of customers*.
Then, today, it didn't.
Auth0 gave us:
```PasswordNoUserInfoError: Password contains user information```
I'm sorry, what?
A certain amount of back-and-forth ensued with the devs who feed-and-water Auth0. It turns out there's a rule in Auth0 that is trying to avoid users including part of their username in their password. You know, how Granny likes her credentials to be `grangran@hotmail.com / grangran`.
So this *particular* customer had a custom "vanity" domain (which I will change for the sake of privacy) and was using a single letter as their email address; e.g.:
```d@dangermouse.com``` *(not their real address)*
And the Auth0 check was thus exploding if it found ***any instance of `d` in the random UUID password***. A [quick check](https://stackblitz.com/edit/node-yab2dv?file=index.js) shows that *~85% of UUIDs* generated by [Node's `crypto/randomUUID`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID) will contain a `d`.
**Facepalm.**
Tuesday 9 April 2024
My Clojure Brings All The Recruiters To The Yard
So my LinkedIn profile has included the word **Clojure** for the last few years, despite my only having used it for ~2 years about a decade ago. I enjoyed it back then, and even [blogged about how nice an experience it was](https://blog.themillhousegroup.com/2014/09/why-clojure-is-fascinating-me.html). But I can't say I've kept current with it since. It's pretty niche, and I suspect only getting more so ...
Which is I guess why my inbox **exploded** yesterday with recruiters _all clamouring to tell me about the same Clojure position_:
I guess **QANTAS** aren't bothering with any exclusivity agreements on that one 😂
Subscribe to:
Posts (Atom)