Simplify development for "simple" bindings

Moderators Note: This discussion has been split out from VS Code and Copilot free plan - #170 .

As an experiment I’ve submitted a mostly AI generate PR add-on. This is a very simple add-on which all it has to do is log into a web page and scrape some contents. I’m on round three of review comments and indeed most of the comments are valid, and I am addressing, sometimes manually, sometimes having the AI do it.

But none of the problems found have anything to do with the authentication and scraping of the data from the page. They are all related to Thing life cycle management, multithreading edge cases, and coding style. I’ve no problem with the last one, but any contributor, AI or human, has to keep far too much context in their mind about OH for the former two. You almost have to be an expert in the life cycle of a Thing and add-on to even hope to be successful, and that’s really complicated.

I’m still persisting, but I’m doing more manually, so the progress is slowing down (I can’t just have the AI do its think [pun intended] in the background and then come and review what it’s done later).

But there has to be a way to do more of this life cycle management for the end users. Maybe have more than one skeleton to start from with varying levels of hand holding. For super simple polling add-ons it seems like all the add-on developer should have to worry about is coding one class or even one “run” method for discovery and another for the Things polling. The more OH stuff you can do for the OH add-on developer the less work it will be for everyone and all the OH life cycle management and multithreading stuff will be done right the first time every time, at least for the relatively simple ones.

I’d never suggest changing the existing add-ons but if it can be made easier going forward it would be a huge improvement.

It will be some time, but I hope to generate an outline of my findings once I get the add-on merged and can really look at what and how could really be extracted.

When it comes to Thing lifecycle management, I think you might be making things worse for yourself by having the AI generate the code instead of building it manually in the first place. I haven’t seen the kind of problems you’ve encountered, but there is a “system” in how BaseThingHandler (I’m assuming that you’re extending that) is made, so that most of the lifecycle management is done for you, if you only have “standard” needs.

As far as I can recall, you just have to do initialize() and dispose(), which can’t be done “for you” since this is where you set up and tear down the resources the binding needs. Again, without having seen the PR, I suspect that the AI might have done things that makes it harder than it could have been.

When it comes to the multithreading part, there shouldn’t be much of that either, but there are some “OH rules” about thread pools and such that the AI probably knows nothing about. But, generally, this is a “challenge” in the way that it seems to me that this is what many people (not only AIs) have problems handling. It’s a hard problem to solve, because that’s how computers actually work. Back when CPUs were single core, you could sort of get away with ignoring it all make making single-threaded programs, but today that’s just not viable. The system would be very slow and limited compared to a multithreaded one.

So, this has to be handled one way or another. There are “simplified” languages that removes multithreading and does various “tricks” to compensate, like turning some of this into async/event based things that are run in a single thread. Performance is still way, way worse, but as we can see from the popularity, many people still prefer these. Java isn’t one of those languages though, and it’s more or less impossible to “shield” a binding author from this. Also, many bindings use threads and do things that they couldn’t otherwise do, so it would be a bad thing to do as well.

Before I disengaged with OH, I was pondering if there was some way to do some of the same as what e.g. JavaScript is doing for very simple bindings: “translate” the multithreaded reality into some kind of event based, “async” system. It’s no small feat though, this is usually implemented on the programming language level, essentially replicating that for OH bindings might turn out to be very complicated. But, I had some ideas that I won’t know how well would work until I had actually implemented and tested them.

Apart from doing something like that, I don’t think there is much that can be done to avoid the reality of multithreading. That said, for a simple binding, there shouldn’t be much you’d have to worry about it.

In the standard flow yes. But when all the error cases start coming into play it’s not always so straight forward.

My proposal is to handle this for the add-on developer, at least for super simple add-ons. Were it not for all the openHAB binding “stuff” I could have implemented this whole binding in two methods, one that gets called when it’s time to do discovery and another that gets called when it’s time to do a poll.

Everything else could be done for the add-on developer. I’m not looking to eliminate the multi-threading. I want the experts (i.e. maintainers) to do it for the add-on developers. Give me a class to override where I need override two methods where I put my add-on specific code. If a problem occurs I throw an exception and all the Thing status management and logging is handled.

Obviously I recognize it probably cannot be as simple as that. But it certainly could be closer to that then we have now.

