]> git.basschouten.com Git - openhab-addons.git/blob
ede2e00f36935c23ab4b383fbb708caf6ed250d3
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.tradfri.internal.discovery;
14
15 import static org.openhab.binding.tradfri.internal.TradfriBindingConstants.*;
16 import static org.openhab.core.thing.Thing.*;
17
18 import java.util.Arrays;
19 import java.util.Collections;
20 import java.util.Date;
21 import java.util.HashMap;
22 import java.util.Map;
23 import java.util.Set;
24 import java.util.stream.Collectors;
25 import java.util.stream.Stream;
26
27 import org.eclipse.jdt.annotation.NonNullByDefault;
28 import org.eclipse.jdt.annotation.Nullable;
29 import org.openhab.binding.tradfri.internal.DeviceUpdateListener;
30 import org.openhab.binding.tradfri.internal.handler.TradfriGatewayHandler;
31 import org.openhab.core.config.discovery.AbstractDiscoveryService;
32 import org.openhab.core.config.discovery.DiscoveryResult;
33 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
34 import org.openhab.core.config.discovery.DiscoveryService;
35 import org.openhab.core.thing.ThingTypeUID;
36 import org.openhab.core.thing.ThingUID;
37 import org.openhab.core.thing.binding.ThingHandler;
38 import org.openhab.core.thing.binding.ThingHandlerService;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 import com.google.gson.JsonObject;
43 import com.google.gson.JsonSyntaxException;
44
45 /**
46  * This class identifies devices that are available on the gateway and adds discovery results for them.
47  *
48  * @author Kai Kreuzer - Initial contribution
49  * @author Christoph Weitkamp - Added support for remote controller and motion sensor devices (read-only battery level)
50  * @author Andre Fuechsel - fixed the results removal
51  * @author Manuel Raffel - Added support for blinds
52  */
53 @NonNullByDefault
54 public class TradfriDiscoveryService extends AbstractDiscoveryService
55         implements DeviceUpdateListener, DiscoveryService, ThingHandlerService {
56     private final Logger logger = LoggerFactory.getLogger(TradfriDiscoveryService.class);
57
58     private @Nullable TradfriGatewayHandler handler;
59
60     private static final String REMOTE_CONTROLLER_MODEL = "TRADFRI remote control";
61
62     private static final Set<String> COLOR_TEMP_MODELS = Collections
63             .unmodifiableSet(Stream
64                     .of("TRADFRI bulb E27 WS opal 980lm", "TRADFRI bulb E27 WS clear 950lm",
65                             "TRADFRI bulb GU10 WS 400lm", "TRADFRI bulb E14 WS opal 400lm", "FLOALT panel WS 30x30",
66                             "FLOALT panel WS 60x60", "FLOALT panel WS 30x90", "TRADFRI bulb E12 WS opal 400lm")
67                     .collect(Collectors.toSet()));
68
69     private static final String[] COLOR_MODEL_IDENTIFIER_HINTS = new String[] { "CWS", " C/WS " };
70
71     public TradfriDiscoveryService() {
72         super(SUPPORTED_DEVICE_TYPES_UIDS, 10, true);
73     }
74
75     @Override
76     protected void startScan() {
77         handler.startScan();
78     }
79
80     @Override
81     protected synchronized void stopScan() {
82         super.stopScan();
83         removeOlderResults(getTimestampOfLastScan());
84     }
85
86     @Override
87     public void setThingHandler(@Nullable ThingHandler handler) {
88         if (handler instanceof TradfriGatewayHandler) {
89             this.handler = (TradfriGatewayHandler) handler;
90         }
91     }
92
93     @Override
94     public @Nullable ThingHandler getThingHandler() {
95         return handler;
96     }
97
98     @Override
99     public void activate() {
100         handler.registerDeviceUpdateListener(this);
101     }
102
103     @Override
104     public void deactivate() {
105         removeOlderResults(new Date().getTime());
106         handler.unregisterDeviceUpdateListener(this);
107     }
108
109     @Override
110     public void onUpdate(@Nullable String instanceId, @Nullable JsonObject data) {
111         ThingUID bridge = handler.getThing().getUID();
112         try {
113             if (data != null && data.has(INSTANCE_ID)) {
114                 int id = data.get(INSTANCE_ID).getAsInt();
115                 String type = data.get(TYPE).getAsString();
116                 JsonObject deviceInfo = data.get(DEVICE).getAsJsonObject();
117                 String model = deviceInfo.get(DEVICE_MODEL).getAsString();
118                 ThingUID thingId = null;
119
120                 if (TYPE_LIGHT.equals(type) && data.has(LIGHT)) {
121                     JsonObject state = data.get(LIGHT).getAsJsonArray().get(0).getAsJsonObject();
122
123                     // Color temperature light:
124                     // We do not always receive a COLOR attribute, even the light supports it - but the gateway does not
125                     // seem to have this information, if the bulb is unreachable. We therefore also check against
126                     // concrete model names.
127                     // Color light:
128                     // As the protocol does not distinguishes between color and full-color lights,
129                     // we check if the "CWS" or "CW/S" identifier is given in the model name
130                     ThingTypeUID thingType = null;
131                     if (model != null && Arrays.stream(COLOR_MODEL_IDENTIFIER_HINTS).anyMatch(model::contains)) {
132                         thingType = THING_TYPE_COLOR_LIGHT;
133                     }
134                     if (thingType == null && //
135                             (state.has(COLOR) || (model != null && COLOR_TEMP_MODELS.contains(model)))) {
136                         thingType = THING_TYPE_COLOR_TEMP_LIGHT;
137                     }
138                     if (thingType == null) {
139                         thingType = THING_TYPE_DIMMABLE_LIGHT;
140                     }
141                     thingId = new ThingUID(thingType, bridge, Integer.toString(id));
142                 } else if (TYPE_BLINDS.equals(type) && data.has(BLINDS)) {
143                     // Blinds
144                     thingId = new ThingUID(THING_TYPE_BLINDS, bridge, Integer.toString(id));
145                 } else if (TYPE_PLUG.equals(type) && data.has(PLUG)) {
146                     // Smart plug
147                     thingId = new ThingUID(THING_TYPE_ONOFF_PLUG, bridge, Integer.toString(id));
148                 } else if (TYPE_SWITCH.equals(type) && data.has(SWITCH)) {
149                     // Remote control and wireless dimmer
150                     // As protocol does not distinguishes between remote control and wireless dimmer,
151                     // we check for the whole model name
152                     ThingTypeUID thingType = (model != null && REMOTE_CONTROLLER_MODEL.equals(model))
153                             ? THING_TYPE_REMOTE_CONTROL
154                             : THING_TYPE_DIMMER;
155                     thingId = new ThingUID(thingType, bridge, Integer.toString(id));
156                 } else if (TYPE_REMOTE.equals(type)) {
157                     thingId = new ThingUID(THING_TYPE_OPEN_CLOSE_REMOTE_CONTROL, bridge, Integer.toString(id));
158                 } else if (TYPE_SENSOR.equals(type) && data.has(SENSOR)) {
159                     // Motion sensor
160                     thingId = new ThingUID(THING_TYPE_MOTION_SENSOR, bridge, Integer.toString(id));
161                 }
162
163                 if (thingId == null) {
164                     // we didn't identify any device, so let's quit
165                     logger.debug("Ignoring unknown device on TRADFRI gateway:\n\ttype : {}\n\tmodel: {}\n\tinfo : {}",
166                             type, model, deviceInfo.getAsString());
167                     return;
168                 }
169
170                 String label = data.get(NAME).getAsString();
171
172                 Map<String, Object> properties = new HashMap<>(1);
173                 properties.put("id", id);
174                 if (model != null) {
175                     properties.put(PROPERTY_MODEL_ID, model);
176                 }
177                 if (deviceInfo.get(DEVICE_VENDOR) != null) {
178                     properties.put(PROPERTY_VENDOR, deviceInfo.get(DEVICE_VENDOR).getAsString());
179                 }
180                 if (deviceInfo.get(DEVICE_FIRMWARE) != null) {
181                     properties.put(PROPERTY_FIRMWARE_VERSION, deviceInfo.get(DEVICE_FIRMWARE).getAsString());
182                 }
183
184                 logger.debug("Adding device {} to inbox", thingId);
185                 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingId).withBridge(bridge)
186                         .withLabel(label).withProperties(properties).withRepresentationProperty("id").build();
187                 thingDiscovered(discoveryResult);
188             }
189         } catch (JsonSyntaxException e) {
190             logger.debug("JSON error during discovery: {}", e.getMessage());
191         }
192     }
193 }