]> git.basschouten.com Git - openhab-addons.git/blob
46eb29b093ec9aba1218a45112b43827c79a5099
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.netatmo.internal.discovery;
14
15 import static org.openhab.binding.netatmo.internal.APIUtils.*;
16 import static org.openhab.binding.netatmo.internal.NetatmoBindingConstants.*;
17
18 import java.util.HashMap;
19 import java.util.List;
20 import java.util.Map;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.netatmo.internal.handler.NetatmoBridgeHandler;
25 import org.openhab.binding.netatmo.internal.handler.NetatmoDataListener;
26 import org.openhab.core.config.discovery.AbstractDiscoveryService;
27 import org.openhab.core.config.discovery.DiscoveryResult;
28 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
29 import org.openhab.core.i18n.LocaleProvider;
30 import org.openhab.core.i18n.TranslationProvider;
31 import org.openhab.core.thing.Thing;
32 import org.openhab.core.thing.ThingTypeUID;
33 import org.openhab.core.thing.ThingUID;
34 import org.osgi.framework.Bundle;
35 import org.osgi.framework.FrameworkUtil;
36
37 import io.swagger.client.model.*;
38
39 /**
40  * The {@link NetatmoModuleDiscoveryService} searches for available Netatmo
41  * devices and modules connected to the API console
42  *
43  * @author GaĆ«l L'hopital - Initial contribution
44  * @author Ing. Peter Weiss - Welcome camera implementation
45  *
46  */
47 @NonNullByDefault
48 public class NetatmoModuleDiscoveryService extends AbstractDiscoveryService implements NetatmoDataListener {
49     private static final int SEARCH_TIME = 5;
50     private final NetatmoBridgeHandler netatmoBridgeHandler;
51
52     public NetatmoModuleDiscoveryService(NetatmoBridgeHandler netatmoBridgeHandler, LocaleProvider localeProvider,
53             TranslationProvider translationProvider) {
54         super(SUPPORTED_DEVICE_THING_TYPES_UIDS, SEARCH_TIME);
55         this.netatmoBridgeHandler = netatmoBridgeHandler;
56         this.localeProvider = localeProvider;
57         this.i18nProvider = translationProvider;
58     }
59
60     @Override
61     public void activate(@Nullable Map<String, Object> configProperties) {
62         super.activate(configProperties);
63         netatmoBridgeHandler.registerDataListener(this);
64     }
65
66     @Override
67     public void deactivate() {
68         netatmoBridgeHandler.unregisterDataListener(this);
69         super.deactivate();
70     }
71
72     @Override
73     public void startScan() {
74         if (netatmoBridgeHandler.configuration.readStation) {
75             netatmoBridgeHandler.getStationsDataBody(null).ifPresent(dataBody -> {
76                 nonNullList(dataBody.getDevices()).forEach(station -> {
77                     discoverWeatherStation(station);
78                 });
79             });
80         }
81         if (netatmoBridgeHandler.configuration.readHealthyHomeCoach) {
82             netatmoBridgeHandler.getHomecoachDataBody(null).ifPresent(dataBody -> {
83                 nonNullList(dataBody.getDevices()).forEach(homecoach -> {
84                     discoverHomeCoach(homecoach);
85                 });
86             });
87         }
88         if (netatmoBridgeHandler.configuration.readThermostat) {
89             netatmoBridgeHandler.getThermostatsDataBody(null).ifPresent(dataBody -> {
90                 nonNullList(dataBody.getDevices()).forEach(plug -> {
91                     discoverThermostat(plug);
92                 });
93             });
94         }
95         if (netatmoBridgeHandler.configuration.readWelcome || netatmoBridgeHandler.configuration.readPresence) {
96             netatmoBridgeHandler.getWelcomeDataBody(null).ifPresent(dataBody -> {
97                 nonNullList(dataBody.getHomes()).forEach(home -> {
98                     discoverWelcomeHome(home);
99                 });
100             });
101         }
102     }
103
104     @Override
105     protected synchronized void stopScan() {
106         super.stopScan();
107         removeOlderResults(getTimestampOfLastScan(), netatmoBridgeHandler.getThing().getUID());
108     }
109
110     @Override
111     public void onDataRefreshed(Object data) {
112         if (!isBackgroundDiscoveryEnabled()) {
113             return;
114         }
115         if (data instanceof NAMain) {
116             discoverWeatherStation((NAMain) data);
117         } else if (data instanceof NAPlug) {
118             discoverThermostat((NAPlug) data);
119         } else if (data instanceof NAHealthyHomeCoach) {
120             discoverHomeCoach((NAHealthyHomeCoach) data);
121         } else if (data instanceof NAWelcomeHome) {
122             discoverWelcomeHome((NAWelcomeHome) data);
123         }
124     }
125
126     private void discoverThermostat(NAPlug plug) {
127         onDeviceAddedInternal(plug.getId(), null, plug.getType(), plug.getStationName(), plug.getFirmware());
128         nonNullList(plug.getModules()).forEach(thermostat -> {
129             onDeviceAddedInternal(thermostat.getId(), plug.getId(), thermostat.getType(), thermostat.getModuleName(),
130                     thermostat.getFirmware());
131         });
132     }
133
134     private void discoverHomeCoach(NAHealthyHomeCoach homecoach) {
135         onDeviceAddedInternal(homecoach.getId(), null, homecoach.getType(), homecoach.getName(),
136                 homecoach.getFirmware());
137     }
138
139     private void discoverWeatherStation(NAMain station) {
140         final boolean isFavorite = station.isFavorite() != null && station.isFavorite();
141         final String weatherStationName = createWeatherStationName(station, isFavorite);
142
143         onDeviceAddedInternal(station.getId(), null, station.getType(), weatherStationName, station.getFirmware());
144         nonNullList(station.getModules()).forEach(module -> {
145             onDeviceAddedInternal(module.getId(), station.getId(), module.getType(),
146                     createWeatherModuleName(station, module, isFavorite), module.getFirmware());
147         });
148     }
149
150     private void discoverWelcomeHome(NAWelcomeHome home) {
151         // I observed that Thermostat homes are also reported here by Netatmo API
152         // So I ignore homes that have an empty list of cameras
153         List<NAWelcomeCamera> cameras = nonNullList(home.getCameras());
154         if (!cameras.isEmpty()) {
155             onDeviceAddedInternal(home.getId(), null, WELCOME_HOME_THING_TYPE.getId(), home.getName(), null);
156             // Discover Cameras
157             cameras.forEach(camera -> {
158                 onDeviceAddedInternal(camera.getId(), home.getId(), camera.getType(), camera.getName(), null);
159             });
160
161             // Discover Known Persons
162             nonNullStream(home.getPersons()).filter(person -> person.getPseudo() != null).forEach(person -> {
163                 onDeviceAddedInternal(person.getId(), home.getId(), WELCOME_PERSON_THING_TYPE.getId(),
164                         person.getPseudo(), null);
165             });
166         }
167     }
168
169     private void onDeviceAddedInternal(String id, @Nullable String parentId, String type, String name,
170             @Nullable Integer firmwareVersion) {
171         ThingUID thingUID = findThingUID(type, id);
172         Map<String, Object> properties = new HashMap<>();
173
174         properties.put(EQUIPMENT_ID, id);
175         if (parentId != null) {
176             properties.put(PARENT_ID, parentId);
177         }
178         if (firmwareVersion != null) {
179             properties.put(Thing.PROPERTY_VENDOR, VENDOR);
180             properties.put(Thing.PROPERTY_FIRMWARE_VERSION, firmwareVersion);
181             properties.put(Thing.PROPERTY_MODEL_ID, type);
182             properties.put(Thing.PROPERTY_SERIAL_NUMBER, id);
183         }
184         addDiscoveredThing(thingUID, properties, name);
185     }
186
187     private void addDiscoveredThing(ThingUID thingUID, Map<String, Object> properties, String displayLabel) {
188         DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
189                 .withBridge(netatmoBridgeHandler.getThing().getUID()).withLabel(displayLabel)
190                 .withRepresentationProperty(EQUIPMENT_ID).build();
191
192         thingDiscovered(discoveryResult);
193     }
194
195     private ThingUID findThingUID(String thingType, String thingId) throws IllegalArgumentException {
196         for (ThingTypeUID supportedThingTypeUID : getSupportedThingTypes()) {
197             String uid = supportedThingTypeUID.getId();
198
199             if (uid.equalsIgnoreCase(thingType)) {
200                 return new ThingUID(supportedThingTypeUID, netatmoBridgeHandler.getThing().getUID(),
201                         thingId.replaceAll("[^a-zA-Z0-9_]", ""));
202             }
203         }
204
205         throw new IllegalArgumentException("Unsupported device type discovered : " + thingType);
206     }
207
208     private String createWeatherStationName(NAMain station, boolean isFavorite) {
209         StringBuilder nameBuilder = new StringBuilder();
210         nameBuilder.append(localizeType(station.getType()));
211         if (station.getStationName() != null) {
212             nameBuilder.append(' ');
213             nameBuilder.append(station.getStationName());
214         }
215         if (isFavorite) {
216             nameBuilder.append(" (favorite)");
217         }
218         return nameBuilder.toString();
219     }
220
221     private String createWeatherModuleName(NAMain station, NAStationModule module, boolean isFavorite) {
222         StringBuilder nameBuilder = new StringBuilder();
223         if (module.getModuleName() != null) {
224             nameBuilder.append(module.getModuleName());
225         } else {
226             nameBuilder.append(localizeType(module.getType()));
227         }
228         if (station.getStationName() != null) {
229             nameBuilder.append(' ');
230             nameBuilder.append(station.getStationName());
231         }
232         if (isFavorite) {
233             nameBuilder.append(" (favorite)");
234         }
235         return nameBuilder.toString();
236     }
237
238     private String localizeType(String typeName) {
239         Bundle bundle = FrameworkUtil.getBundle(this.getClass());
240         @Nullable
241         String localizedType = i18nProvider.getText(bundle, "thing-type.netatmo." + typeName + ".label", typeName,
242                 localeProvider.getLocale());
243         if (localizedType != null) {
244             return localizedType;
245         }
246         return typeName;
247     }
248 }