BaseThingHandler already is quite simplified, but I’m sure that you’re right, that it could be streamlined even more. That still won’t get rid of the multithreading issue though, which I think is the issue that causes the most “challenges”. Also, BaseThingHandler is so widely inherited by existing bindings, that you can’t really do any structural changes to it. So, I guess another abstract class would have to be created instead, e.g. SimpleThingHandler or something like that.

I would not expect to change any of the existing classes. And frankly, I think there should be more than one. Super simple bindings would have one abstract class, those that need to manage some of their own treads for some reason could have another.

Or leave everything the same and make the skeleton generator smarter and fill in more for a simple add-on than it does now.

I’m just at the ideas stage right now so don’t have a whole lot of concrete ideas yet. Just a lot of “I shouldn’t have to do this myself” impressions while building this add-on.

I’m not saying that this is wrong, but I think it could also be very helpful if a really simple, well-written binding was identified and suggested as “inspiration”. When I wrote my binding, which was pretty much the first thing I did with OH, I looked at both the Astro binding and the HTTP binding as far as I can remember. Neither of these are simple enough to be suitable for this, but since I already knew Java, it worked for me. My point is that by looking at how existing bindings did things, a lot of the guesswork was removed - it became pretty clear to me how the structure had to be.

If there is a much simpler binding that is equally “well structured”, I think that could be of great help. Reading documentation is fine, but it’s often hard to get the answer to exactly what you’re looking for at that moment when reading documentation. You kind of have to read and remember it all, which can quickly lead to “overload”. When I could just look in the existing bindings, I could quickly find exactly what I was trying to figure out at that point, and see how it was done.

I don’t know if such a well-written, very simple binding exists, but considering how many bindings there are, chances are that it does.

Indeed, but I still feel that puts too much on the add-on developer. It’s just yet another bunch of stuff you have to read to begin to understand all the stuff you have to in order to build an add-on that doesn’t cause problems and actually works.

Rather than and example “do it like this,” it would be much better for the developers and the maintainers if as much as possible is done for the add-on developer. There’s less code to review. Less mental gymnastics required to deal with stuff like “if an exception occurs here I need to remember to catch it but don’t log it because core will log it for me” or “should I use the default scheduler or do I need to create my own” or “is this one of the places where I must catch all exceptions or one of the places I must not catch all”, and so on.

I couldn’t find one. But also the simplicity of the binding ends up being hidden by the complexity imposed by openHAB and thread and life cycle management.

I didn’t mean it as an either-or. I mean to say that for me at least, it’s the absolutely quickest way to “get up to speed” and know how to do things.

The suggestion with a “more complete” abstract class is fine, somebody just has to make it. But, there are some caveats, generally, the more the abstract class handles, the less the binding can do.The existing BaseThingHandler is already doing this to a relatively large extent, but you want more. The question is if this “balance” of capability and simplicity exists generally, or if it would in reality have different requirements for each binding. I’m not claiming that the case, I can’t really speak to that without getting intimate with the details, but I see that there’s the potential that you’d end up with “many levels of assistance” to cater to the different needs. As such, the current BaseThingHandler is a compromise between capability and simplicity, and I’m not sure you could “cover the rest” with one more abstract class.

Speculating is kind of pointless though, because many things might be obvious when you look at the details, some things might be impossible to simplify without making them “useless”.

When it comes to which threadpool to use, I very much agree that there should be more clarity. I don’t even think that the “rules” are clear or applied consistently. With the “standard” threadpool having just 5 (later increased to 15?) threads, it doesn’t take much for a binding to starve the others. I’d say that even potentially waiting for 1 second might not be “acceptable” for that thread pool.

It should absolutely be clearer, “cleaner” to know when to do what. I made a suggestion for changes to the thread pools quite a long time ago where this would become essentially a non-issue, because the thread pools would just grow as needed - they would scale to demand. I still think that’s the better solution, some testing shows it actually ending up using fewer threads than “standard OH”, but a bit more during startup. Things happened quicker as well. In such a scenario, having blocking calls on the “standard scheduler” doesn’t really matter anymore. It won’t starve anything else from being executed.

