Media

AV Control: One Plugin to Rule Them All

Turning brands, protocols, zones, and even infrared into one common HomeSeer language

home automationhomeseerhdmi-cecavreceiverstelevisionsirarchitecturenetworksoftwareaudio-videosoftware-automation

One Engine, Many Dialects

Not in the HomeSeer store quite yet. It’s been running here every day, and the plan is to send it to the store as a beta.

This one began as a limitation somewhere else.

While building the Roku plugin, I wanted HomeSeer to know the actual volume in the room.

The Roku could participate in volume control.

What it could not tell me was:

What is the television or receiver’s actual volume right now?

That distinction matters.

A Roku streaming player can participate in controlling downstream equipment through HDMI-CEC, and newer Roku firmware can even report whether TV power and audio-volume control are supported or enabled.

What it does not give me is:

The receiver is at -32 dB.

Or:

The television is at volume 27.

That state belongs to the device actually producing the sound.

The Roku can help send the instruction downstream.

It does not necessarily know what happened afterward.

So if I wanted the real state of the room, I had to talk to the television or receiver itself.

That sounded like a small problem.

It did not remain one for long.

The obvious answer was the wrong one

The immediate solution is easy to imagine.

Write a Samsung plugin.

Then an LG plugin.

Then an Onkyo plugin.

Then Denon.

Yamaha.

Marantz.

Sony.

And continue doing that until either every AV manufacturer is supported or civilization ends, whichever comes first.

I actually started thinking in those terms.

Then the duplication became obvious.

Every one of those plugins would spend most of its life doing essentially the same things:

Discover a device.

Identify it.

Connect to it.

Maintain whatever session the protocol requires.

Send commands.

Receive or request state.

Translate the manufacturer’s vocabulary into something HomeSeer understands.

Expose controls and status.

Only the wire protocol really changes.

That means they are not fundamentally different plugins.

They are different dialects of the same problem.

So the Samsung and LG plugins that were going to exist never became separate projects.

They became definitions inside one AV system instead.

One plugin, definitions underneath

The plugin is driven by a library of device definitions.

Those definitions describe the things that vary:

How a device is recognized.

What transport it uses.

How a connection is established.

Whether a session must be maintained.

How commands are formatted.

How responses are parsed.

Which capabilities exist.

How zones work.

How power, volume, mute, inputs, transport, and other state are represented.

The core plugin deals in AV concepts.

The definition translates those concepts into whatever language a particular device speaks.

So adding a new receiver should mean:

Add another definition.

Not:

Start another HomeSeer plugin.

That is the promise the architecture makes.

And because promises like that have a habit of becoming negotiable later, the code contains a deliberately unfriendly rule.

Nothing here knows any brand

The core files carry a warning in capitals.

Essentially:

NOTHING IN HERE KNOWS ANY BRAND.

No Samsung.

No LG.

No Onkyo.

No Denon.

No protocol-specific three-letter command buried in an innocent-looking helper method.

The core is not allowed to know.

Everything manufacturer-specific comes from the definition or the transport layer beneath it.

And then comes the more important half of the warning:

If supporting a new receiver requires adding a brand-specific line to these core files, the design has failed.

Not:

Try to avoid doing that.

Not:

Keep it generic where practical.

Failed.

That wording is intentional.

Data-driven designs tend to develop exceptions

A great many systems begin life described as:

data-driven.

Then device number two arrives.

It does one strange thing.

So one little special case goes into the engine.

Then device seven has an unusual startup sequence.

Another exception.

Device twelve returns volume in a peculiar format.

Another.

And somewhere around device thirty, the beautiful definition library is sitting on top of:

if Samsung...
else if LG...
else if Onkyo...
else if this particular Denon...

At that point the data is no longer driving the system.

It is decorating it.

I want the first violation of the rule to be uncomfortable enough that I stop and ask whether the abstraction is wrong.

Because if the model fails, I would much rather discover that while there are four definitions in the library than after there are ninety.

A new device should test the definition language.

It should not gradually contaminate the core.

The core describes structure; the definitions describe weirdness

That is probably the most important separation in this project.

The core owns things that are genuinely common:

A device exists.

It may have zones.

A zone may have volume.

Mute.

Power.

Inputs.

Transport.

Media information.

Capabilities.

The definitions own the manufacturer’s opinion about how any of that works.

If one television retrieves now-playing metadata differently from everything else it does, that oddity belongs in its definition.

If one receiver represents -32.5 dB as an integer with an implied decimal place, that belongs in the definition.

If another uses a persistent TCP socket and a third expects independent HTTP requests, the transport layer absorbs it.

The core describes the architecture.

