[SOLVED] Multiple Polling - thing handler

Evening guys
Bear with me as this is all new to me and learning by trial and error.
I’m querying a remote api using httputil for status updates.
I cannot poll each channel idividually due to rate limits, and I cannot poll all channels at once as the payload is too big for the server to respond.
So I’ve settled on polling each thing (about 10 channels).
The trouble is, if I have say 20 things then the system overloads as I believe it is using a single thread.
What is the best way to handle this?
I’ve done a bit of reading and I’ve implemented new thread(new runnable).start and it’s decreased the time taken to issue/refresh statuses. But I feel this is bad design.

Any help with examples would be most appreciated.

Remote app —> openhab works fine
Device operation —> openhab works fine
Openhab —> remote device suffers delays and race condition, so I believe I need to interrupt the thread/http whilst it sends the command

Example of new thread I have implemented:

Last version on there

What would you like to know (other than posting all my classes)
@Bruce_Osborne I’m new to java so I’m trying to learn good / bad practices. My system shouldn’t have anything to do with the problem I’m trying to get around / find the best way to implement.
For reference though:
Qnap TS451 16gb Ram 24tb raid 5 array
Development environment: (Xeon latest gen), windows server 2016, 256gb ram, vscode

Api examples:
Get statuses : post request with channels as json payload (generic url)
Send status: post request with value as payload (channel specific url)

The payload to get statuses I’m restricted to absolute max 20 channels (I’ve asked the manufacturer regarding their low timeout but don’t get responses)

Ways I’ve tried:
Poll each channel - api limit
Poll all channels - api timeout

Now I’ve found a way that works:
Thing requests status update (polling job) after bridge initialization (Refresh interval set on bridge configuration)
Thing handler constructs the json payload for each channel
Sends a httputil.requesturl
Received response is translated to each channel .updatestate

Now just doing this (20odd things) each polling, it floods the binding and whatever the refresh interval runs at about 20 seconds to post all updates for all things.

Using the example above with new threads I can get this down to every single second, However I get race condition if I send a command from openhab (takes about 6 seconds to get through where the system is still flooded)

So I’m looking for ideas on the best way to implement. Is the calling of additional threads bad?
Why is it?
How can I get round this?
Is this going to affect the overall system or just this binding?
Does it add additional threads to the pool or just consume what openhab has already set?

Sorry I did not realize this is in development. I thought you were a new user asking for assistance.

Sounds like you have a “service” Thing (Bridge?), with a number of sub-Things, each of those producing many channels. You’ll only want one of those sub-things to be polling at a time I would imagine? And keeping an orderly queue for who’s next?

You might look at how Modbus binding manages scheduled poller Things and queues. It’s not simple because it has to manage multiple “top level” things that may or may not have the same target, as well as arbitrary writes, but you can perhaps simplify the principles.