When it comes to exeptions, Java is actually brilliant. It has checked exception, which means that you don’t need to know what to catch, it will tell you what you need to handle where. Unfortunately, the “functional trend” largely ruined that, because these “chains” can’t handle exceptions in a good way. Java has always had what’s called RuntimeExceptions that are not checked, which means that Java won’t require you to handle them. But, they were only meant to be used for clear bugs that shouldn’t happen during runtime, there were never meant to be used for “expected” exceptions. But now, this has largely been ruined, and few things use checked exceptions anymore (libraries etc. has switched from throwing checked to unchecked exception). So, now it’s just a mess, where you have to guess what exceptions might be thrown where. I don’t know a good solution to this, it’s not OH specific, it’s a “rot” that has spread through Java because it’s more convenient when using Streams.

The general rule for exceptions is that you should catch anything that you expect can happen and that you want to be handled at that level. Things you want to handle at a higher/parent level, you leave unhandled, and then you handle them there instead. But, ultimately, they must all be handled at some stage before letting them crash the thread.

Because I’m stubborn, I tend to still declare throws xxxException in methods even for RuntimeExceptions. It’s only required for checked exceptions, so most people don’t do it these days, but I think that makes it so hard to know what needs to be caught. By declaring that for every method (that can throw), it becomes relatively manageable to determine what needs to be caught in a certain block of code, by checking what external calls are made. But it’s really primitive compared to the elegant checked exception system that’s almost not being used anymore.

For tons and tons of bindings, they don’t need to do much. My argument is for these super simple bindings, the balance between OH concerns and the add-on’s concerns is off. I’m never going to advocate for taking away the flexibility need for more complicated bindings. But the simpler ones shouldn’t be this hard.

My binding could be as simple as two method calls. One to call for discovery and another to call to poll the Things. All of the OH stuff could be hidden. This would cover many simple bindings too.

And yet I’m in conflict with the maintainers because in one place it says if you have an exception throw in a background thread you must catch all to prevent problems. But when you are in the main thread you must never catch all because it interferes with the Thing status handling.

This isn’t a programming language or functional programming issue (which I don’t think it relevant here anyway). This is a “that is how we require add-on developers to code” issue.

For a simple add-on, I shouldn’t have to:

  • decide whether I must put discovery in a background thread or not
  • decide whether I must put my polling in a background thread or not
  • deal with scheduling discovery or polling in the first place (beyond defining how often)
  • DTO stuff
  • needing to keep track of what exceptions I must catch vs what ones I should never catch
  • manually deal with InterruptException unless I actually need to do something beyond logging and calling Thread.currentThread().interrupt();
  • manually update a Thing’s status (maybe, gotta think on that one)

The overriding issue is I shouldn’t have to be this intimately involved with scheduling for a simple binding like this. The only parameter I really need is how often. Beyond that the binding skeleton could handle everything else for me: setting up background threads, dealing with interrupt exceptions, handling Thing status management, etc. Take all the scheduling out and a simple binding can remain pretty darn simple, direct and to the point.

Again, I’m not saying the scheduling isn’t necessary and that some bindings need to control their own destiny (so to speak). But for something simple which runs on a polling schedule, all the scheduling stuff can be extracted up a level. The add-on developer doesn’t have to do it, and as a result it’s going to be done correctly every time and there is less work for the maintainers to review.

Sounds like it could be done with a rule with cron trigger and then the script could update whatever items it wants to.

Yes, or as a sidecar, or I could run Home Assistant which has support for EyeOnWater. All of these would have been valid options.

But I deliberately wanted to see how hard it is to build an OH add-on even with AI support and try to identify the pain points to hopefully make it easier to build and review new add-ons.

I understand that, what I’m trying to say is that I’m not so sure that all “simple bindings” are simple in the same way, and would fit withing the same “training wheels”. The simpler you make the abstract class, the less versatile it will be, so it might be difficult to strike the exact “right” balance. Not necessarily for the binding you’re making now, I’m sure it could be figured out exactly what “should” and “shouldn’t” be handled in your case, but how many other “simple bindings” would the resulting abstract class be suitable for.

