]> git.basschouten.com Git - openhab-addons.git/commitdiff
[netatmo] Console command to show all devices/modules ids (#13555)
authorlolodomo <lg.hc@free.fr>
Wed, 19 Oct 2022 06:47:04 +0000 (08:47 +0200)
committerGitHub <noreply@github.com>
Wed, 19 Oct 2022 06:47:04 +0000 (08:47 +0200)
* [netatmo] Console command to show all devices/modules ids

Fix #13091

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
bundles/org.openhab.binding.netatmo/README.md
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/ModuleType.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/console/NetatmoCommandExtension.java [new file with mode: 0644]
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/discovery/NetatmoDiscoveryService.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/ApiBridgeHandler.java

index c389fc19832fba213421600d36e78a5e61d1bf5e..2f17d23d927d48e340d4ec379bcdb440529ce5ca 100644 (file)
@@ -123,6 +123,12 @@ NB: Allowed ports for webhooks are 80, 88, 443 and 9443.
 
 ### Configure Things
 
+The easiest way to retrieve the IDs for all the devices and modules is to use the console command `openhab:netatmo showIds`.
+It shows the hierarchy of all the devices and modules including their IDs.
+This can help to define all your things in a configuration file.
+
+**Another way to get the IDs is to use the developer documentation on the netatmo site:**
+
 The IDs for the modules can be extracted from the developer documentation on the netatmo site.
 First login with your user.
 Then some examples of the documentation contain the **real results** of your weather station.
index 58e9855104afeba00638b300e929400493f75a54..82fc791f6771d8ca60695aeddde523142875b131 100644 (file)
@@ -207,6 +207,11 @@ public enum ModuleType {
                                         : ModuleType.UNKNOWN.equals(getBridge()) ? "configurable" : "device")));
     }
 