Yes, so the api is:
Structure: Devices : featuresets : features
Structure being the ‘house’
Devices the physical item
Featuresets the usable items (ie a double socket has 2 featuresets
Features are the channels

The urls is the same for all polls, it’s the json body that changes to include the various features to check

Is that how it should be done? I mean in an ideal world it would be nice for all polls to happen concurrently every 2-5 seconds.

I’ll take a look at modbus tomorrow.
Excuse my complete lack of programming knowledge :exploding_head:

Without seeing your code it will be very difficult to give useful advise. For example, based on the link to the page about threads I’m fairly sure you’ve used a very bad pattern. There are a lot of bindings you can look at, but if you don’t know what to look for it won’t help. So if you can post a link to GitHub where you have put your code I can give some direct advise.

1 Like

@hilbrand haven’t got round to putting it up yet.
The main issue I have is that I need to execute an http request for each thing.
So for my setup I’m testing on I currently have 14 things.
Each http request takes 2 seconds to get the response, so for openhab to carry out this task of updating each thing takes 28 seconds as they seem to be executed synchronously
Is there a way for these to all be executed at once concurrently?

I’ve worked around the issue, although not perfect by creating a single polling job.
Each thing handler adds to a list all channels that are linked, the polling job then splits this into chunks of 30 (any higher times the api out) and updates the states of each thing.

Is there anyway to directly access all the linked channels of all the things for the binding? I couldn’t seem to find anything for ‘all things’?

Why don’t you do this in your bridge handler, which will set you up to do what I outline below?

Your bridge handler finds out about all thing handler(s) by virtue of the childHandlerInitialized and childHandlerDisposed callbacks (you’ll need to maintain the child thing handlers in a list within the bridgeHandler). Then, once you have the child thing handler(s), you can get the channels using childThingHandler.getThing().getChannels().

Is this along the lines of what you’re looking for?

@mhilbush isn’t he talking about linked channels?

If so - I’d create a ChannelLinkedManager in your thing handler and then pass it to each thing. The thing can then override the channelLinked/channelUnlinked methods and update the manager as needed. You can then use your manager to determine what’s linked (over all things) or not.

Yes, but that’s easy once you have the list of channels from getChannels() (by using the ThingHandler’s isLinked(ChannelUID)).

I was taking him rather literally when he said he wanted “all the linked channels of all the things”. I should’ve mentioned the isLinked step to find out if the channel is linked.

Yep, that’s another way to do it.

Based on what I’ve read so far (and admittedly there are some holes in my understanding), I’d do all the communications in the bridge handler in order to best optimize the calls to the API. Then I’d pass the results of the comms to each child thing handler for it to decide what to do with the data.

So at the moment I’m doing this:

private void createUpdateList() {
    for (Channel channel : getThing().getChannels()) {
        if (isLinked(channel.getUID())) {
            String featureSetId = this.thing.getConfiguration().get("featureSetId").toString();
            FeatureSets featureSet = LWBindingConstants.featureSets.stream()
            .filter(i -> featureSetId.equals(i.getFeatureSetId())).findFirst().orElse(featureSets.get(0));            
            Features feature = featureSet.getFeatures().stream().filter(i -> 
                channel.getUID().getId().equals(i.getType())).findFirst().orElse(features.get(0));
            channelList.add(feature.getFeatureId());  
            logger.debug("channel added to Update List: {}", channel.getUID().toString());
        }

In each thing handler, Which gets the ‘featureId’ (channel in api) for each linked channel and adds them to a list. The polling job retrieves this list, creates the JSON payload and queries the api. The response is then passed into a list. The thing handler has its own polling job to update the things from the list as follows:

private synchronized void updateChannels() {
    String featureSetId = this.thing.getConfiguration().get("featureSetId").toString();
    FeatureSets featureSet = LWBindingConstants.featureSets.stream()
        .filter(i -> featureSetId.equals(i.getFeatureSetId())).findFirst().orElse(LWBindingConstants.featureSets.get(0));
    for (Channel channel : getThing().getChannels()) {
        if (isLinked(channel.getUID())) {
        Features feature = featureSet.getFeatures().stream().filter(i -> 
            channel.getUID().getId().equals(i.getType())).findFirst().orElse(LWBindingConstants.features.get(0));
        FeatureStatus status = featureStatus.stream().filter(i -> 
            feature.getFeatureId().equals(i.getFeatureId())).findFirst().orElse(LWBindingConstants.featureStatus.get(0));
        Integer value = status.getValue();
        updateChannels(channel.getUID().getId(),value);
        }
    }
}

private void updateChannels(String channelId,Integer value) {
    switch (channelId) {
        case "switch": case "diagnostics": case "outletInUse": case "protection":
        case "identify": case "reset": case "upgrade": case "heatState":
        case "callForHeat": case "bulbSetup": case "dimSetup":
            if (value == 1) {
                updateState(channelId, OnOffType.ON);
            } else {
                updateState(channelId, OnOffType.OFF);
            }
            break;
        case "voltage":
            updateState(channelId, new DecimalType(Float.parseFloat(value.toString()) / 10));
            break;
        case "power": 
            updateState(channelId, new DecimalType(Float.parseFloat(value.toString()) / 10));
            break;
        case "energy":
            updateState(channelId, new DecimalType(Float.parseFloat(value.toString()) / 1000));
            break;
        case "rssi": case "batteryLevel": case "temperature": case "targetTemperature":
            updateState(channelId, new DecimalType(Float.parseFloat(value.toString()) / 10));
            break;
        case "dimLevel":
            updateState(channelId,new DecimalType(value));
            break;
    }
    logger.debug("Finished Channel Update for {}",this.thing.toString());
}

So managing my list of id’s to fetch from the api…
How do I monitor if an item is unlinked (removed from channel).
I’m assuming there is something similar to handleCommand() and updateChannels() that I can create a method for in each thing handler?

There are channelLinked and channelUnlinked methods.

@mhilbush I tried adding these two methods and added a single line in each to log the channel had been added or removed but it didn’t do anything. just the item change event in the logs.

These methods are not called when a channel is added or removed. They’re called when a channel is linked to an item or unlinked from an item. If you search through the openhab-addons repo, you’ll see plenty of examples of how this is used.

@mhilbush apologies, my terminology was wrong. I meant they didn’t fire when i linked or un-linked an item from the channel.

It’s hard to tell without seeing the code… Did you @Override those methods?

1 Like

If you want an example of how to use link/unlink: https://github.com/tmrobert8/openhab-addons/blob/1234-Sony/bundles/org.openhab.binding.sony/src/main/java/org/openhab/binding/sony/internal/scalarweb/ScalarWebHandler.java

1 Like

@tmrobert8 @mhilbush thanks got it working, @Override was my issue, new to all this but getting there.
On another note, I’ve seen alot of info on adding and removing channels dynamically, however i cant find much info on channel-groups. I’m looking to change to the group name in the handler based on what the api returns. at present they are just Channel 1, Channel 2 as defined in my xml’s.