Polling for example, is a bad thing in itself. It’s inefficient and causes delays. When I write something, I will do what I can to avoid polling (if you can e.g. listen to events/subscribe instead). But, sometimes, you have no choice but to poll. In such cases, you might want multiple poll timers, in my binding I use two. One for a few data that need to be updated frequently, another for all the other stuff that don’t need that frequent refresh.

So, building an abstract class that implements one polling on your behalf, may already make it a misfit for many even simple bindings.

This is why I have suggested that there might need to be a “REST” binding just like the HTTP binding, to capture such standardized situations without having to write a binding.

The “exception rot” is because of the functional stuff. It applies whether you use these constructs or not, because a lot of the libraries you use, have moved away form using checked exceptions and replaced these with unchecked. That makes it completely “opaque” which exceptions are thrown in a certain piece of code, but that’s where we are, sadly.

But, there are some fundamental issues here:

  • InterruptedException This is a flaw in Java itself, and has been there from the very beginning AFAIK. Interrupting a thread is the only way you have to try to tell it to wrap up and abort what it’s doing, it’s like “killing” it. An InterruptedException will only be thrown if the thread is interrupted, which isn’t part of “normal execution”, this is typically during e.g. shutdown. So, it usually needs to be handled different than other exceptions anyway, because it needs to just do the most necessary “cleanup” and the exit. But, the Java flaw is in how this is done. When a thread is interrupted, a flag is set on that thread. However, when an InterruptedException is caught, the flag is cleared. It’s really hard to understand why those that designed it chose to do that, clearing the flag should have been an explicit operation instead. But, it is what it is, so that means that in Java, you must explicitly re-set the interrupted flag every time you have caught an InterruptedException, or the interrupt status simply “vanishes” and the thread won’t in fact terminate as it should. This is one of those things that you just learn have to be that way, and do it, without thinking too much about it. The AI should “know” this though, it’s the same in Java everywhere.
  • Generally, you should almost never catch all Exceptions. That’s just bad design, you should only catch the exeptions that “can happen” in that code, and the reason for this, is that you can only decide how to deal with the exceptions that you expect. Simply just catching Exception, logging it and then ignoring, is a huge anti-pattern, it will in ignore many situations that shouldn’t be ignored. As a consequence, the “rule” is that you should only catch and deal with expected exceptions. If one that isn’t expected arrives, it’s better that it crashes so that you can find out that this can happen, why, and how to deal with it.
  • In thread pools, threads are reused. If an exception isn’t caught at any level and reaches “the thread” itself, the thread will terminate. A terminated thread can’t be restarted, a new one must be created instead. This is very inconvenient for thread pools, you can’t replace individual threads, so you must effectively recycle the whole pool. Therefore, there must be an “outer catch” that catches all exceptions that have made it that far, to avoid crashing the thread. This is the exception to the rule, where you actually want to catch Exception. Nothing should get to that catch if the code is sound, all expected exceptions should be caught before they get there, but it must be there to not mess up the thread pool. All you can do there is log it “loudly” and make sure to include the stack trace, so that if an exception is caught there, it’s easy to figure out what caused this. I think that the “standard” OH thread pools already are equipped with such “outer catches” that protects the thread pool, but if you make your own, you’ll have to deal with this yourself. I assume that this is the kind of situation that you’ve stumbled into?
  • You’re never actually in the “main thread” in a binding. There is a thread called main, but it’s only used to start OSGi, it doesn’t actually perform any of the tasks in OH. Most code that enters your binding will be using some thread from a threadpool already. I’m not sure what you’re referring to with “not catching because it interferes with the Thing status handling”, maybe you could link to the situation in the PR?

Generally though, nobody expects beginners to be aware of strangeness like Java’s InterruptedException handling. I’d think that the reviewer would explain what you need to do in such situations, and then you know it. But, if an LLM generated the code for you, this should already be correct unless the LLM lacks rudimentary Java knowledge.

Who should decide? What determines this is the time it may take to “discover”. If it’s quick, you don’t need a background thread, if not, you need one. It’s hard to see how this can be “simplified away”, many things can be discovered very quickly, but if you have to start doing HTTP calls and such, you need to use a background thread, or other things in OH will have to wait for the HTTP call to time out before they can do their thing.

