2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.smartthings.internal;
15 import static org.openhab.binding.smartthings.internal.SmartthingsBindingConstants.*;
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.List;
20 import java.util.concurrent.ExecutionException;
21 import java.util.concurrent.TimeUnit;
22 import java.util.concurrent.TimeoutException;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.eclipse.jetty.client.HttpClient;
27 import org.eclipse.jetty.client.api.ContentResponse;
28 import org.eclipse.jetty.client.util.StringContentProvider;
29 import org.eclipse.jetty.http.HttpMethod;
30 import org.openhab.binding.smartthings.internal.dto.SmartthingsStateData;
31 import org.openhab.binding.smartthings.internal.handler.SmartthingsBridgeHandler;
32 import org.openhab.binding.smartthings.internal.handler.SmartthingsThingHandler;
33 import org.openhab.core.io.net.http.HttpClientFactory;
34 import org.openhab.core.thing.Bridge;
35 import org.openhab.core.thing.Channel;
36 import org.openhab.core.thing.Thing;
37 import org.openhab.core.thing.ThingTypeUID;
38 import org.openhab.core.thing.ThingUID;
39 import org.openhab.core.thing.binding.BaseThingHandlerFactory;
40 import org.openhab.core.thing.binding.ThingHandler;
41 import org.openhab.core.thing.binding.ThingHandlerFactory;
42 import org.osgi.service.component.annotations.Component;
43 import org.osgi.service.component.annotations.Reference;
44 import org.osgi.service.event.Event;
45 import org.osgi.service.event.EventHandler;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
49 import com.google.gson.Gson;
52 * The {@link SmartthingsHandlerFactory} is responsible for creating things and thing
55 * @author Bob Raker - Initial contribution
58 @Component(service = { ThingHandlerFactory.class, SmartthingsHubCommand.class,
59 EventHandler.class }, configurationPid = "binding.smarthings", property = "event.topics=org/openhab/binding/smartthings/state")
60 public class SmartthingsHandlerFactory extends BaseThingHandlerFactory
61 implements ThingHandlerFactory, EventHandler, SmartthingsHubCommand {
63 private final Logger logger = LoggerFactory.getLogger(SmartthingsHandlerFactory.class);
65 private @Nullable SmartthingsBridgeHandler bridgeHandler = null;
66 private @Nullable ThingUID bridgeUID;
68 private List<SmartthingsThingHandler> thingHandlers = Collections.synchronizedList(new ArrayList<>());
70 private @NonNullByDefault({}) HttpClient httpClient;
73 public boolean supportsThingType(ThingTypeUID thingTypeUID) {
74 return THING_TYPE_SMARTTHINGS.equals(thingTypeUID) || SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
77 public SmartthingsHandlerFactory() {
78 // Get a Gson instance
83 protected @Nullable ThingHandler createHandler(Thing thing) {
84 ThingTypeUID thingTypeUID = thing.getThingTypeUID();
86 if (thingTypeUID.equals(THING_TYPE_SMARTTHINGS)) {
87 // This binding only supports one bridge. If the user tries to add a second bridge register and error and
89 if (bridgeHandler != null) {
91 "The Smartthings binding only supports one bridge. Please change your configuration to only use one Bridge. This bridge {} will be ignored.",
92 thing.getUID().getAsString());
95 bridgeHandler = new SmartthingsBridgeHandler((Bridge) thing, this, bundleContext);
96 bridgeUID = thing.getUID();
97 logger.debug("SmartthingsHandlerFactory created BridgeHandler for {}", thingTypeUID.getAsString());
99 } else if (SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) {
100 // Everything but the bridge is handled by this one handler
101 // Make sure this thing belongs to the registered Bridge
102 if (bridgeUID != null && !bridgeUID.equals(thing.getBridgeUID())) {
103 logger.warn("Thing: {} is being ignored because it does not belong to the registered bridge.",
107 SmartthingsThingHandler thingHandler = new SmartthingsThingHandler(thing, this);
108 thingHandlers.add(thingHandler);
109 logger.debug("SmartthingsHandlerFactory created ThingHandler for {}, {}",
110 thing.getConfiguration().get("smartthingsName"), thing.getUID().getAsString());
117 * Send a command to the Smartthings Hub
119 * @param path http path which tells Smartthings what to execute
120 * @param data data to send
121 * @return Response from Smartthings
122 * @throws InterruptedException
123 * @throws TimeoutException
124 * @throws ExecutionException
127 public void sendDeviceCommand(String path, int timeout, String data)
128 throws InterruptedException, TimeoutException, ExecutionException {
129 ContentResponse response = httpClient
130 .newRequest(bridgeHandler.getSmartthingsIp(), bridgeHandler.getSmartthingsPort())
131 .timeout(timeout, TimeUnit.SECONDS).path(path).method(HttpMethod.POST)
132 .content(new StringContentProvider(data), "application/json").send();
134 int status = response.getStatus();
137 "Sent message \"{}\" with path \"{}\" to the Smartthings hub, received HTTP status {} (This is the normal code from Smartthings)",
140 logger.warn("Sent message \"{}\" with path \"{}\" to the Smartthings hub, received HTTP status {}", data,
146 * Messages sent to the Smartthings binding from the hub via the SmartthingsServlet arrive here and are then
147 * dispatched to the correct thing's handleStateMessage function
149 * @param event The event sent
152 public synchronized void handleEvent(@Nullable Event event) {
154 String data = (String) event.getProperty("data");
155 SmartthingsStateData stateData = new SmartthingsStateData();
156 stateData = gson.fromJson(data, stateData.getClass());
157 if (stateData == null) {
160 SmartthingsThingHandler handler = findHandler(stateData);
161 if (handler != null) {
162 handler.handleStateMessage(stateData);
167 private @Nullable SmartthingsThingHandler findHandler(SmartthingsStateData stateData) {
168 synchronized (thingHandlers) {
169 for (SmartthingsThingHandler handler : thingHandlers) {
170 if (handler.getSmartthingsName().equals(stateData.deviceDisplayName)) {
171 for (Channel ch : handler.getThing().getChannels()) {
172 String chId = ch.getUID().getId();
173 if (chId.equals(stateData.capabilityAttribute)) {
182 "Unable to locate handler for display name: {} with attribute: {}. If this thing is included in your OpenHabAppV2 SmartApp in the Smartthings App on your phone it must also be configured in openHAB",
183 stateData.deviceDisplayName, stateData.capabilityAttribute);
188 protected void setHttpClientFactory(HttpClientFactory httpClientFactory) {
189 this.httpClient = httpClientFactory.getCommonHttpClient();
192 protected void unsetHttpClientFactory() {
193 this.httpClient = null;
197 public SmartthingsBridgeHandler getBridgeHandler() {
198 return bridgeHandler;
203 public ThingUID getBridgeUID() {
204 return bridgeHandler.getThing().getUID();