I’m trying to make a binding that use the local API from Mill devices that has become available from “generation 3” of the devices. There is an existing, official Millheat binding, but it works in a completely different way using the “cloud” and reverse engineering to manage devices.
I intend to host this binding on the marketplace, not as part of the official add-on repository (because of the CLA).
I’m reading documentation and looking at other implementations to the best of my ability, but I have some questions that I haven’t been able to get to the bottom of, and would like some input on. I’m sure more will arise as I progress, so I chose to create a thread/topic where I can put these.
At this moment I have these questions that I haven’t managed to figure out:
What coordinates to use for my binding. I read somewhere that org.openhab.binding should only be used for official bindings. I could use some of the coordinates I “usually use”, like org.digitalmediaserver, but I’m uncertain of how all the OSGi stuff works and if that would really work like it should. Would it even be picked up as a binding? Almost all examples and documentation I can find use the official coordinates. As of right now, I still use the official coordinates made by the “skeleton script”, but the more my work progresses, the more work it will be to change this, so I’d like to clarify what to do sooner rather than later.
What exactly is a “trigger channel”. I get that it’s supposed to be obvious, because I can’t find that it’s really explained, but I don’t get it. I thought a “normal” channel could go either or both ways, so where does trigger channels come in?
The API these devices use have no subscription service that allows me to be notified when something changes, so I have to base all state on polling. Several of the API calls return information for many “channels” at once. I assume that means that I have to parse and call updateState for each of the affected channels after a poll. It’s a little murky for me what is and isn’t threadsafe to do in the framework, but for now I assume that I can do these updates from the scheduled thread that does the polling. But, how does RefreshType come in? When does the framework issue these (I suspect that they follow a command that changes one of the channels, but are there other situations)? “How timely” must I respond to a “refresh” - should I do a poll for that immediately, or wait for the next scheduled poll if it’s not too far in the future?
The API has some calls that I really don’t feel should be channels, but that still should be possible to set in some way. The “set API key” is the best example. If the user lose his API key, the device must be factory reset. So, it should not be “set by mistake”. At the same time, I can’t expect the user to use Postman or similar to set it him/herself, so I should find a way to let them configure it, with ample warning of the consequences of not knowing it. Is there a suitable place to do things like this?
There are State Channels and Event/Trigger Channels.
A State Channel must be linked to an Item and cannot be used to trigger a rule directly. State Channels are used in situations where the Channel represents something that has a state like the door is open, the temperature is X, etc. State Channels are two way meaning you can both receive updates and send commands.
A Trigger Channel cannot be linked to an Item and can be used to trigger a rule directly. Trigger Channels are usually used to represent momentary events that do not denote a state. For example a button press event, dawn just started, etc. Trigger Channels are one way, from the “device” to OH. Relatively speaking Trigger Channels are rare.
Often stuff like this is captured in the configuration of the Bridge Thing. But it seems like you won’t have a Bridge Thing. The closes analog to me is something like the encryption key on the Zwave binding. You can set it but once you set it you don’t want to change it or else you’ll have to repair your nodes. That’s just a manually configured property on the Thing.
Beyond that I’m not of much help. I’ve not done add-on development.
Up to now, I thought you are going to extend the existing Binding with new finctions. This would still be a org.openHAB.binding.
If it is going to be a completely separate Binding with different namespace (not openHAB), it cannot be published on the community marketplace, but needs to be hosted on a „3rd-party-addon store“, like the Smarthom/J addons.
Channels are statefull and linked to items, whereas trigger-channels are stateless and unlinked. Have a look at the astro Binding, which has a lot of trigger-channels.
It is very common to get multiple values with one API call and update several channels after parsing the results. This should be handled in the scheduled polling task.
RefreshType is a special command type in handleCommand() which will trigger the polling task.
Thanks, that answers what I needed regarding trigger channels.
Yes, that sounds somewhat like what I had in mind, except of course that I wished that there would be some “confirmation” function for setting such an important parameter. But, I assume that’s not possible, so a part of the thing configuration (under “advanced”) is probably the best option. I haven’t yet found the mechanism that notifies the binding if the configuration changes, but I assume there must be one, so I guess I’ll just have to find that.
This binding and the existing binding will have very little in common. The reason is that the “cloud” solution has some concepts that isn’t strictly in the devices, like “rooms” (where several heaters will work as one I assume), weekly schedules etc. This isn’t something I had in mind to try to replicate, I was just thinking of giving direct control of the device as any other Thing. Also, I thought that hosting a modified version of an official binding on the Marketplace would be a no-go. But all in all, I think these will be so different that trying to combine them would just be confusing.
When it comes to the namespace, I have no “desires”. I just want to use something that works. I thought that I read somewhere (but I can’t find it right now) that I wasn’t allowed to use org.openhab if it wasn’t official, but if I can, that is absolutely the most convenient for me. I’m just trying to figure out these things before I put a lot of effort into it so that there’s not some “technicality” that makes it all a waste. In short, I’m happy to use org.openhab.binding if I can.
The other questions are answered as far as I can see, so thank you both for that.
Another question: The device can display temperatures in Celsius or Fahrenheit, but internally everything seems to be Celsius. As a consequence, all temperatures in the API are in Celsius. How does this “work” in conjunction with Number:Temperature? I mean, it’s great that OH can convert it automatically and all, but there should be a way for me to tell OH that the temperatures the binding report are always in Celsius, so that conversion can be done appropriately. How do I handle this correctly?
I’m also wondering how to handle if the device goes offline (is turned off manually or disconnected from power or Wi-Fi). It would be nice that OH would discover if it comes online again automatically, so do I just keep polling like nothing has happened, or is there some particular way to handle this in OH?
Post update a QuantityType to the Item with the units that the number is in. If it needs to be converted because the Item has a differen unti that will happen automatically. If the user doesn’t want units and has the channel linked to a Number Item, the units will be stripped.
So all you need to do is specify the unit (degrees C in this case) in your updates to the Item and, for commands, call toUnit() on the QuantityType you receive or assume it’s celcius if it’s a DecimalType.
You are polling so if the poll times out, marking the Thing as OFFLINE and in some erorr state. When it responds again you can move it back to ONLINE. There are a whole set of states a binding author can use to track the various states of a device.
The question was probably unclear, I just haven’t understood how I specify which unit the for the QuantityType, but I can peek at some other bindings, and I’m sure I’ll figure it out.
edit: new QuantityType<>(number, SIUnits.CELSIUS)
Yes, I already set the ONLINE/OFFLINE state - I was just wondering if I should just keep on polling while it was offline (which presumably involves waiting for the request to time out each time), or if there were some other, more gentle “best practice” for how to handle it. But I do realize that since there’s no advertisement from these devices, ultimately they must be polled in some way. Maybe it’s a good idea to increase the polling interval while it’s offline?
I don’t think OH has any standard or policy or anything built in as a best practice for this. Choose the approach that you think works best and I’m sure it will be the rigfht approach. The polling isn’t going to really tie up a significant amount of resrouces when it times out so I’m not sure it matters a whole lot either way.
No, development docs define when to set a Thing to offline and what about Thing status details. While thing is Offline, Binding should continue polling to check if the Thing comes back online and then change the status to ONLINE
But it doesn’t say anything about how often to poll, whether there should be a backoff in how often to poll, etc. That’s what I was referring to. I didn’t mean to imply that there isn’t any standards whatsoever.
Indeed, this is absolutely device independent. Some devices don‘t like to be polled to often and sometimes values don‘t change so often. That‘s the reason why some Bindings implement the polling rate (REFRESH_RATE) as configurable on Thing basis.
In my case the refresh rate is configurable, as I see it, it’s up to the user how “current” information he/she desires. Some of the values can change relatively frequently (this is about PID controlled heaters, and the “PID control signal” can change quite frequently as it works to try to “hit” the target temperature). But, it is debatable how important it is to track these changes in OH - and even more - to store them in persistence.
But, my question was rather out of concern for OH. I mean, if bindings keep polling for devices that aren’t there frequently, I imagine that it can “bog down” OH somewhat. Although timing out isn’t “work” (the OS just holds a lock, releasing it as it times out), it does “consume” threads if this in done at some scale. But, it might not be something to worry about - it’s typical me, because I always try to avoid waste. All I wanted to know is if OH has some “directions” regarding this, and I think I’ve gotten the answer to that: It’s up to the binding developer(s) to find what suits best.
You should keep polling the be able to set the Thing back to ONLINE. Otherwise users won’t be able to determine if there is an issue with the device.
Flow looks like :
Polling successfull → set Thing status to ONLINE
Polling unsuccessfull → set Thing status to OFFLINE, Thing status details should be added with further information.
I get that, this whole discussion was about how often to poll when OFFLINE. Also, I plan to drop most of my polls while OFFLINE, it’s sufficient that one of them run to discover when it comes back ONLINE.
I’m done implementing most of the “straight forward” stuff now, so I’m forced to start touching some of the more challenging stuff. I realize that I don’t have to implement everything from the API in the binding, but some of these things would be rather convenient for the user to have access to.
One thing that applies to several of the remaining API calls is that they require several parameters to be sent at once. A simple example of this is “vacation mode”. The devices fetch the time from NTP servers so that they “know” what time it is, so several parameters use “regular” 32-bit time parameters (I haven’t figured out the epoch yet, but I’m sure it’s one of the “common” ones). This allows you to tell the device that “I will be on vacation from between and ”, and the device will behave accordingly when the time comes. Here is the payload of such an API call:
{
"start_timestamp": 0,
"end_timestamp": 0
}
The “problem” I’m facing is that I don’t know how to “build” this data structure (there are other calls with more “fields”). Both parameters must either be zero (to disable vacation mode), or be valid timestamps. If I make one channel for each, one parameter will have to be set before the other, and thus I won’t be able to build a “valid” data structure before sending (because one of them will always be zero).
There are more complex cases of the same, I picked this because it’s low complexity but shows the “challenge”. How do I handle this? I don’t even have a vague idea at this point.
I haven’t really looked into actions that much, but I’m wondering how many users that know enough about rules/scripting to actually use them. Are they actually useful for “most users”? Another thing is that the user would face the exact same problem when making this rule/script of how to collect all values before sending it.
I’ve thought of that, but there are calls with up to 6 different parameters. How would I make the user understand that nothing is sent before all channels are set? It’s the only “solution” I have thought of at this point, but I don’t consider it very good.
Yes, technically a possibility, but really, really hacky. I’d consider it unlikely that it would actually be used by users.
Aren’t you the author of the official Millheat binding? Didn’t you face the same problems, or is the information given to the “cloud” different?
Acctions are a core feature of OH and very commonly used by all levels of users. I doubt you would find many who do not use Actions.
From an end user perspective, a rule Action is the best of the three.
It’s even possible to create a custom Block library to make the Action available in Blockly (e.g. here’s Telegram’s Block Library: Telegram Actions).
There have been some recent changes to to MainUI to make them more accessable from siimple UI rules too so one doesn’t even need Blockly code to invoke them.
You should have no reservations about implementing this as an Action.
Have another Channel with a Switch Item to command to send the message all at once.