The definitions describe the weirdness.

And AV equipment is an exceptionally generous supplier of weirdness.

The address scheme was decided before anything depended on it

Another decision happened very early.

HomeSeer device addresses follow this general form:

host | zone | capability

Conceptually:

LivingRoomReceiver | Main  | Volume
LivingRoomReceiver | Zone2 | Power
BedroomTV          | Main  | Input

The exact punctuation is not important.

The structure is.

A HomeSeer device represents:

this capability

of:

this zone

on:

this physical host.

And that scheme was settled before dependent code was allowed to grow around it.

There was a reason for the unusual discipline.

Lutron already paid for the lesson.

Lutron learned it; this one inherited the scar

A HomeSeer device address looks like an internal implementation detail.

Until a user writes an event against it.

Then it is part of the public interface.

Change the address later and the plugin may look completely healthy.

The new devices may be created beautifully.

The revised structure may be objectively cleaner.

And an event written against the old address may quietly point at nothing.

That is a terrible upgrade failure because it does not necessarily crash.

It just stops doing something.

So the AV plugin inherited a rule from the Lutron project:

Once released, device identity does not move.

There is even a comment in the code saying where that rule came from.

I like comments with named casualties.

A future version of me is much less likely to “clean up” something when Present Me has left a note explaining exactly who already paid the tuition.

The parts that resist being generic

Real AV equipment is not uniform.

The definition system succeeds only if it can absorb those differences honestly rather than flattening them into a fictional common protocol.

Some devices maintain long-lived sockets.

Some answer individual HTTP requests.

Some report state spontaneously.

Some must be polled.

Some expose several zones cleanly.

Some barely admit the second zone exists.

Some provide rich metadata.

Some provide almost none.

Some have excellent documentation.

Some have documentation that appears to describe a close relative of the product you actually bought.

The generic engine cannot make those differences disappear.

Its job is to give them somewhere appropriate to live.

Samsung gave me a good test

On the Samsung hardware and control path I tested, most ordinary control fit the definition model quite nicely.

Then I got to now-playing information.

That arrived through a different mechanism from much of the ordinary control and state information.

Exactly the sort of difference that tempts this:

if Samsung then
    go ask this other thing

That would work.

It would also break the central rule.

So the definition model had to become expressive enough to describe:

This capability comes from a different source.

That is a much healthier fix.

The irregularity belongs to the device.

Therefore the irregularity belongs in the device definition.

If another manufacturer later has the same kind of split, the architecture is already ready for it.

That is the point.

Then there is infrared

And then we reach equipment that does not really have a network protocol at all.

Some devices can only realistically be controlled through infrared.

That means putting an IP-to-IR bridge between HomeSeer and the equipment.

From the plugin’s point of view, that can still fit the same general command model.

Power.

Input.

Volume.

Transport.

Send the appropriate IR code.

But infrared introduces a much deeper difference.

It is often one-way.

You can say:

Power On.

The device does not answer:

Yes, I am now on.

You can say:

Input HDMI 2.

It does not confirm:

Correct, HDMI 2 is active.

You can send three volume-up commands.

You still do not know the resulting volume.

The command went out.

That is all you know.

State becomes belief instead of observation

That distinction matters enough that I do not want the plugin hiding it.

With a receiver that reports:

Volume = -32.5 dB

we have observed state.

The device told us.

With a one-way infrared device, the plugin may know:

I sent Power On.

That is not the same as:

The device is definitely on.

Maybe the infrared command missed.

Maybe something blocked the emitter.

Maybe the device was already on and uses a toggle command.

Maybe someone used the original remote five seconds later.

Maybe another automation system changed it.

For one-way control, the plugin may maintain an expected or believed state based on the commands it sent.

But that is not authoritative device state.

Automation software has a tendency to make every value look equally certain once it is on a screen.

I do not think it should.

Observed state is knowledge.

Inferred state is belief.

Both can be useful.

They should not pretend to be the same thing.

Not all infrared has to remain unknowable

That also leaves room for richer definitions later.

An IR-controlled device might have another source of feedback.

A smart power strip may reveal whether it is consuming power.

The television might expose state over the network even though some commands are easier over infrared.

Another component in the room may provide enough information to infer what happened.

The generic model should be able to combine those things rather than declaring:

IR means no state forever.

The real rule is narrower:

A one-way transport does not magically become a feedback channel simply because the plugin wishes it were one.

If state comes from somewhere else, great.

If it does not, say so.

The signature database is doing the real detective work

The device-definition library needs another piece:

How do I know which definition applies?

That is where the device-signature database becomes important.

Devices reveal themselves in all sorts of ways.

