2 * Copyright (c) 2010-2021 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.lgwebos.internal.action;
15 import java.awt.image.BufferedImage;
16 import java.io.ByteArrayOutputStream;
17 import java.io.IOException;
18 import java.io.OutputStream;
20 import java.nio.charset.StandardCharsets;
21 import java.util.Base64;
22 import java.util.Collections;
23 import java.util.List;
24 import java.util.Optional;
25 import java.util.stream.Collectors;
26 import java.util.stream.Stream;
28 import javax.imageio.ImageIO;
30 import org.eclipse.jdt.annotation.NonNullByDefault;
31 import org.eclipse.jdt.annotation.Nullable;
32 import org.openhab.binding.lgwebos.internal.handler.LGWebOSHandler;
33 import org.openhab.binding.lgwebos.internal.handler.LGWebOSTVMouseSocket.ButtonType;
34 import org.openhab.binding.lgwebos.internal.handler.LGWebOSTVSocket;
35 import org.openhab.binding.lgwebos.internal.handler.LGWebOSTVSocket.State;
36 import org.openhab.binding.lgwebos.internal.handler.command.ServiceSubscription;
37 import org.openhab.binding.lgwebos.internal.handler.core.AppInfo;
38 import org.openhab.binding.lgwebos.internal.handler.core.ResponseListener;
39 import org.openhab.binding.lgwebos.internal.handler.core.TextInputStatusInfo;
40 import org.openhab.core.automation.annotation.ActionInput;
41 import org.openhab.core.automation.annotation.RuleAction;
42 import org.openhab.core.thing.binding.ThingActions;
43 import org.openhab.core.thing.binding.ThingActionsScope;
44 import org.openhab.core.thing.binding.ThingHandler;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
48 import com.google.gson.JsonObject;
49 import com.google.gson.JsonParseException;
50 import com.google.gson.JsonParser;
53 * The {@link LGWebOSActions} defines the thing actions for the LGwebOS binding.
55 * @author Sebastian Prehn - Initial contribution
56 * @author Laurent Garnier - new method invokeMethodOf + interface ILGWebOSActions
58 @ThingActionsScope(name = "lgwebos")
60 public class LGWebOSActions implements ThingActions {
61 private final Logger logger = LoggerFactory.getLogger(LGWebOSActions.class);
62 private final ResponseListener<TextInputStatusInfo> textInputListener = createTextInputStatusListener();
63 private @Nullable LGWebOSHandler handler;
66 public void setThingHandler(@Nullable ThingHandler handler) {
67 this.handler = (LGWebOSHandler) handler;
71 public @Nullable ThingHandler getThingHandler() {
75 // a NonNull getter for handler
76 private LGWebOSHandler getLGWebOSHandler() {
77 LGWebOSHandler lgWebOSHandler = this.handler;
78 if (lgWebOSHandler == null) {
79 throw new IllegalStateException(
80 "ThingHandler must be set before any action may be invoked on LGWebOSActions.");
82 return lgWebOSHandler;
97 @RuleAction(label = "@text/actionShowToastLabel", description = "@text/actionShowToastDesc")
98 public void showToast(
99 @ActionInput(name = "text", label = "@text/actionShowToastInputTextLabel", description = "@text/actionShowToastInputTextDesc") String text)
101 getConnectedSocket().ifPresent(control -> control.showToast(text, createResponseListener()));
104 @RuleAction(label = "@text/actionShowToastWithIconLabel", description = "@text/actionShowToastWithIconLabel")
105 public void showToast(
106 @ActionInput(name = "icon", label = "@text/actionShowToastInputIconLabel", description = "@text/actionShowToastInputIconDesc") String icon,
107 @ActionInput(name = "text", label = "@text/actionShowToastInputTextLabel", description = "@text/actionShowToastInputTextDesc") String text)
109 BufferedImage bi = ImageIO.read(new URL(icon));
110 try (ByteArrayOutputStream os = new ByteArrayOutputStream(); OutputStream b64 = Base64.getEncoder().wrap(os)) {
111 ImageIO.write(bi, "png", b64);
112 String string = os.toString(StandardCharsets.UTF_8.name());
113 getConnectedSocket().ifPresent(control -> control.showToast(text, string, "png", createResponseListener()));
117 @RuleAction(label = "@text/actionLaunchBrowserLabel", description = "@text/actionLaunchBrowserDesc")
118 public void launchBrowser(
119 @ActionInput(name = "url", label = "@text/actionLaunchBrowserInputUrlLabel", description = "@text/actionLaunchBrowserInputUrlDesc") String url) {
120 getConnectedSocket().ifPresent(control -> control.launchBrowser(url, createResponseListener()));
123 private List<AppInfo> getAppInfos() {
124 LGWebOSHandler lgWebOSHandler = getLGWebOSHandler();
126 if (!this.getConnectedSocket().isPresent()) {
127 return Collections.emptyList();
130 List<AppInfo> appInfos = lgWebOSHandler.getLauncherApplication()
131 .getAppInfos(lgWebOSHandler.getThing().getUID());
132 if (appInfos == null) {
133 logger.warn("No AppInfos found for device with ThingID {}.", lgWebOSHandler.getThing().getUID());
134 return Collections.emptyList();
139 @RuleAction(label = "@text/actionLaunchApplicationLabel", description = "@text/actionLaunchApplicationDesc")
140 public void launchApplication(
141 @ActionInput(name = "appId", label = "@text/actionLaunchApplicationInputAppIDLabel", description = "@text/actionLaunchApplicationInputAppIDDesc") String appId) {
142 Optional<AppInfo> appInfo = getAppInfos().stream().filter(a -> a.getId().equals(appId)).findFirst();
143 if (appInfo.isPresent()) {
145 .ifPresent(control -> control.launchAppWithInfo(appInfo.get(), createResponseListener()));
147 logger.warn("Device with ThingID {} does not support any app with id: {}.",
148 getLGWebOSHandler().getThing().getUID(), appId);
152 @RuleAction(label = "@text/actionLaunchApplicationWithParamsLabel", description = "@text/actionLaunchApplicationWithParamsDesc")
153 public void launchApplication(
154 @ActionInput(name = "appId", label = "@text/actionLaunchApplicationInputAppIDLabel", description = "@text/actionLaunchApplicationInputAppIDDesc") String appId,
155 @ActionInput(name = "params", label = "@text/actionLaunchApplicationInputParamsLabel", description = "@text/actionLaunchApplicationInputParamsDesc") String params) {
157 JsonParser parser = new JsonParser();
158 JsonObject payload = (JsonObject) parser.parse(params);
160 Optional<AppInfo> appInfo = getAppInfos().stream().filter(a -> a.getId().equals(appId)).findFirst();
161 if (appInfo.isPresent()) {
162 getConnectedSocket().ifPresent(
163 control -> control.launchAppWithInfo(appInfo.get(), payload, createResponseListener()));
165 logger.warn("Device with ThingID {} does not support any app with id: {}.",
166 getLGWebOSHandler().getThing().getUID(), appId);
169 } catch (JsonParseException ex) {
170 logger.warn("Parameters value ({}) is not in a valid JSON format. {}", params, ex.getMessage());
175 @RuleAction(label = "@text/actionSendTextLabel", description = "@text/actionSendTextDesc")
176 public void sendText(
177 @ActionInput(name = "text", label = "@text/actionSendTextInputTextLabel", description = "@text/actionSendTextInputTextDesc") String text) {
178 getConnectedSocket().ifPresent(control -> {
179 ServiceSubscription<TextInputStatusInfo> subscription = control.subscribeTextInputStatus(textInputListener);
180 control.sendText(text);
181 control.unsubscribe(subscription);
185 @RuleAction(label = "@text/actionSendButtonLabel", description = "@text/actionSendButtonDesc")
186 public void sendButton(
187 @ActionInput(name = "text", label = "@text/actionSendButtonInputButtonLabel", description = "@text/actionSendButtonInputButtonDesc") String button) {
189 switch (Button.valueOf(button)) {
191 getConnectedSocket().ifPresent(control -> control.executeMouse(s -> s.button(ButtonType.UP)));
194 getConnectedSocket().ifPresent(control -> control.executeMouse(s -> s.button(ButtonType.DOWN)));
197 getConnectedSocket().ifPresent(control -> control.executeMouse(s -> s.button(ButtonType.LEFT)));
200 getConnectedSocket().ifPresent(control -> control.executeMouse(s -> s.button(ButtonType.RIGHT)));
203 getConnectedSocket().ifPresent(control -> control.executeMouse(s -> s.button(ButtonType.BACK)));
206 getConnectedSocket().ifPresent(control -> control.sendDelete());
209 getConnectedSocket().ifPresent(control -> control.sendEnter());
212 getConnectedSocket().ifPresent(control -> control.executeMouse(s -> s.button("HOME")));
215 getConnectedSocket().ifPresent(control -> control.executeMouse(s -> s.click()));
218 } catch (IllegalArgumentException ex) {
219 logger.warn("{} is not a valid value for button - available are: {}", button,
220 Stream.of(Button.values()).map(b -> b.name()).collect(Collectors.joining(", ")));
224 @RuleAction(label = "@text/actionIncreaseChannelLabel", description = "@text/actionIncreaseChannelDesc")
225 public void increaseChannel() {
226 getConnectedSocket().ifPresent(control -> control.channelUp(createResponseListener()));
229 @RuleAction(label = "@text/actionDecreaseChannelLabel", description = "@text/actionDecreaseChannelDesc")
230 public void decreaseChannel() {
231 getConnectedSocket().ifPresent(control -> control.channelDown(createResponseListener()));
234 @RuleAction(label = "@text/actionSendRCButtonLabel", description = "@text/actionSendRCButtonDesc")
235 public void sendRCButton(
236 @ActionInput(name = "text", label = "@text/actionSendRCButtonInputTextLabel", description = "@text/actionSendRCButtonInputTextDesc") String rcButton) {
237 getConnectedSocket().ifPresent(control -> control.executeMouse(s -> s.button(rcButton)));
240 private Optional<LGWebOSTVSocket> getConnectedSocket() {
241 LGWebOSHandler lgWebOSHandler = getLGWebOSHandler();
242 final LGWebOSTVSocket socket = lgWebOSHandler.getSocket();
244 if (socket.getState() != State.REGISTERED) {
245 logger.warn("Device with ThingID {} is currently not connected.", lgWebOSHandler.getThing().getUID());
246 return Optional.empty();
249 return Optional.of(socket);
252 private ResponseListener<TextInputStatusInfo> createTextInputStatusListener() {
253 return new ResponseListener<TextInputStatusInfo>() {
256 public void onError(@Nullable String error) {
257 logger.warn("Response: {}", error);
261 public void onSuccess(@Nullable TextInputStatusInfo info) {
262 logger.debug("Response: {}", info == null ? "OK" : info.getRawData());
267 private <O> ResponseListener<O> createResponseListener() {
268 return new ResponseListener<O>() {
271 public void onError(@Nullable String error) {
272 logger.warn("Response: {}", error);
276 public void onSuccess(@Nullable O object) {
277 logger.debug("Response: {}", object == null ? "OK" : object.toString());
282 // delegation methods for "legacy" rule support
284 public static void showToast(ThingActions actions, String text) throws IOException {
285 ((LGWebOSActions) actions).showToast(text);
288 public static void showToast(ThingActions actions, String icon, String text) throws IOException {
289 ((LGWebOSActions) actions).showToast(icon, text);
292 public static void launchBrowser(ThingActions actions, String url) {
293 ((LGWebOSActions) actions).launchBrowser(url);
296 public static void launchApplication(ThingActions actions, String appId) {
297 ((LGWebOSActions) actions).launchApplication(appId);
300 public static void launchApplication(ThingActions actions, String appId, String param) {
301 ((LGWebOSActions) actions).launchApplication(appId, param);
304 public static void sendText(ThingActions actions, String text) {
305 ((LGWebOSActions) actions).sendText(text);
308 public static void sendButton(ThingActions actions, String button) {
309 ((LGWebOSActions) actions).sendButton(button);
312 public static void increaseChannel(ThingActions actions) {
313 ((LGWebOSActions) actions).increaseChannel();
316 public static void decreaseChannel(ThingActions actions) {
317 ((LGWebOSActions) actions).decreaseChannel();
320 public static void sendRCButton(ThingActions actions, String rcButton) {
321 ((LGWebOSActions) actions).sendRCButton(rcButton);