That’s much the same thing, it depends on how long it takes to do what you need to do. But, as I’ve already said, I don’t think the design as it is, is good. I think the pools should be dynamic, which would mean that it didn’t matter if you did something that potentially takes 5 seconds on one of the standard thread pools. I’ve not gotten much support when I’ve suggested changing it though, which is why I never did anything more with it.

I’m not sure where you met DTOs if you’re scraping. But, you don’t “have to” use DTOs if the class maps perfectly to the objetc to be deserialized. That’s more of a “OH convention”, but it provides the freedom to handle situations where they don’t map perfectly. There are lots of places even in OH core where DTOs aren’t used though. So, I don’t know why you’ve had to use them here.

I think that must be done by the binding in one way or another, because it must be defined somewhere what should “map to” what Thing status. I mean, if this happens → set that status. The abstract class can’t really make broad assumptions about that.

I’m not sure I see how this could be achieved, at least not without huge “compromises”. The backgrond thread situation for example, that would either mean that it would have to simply be unsuitable for bindings that use potentially slow operations, like HTTP, or that it would always have to set up dedicated threads also when it’s not needed. There’s a “don’t waste threads” philosophy in OH (beacause threads = memory), so doing so also when it’s not needed, would clash quite badly with the existing principle.

The abstract class can’t deal with InterruptedExceptions for you, how can it know what you need to do to “wrap up and exit”? Again, I’m not sure Thing status can be generalized and still be “meaningful”.

As I’ve said before, I think things could be made even simpler for simple bindings, but I’m not sure if it’s possible/viable to go as far as you suggest.

Got it. So we shouldn’t even try.

Which means all philosophical discussion bout how this, that and what ever else is doing it wrong is irrelevant. I can’t fix it. You can’t fix it. We must learn to live with it or go build something else.

I don’t care what’s right or wrongly implemented. I only care about those things I have control over.

Yes and it’s one more thing tied to “why do I have to mess with scheduling in the first place.” But it’s too hard to make the support classes and skeleton be all things to all people so let’s not even attempt it.

I shouldn’t have to know it. The reviewer shouldn’t have to explain it to me.

The LLM got it right. But I had to spend a ton of time figuring out why it did what it did.

What’s the danger if the default behavior is all discovery happened in a background thread?

Discovery doesn’t happen all that often. What are we saving to justify the added complexity pushed on the add-on developer?

Or just do it by default and make not using the shared thread pool the exception. If your polling is fast there’s no real harm in putting the poll in a background thread. And in those exceptional cases where there is we can accommodate that.

And that doesn’t really require many changes. Just initialize the schedule with a new scheduler in the parent class instead of with the shared scheduler. Of do it in the skeleton.

It doesn’t need to be a whole redesign of core.

Which makes them a requirement if you want your add-on to be merged. And getting an add-on merged is the whole point of this exercise.

I was asked to.

There might be more than one base to choose from. And the compromises are the point. Yes, we make compromises. It’s the cost for the reduction in complexity. But no one has to use the “compromised” base. If they want to have full 100% control they can code their add-on same as we ever have.

My point is that shouldn’t be the default. It should be much easier for our users to contribute add-ons.

I don’t know that either. But if we just give up now, no progress can be made. Let’s aim for the ideal and see how close we can get.

It will take some weeks, maybe longer. But I plan on building a proof of concept.

Nice informative post from @Nadahar, thanks!

I can see the points from both sides. How about:

  • Firstly split the posts into a separate topic, since it seems useful and good not to get buried in here

Maybe the solution isn’t “one generic pattern”, but several small, well‑defined patterns for the common simple cases.

I don’t yet know what the “simple cases” are — that’s exactly the part we should explore. But having a handful of clear patterns or classes to choose from might solve the problem of “we can’t make it generic for all cases”.

Each pattern could be completely different from the others, because different bindings and integrations have fundamentally different shapes, constraints, and behaviours.

