2025-12-27

Having your "Smart" and your "Open", too

This is the third of a group of of posts on "Smart" appliance. You don't strictly need the first and second entries one to read this, but they're meant to be building up a common context. In this episode I talk about a mechanism that would relieve most of my worries about these devices while allowing manufactures to maintain control of their trade secretes and the presentation of their interfaces.


My complaints in the first post aren't intrinsic. Instead they are complaints about a particular implementation of the smart model where the physical device requires a specific (rather than generic) piece of paired software; in that implementation our intellectual property regime around software puts the manufacturer in control your ability to have that software, and consequently puts the manufacture in control of your ability to use an physical device that you bought. That is totally unacceptable.

Out strategic goals are

  • Possession of the physical device grants access to its functionality1 because the requisite software is generic enough to be re-implemented on any suitable platform.
  • The manufacture gets to keep their trade secrets to themselves.2

That's an interesting pair, because the first requires that the trade secrets be physically embodied in the machine (otherwise you can't guarantee that the functionality moves with the physical object). But that seems to be in tension with the second requirement, because how can the manufacturer control trade secrets if they're trading freely around the economy?

The thing is that the control software (which is already on board) also encodes the trade secrets. Anyone with the right tools and knowledge can already can extract the firmware, decompile it, and sus out the meaning of resulting code. It's just that that's a pretty hard trick (those boards don't need debug ports and I'd be unsurprised to find that many don't have them) and consequently time consuming and expensive. Then, as always, the legal regime puts up further barriers to someone trying to compete using that approach.

And we deal with systems that have those properties all the time. I'm describing a client-server architecture. The appliance is the server, your phone or tablet is the client, and the manufacturer can hide as much detail as they want server-side. By using open protocols for interchange and open standards to present the interface consumers get a extortion-free way to talk to the appliance, and in return manufacturers get reduced software development costs and all-platform3 functionality for free. It's that easy.

My hot-take, off-the-cuff, proposal for the whole thing:
HTML5.

Literally put a little web-server inside every appliance. They already have non-trivial computers and at least one of WiFI or Bluetooth, so this is not a stretch. In the worst case users can use a plain web-browser to access it.4 With HTML5, manufacturers can control every aspect of their interface (I mean their pages can just be a canvas if they're that hung up on controlling the appearance), control how much functionality is pushed to the client-side, and so on.

What's not to like?5


1 All of it. If you own the thing, you own it. This is non-negotiable.

2 Within reason. There is no way to guarantee this absolutely, and (importantly) never has been. Anyone with the tools and expertise has always been able to reverse engineer a product, and it was only ever the high cost of reverse engineering and IP law that prevented them from taking that route to develop a competing product. We're looking for a regime where the level of difficulty in obtaining and profiting from the trade secrets remains similarly high.

3 And not just iOS and Android, either. Everything, everywhere, all at once. As it were.

4 I imagine the ecosystem will rapidly spawn a genre of specialized appliance control apps with a HTML renderer at the core, and featuring convenience functions for organization and access, but a plain web-browser provides a fallback position.

5 Okay, so there is the addressing problem for devices that use WiFI. Some kind of discovery mechanism will be needed, and I don't know— off the top of my head—what the options for that are. But it's not like this is a new problem: printers and scanners, especially, already handle this in a variety of ways. Similarly bluetooth has a pairing problem to solve, but that's an issue for existing bluetooth devices as well.

2025-12-20

Why "Smart" appliances might make sense

This is the second of three related articles. If you haven't seen it already, perhaps you should read the first installment.


Some devices have very simple interfaces: they're either running or not, so they need a switch. But then, maybe there is a thing you make stronger a weaker, so you add a dial or a slider. And maybe it can run a couple of ways, so you add a selector dial. Maybe the user needs some progress feedback, which means some kind of clock.1 And so on.

In my youth, a typical washing machine, dryer, or dish washer had a handful of controls supporting as many as a dozen modes of operation, and that felt like progress compared to the kit my parents grew up with. Yeah! Living in the future!

This is the control panel from the dryer at Casa NoSwampCoolers. There is a power button and a separate start button (because power means the controls are active and start means the machine is running), a mode-select dial with fourteen options, four categories of adjustable parameters (some of which only apply to some modes), six additional Boolean settings (again, applicability varies), and a time-display-and-control group. I'm not sure if that reaches a thousand front-panel accessible combinations but it is certainly hundreds. And there are extended features only available with an app.2

Okay, so we've established that (at least some) modern appliances require complex interfaces. But we've also established that you can build the interface into the device. So, how does that justify connecting it to your carry-around-computer-thingy?

Well, we're in engineering trade-offs land. We get to compare costs, convenience, maintainability, utility, and user preferences between alternatives. And how we rate some of those depend on how the machine works. If you have to physically load and unload a device for it to be useful (as in clothes washers, clothes dryers, and dish washers) then remote start is less helpful compared to remote access to your car's defroster.

Settable defaults are nice
In principle, to know that my dryer is doing what I want I have to memorize the desired state of approximately a dozen controls, and check each of them on the control panel. In practice I memorize the smaller number of things it takes to get from "just booted" to the state I want: switch the mode to "Speed dry", set the temperature to low, increase the time, then go. Wouldn't it be nice if I could set the default state or name several default states? If there is a win here it's for "smart"
Control panels cost money and represent additional points of failure
Controls are a pain. They have to be robust, when the options are discrete they should be unambiguously in one state or another, they should give clear feedback, and in the physical realm all of that takes engineering. The cheapest switches and dials general lack something. I'm not in this business, but over the years I've talked to people who are. Controls often represents a surprising portion of the cost. To be sure, digital interface can fail too. If the magic smoke gets out of the computer on the device, your done with either kind of controls.3 If your phone smokes, you need a different interface device. At least partly in favor of "smart"
You're right there anyway
As mentioned above, some devices require your presence. For those I really want a on-device control set for the primary features. Because otherwise they require the presence of you and your phone or tablet. But I'm pretty happy with our machine that presents the big easy stuff on the panel and leaves the fiddly choices for a computerized interface. Definitely favors some physical controls on machines you have to attend in person
Translation
Did you notice that the controls on my washer are labeled in English? That's built into the physical medium, so a French-speaking, would-be user can't just change it. Now the company could (and presumably does) provide that part in multiple language variants, but that becomes a logistical headache and thus a cost and you can't easily switch back and forth. Adding translation to a software interface is not free, but (with some support from your OS or framework) it can be relatively painless, and users can change the language on demand. That's cool. A reason for pixelated UI, where ever the display is mounted
Maintainability
Once a physical control panel is engineered, manufactured, and sent into the wild changes are hard and expensive. In principle, software changes are easier, though if the software in question resides on the machine, you'll need to provide a firmware-update facility of some kind.4 If there is a win here it's for "smart"
"Just give it a proper display" is a good idea, but you already have a display...
There is nothing magical about using your phone or your tablet that enables digital UI. You could put a display (probably a touch display) on the device and then have a programmed interface. Yeah!. But ... how many pixels are you willing to pay for? And on every appliance in your house? what about physically small devices like my espresso machine? And those question lead to the idea of sharing a single interface device. Your potential customers almost all have a phone or tablet already,5 why not just borrow that display? Favors "smart"
Human stuff
This is hard for me. I'm not trained in UI/UX. I haven't done any surveys. Basically this whole essay has been me ranting about how I feel. But people sometimes care about appearance, and sometime don't like to learn new things. And so on. I'm not going to guess how this breaks for smart versus not smart.

Anyway, the next article presents what I think might be a viable approach that everyone can live with.


1 In some instances the dial that the user used to set the run-time was a mechanical clock and would wind back down also serving as the time-remaining indicator. Parsimonious design, that.

2 This is the sort of situation that prompted the third Smart Appliance rule. That dryer is very useful even without the app. Indeed, we've never used the app. Mrs. NoSwampCoolers tells me she has used the app for the matching washer (with a very similar control panel) once in the fourish years we've owned these things. Once.

3 Yeah. I'm just assuming there is a computer in there. My sense of it is that no one is designing these machines with end-to-end analog circuits anymore. I once did a few hours of analog diagnostics on a Kenmore model 40, but I'd expect to find a MCU behind any reasonably modern control panel.

4 My car's entertainment center has a SD card slot hidden behind a pop-up panel.

5 If they don't we're talking an additional up-front cost for them, but if they can afford the appliance, ... well Walmart's website lists tablets starting from under US$50 as I'm writing this..

2025-12-19

Little surprises #10: iterators, half-open intervals, and git range selections

Let's talk about for loops. That's not actually the subject of the post, but we have to start there, hit a couple of stops along the way, and only then complain about git.1

Back in the misty depths of time, there were programming languages that indexed their arrays starting with one. If you write that in a c-like syntax you end up with something like

int array[ARRAY_SIZE];
// fill it somehow
for (size_t i=1; i<= ARRAY_SIZE; i++)
	print(array[i]);
where the end condition is controlled by <=. That has never really gone away—despite the efforts of some influential people—but I want to direct your attention briefly to the same for-loop in a language that uses zero-indexed arrays. Now idomatic code uses a strict less-than comparison:
int array[ARRAY_SIZE];
// fill it somehow
for (size_t i=0; i<ARRAY_SIZE; i++)
	print(array[i]);
but you could also use a not-euqal-to (!=) because the upper limit of the loop no longer represents a cell of the array that is accessed. Instead it represents where a cell would be if it were one-past-the-current-end.

Pointer and Iterator loops

Of course, you don't have to use an index for a loop counter. K&R is full of examples where they use a pointer for processing a (null terminated) string:

const char *s = "Yellow whirled!";
for (char *p=s; *p != '\0'; p++)
	print(*p);
or a linked list:
LIST_NODE *list;
// fill it somehow
for (LIST_NODE *p=list; p != NULL; p=p->next)
	print(p->payload);
In those cases the termination condition is a not-equal comparison to something that is (again) not processed.

And that is the pattern than many languages (especially the ones I use regularly) use for iterating a (possible subset of a) collection. The beginning iterator designates something that is to be processed and the ending iterator either signals the lack of further data or designates data to not process. In C++ this is the pattern for the algorithms library (where you hand the routines explicit iterator pairs), and by ranged-for loops where the syntactic sugar calls std::begin and std::end on the object to be iterated. The end-marker does not represent data to be processed.

On to git

Some git operation let you interact with groups of prior commits. To inspect a sub-set of the history, run a partially automated bisection process for finding where an issue was introduced, or (as I was doing when I discovered2 this behavior) to select a set of commits to "cherry-pick" to another branch. Unsurprisingly the command line syntax needs you to say what commits you want, and listing more than a few hashes explicitly is a pain. Luckily there is a "range" notation: [Early commit reference]..[later commit reference].

Now, if you use this notation with cherry-pick it will apply your selected commits to the new branch in time order: starting with the earliest selected commit and working it's way steadily toward the latest. Which is exactly what you would expect.

What you probably wouldn't expect (unless you, ya'know, actually read the documentation), is that it will exclude the earliest commit you entered and included the latest. This was so surprising to me that I actually did it wrong a second time before I started scouring the manual for an explanation.

That is a half-open range like we find in the discussion of iterator for-loop above. But instead of being [start, end) it is (start, end]. What. The. Absolute. Heck?!?

That said...

Interestingly, if you look a little deeper, there is a reason. Or at least a way in which this behavior arguably conforms to my description above.

Start by understanding how a git repository is structured. Each commit contains a record (effectively a pointer-to) all the commits that act as its parent(s) (there might be zero, one, two...). But commits are immutable once created, so they can't be amended with pointers to their descendants. The effect is that (assuming your range selection represents a branchless part of the graph3) you're looking at a linked list with the last commit as the head and the earliest commit at the end (of the range you care about, anyway).

So, even though commits are applied in time-order, they are found starting from the last event and working back in time. The docs make this clear: the selection is all the nodes reachable from the final commit, but not reachable from the first commit.4

And in that view, this is exactly like the linked list example I wrote above.


1 A little like eating your vegetables before dessert.

2 The mere fact that it is clearly documented if you bother to read doesn't affect my right to use that word. Does it?

3 I haven't actually tried it yet, but I imagine that selecting a span with this syntax that has a merge in it would include a large historical sub-tree you don't want.

4 And a commit is reachable from itself, naturally.

2025-12-14

The "Smart" appliance policy at Casa NoSwampCoolers

By way of introduction to some follow up posts: appliances that require a external computer to make them go are a kind of cyber vulnerability. The utility of the device is dependent on the availability of a working interface and thus on the whims of a third party. If they drop support; if they decide to lock some features behind a paywall;1 if they don't support the app on the platform you use, your device is worthless.

While someone could, in principle, program a new interface there are a number of problems:

  • Access to information about the device side of the interface. This stuff is trade secrets, and the holders rarely see it as profitable to share it, even if they are no longer supporting the affected devices.
  • Good engineering practce may suggest using a a common code base, but manufacturers are also willing to switch hardaware platfomrs to minimize costs. The result is a lot of diversity even within single product lines, as witnessed by the long running struggle to support various peripheries in the linux kernel.
  • Finally there are legal barriers to the "just hack it scheme". In the US, for instance, the DMCA, means that the most trival effort by the manufacture to "protect" their kit makes the hacking job a felenoy. Until the right to repair is legally recognized,2 this multiplies the difficulties improsed by the others.

So here in Casa NoSwampCoolers we have a three part policy:

  • No device which requies an account registered with the manufacturer.
  • No device which requies an app provided by the manufacture to access it's core function.
  • Any device which requires an app to access special features or functions must be explicitly discussed by the grownups prior to purchase.

The second part of this series will address the engineering reasons in favor of "use you device to control it", and the third will look at a solution that lets us have the upsides without the downsides.


1 No names, BMW.

2 I consider that a cause worth my political dollars. Would you care to join me?

The anagram problem (part 1/N)

Programmatically finding the anagrams of a given input is an easy problem if you have a list of valid words. Easy enough that it is often used as an example problem or assignment in introductory programming classes. The key1 observation is that the order of the letters doesn't matter so you can safely place them in a deterministic order for comparison. You decide if two words are anagrams of one another by sorting the letter in each and comparing the resulting strings. In psudocode it looks roughly like skey := sorted(input) for (word in words) if (sorted(word) = skey) println(word) Easy peasy.

Except that I'm writing in C++, so some things are wordier than in other langauges and there some boilerplate to wrap around it all. But, hey, at least it isn't Java. Oh, and I usually work on more substantial projects and in a pinch you code like you practice, so perhaps I should employ some techniques that I don't actually need for a toy example like this. Yeah, that's the ticket. Let's make the key a strong type.

The point is that the sorted nature of the contents of the key are the very core of it's nature. There is no point in comparing a key against an unsorted word: it won't tell us what we need to know. So make ourselves a type that wraps up a string with the content being sorted as a class invariant and provide comparison support between keys but not between keys and plain strings. That way the compiler will tell us if we make a mistake like that. Not really needed in a toy project like this—debugging those kinds of issues isn't too painful in a tiny code base like this—but good practice.

class key
{
public:
  template <typename Iter>
  explicit key(Iter first, Iter last)
      : val(first, last)
  {
    make_invariant(val);
  }
  explicit key(const std::string &input)
    : key(input.begin(), input.end()) {}
  // Move and copy c'tors and assignments operator

  store_type::size_type length() const;
  store_type::size_type size() const;

  // Provide iterators for use in the algorithms header

  auto operator<=>(const key &rhs) const = default;

private:
  void make_invariant(store_type &);

  std::vector<std::string::value_type> val;
  };

So, put into almost up-to-date C++ idiom (i.e. C++20, as I'm not yet ready to deal with the ranges and views library), the core of the anagram program looks like:

  std::ifstream in(argv[2]);
  if (!in) {
    std::cerr << "Failed to open '" << argv[2] << "' for reading."
              << std::endl;
    return EXIT_FAILURE;
  }
  std::vector<std::string> dictionary = anagram::get_dict(in);
  const anagram::key search_key = anagram::key(std::string(argv[1]));

  std::vector<std::string> alternatives;
  std::copy_if(dictionary.begin(), dictionary.end(), std::back_inserter(alternatives),
	       [&search_key](const std::string &word){
		 return anagram::key(word) == search_key;
	       });

  for (const auto &word : alternatives)
      std::cout << word << std::endl;

Digression

Perhaps you're wondering why I would care about an introductory exercise? Afterall, the problem describe above had no surprises. That is a fair question, and the answer is simple: I've recently started doing more puzzles involving letter and words. Cryptics, wordle and things like that. I've been a fan of plain crosswords for a long time, but new things are good for the brain at my age.

Anyway. Anagraming is a common sub-puzzle in cryptics, and I'm only so good at it. So I wrote a tool to use when I'm struggling. But it turns out the puzzles present cases that extend the basic anagram problem...

Back to the main story arc

Sometimes you know how long the desired word is but only some of the input letter. Say in cryptics you have an anagram idicator and you're confident about what fodder goes with it, but you have more spaces than characters in the existing source. For that we need a little more input (the range of lengths to consider, and to change the condition from equality of keys, from strict equality to the search key being a strict subset of the word key. We'll need to user to indicate the total size as well as the known source characters. As a minor generalization we can allow a range of sizes. Then we replace the main loop above with

  std::vector<std::string> anagram::find_single(const anagram::key &search_key, size_t minl,
					      size_t maxl,
					      const std::vector<std::string> &words)
{
  std::vector<std::string> alternatives;
  std::copy_if(std::execution::par_unseq,
	       words.begin(), words.end(), std::back_inserter(alternatives),
	       [&search_key, minl, maxl](const std::string &word){
		 const key word_key(word);
		 const auto word_len = word_key.length();
		 return (word_len > 0 && word_len >= minl && word_len <= maxl &&
			 is_superset_of(word_key, search_key));
	       });
    return alternatives;
      }
which calls
bool anagram::is_superset_of(const key ∓candidate, const key ∓search_key)
{
  std::string unmatched;
  std::set_difference(search_key.begin(), search_key.end(), candidate.begin(),
		      candidate.end(), std::back_inserter(unmatched));
  return unmatched.size() == 0;
}
I call that "partialgram".

Multi-word results remain outstanding

One more problem. Maybe you have a set of source character and want to create more then one word from them That will be the subject of the next part.


1 Pun intended.

2025-11-29

Has "AI" just made that experience worse?

Courtesy of my father's lengthy service in the Navy, I've been able to use USAA1 for insurance for my whole adult life. A small but comfortable privilege. Now, I bring this up because of USAA has had a ready-for-prime-time, flexible, responsive, automated-voice-interface answering their phones since (at least) the mid nineties (it informed my comments on an earlier post about this category of convenience). It's been my preferred interface for paying bills since I got my first policy with them.

Anyway. I paid a bill recently, and the voice interaction on the phone felt laggy—compared to a system that's been working since before the turn of the millennium—and I formed a suspicion that someone has replaced the gut of the existing system with a big machine learning model.

Now, there is a sense in which that is a very good idea. The existing system presumably had trouble with some accents and a large model can learn to overcome that. But it still felt like a worse experience.


1 A mutual insurance corporation in the United States. Originally for US Army officers, they've expanded their membership criteria over the years but the idea appears to be to select a highly responsible membership base.

2025-10-17

Just where are you from, anyway, my good mammal?

I've been packing for a short trip, and I found some paperwork from our last short trip. The one to Carlsbad Cavarerns. We were prevailed upon in the gift shop and "adopted" a bat (i.e. made a donation, but the kiddo liked the idea).

Only I see in the paperwork that it is a Brazilian Free-Tailed bat (one of the many speicies found in the park). Brazilian. In America.

No one tell Trump.