### 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.
: 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());
--- /dev/null
+/**
+ * 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"));
+ }
+}
*/
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;
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);
} 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
*/
package org.openhab.binding.netatmo.internal.handler;
+import static java.util.Comparator.*;
import static org.openhab.binding.netatmo.internal.NetatmoBindingConstants.*;
import java.io.ByteArrayInputStream;
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;
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;
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;
}
}
+ 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;
}