From 29d48c3e30f021b28bac647b03bfbccdab8fe9a0 Mon Sep 17 00:00:00 2001 From: J-N-K Date: Wed, 3 Jan 2024 15:50:33 +0100 Subject: [PATCH] New ThingHandlerService documentation (#2194) * New ThingHandlerService documentation * Update developers/bindings/index.md Co-authored-by: Wouter Born Signed-off-by: J-N-K * improvement * improvement Signed-off-by: Jan N. Klug * Update developers/bindings/index.md Co-authored-by: Wouter Born Signed-off-by: J-N-K * Update developers/bindings/index.md Co-authored-by: Wouter Born Signed-off-by: J-N-K * Update developers/bindings/index.md Co-authored-by: Wouter Born Signed-off-by: J-N-K --------- Signed-off-by: J-N-K Signed-off-by: Jan N. Klug Co-authored-by: Wouter Born --- developers/bindings/index.md | 47 ++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/developers/bindings/index.md b/developers/bindings/index.md index 29d1beeb67..2c94704da7 100644 --- a/developers/bindings/index.md +++ b/developers/bindings/index.md @@ -571,9 +571,11 @@ If you implement the `ThingActions` interface, you can tell the framework about Please note that for actions not related to Things you will instead implement an `ActionHandler` as described in the developing [Module Types](../module-types/) chapter. -You start things off by implementing `ThingActions` and annotate your class with `@ThingActionsScope`: +You start things off by implementing `ThingActions` and annotate your class with `@ThingActionsScope`. +Since a new service is required for each thing, the component needs to be a `PROTOTYPE`: ```java +@Component(scope = ServiceScope.PROTOTYPE, service = MQTTActions.class) @ThingActionsScope(name = "mqtt") // Your bindings id is usually the scope @NonNullByDefault public class MQTTActions implements ThingActions { @@ -594,7 +596,7 @@ public class MyThingHandler extends BaseThingHandler { ... @Override public Collection> getServices() { - return Collections.singleton(MQTTActions.class); + return List.of(MQTTActions.class); } } ``` @@ -873,7 +875,7 @@ public class HueBridgeDiscoveryParticipant implements UpnpDiscoveryParticipant { @Override public Set getSupportedThingTypeUIDs() { - return Collections.singleton(THING_TYPE_BRIDGE); + return Set.of(THING_TYPE_BRIDGE); } @Override @@ -976,41 +978,44 @@ Here the developer only needs to implement four simple methods: When the discovery process is dependent on a configured bridge the discovery service must be bound to the bridge handler. Binding additional services to a handler can be achieved by implementing the service as a `ThingHandlerService`. -Instead of using the Component annotation your discovery service implements the `ThingHandlerService`. -It should extend the `AbstractDiscoveryService` (which implements `DiscoveryService`) just like a normal service: +It should extend the `AbstractThingHandlerDiscoveryService` (which implements `ThingHandlerService` and `DiscoveryService`) just like a normal service. +Since a new service is created for each thing, it has to be a `PROTOTYPE` component: ```java -public class extends AbstractDiscoveryService - implements ThingHandlerService { +@Component(scope = ServiceScope.PROTOTYPE, service = YourBindingDiscoveryService.class) +public class extends AbstractThingHandlerDiscoveryService { ``` -The interface `ThingHandlerService` has 2 methods to pass the handler of the bridge. -A typical implementation is: +In the class there is a field `protected YourBridgeHandler thingHandler;` which is automatically assigned. +This field is guaranteed to be non-null after the service has been activated. + +During service creation, first `activate()` is called, then the `thingHandler` is injected and finally `ìnitialize()` is called. +The opposite order is used when the service is destroyed, first `dispose()` is called (when the thing handler is still available in the service), then `deactivate()`. +`initialize()` and `dispose()` take care of background discovery. + +If you need additional code for initializing / disposing the discovery service, you can place them in the `initialize()` / `dispose()` methods. +To ensure everything is working correctly, you should call `super.initialize()` AFTER your own code and `super.dispose()` BEFORE your own code. + +The `thingHandler` can be used to get the bridge UID or to get access to the configured device connected to the bridge handler. +Fields set in `activate()` or `initialize()` can be regarded as "injected" and therefore be annotated with `@NonNullByDefault({})`. ```java - @Override - public void setThingHandler(@Nullable ThingHandler handler) { - if (handler instanceof ) { - bridgeHandler = () handler; - } - } + private @NonNullByDefault({}) ThingUID bridgeUid; @Override - public @Nullable ThingHandler getThingHandler() { - return bridgeHandler; + public void initialize() { + bridgeUid = thingHandler.getThing().getUID(); + super.initialize(); } ``` -The `setThingHandler` is called by the openHAB framework and give you access to the binding bridge handler. -The handler can be used to get the bridge UID or to get access to the configured device connected to the bridge handler. - In the bridge handler you need to activate the thing handler service. This is done by implementing the `getServices` method in your bridge handler: ```java @Override public Collection> getServices() { - return Collections.singleton(.class); + return List.of(YourBindingDiscoveryService.class); } ```