Many devices have channels with interrelated dependencies. IP address is only enabled when Type is Static. The Zoom and Focus motors are disabled when the Position motor is active. Most channels are disabled when the Power channel is off, except model and firmware version channel may be valid.
The binding is the authority when it comes to the device state machine and being able to inform on what is and is not valid at any given moment, and this can get quite complex on a device with 180+ channels.
How should the binding inform the framework when channels become available/unavailable due to other state changes? The binding knows this, Administrators who are creating Groups and rules probably do not.
First of all, model and firmware information should never be channels as they are static properties of a device/thing. Not completely true for firmware but a thing should be updated (restarted) after firmware update and then reread the properties.
Within your binding, you can implement a logic preventing command handling when a certain channel received a special command.
Completely removing and readding channels cause of other commands seems to be oversized to me, not only cause it could cause warnings in the logs as a then unlinked item might receive a command.
Btw, you can link/unlink or add/remove channels within a binding and the framework will automatically be informed. But this information can AFAIK not be reused in rules, so will not prevent users from reacting.
What can be done, and i used it in the past within BasicUI, is use a visibility option based on states. So you can hide zoom control if motor is moving e.g. This option will be added to openHAB 3.0 MainUI as well.
So, if I understand you correctly, all I can do is provide a status that would allow whoever is implementing the UI the ability to hide or otherwise inactivate a control/group if they so choose? There is no notion of enabled/disabled (still present and linked, but inactive) in the framework? What would changing the channel to read only do? Ultimately, I can only supply the status from the binding, but not the UI implementation. I’d have to supply documentation to the Admin on suggested way to implement the other side?
One interesting use case begins with something as simple as the lamp (power) channel.
You turn channel on
The lamp turns on
lamp goes to status=On
You turn the channel off
A separate R/O channel “Cooling” goes to status=On
lamp goes to status=Off and disabled=true
When cooling completes
Cooling goes to status=off
lamp goes to disabled=false
This is extremely critical. I’m replacing another system I put together 15 years ago, and I know from experience that the system won’t work correctly if the rules can’t follow the status, because someone invariably turns the room back on before the projector has cooled and the ON event gets ignored. The high level logic has got to be smart enough to know it’s got a pending item in the aggregate that it’s waiting on, it’s got to satisfy it as soon as it can, and it’s got to report to the user what’s going on, otherwise all kinds of human drama breaks out.
Also, the Model is useful because not all models have all channels, so it can be used for auto discovery, and the firmware is important to consumers because some of the calibration use cases are firmware dependent. The manufacturer has changed algorithms, so different settings are recommended and channels may need to be dynamically altered… it’s a whole can of worms. Needless to say, some consumers may need the version to know how to interpret some channel values.
Still no reason t make it a channel. A binding would implement a discovery service that should identify the device in some way, let’s say the modelId. Based on that information, the handlerFactory would create a new handler instance specially for that device model and puts the modelId into a system property (properties.put(PROPERTY_MODEL_ID, deviceModel);
During discovery or handler initialization, the firmware version can be requested and also put into a system property (properties.put(PROPERTY_FIRMWARE_VERSION, fwVersion);
Both can be passed as params to the thingHandler, so during initialisation, you can create channels dynamically based on modelId and/or firmware version or even calibrate the device based on that information. No need to create a channel for both.
This should be part of documentation.
You could simply create a r/o string channel with this important user information to be displayed in the ui. Within the binding you than can “block” switching the device on for a certain time after being switched off. This should be quite simple. Nothing the framework really needs to know. Even if the framework would know, how should the UI know how to handle that information. So it is admins job to create a UI presentation fitting for that device or special use case.
I’m not sure what you mean. The version is transient. It’s generally preferable to read actual values out of a device, when provided. User metadata would be a fallback. Admins usually allow metadata to get stale, assuming they felt the need to enter an optional parameter in the first place. And if you were going to enter a version, what would you source it from?.. it never gets entered because it’s to inconvenient to get. Then there’s the issue with the string not being entered consistently by an admin.
But my perspective of one of being heavily involved in Enterprise Asset Management, where we rely heavily on tools which mine model, serial number, firmware version, options, etc., from devices in order to build a portfolio and a maintenance schedule for it all.
I’m also involved in software development which is dependent on reading accurate version information from an API in order to know what contract to expect. This is a huge deal in terms of interoperability with 3rd party plugins and such. Making things inconsistent and hard is an impediment to driving adoption and partnerships.
But I don’t want to get hung up on these items, it’s already been paid much more attention than need be. I’m using a metadata driven approach, so every channel is simply a single line in an XML file, so anyone who does not want a channel to exist can simply remove it.
In this day and age one typically expects UI’s, usually supported by the underlying layers, to implement the most common metaphors that we’ve been drilling into users heads for decades. Hidden and Enabled have been properties on visual objects since the 90’s and “grayed out” has a dictionary definition.
In 2020 these ubiquitous visual states represent an implied contract which set user expectations. When you tell a user that a control is active, by showing it in an active state, a bad user experience occurs when the control does not behave as expected according to the implied contract.
While programmers have typically been responsible for setting properties on UI controls they instantiate and maintain, there has long been the concept of a “bound control” where the UI element is more tightly coupled to the semantics of the underlying layers. openHAB certainly implements the “bound control” paradigm; binding UI elements to items which are bound to channels which are bound to physical device properties. But my understanding as of now is there is no way, by design anyway, for a binding to inform the framework of a device property (channel/item) which is either inactive or not relevant to the current context?.?. What the other layers choose to do with that information is entirely theirs to decide, but if they’re not informed in the first place there’s no way for them to make informed decisions.
Now, I can go around that by simply adding some additional status values I send up, like “disabled” and “hidden”, which is the direction I’m leaning, or even send up JSON objects as a string value, but either way I need to create my own one-off UI elements that know how to interpret a proprietary binding protocol which is highly undesirable. So, right now I’m not feeling very comfortable with anything resembling a clean solution. I think I still have more to learn.
Okay, having said all that it seems like that thread had reinforced a misconception. The framework is really state attribute agnostic. It actually just bounces messages around, there’s no tight coupling to a fixed schema. Status is loosely coupled to state types. It seems the only tight coupling is between the UI elements and the types they bind to?
Would I not be able to extend exiting types, with additional attributes, while still maintaining backwards compatability? The UI element won’t understand the additional elements, but it won’t break either? Later, UI elements could be enhanced to understand the extended attributes?
There are ways to do that, Thing updates, but that can have disruptive effects due to reinitialising stuff, and is really intended for permanent property changes. Don’t think this is what you want.
I read it that what you are looking for is more dynamic, some property that is active in mode X but not mode Y, all temporary.
What you are coming up against is that Items are at the heart of the openHAB philosophy, and by design these are very abstract and deliberately loosely linked to real device behaviour. That’s to hide the differences between models and technologies, intentionally.
I’m trying to say you are swimming against the flow, so it is hard work.
So, the channels provide the links - one end to the idealised Item, the other to your binding’s particular technology. Your binding only owns one end. Do what you like to the channel, it is not supposed to affect the Item other than by commands and state updates. Your binding has no idea what else the user has configured his Item to do, and should not otherwise interfere. Disabling or disappearing your channel would not achieve what you have in mind - it won’t affect the Item or inform the user.
Having said all that, there is the crude but effective mechanism where a binding may update a state channel to UNDEF to show that it is not possible to currently get a sensible state. UNDEF states can be acted on by ordinary rules and UI elements.
Your binding is also at liberty to ignore any Item commands for any reason, such as not being appropriate right now.
Maybe that combination suits your purpose.
You can say the Items are too simple if you wish, but they are what they are.
In general to get more complex behaviour, we aggregate Items and add rules. Example, there is no thermostat type Item - but we can build one with Items, for a measurement, a setpoint, and an output switch, and a rule to create behaviour.
You might review the channels/Items that your binding provides, so as to enable and encourage the use of this kind of technique.
You are reading the dynamic nature correctly, transient, not permanent. The functionality is probably best implemented in groups of controls which are mutually exclusive.
When Network is DHCP, that and MAC are in the network group.
Then Network is Static, now you have IP, Mask, Gateway, MAC DNS, etc. The status of one channel may change the context of other channels.
When the source material is standard you’ll have
Color Profile and Gamma controls.
When the source changes to HDR material, those controls are replaced by
HDR Level, HDR Processing, HDR Type, Max CLL, Max FALL
…because some controls are used in one context and some are used in another.
Those are a few examples, but by no means the whole list. It’s a complex projector, not a light bulb with just on and off states.
There are also cases where a control is still technically in context, but there should be a visual indication that it’s not active so a user knows to wait, and scripts which do aggregation know they can’t complete because one of the items in the group needs to go active and get selected first.
I’ve had this running on a simple combination of Girder and MainLobby for at least 16 years, and that old Windows infrastructure needs to go away ASAP. I’m finding replacing it without sacrifice it to be challenging.
This question is really one of architectural best practice. I can find a dozen ways to hack it to work for me. But the binding won’t make a good contribution if it leaves a mess for others to implement, or goes against existing design principals.
A lot of questions/thoughts here so a bit difficult to answer. But I’ll try to add something.
Bindings have no control over what items are created. If a channel is used is controlled by the user by creating an item that uses a specific channel. This means dynamically adding or removing channels based on state is an anti-pattern, because a channel added can only be used when the user adds an item. And while removing a channel on an existing item is not a problem, its not something that is expected behavior. Therefore bindings should only manage channels during initialization of the binding.
Channels can have dynamic option state. So its possible to dynamically change the list of options. For example change the list of options or remove an option in a specific state. Note that the binding itself should always be able to handle any state value passed. So even if an option is removed the binding should still be able to handle it when that option value is passed as command.
Static data, like firmware should not be modelled as channels, but be added as properties. The binding can update these properties if they would change.
I think we agree on all of that. The binding keeps state and it knows what is active and when, but it cannot reach out to the UI and control it. It’s understood that whatever it sends downstream is subject to interpretation On the other hand, whoever implements the UI for it will not be keeping state, and will not know the state transition rules, so, it will be next to impossible for them to code appropriate rules. So the question really is what should a binding that I contribute do so that others are able to implement it?
I’ve considered whether extending some existing state classes with additional attributes would be viable.
I’ve considered creating group channels whose on/off state represent whether a collection of controls should be active or not.
I’ve considered collapsing multiple controls into the string value of the control which changes the context, like
The first might be the most backwards compatible The last would obviously require a custom widget. And I don’t know if any of those would work at all. The thing is, I can make it work for me. But if I contribute a binding, what should I be giving others? No one will see or care what I implement here, but what I release into the wild matters.
I think I may be okay with UnDefType, as long as no one objects, since I see it has two distinct states, which I’d propose defining like so:
UnDefType.NULL = Binding “suggests” the UI hide the control/status because it is not relevant in the current operating context. Commands will be ignored and Status is currently not in a valid state on the item.
UnDefType.UNDEF = Binding “suggests” the UI disable (gray-out, etc.) the control because it is temporarily unavailable in the current context due to the devices current state. Commands will be ignored in the item.
The biggest deficiency with this is state is actually known in the second case, but item can’t have both the UnDefType.UNDEF & OnOffType.OFF types at the same time.
Strangely, we all manage with complex and unique machines like an automated household. That’s glib, but your binding should not worry about what users are going to do with info it supplies, concentrate instead on providing useable info.
Again, the ideal ‘home automation’ has no UI at all, it’s automated.
Likewise, your binding cannot control what users or their rules can attempt to do via command. Concentrate on having the binding screen out the impossible and ridiculous, can also consider if you need to signal bad attempts in some way.
I do not think your binding should be posting states NULL. (I’m not even sure it is possible via channel). So far as I understand it, this is the void state of an uninitialized Item.
When something/someone makes a positive decision to post a void state, they should use UNDEF.
It’s a subtle distinction between “no idea yet” and “yes I know it’s broken/incalculable/irrecoverable/not allowed”. Known unknowns, to turn a phrase.
If this is the example, it looks like six channels/Items to me.
When the mode is “DHCP”, three of the Items are redundant.
Your binding can choose to -
post UNDEF to the redundant Items when mode changes to DHCP. Or, don’t bother, it’s just redundant.
Your binding can choose to -
ignore commands posted to redundant Items. I’ve no idea what you’re doing here, assembling and posting a package of data or individual chunks, but it doesn’t matter much.
I have to say this part doesn’t really look like the usual binding business. openHAB is about running home automation, not going round and reconfiguring other boxes every five minutes. (you did say this is about dynamic changes). Maybe this is just a readily understood example.
Yes, that is a theoretical example of a single channel which encapsulates multiple, context specific values. The theoretical xsd would look like this:
now, isn’t that the same abstraction as HSBType? The theoretical xsd would look like this:
Even in it’s simplest implementation HSBType is a 3 value tuple which would represent 3 different commands and 3 different statuses on the physical device. It’s been aggregated into a single channel where the tuple allows the values to be used together in the appropriate context, while eliminating the complexity of inter-channel dependencies.
It would seem that the platform has already had to cope with complexity beyond one primitive type per channel, and the pattern of one context per channel seems to have already been implemented.
I’m really most interested in the abstraction. I gave a concrete example just for understanding. Most LAN connected devices today include their network setup in their API. Network setup is a poor use case from a “demand” perspective since I doubt that ranks high as something people want access to, but network setup is a readily understood use case because it’s so ubiquitous.
The abstraction I’m trying to get across is an aggregation pattern. A collection of values which can, and probably should, be treated as a single unit. If the channels get too granular then you run into problems interpreting them if the change of the value in one channel changes the meaning of other channels.
It makes sense to carry HSB as a tuple in a single channel. The are other cases where one context per channel would make more sense than multiple primitive vales spread across multiple channels.
True, but, something that granular would establish a precedent which would cause the number of granular types to explode. It would need to be implemented as something more generalized to fit a wider array of cases.
I think a String type will carry an XML fragment, so as long as I can construct a UI element which implements the XSD the framework wouldn’t need to know or care, but I’m not sure about that. I do know that it tries to parse strings into more specific types if it can get a match.
Where I’m lacking is knowing what would be a good fit for this platform.
Bear in mind at least 3 sitemap based UIs, one admin UI, and the scripted HABpanel UI would need widgets to suit each new Item structure.
EDIT - nearly forgot, Persistence services need updating to deal with new complex Items too. I don’t think newest Location type ever got this for OH2.
For a real-world example, consider a ubiquitous ‘thermostat’ Item. This would have multiple states - ambient temp, on/off - at least one control , setpoint - optional controls, hysteresis etc. - and somehow needs a behaviour assigned to make the internal parts interact.
EDIT - well I suppose it does not need a behaviour at all, as it is a representation of a real external device. One could be easily arranged by rule to create a virtual device.