For EXAMPLE (don’t focus too much on the details — these are just loose guesses):

  • HTTP pattern
    For devices that expose a simple REST API, fixed endpoints, or periodic polling.

  • Polling pattern
    For integrations where the device cannot push updates and must be polled at intervals.

  • MQTT pattern
    For devices that publish/subscribe to topics, often with multiple attributes or nested JSON.

  • Single‑value sensor pattern
    One channel, one state, minimal mapping.

  • Multi‑attribute device pattern
    One entity with several strongly‑typed attributes (temperature + humidity + pressure).

  • Command‑driven actuator pattern
    Devices where the primary interaction is sending commands (switches, covers, fans).

  • Stateful device with mode + attributes
    Climate, media players, vacuums — anything with a “mode” plus additional properties.

  • Pure event/trigger pattern
    Doorbell presses, motion events, stateless triggers.

  • List/JSON passthrough pattern
    For devices that expose structured data that shouldn’t be decomposed.

  • Composite device pattern
    Multiple entities grouped into one Thing (e.g., a weather station or robot mower).

Each pattern could define:

  • expected channel types
  • naming conventions
  • attribute mapping rules
  • discovery behaviour
  • update/command flow
  • optional extensions (forecast, events, images, diagnostics)

Not a framework, not a generic abstraction — just a set of ready‑to‑use patterns that developers can pick from when starting a new binding or modelling a new device type.

If others think this is useful, we could start identifying the most common cases and draft the first few skeletons.

Java Virtual Threads?

That’s not at all what I said, I’m just trying to say that it might be a bit more complicated to “draw the lines” than what it appears.

It’s not quite that black and white, Java hasn’t really changed, but a lot of the code did. It’s a choice people make, often the wrong one IMO, but it explains why it’s hard to know what may be thrown. Which is why I try to define all methods with their throws declarations, so that methods calling them can just check the method, not “the whole tree” of methods called.

You care about it to the extent that you say that “I don’t want to have to deal with it”. I’m just explaining why that’s not really a choice. The abstract class certainly can’t predict what exception types can be thrown in the code you make.

That’s the same as saying: I shouldn’t have to use Java. There’s a reason why simplified languages like Python or JavaScript aren’t multithreaded, it allows you to not think about certain things. But, the cost is huge. I think it’s a bit late to make OH in one of those languages anyway, so it’s a moot point. Just think of it as an onShutdown() event that can occur anywhere where you make blocking calls. You have to define what should happen then, there’s no way for the abstract class, or any “external framework” to make that decision. Many people ignore these things, and stuff hangs when you try to shut it down. I guess that’s an option too.

I’m not sure I understand what you mean here. I looked in the PR, and I can’t see that you use any “background thread” to do it, and don’t fully remember which threads call startScan(), but there is a background scanning going on in intervals as far as I remember, so it’s good if you don’t have to keep creating threads every time.

The harm is that you have to keep spawning threads. That’s considered an expensive operation (it’s considered more expensive than it really is IMO). Also, there is responsibility with managing your own thread pools. You have to handle the “outer” exeption to prevent crashing the thread, which is already handled in the shared pools. You also have to interrupt all threads, wait for them to finish and then shut down the thread pool if your class/component is disposed/stopped. If you don’t manage that properly, you will “leak threads”, wasting resources on the host system. Also, the whole idea behind the shared pools is to reuse threads, to be efficient. Doing this “as standard” would cicumvent this whole thing. We might as well just forget about the thread pools, and let every implementation handle their own threads. It adds complexity on everybody though, and it’s certain to come with a lot of bugs.

“My way” has none of these drawbacks, it changes how the shared pools themselves work. You still get all the benefits, no cleanup, no extra exception handling. And, it benefits OH overall, not just the Thing handlers.

There are so many (closed) comments in the PR that I struggle to find this. I don’t know what the reasoning behind it is, but it is kind of “convention” to do this in OH. I hope there’s more reason than just convention to require it though.

I agree that something should be done, and I’ve had my own thoughts on this. Since all my current PRs are stalled, and because of all this AI “mess”, I’ve halted all my work anyway, so it never moved out of the planning phase. If you, or anybody else, can come up with a simplified abstract class, I think that would be great. Everything that contributes to make it easier to integrate OH is valuable IMO. It doesn’t have to be done only one way either, my ideas could be implemented in parallell with this approach, there would be no “conflict”, just more options.

I think this sounds useful, and you might discover that some of the categories can be merged as you go, because the required functionality is almost identical.