+    public int getDepth() {
+        ModuleType parent = bridgeType;
+        return parent == null ? 1 : 1 + parent.getDepth();
+    }
+
     public static ModuleType from(ThingTypeUID thingTypeUID) {
         return ModuleType.AS_SET.stream().filter(mt -> mt.thingTypeUID.equals(thingTypeUID)).findFirst()
                 .orElseThrow(() -> new IllegalArgumentException());
diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/console/NetatmoCommandExtension.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/console/NetatmoCommandExtension.java
new file mode 100644 (file)
index 0000000..b8b28be
--- /dev/null
@@ -0,0 +1,97 @@
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.netatmo.internal.console;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.netatmo.internal.NetatmoBindingConstants;
+import org.openhab.binding.netatmo.internal.api.data.ModuleType;
+import org.openhab.binding.netatmo.internal.api.dto.NAModule;
+import org.openhab.binding.netatmo.internal.handler.ApiBridgeHandler;
+import org.openhab.core.io.console.Console;
+import org.openhab.core.io.console.extensions.AbstractConsoleCommandExtension;
+import org.openhab.core.io.console.extensions.ConsoleCommandExtension;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingRegistry;
+import org.openhab.core.thing.ThingUID;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+/**
+ * The {@link NetatmoCommandExtension} is responsible for handling console commands
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+
+@NonNullByDefault
+@Component(service = ConsoleCommandExtension.class)
+public class NetatmoCommandExtension extends AbstractConsoleCommandExtension {
+
+    private static final String SHOW_IDS = "showIds";
+
+    private final ThingRegistry thingRegistry;
+    private @Nullable Console console;
+
+    @Activate
+    public NetatmoCommandExtension(final @Reference ThingRegistry thingRegistry) {
+        super(NetatmoBindingConstants.BINDING_ID, "Interact with the Netatmo binding.");
+        this.thingRegistry = thingRegistry;
+    }
+
+    @Override
+    public void execute(String[] args, Console console) {
+        if (args.length == 1 && SHOW_IDS.equals(args[0])) {
+            this.console = console;
+            for (Thing thing : thingRegistry.getAll()) {
+                ThingHandler thingHandler = thing.getHandler();
+                if (thingHandler instanceof ApiBridgeHandler) {
+                    console.println("Account bridge: " + thing.getLabel());
+                    ((ApiBridgeHandler) thingHandler).identifyAllModulesAndApplyAction(this::printThing);
+                }
+            }
+        } else {
+            printUsage(console);
+        }
+    }
+
+    private Optional<ThingUID> printThing(NAModule module, ThingUID bridgeUID) {
+        Console localConsole = this.console;
+        Optional<ThingUID> moduleUID = findThingUID(module.getType(), module.getId(), bridgeUID);
+        if (localConsole != null && moduleUID.isPresent()) {
+            String indent = "";
+            for (int i = 2; i <= module.getType().getDepth(); i++) {
+                indent += "    ";
+            }
+            localConsole.println(String.format("%s- ID \"%s\" for \"%s\" (thing type %s)", indent, module.getId(),
+                    module.getName() != null ? module.getName() : "...", module.getType().thingTypeUID));
+        }
+        return moduleUID;
+    }
+
+    private Optional<ThingUID> findThingUID(ModuleType moduleType, String thingId, ThingUID bridgeUID) {
+        return moduleType.apiName.isBlank() ? Optional.empty()
+                : Optional.ofNullable(
+                        new ThingUID(moduleType.thingTypeUID, bridgeUID, thingId.replaceAll("[^a-zA-Z0-9_]", "")));
+    }
+
+    @Override
+    public List<String> getUsages() {
+        return Arrays.asList(buildCommandUsage(SHOW_IDS, "list all devices and modules ids"));
+    }
+}
index 7b567ba698db2eb1d390daa0cf5333ceffbbaebc..3e6f6933efa8ff81325f2a520d41db1b3d0aa8f8 100644 (file)
  */
 package org.openhab.binding.netatmo.internal.discovery;
 
-import static java.util.Comparator.*;
-
-import java.util.HashMap;
-import java.util.Map;
 import java.util.Optional;
 import java.util.stream.Collectors;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.netatmo.internal.api.AircareApi;
-import org.openhab.binding.netatmo.internal.api.HomeApi;
-import org.openhab.binding.netatmo.internal.api.ListBodyResponse;
-import org.openhab.binding.netatmo.internal.api.NetatmoException;
-import org.openhab.binding.netatmo.internal.api.WeatherApi;
 import org.openhab.binding.netatmo.internal.api.data.ModuleType;
-import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.FeatureArea;
-import org.openhab.binding.netatmo.internal.api.dto.HomeDataModule;
-import org.openhab.binding.netatmo.internal.api.dto.NAMain;
 import org.openhab.binding.netatmo.internal.api.dto.NAModule;
 import org.openhab.binding.netatmo.internal.config.NAThingConfiguration;
 import org.openhab.binding.netatmo.internal.handler.ApiBridgeHandler;
@@ -65,77 +53,20 @@ public class NetatmoDiscoveryService extends AbstractDiscoveryService implements
     public void startScan() {
         ApiBridgeHandler localHandler = handler;
         if (localHandler != null) {
-            ThingUID accountUID = localHandler.getThing().getUID();
-            try {
-                AircareApi airCareApi = localHandler.getRestManager(AircareApi.class);
-                if (airCareApi != null) { // Search Healthy Home Coaches
-                    ListBodyResponse<NAMain> body = airCareApi.getHomeCoachData(null).getBody();
-                    if (body != null) {
-                        body.getElements().stream().forEach(homeCoach -> createThing(homeCoach, accountUID));
-                    }
-                }
-                WeatherApi weatherApi = localHandler.getRestManager(WeatherApi.class);
-                if (weatherApi != null) { // Search owned or favorite stations
-                    weatherApi.getFavoriteAndGuestStationsData().stream().forEach(station -> {
-                        if (!station.isReadOnly() || localHandler.getReadFriends()) {
-                            createThing(station, accountUID).ifPresent(stationUID -> station.getModules().values()
-                                    .stream().forEach(module -> createThing(module, stationUID)));
-                        }
-                    });
-                }
-                HomeApi homeApi = localHandler.getRestManager(HomeApi.class);
-                if (homeApi != null) { // Search those depending from a home that has modules + not only weather modules
-                    homeApi.getHomesData(null, null).stream()
-                            .filter(h -> !(h.getFeatures().isEmpty()
-                                    || h.getFeatures().contains(FeatureArea.WEATHER) && h.getFeatures().size() == 1))
-                            .forEach(home -> {
-                                createThing(home, accountUID).ifPresent(homeUID -> {
-                                    home.getKnownPersons().forEach(person -> createThing(person, homeUID));
-
-                                    Map<String, ThingUID> bridgesUids = new HashMap<>();
-
-                                    home.getRooms().values().stream().forEach(room -> {
-                                        room.getModuleIds().stream().map(id -> home.getModules().get(id))
-                                                .map(m -> m != null ? m.getType().feature : FeatureArea.NONE)
-                                                .filter(f -> FeatureArea.ENERGY.equals(f)).findAny().ifPresent(f -> {
-                                                    createThing(room, homeUID).ifPresent(
-                                                            roomUID -> bridgesUids.put(room.getId(), roomUID));
-                                                });
-                                    });
-
-                                    // Creating modules that have no bridge first, avoiding weather station itself
-                                    home.getModules().values().stream()
-                                            .filter(module -> module.getType().feature != FeatureArea.WEATHER)
-                                            .sorted(comparing(HomeDataModule::getBridge, nullsFirst(naturalOrder())))
-                                            .forEach(module -> {
-                                                String bridgeId = module.getBridge();
-                                                if (bridgeId == null) {
-                                                    createThing(module, homeUID).ifPresent(
-                                                            moduleUID -> bridgesUids.put(module.getId(), moduleUID));
-                                                } else {
-                                                    createThing(module, bridgesUids.getOrDefault(bridgeId, homeUID));
-                                                }
-                                            });
-                                });
-                            });
-                }
-            } catch (NetatmoException e) {
-                logger.warn("Error during discovery process : {}", e.getMessage());
-            }
+            localHandler.identifyAllModulesAndApplyAction(this::createThing);
         }
     }
 
-    private @Nullable ThingUID findThingUID(ModuleType thingType, String thingId, ThingUID bridgeUID) {
-        ThingTypeUID thingTypeUID = thingType.thingTypeUID;
+    private Optional<ThingUID> findThingUID(ModuleType moduleType, String thingId, ThingUID bridgeUID) {
+        ThingTypeUID thingTypeUID = moduleType.thingTypeUID;
         return getSupportedThingTypes().stream().filter(supported -> supported.equals(thingTypeUID)).findFirst()
-                .map(supported -> new ThingUID(supported, bridgeUID, thingId.replaceAll("[^a-zA-Z0-9_]", "")))
-                .orElse(null);
+                .map(supported -> new ThingUID(supported, bridgeUID, thingId.replaceAll("[^a-zA-Z0-9_]", "")));
     }
 
     private Optional<ThingUID> createThing(NAModule module, ThingUID bridgeUID) {
-        ThingUID moduleUID = findThingUID(module.getType(), module.getId(), bridgeUID);
-        if (moduleUID != null) {
-            DiscoveryResultBuilder resultBuilder = DiscoveryResultBuilder.create(moduleUID)
+        Optional<ThingUID> moduleUID = findThingUID(module.getType(), module.getId(), bridgeUID);
+        if (moduleUID.isPresent()) {
+            DiscoveryResultBuilder resultBuilder = DiscoveryResultBuilder.create(moduleUID.get())
                     .withProperty(NAThingConfiguration.ID, module.getId())
                     .withRepresentationProperty(NAThingConfiguration.ID)
                     .withLabel(module.getName() != null ? module.getName() : module.getId()).withBridge(bridgeUID);
@@ -143,7 +74,7 @@ public class NetatmoDiscoveryService extends AbstractDiscoveryService implements
         } else {
             logger.info("Module '{}' is not handled by this version of the binding - it is ignored.", module.getName());
         }
-        return Optional.ofNullable(moduleUID);
+        return moduleUID;
     }
 
     @Override
index bdbccab760c3e4755d8c1b3401c1733d9a80b19a..d23d5aa7f25e525b687f58918f8eddc6a360f556 100644 (file)
@@ -12,6 +12,7 @@
  */
 package org.openhab.binding.netatmo.internal.handler;
 
+import static java.util.Comparator.*;
 import static org.openhab.binding.netatmo.internal.NetatmoBindingConstants.*;
 
 import java.io.ByteArrayInputStream;
@@ -32,6 +33,7 @@ import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
+import java.util.function.BiFunction;
 
 import javax.ws.rs.core.UriBuilder;
 
@@ -45,13 +47,21 @@ import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpMethod;
 import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.http.HttpStatus.Code;
+import org.openhab.binding.netatmo.internal.api.AircareApi;
 import org.openhab.binding.netatmo.internal.api.ApiError;
 import org.openhab.binding.netatmo.internal.api.AuthenticationApi;
+import org.openhab.binding.netatmo.internal.api.HomeApi;
+import org.openhab.binding.netatmo.internal.api.ListBodyResponse;
 import org.openhab.binding.netatmo.internal.api.NetatmoException;
 import org.openhab.binding.netatmo.internal.api.RestManager;
 import org.openhab.binding.netatmo.internal.api.SecurityApi;
+import org.openhab.binding.netatmo.internal.api.WeatherApi;
+import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.FeatureArea;
 import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.Scope;
 import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.ServiceError;
+import org.openhab.binding.netatmo.internal.api.dto.HomeDataModule;
+import org.openhab.binding.netatmo.internal.api.dto.NAMain;
+import org.openhab.binding.netatmo.internal.api.dto.NAModule;
 import org.openhab.binding.netatmo.internal.config.ApiHandlerConfiguration;
 import org.openhab.binding.netatmo.internal.config.BindingConfiguration;
 import org.openhab.binding.netatmo.internal.config.ConfigurationLevel;
@@ -66,6 +76,7 @@ import org.openhab.core.thing.ChannelUID;
 import org.openhab.core.thing.Thing;
 import org.openhab.core.thing.ThingStatus;
 import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.ThingUID;
 import org.openhab.core.thing.binding.BaseBridgeHandler;
 import org.openhab.core.thing.binding.ThingHandlerService;
 import org.openhab.core.types.Command;
@@ -286,6 +297,66 @@ public class ApiBridgeHandler extends BaseBridgeHandler {
         }
     }
 
+    public void identifyAllModulesAndApplyAction(BiFunction<NAModule, ThingUID, Optional<ThingUID>> action) {
+        ThingUID accountUID = getThing().getUID();
+        try {
+            AircareApi airCareApi = getRestManager(AircareApi.class);
+            if (airCareApi != null) { // Search Healthy Home Coaches
+                ListBodyResponse<NAMain> body = airCareApi.getHomeCoachData(null).getBody();
+                if (body != null) {
+                    body.getElements().stream().forEach(homeCoach -> action.apply(homeCoach, accountUID));
+                }
+            }
+            WeatherApi weatherApi = getRestManager(WeatherApi.class);
+            if (weatherApi != null) { // Search owned or favorite stations
+                weatherApi.getFavoriteAndGuestStationsData().stream().forEach(station -> {
+                    if (!station.isReadOnly() || getReadFriends()) {
+                        action.apply(station, accountUID).ifPresent(stationUID -> station.getModules().values().stream()
+                                .forEach(module -> action.apply(module, stationUID)));
+                    }
+                });
+            }
+            HomeApi homeApi = getRestManager(HomeApi.class);
+            if (homeApi != null) { // Search those depending from a home that has modules + not only weather modules
+                homeApi.getHomesData(null, null).stream()
+                        .filter(h -> !(h.getFeatures().isEmpty()
+                                || h.getFeatures().contains(FeatureArea.WEATHER) && h.getFeatures().size() == 1))
+                        .forEach(home -> {
+                            action.apply(home, accountUID).ifPresent(homeUID -> {
+                                home.getKnownPersons().forEach(person -> action.apply(person, homeUID));
+
+                                Map<String, ThingUID> bridgesUids = new HashMap<>();
+
+                                home.getRooms().values().stream().forEach(room -> {
+                                    room.getModuleIds().stream().map(id -> home.getModules().get(id))
+                                            .map(m -> m != null ? m.getType().feature : FeatureArea.NONE)
+                                            .filter(f -> FeatureArea.ENERGY.equals(f)).findAny().ifPresent(f -> {
+                                                action.apply(room, homeUID)
+                                                        .ifPresent(roomUID -> bridgesUids.put(room.getId(), roomUID));
+                                            });
+                                });
+
+                                // Creating modules that have no bridge first, avoiding weather station itself
+                                home.getModules().values().stream()
+                                        .filter(module -> module.getType().feature != FeatureArea.WEATHER)
+                                        .sorted(comparing(HomeDataModule::getBridge, nullsFirst(naturalOrder())))
+                                        .forEach(module -> {
+                                            String bridgeId = module.getBridge();
+                                            if (bridgeId == null) {
+                                                action.apply(module, homeUID).ifPresent(
+                                                        moduleUID -> bridgesUids.put(module.getId(), moduleUID));
+                                            } else {
+                                                action.apply(module, bridgesUids.getOrDefault(bridgeId, homeUID));
+                                            }
+                                        });
+                            });
+                        });
+            }
+        } catch (NetatmoException e) {
+            logger.warn("Error while identifying all modules : {}", e.getMessage());
+        }
+    }
+
     public boolean getReadFriends() {
         return bindingConf.readFriends;
     }