Discovery responses.

Model strings.

HTTP headers.

Service fingerprints.

Open ports.

Protocol responses.

Names.

Sometimes combinations of several of those.

The signature system takes what a device actually exposes and uses it to choose the appropriate definition.

And I am building those signatures from observations against real equipment wherever possible.

Because manufacturer documentation in the AV world is useful...

...but occasionally aspirational.

The PDF does not get the final vote

Specifications tell you what a model family is supposed to do.

Firmware decides what the box on your shelf actually does.

Model revisions happen.

Features migrate.

Ports appear.

Ports disappear.

A command exists in a manual but the hardware ignores it.

Another command works perfectly and barely appears in the documentation.

The same retail name spans several generations internally.

So manufacturer documentation is evidence.

Often very useful evidence.

It is not the only evidence.

Where possible, a definition gets spot-checked against actual hardware.

Because HomeSeer ultimately talks to the device.

Not the PDF.

That is the same lesson several of these plugins keep teaching in different costumes.

Capability-driven wherever possible

The best definitions do not merely say:

This is Model XYZ, therefore it can do A, B, and C.

Where the protocol allows it, I would rather ask the device what it actually supports.

That principle showed up strongly in the HDHomeRun plugin.

It belongs here too.

If the device can advertise:

Multiple zones.

Supported inputs.

Volume.

Transport.

Media information.

Power feedback.

Then let the definition and discovery process use that information.

A static model catalogue is sometimes unavoidable.

But it should not be the first choice when the hardware can describe itself.

Model-string matching is easy today and surprisingly expensive five years later.

One room can contain several partial truths

The Roku limitation that started all of this also revealed a broader architectural problem.

A modern AV room is not one device.

It may contain:

A streaming player.

A television.

An AV receiver.

A media server.

An HDMI matrix.

An infrared-controlled source.

A serial-controlled projector.

Something else on the network.

Each may own a different piece of state.

The Roku knows what app is active.

The media server knows what title is playing.

The receiver knows the real volume.

The display knows its current input.

The IR bridge knows what command it sent.

No single participant necessarily understands the whole room.

That is increasingly what the AV plugin is becoming:

Not merely a way to control lots of boxes.

A way to assemble the room’s state from the devices that actually own each part of it.

That is a much more interesting problem.

Why not separate plugins?

There is still a perfectly reasonable alternative architecture:

Samsung plugin.

LG plugin.

Onkyo plugin.

Denon plugin.

IR plugin.

And let HomeSeer events stitch them all together.

That would work.

What I dislike is where the duplication lands.

Every plugin needs:

Discovery.

Connection health.

Logging.

Session management.

Zones.

HomeSeer device creation.

State publishing.

Configuration pages.

Error handling.

Command routing.

Then every one of those implementations gets to drift independently.

I would rather solve those common problems once and spend the manufacturer-specific effort where it belongs:

describing the device.

That makes support for another brand a library-growth problem instead of another software-product problem.

At least if the architecture keeps its promise.

Hence the capital letters.

This is the ambitious one

Of all the plugins in this collection, this is probably the most ambitious.

It is also the least finished.

Those two facts are related.

The core architecture is not especially mysterious.

The scope is.

Every manufacturer adds another dialect.

Every product family adds another set of capabilities.

Every firmware generation is another opportunity for something to be almost, but not quite, the same.

Then come:

Zones.

Inputs.

Volume scales.

Mute.

Power.

Media state.

Transport.

Source metadata.

Persistent sessions.

Serial.

Infrared.

Network protocols.

Discovery.

And all the strange little behaviors that never made it into the manual.

But that is also what makes it interesting.

The goal is not to cram knowledge of every manufacturer into one enormous engine.

The goal is to make the engine generic enough that manufacturer knowledge stays outside it.

If that holds, the plugin can grow for years without the core becoming progressively more brittle.

A new model becomes a definition.

A new protocol behavior becomes a transport capability.

A strange response becomes a parser rule.

And the center remains boring.

Boring cores age well.

The one that grew from a missing volume number

I still like how small the original problem was.

I wanted to know the volume in a room.

The Roku could not tell me.

So I needed to ask the television.

Then perhaps the receiver.

Then I realized those integrations were mostly the same problem.

Then the brand plugins became definitions.

Then definitions required transports.

Transports required capability models.

Infrared forced a distinction between observed and inferred state.

And somewhere along the way:

“How loud is the television?”

became a general AV architecture.

That is fairly typical of the projects I enjoy most.

You begin by trying to answer one small question.

Then discover the interesting problem was hiding underneath it the entire time.

This one is still growing.

I rather suspect it will be for a long while.