Virtual threads is the exact opposite of “the OH philosophy”, it will create new threads without any restrictions. It’s in a way similar to what I have suggested with dynamic pools, only that it’s less effective and less structured (not configurable, no “centralized” thread naming system etc.). Remeber that virtual threads have quite a lot of overhead, Oracle itself said that unless you have more than 10000 concurrent threads, the overhead will be larger than the savings. Virtual threads is essentially made for one specific use case, and that is huge network/web servers where each session/client is one thread. In those situations I’m sure it can save quite a lot of resources, but OH is so very, very far away from that.

My case also as an example:
In Portugal we have several open APIs with really cool data:
Electric grid planned maintenance;
Sea and Air institute providing things like earthquake monitoring, weather monitoring and forecasting;

These are things that are easily integrated with the HTTP binding, but that no one does because:
Effort;
People don’t know;
There’s no :sparkles: shiny to bring attention to it;

But if I could work out a binding for these small integrations I’m making, I’d definitely do that.
It’s an HTTP binding, but pre configured with a set url and channels and the like.

How cool would it be if we have like openHAB binding boilerplates that we could use to make new ones?

There’s a bit of overlap in these (you could have an HTTP pattern and Multi-attribute device pattern in the same binding) but this is exactly along the lines of what I was thinking. And I wasn’t even going as far as predefining the channels in my thoughts. I was mainly focused on the behaviors. From simplest to complex I was thinking:

  • Polling add-ons (with or without discovery) without a maintained connection (i.e. the connection is re-established every poll) (e.g. Astro, Exec)
  • Polling with a maintained connection (e.g. HTTP client that has performed some sort of authorization)
  • OAuth2 binding (could be other types of auth also) mostly relevant to cloud REST APIs
  • Subscription/listeners type bindings (e.g. MQTT, Bluetooth)
  • I don’t know yet if hardware based add-ons are different enough.

I think if we focus on the overall behaviors of the add-ons rather than what they do we might have fewer overall to deal with.

Which is what I was thinking when I say that the skeleton generator could be made smarter. It could ask “what type of binding are you building?” and generate a more complete skeleton suitable for that use case.

Or they could be implemented as abstract classes and the add-on developer chooses which one to inherit from.

I’m not looking to change core nor how add-ons are built for existing add-ons.

No but we can impose a rule “code you make may only throw these exceptions: …” one of which is a custom binding exception. This would mainly only work if this is implemented in the skeleton generator of course.

I remember a whole thread where you were complaining about how unwelcoming OH is to new ideas and it drives people away. Hey kettle …

If you don’t think this is possible, your objections have been noted. I’m still going to try. You can rest easy that your advice on this matter have been heard, fully understood, and rejected after due consideration.

Nowhere here am I advocating for nor looking for ways to change how OH works today. I’m looking for ways to make it easier for add-on developers to creat an add-on with what exists now.

You are completely missing my meaning. Since for simple polling type add-ons the majority of them will have potentially long running discovery or polling tasks (e.g. HTTP calls), these get their own thread pool by default along with helpers, pre-provided code, examples and what ever else is need to either just handle all these edge cases or make it very clear to the developer what they need to do.

Why should the default be the exceptional/rare case where this type of add-on always has a very fast discovery and poll?

And just because it’s the default doesn’t mean it can’t be changed. But it should be changed in the rare cases, not the common cases.

But, once again, your objects have been noted. I understand them (I doubt you understand mine), I have considered them. I am still going to proceed. Thank you.

I wasn’t really give a reason. Just told to do it.

I never said anything differently. Of course, if you’ve stopped contributing to OH, which is well within your rights and no one can fault you for it, your way is very unlikely to be implemented.

That’s kind of what I mean by making the skeleton generator smarter. You’d have a list of options or even a list of questions to answer and the skeleton generator produces a more complete base to start with compared to what it generates now geared towards what you need for the specific binding. Or there is more than one abstract class to inherit from which implements more or less based on what you are doing. Or both.

I don’t know what thread you’re thinking of, but it sounds like something I might have expressed within some context. But, there’s a difference between unwillingness and impossibility, but since you’re not interested in my advice anyway, I’ll let you find out yourself.

As a last, unwanted hint, I’ll say that many of these things could be solved using inheritance instead of different classes.

Which I’ve said multiple times above.