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