]> git.basschouten.com Git - openhab-addons.git/commitdiff
Rework ThingActions (#8732)
authorWouter Born <github@maindrain.net>
Wed, 14 Oct 2020 17:01:12 +0000 (19:01 +0200)
committerGitHub <noreply@github.com>
Wed, 14 Oct 2020 17:01:12 +0000 (19:01 +0200)
* Remove proxy workarounds
* Move ThingActions and a few other classes into the internal package
* Use more consistent action labels/descriptions

Related to:

* openhab/openhab-core#1714
* openhab/openhab-core#1639

Signed-off-by: Wouter Born <github@maindrain.net>
111 files changed:
bundles/org.openhab.binding.alarmdecoder/src/main/java/org/openhab/binding/alarmdecoder/internal/actions/BridgeActions.java
bundles/org.openhab.binding.alarmdecoder/src/main/java/org/openhab/binding/alarmdecoder/internal/actions/IBridgeActions.java [deleted file]
bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/action/AstroActions.java
bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/action/IAstroActions.java [deleted file]
bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java
bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/IAutomowerActions.java [deleted file]
bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/actions/AVMFritzHeatingActions.java [deleted file]
bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/actions/AVMFritzHeatingActions.java [new file with mode: 0644]
bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/actions/IAVMFritzHeatingActions.java [deleted file]
bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/handler/AVMFritzHeatingActionsHandler.java
bundles/org.openhab.binding.avmfritz/src/test/java/org/openhab/binding/avmfritz/actions/AVMFritzHeatingActionsTest.java [deleted file]
bundles/org.openhab.binding.avmfritz/src/test/java/org/openhab/binding/avmfritz/internal/actions/AVMFritzHeatingActionsTest.java [new file with mode: 0644]
bundles/org.openhab.binding.dmx/src/main/java/org/openhab/binding/dmx/action/DmxActions.java [deleted file]
bundles/org.openhab.binding.dmx/src/main/java/org/openhab/binding/dmx/action/IDmxActions.java [deleted file]
bundles/org.openhab.binding.dmx/src/main/java/org/openhab/binding/dmx/internal/DmxBridgeHandler.java
bundles/org.openhab.binding.dmx/src/main/java/org/openhab/binding/dmx/internal/action/DmxActions.java [new file with mode: 0644]
bundles/org.openhab.binding.doorbird/src/main/java/org/openhab/binding/doorbird/action/DoorbirdActions.java [deleted file]
bundles/org.openhab.binding.doorbird/src/main/java/org/openhab/binding/doorbird/action/IDoorbirdActions.java [deleted file]
bundles/org.openhab.binding.doorbird/src/main/java/org/openhab/binding/doorbird/internal/action/DoorbirdActions.java [new file with mode: 0644]
bundles/org.openhab.binding.doorbird/src/main/java/org/openhab/binding/doorbird/internal/handler/DoorbellHandler.java
bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/action/EcobeeActions.java [deleted file]
bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/action/IEcobeeActions.java [deleted file]
bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/action/EcobeeActions.java [new file with mode: 0644]
bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/handler/EcobeeThermostatBridgeHandler.java
bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/actions/Enigma2Actions.java [deleted file]
bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/actions/IEnigma2Actions.java [deleted file]
bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/handler/Enigma2Handler.java [deleted file]
bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/internal/Enigma2HandlerFactory.java
bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/internal/actions/Enigma2Actions.java [new file with mode: 0644]
bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/internal/handler/Enigma2Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.enigma2/src/test/java/org/openhab/binding/enigma2/actions/Enigma2ActionsTest.java [deleted file]
bundles/org.openhab.binding.enigma2/src/test/java/org/openhab/binding/enigma2/handler/Enigma2HandlerTest.java [deleted file]
bundles/org.openhab.binding.enigma2/src/test/java/org/openhab/binding/enigma2/internal/actions/Enigma2ActionsTest.java [new file with mode: 0644]
bundles/org.openhab.binding.enigma2/src/test/java/org/openhab/binding/enigma2/internal/handler/Enigma2HandlerTest.java [new file with mode: 0644]
bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/action/IIpx800Actions.java [deleted file]
bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/action/Ipx800Actions.java
bundles/org.openhab.binding.heos/src/main/java/org/openhab/binding/heos/internal/action/HeosActions.java
bundles/org.openhab.binding.heos/src/main/java/org/openhab/binding/heos/internal/action/IHeosActions.java [deleted file]
bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/action/ILightActions.java [deleted file]
bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/action/LightActions.java
bundles/org.openhab.binding.kaleidescape/src/main/java/org/openhab/binding/kaleidescape/internal/IKaleidescapeThingActions.java [deleted file]
bundles/org.openhab.binding.kaleidescape/src/main/java/org/openhab/binding/kaleidescape/internal/KaleidescapeThingActions.java
bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/ILcnModuleActions.java [deleted file]
bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleActions.java
bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/action/ILGWebOSActions.java [deleted file]
bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/action/LGWebOSActions.java [deleted file]
bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/action/LGWebOSActions.java [new file with mode: 0644]
bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/handler/LGWebOSHandler.java
bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/action/DimmerActions.java [deleted file]
bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/action/IDimmerActions.java [deleted file]
bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/action/DimmerActions.java [new file with mode: 0644]
bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/handler/DimmerHandler.java
bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/action/ISendMailActions.java [deleted file]
bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/action/SendMailActions.java [deleted file]
bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/SMTPHandler.java
bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/action/SendMailActions.java [new file with mode: 0644]
bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/actions/MaxCubeActions.java [deleted file]
bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/actions/MaxDevicesActions.java [deleted file]
bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/actions/IMaxCubeActions.java [deleted file]
bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/actions/IMaxDevicesActions.java [deleted file]
bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/actions/MaxCubeActions.java [new file with mode: 0644]
bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/actions/MaxDevicesActions.java [new file with mode: 0644]
bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/handler/MaxCubeBridgeHandler.java
bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/handler/MaxDevicesHandler.java
bundles/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/internal/action/IMPDActions.java [deleted file]
bundles/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/internal/action/MPDActions.java
bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/action/IMQTTActions.java [deleted file]
bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/action/MQTTActions.java [deleted file]
bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/handler/AbstractBrokerHandler.java
bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/internal/action/MQTTActions.java [new file with mode: 0644]
bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/action/INetworkActions.java [deleted file]
bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/action/NetworkActions.java
bundles/org.openhab.binding.nuvo/src/main/java/org/openhab/binding/nuvo/internal/INuvoThingActions.java [deleted file]
bundles/org.openhab.binding.nuvo/src/main/java/org/openhab/binding/nuvo/internal/NuvoThingActions.java
bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/automation/modules/OnkyoThingActions.java
bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/automation/modules/OnkyoThingActionsService.java [deleted file]
bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/handler/OnkyoHandler.java
bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/action/IPushbulletActions.java [deleted file]
bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/action/PushbulletActions.java
bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/IRadioThermostatThingActions.java [deleted file]
bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/RadioThermostatThingActions.java
bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/action/ISatelEventLogActions.java [deleted file]
bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/action/SatelEventLogActions.java [deleted file]
bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/action/SatelEventLogActions.java [new file with mode: 0644]
bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/handler/SatelEventLogHandler.java
bundles/org.openhab.binding.telegram/src/main/java/org/openhab/binding/telegram/bot/ITelegramActions.java [deleted file]
bundles/org.openhab.binding.telegram/src/main/java/org/openhab/binding/telegram/bot/TelegramActions.java [deleted file]
bundles/org.openhab.binding.telegram/src/main/java/org/openhab/binding/telegram/internal/TelegramHandler.java
bundles/org.openhab.binding.telegram/src/main/java/org/openhab/binding/telegram/internal/action/TelegramActions.java [new file with mode: 0644]
bundles/org.openhab.binding.volvooncall/src/main/java/org/openhab/binding/volvooncall/internal/action/VolvoOnCallActions.java
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/XMPPClientBindingConstants.java [deleted file]
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/action/IXMPPActions.java [deleted file]
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/action/XMPPActions.java [deleted file]
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/PublishTriggerChannel.java [deleted file]
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/PublishTriggerChannelConfig.java [deleted file]
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/XMPPClientConfiguration.java [deleted file]
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/XMPPClientHandler.java [deleted file]
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/XMPPClientMessageSubscriber.java [deleted file]
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClient.java
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClientBindingConstants.java [new file with mode: 0644]
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClientHandlerFactory.java
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/action/XMPPActions.java [new file with mode: 0644]
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/handler/PublishTriggerChannel.java [new file with mode: 0644]
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/handler/PublishTriggerChannelConfig.java [new file with mode: 0644]
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/handler/XMPPClientConfiguration.java [new file with mode: 0644]
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/handler/XMPPClientHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/handler/XMPPClientMessageSubscriber.java [new file with mode: 0644]
bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/action/IZmActions.java [deleted file]
bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/action/ZmActions.java [deleted file]
bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/action/ZmActions.java [new file with mode: 0644]
bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZmMonitorHandler.java

index bbcd10401726cf13e96eb61eccc7008d29e46e17..658f9781793cd2106b80b6cbe53811ecdd918126 100644 (file)
@@ -12,9 +12,6 @@
  */
 package org.openhab.binding.alarmdecoder.internal.actions;
 
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.alarmdecoder.internal.handler.ADBridgeHandler;
@@ -33,7 +30,7 @@ import org.slf4j.LoggerFactory;
  */
 @ThingActionsScope(name = "alarmdecoder")
 @NonNullByDefault
-public class BridgeActions implements ThingActions, IBridgeActions {
+public class BridgeActions implements ThingActions {
 
     private final Logger logger = LoggerFactory.getLogger(BridgeActions.class);
 
@@ -58,8 +55,7 @@ public class BridgeActions implements ThingActions, IBridgeActions {
     /**
      * Reboot thing action
      */
-    @Override
-    @RuleAction(label = "Reboot", description = "Reboot the Alarm Decoder device")
+    @RuleAction(label = "reboot the device", description = "Reboot the Alarm Decoder device.")
     public void reboot() {
         ADBridgeHandler bridge = this.bridge;
         if (bridge != null) {
@@ -72,34 +68,10 @@ public class BridgeActions implements ThingActions, IBridgeActions {
 
     // Static method for Rules DSL backward compatibility
     public static void reboot(@Nullable ThingActions actions) {
-        // if (actions instanceof BridgeActions) {
-        // ((BridgeActions) actions).reboot();
-        // } else {
-        // throw new IllegalArgumentException("Instance is not a BridgeActions class.");
-        // }
-        invokeMethodOf(actions).reboot(); // Remove and uncomment above when core issue #1536 is fixed
-    }
-
-    /**
-     * This is only necessary to work around a bug in openhab-core (issue #1536). It should be removed once that is
-     * resolved.
-     */
-    private static IBridgeActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(BridgeActions.class.getName())) {
-            if (actions instanceof IBridgeActions) {
-                return (IBridgeActions) actions;
-            } else {
-                return (IBridgeActions) Proxy.newProxyInstance(IBridgeActions.class.getClassLoader(),
-                        new Class[] { IBridgeActions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
+        if (actions instanceof BridgeActions) {
+            ((BridgeActions) actions).reboot();
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of BridgeActions");
         }
-        throw new IllegalArgumentException("Actions is not an instance of BridgeActions");
     }
 }
diff --git a/bundles/org.openhab.binding.alarmdecoder/src/main/java/org/openhab/binding/alarmdecoder/internal/actions/IBridgeActions.java b/bundles/org.openhab.binding.alarmdecoder/src/main/java/org/openhab/binding/alarmdecoder/internal/actions/IBridgeActions.java
deleted file mode 100644 (file)
index 849ca7c..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.alarmdecoder.internal.actions;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * The {@link IBridgeActions} defines the interface for all thing actions supported by the bridges.
- * This is only necessary to work around a bug in openhab-core (issue #1536). It should be removed once that is
- * resolved.
- *
- * @author Bob Adair - Initial contribution
- *
- */
-@NonNullByDefault
-public interface IBridgeActions {
-
-    public void reboot();
-}
index 202135e92ef9af2d2875624e15beb2ca55297c1c..f8415c5e67f5bfee3eb682ff9266504220c704c2 100644 (file)
@@ -12,8 +12,6 @@
  */
 package org.openhab.binding.astro.internal.action;
 
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
 import java.time.ZonedDateTime;
 
 import javax.measure.quantity.Angle;
@@ -35,18 +33,13 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * The {AstroActions } defines rule actions for the Astro binding.
- * <p>
- * <b>Note:</b>The static method <b>invokeMethodOf</b> handles the case where
- * the test <i>actions instanceof AstroActions</i> fails. This test can fail
- * due to an issue in openHAB core v2.5.0 where the {@link AstroActions} class
- * can be loaded by a different classloader than the <i>actions</i> instance.
+ * Defines the automation thing actions for the Astro binding.
  *
  * @author Gaël L'hopital - Initial contribution
  */
 @ThingActionsScope(name = "astro")
 @NonNullByDefault
-public class AstroActions implements ThingActions, IAstroActions {
+public class AstroActions implements ThingActions {
 
     private final Logger logger = LoggerFactory.getLogger(AstroActions.class);
     protected @Nullable AstroThingHandler handler;
@@ -64,11 +57,10 @@ public class AstroActions implements ThingActions, IAstroActions {
 
     @Override
     public @Nullable ThingHandler getThingHandler() {
-        return this.handler;
+        return handler;
     }
 
-    @Override
-    @RuleAction(label = "Astro : Get Azimuth", description = "Get the azimuth of the sun for a given time")
+    @RuleAction(label = "get the azimuth", description = "Get the azimuth for a given time.")
     public @Nullable @ActionOutput(name = "getAzimuth", label = "Azimuth", type = "org.openhab.core.library.types.QuantityType<javax.measure.quantity.Angle>") QuantityType<Angle> getAzimuth(
             @ActionInput(name = "date", label = "Date", required = false, description = "Considered date") @Nullable ZonedDateTime date) {
         logger.debug("Astro action 'getAzimuth' called");
@@ -81,8 +73,7 @@ public class AstroActions implements ThingActions, IAstroActions {
         return null;
     }
 
-    @Override
-    @RuleAction(label = "Astro : Get Elevation", description = "Get the Elevation of the sun for a given time")
+    @RuleAction(label = "get the elevation", description = "Get the elevation for a given time.")
     public @Nullable @ActionOutput(name = "getElevation", label = "Elevation", type = "org.openhab.core.library.types.QuantityType<javax.measure.quantity.Angle>") QuantityType<Angle> getElevation(
             @ActionInput(name = "date", label = "Date", required = false, description = "Considered date") @Nullable ZonedDateTime date) {
         logger.debug("Astro action 'getElevation' called");
@@ -95,8 +86,7 @@ public class AstroActions implements ThingActions, IAstroActions {
         return null;
     }
 
-    @Override
-    @RuleAction(label = "Sun : Get Event Time", description = "Get the date time of a given planet event")
+    @RuleAction(label = "get the date time of a sun event", description = "Get the date time of a sun event.")
     public @Nullable @ActionOutput(name = "getEventTime", type = "java.time.ZonedDateTime") ZonedDateTime getEventTime(
             @ActionInput(name = "phaseName", label = "Phase", required = true, description = "Requested phase") String phaseName,
             @ActionInput(name = "date", label = "Date", required = false, description = "Considered date") @Nullable ZonedDateTime date,
@@ -124,39 +114,32 @@ public class AstroActions implements ThingActions, IAstroActions {
 
     public static @Nullable QuantityType<Angle> getElevation(@Nullable ThingActions actions,
             @Nullable ZonedDateTime date) {
-        return invokeMethodOf(actions).getElevation(date);
+        if (actions instanceof AstroActions) {
+            return ((AstroActions) actions).getElevation(date);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of AstroActions");
+        }
     }
 
     public static @Nullable QuantityType<Angle> getAzimuth(@Nullable ThingActions actions,
             @Nullable ZonedDateTime date) {
-        return invokeMethodOf(actions).getAzimuth(date);
-    }
-
-    public static @Nullable ZonedDateTime getEventTime(@Nullable ThingActions actions, @Nullable String phaseName,
-            @Nullable ZonedDateTime date, @Nullable String moment) {
-        if (phaseName != null) {
-            return invokeMethodOf(actions).getEventTime(phaseName, date, moment);
+        if (actions instanceof AstroActions) {
+            return ((AstroActions) actions).getAzimuth(date);
         } else {
-            throw new IllegalArgumentException("phaseName can not be null");
+            throw new IllegalArgumentException("Actions is not an instance of AstroActions");
         }
     }
 
-    private static IAstroActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(AstroActions.class.getName())) {
-            if (actions instanceof IAstroActions) {
-                return (IAstroActions) actions;
+    public static @Nullable ZonedDateTime getEventTime(@Nullable ThingActions actions, @Nullable String phaseName,
+            @Nullable ZonedDateTime date, @Nullable String moment) {
+        if (actions instanceof AstroActions) {
+            if (phaseName != null) {
+                return ((AstroActions) actions).getEventTime(phaseName, date, moment);
             } else {
-                return (IAstroActions) Proxy.newProxyInstance(IAstroActions.class.getClassLoader(),
-                        new Class[] { IAstroActions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
+                throw new IllegalArgumentException("phaseName can not be null");
             }
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of AstroActions");
         }
-        throw new IllegalArgumentException("Actions is not an instance of AstroActions");
     }
 }
diff --git a/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/action/IAstroActions.java b/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/action/IAstroActions.java
deleted file mode 100644 (file)
index c0feeff..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.astro.internal.action;
-
-import java.time.ZonedDateTime;
-
-import javax.measure.quantity.Angle;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.core.library.types.QuantityType;
-
-/**
- * The {@link IAstroActions} defines the interface for all thing actions supported by the binding.
- *
- * @author Gaël L'hopital - Initial contribution
- */
-@NonNullByDefault
-public interface IAstroActions {
-    public @Nullable ZonedDateTime getEventTime(String phaseName, @Nullable ZonedDateTime date,
-            @Nullable String moment);
-
-    public @Nullable QuantityType<Angle> getAzimuth(@Nullable ZonedDateTime date);
-
-    public @Nullable QuantityType<Angle> getElevation(@Nullable ZonedDateTime date);
-}
index e63f453ce09ee016ae6d6dd2e9d3861cf16f86f8..1086a0bfcaf1ef0c0e96afbbf1c3a1d190565ea3 100644 (file)
@@ -12,9 +12,6 @@
  */
 package org.openhab.binding.automower.internal.actions;
 
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.automower.internal.things.AutomowerCommand;
@@ -32,7 +29,7 @@ import org.slf4j.LoggerFactory;
  */
 @ThingActionsScope(name = "automower")
 @NonNullByDefault
-public class AutomowerActions implements ThingActions, IAutomowerActions {
+public class AutomowerActions implements ThingActions {
     private final Logger logger = LoggerFactory.getLogger(AutomowerActions.class);
     private @Nullable AutomowerHandler handler;
 
@@ -46,7 +43,6 @@ public class AutomowerActions implements ThingActions, IAutomowerActions {
         return handler;
     }
 
-    @Override
     @RuleAction(label = "@text/action-start-label", description = "@text/action-start-desc")
     public void start(
             @ActionInput(name = "duration", label = "@text/action-input-duration-label", description = "@text/action-input-duration-desc") int durationMin) {
@@ -59,10 +55,13 @@ public class AutomowerActions implements ThingActions, IAutomowerActions {
     }
 
     public static void start(@Nullable ThingActions actions, int durationMin) {
-        invokeMethodOf(actions).start(durationMin);
+        if (actions instanceof AutomowerActions) {
+            ((AutomowerActions) actions).start(durationMin);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of AutomowerActions");
+        }
     }
 
-    @Override
     @RuleAction(label = "@text/action-pause-label", description = "@text/action-pause-desc")
     public void pause() {
         AutomowerHandler automowerHandler = handler;
@@ -74,10 +73,13 @@ public class AutomowerActions implements ThingActions, IAutomowerActions {
     }
 
     public static void pause(@Nullable ThingActions actions) {
-        invokeMethodOf(actions).pause();
+        if (actions instanceof AutomowerActions) {
+            ((AutomowerActions) actions).pause();
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of AutomowerActions");
+        }
     }
 
-    @Override
     @RuleAction(label = "@text/action-parkuntilnextschedule-label", description = "@text/action-parkuntilnextschedule-desc")
     public void parkUntilNextSchedule() {
         AutomowerHandler automowerHandler = handler;
@@ -89,10 +91,13 @@ public class AutomowerActions implements ThingActions, IAutomowerActions {
     }
 
     public static void parkUntilNextSchedule(@Nullable ThingActions actions) {
-        invokeMethodOf(actions).parkUntilNextSchedule();
+        if (actions instanceof AutomowerActions) {
+            ((AutomowerActions) actions).parkUntilNextSchedule();
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of AutomowerActions");
+        }
     }
 
-    @Override
     @RuleAction(label = "@text/action-parkuntilfurthernotice-label", description = "@text/action-parkuntilfurthernotice-desc")
     public void parkUntilFurtherNotice() {
         AutomowerHandler automowerHandler = handler;
@@ -104,10 +109,13 @@ public class AutomowerActions implements ThingActions, IAutomowerActions {
     }
 
     public static void parkUntilFurtherNotice(@Nullable ThingActions actions) {
-        invokeMethodOf(actions).parkUntilFurtherNotice();
+        if (actions instanceof AutomowerActions) {
+            ((AutomowerActions) actions).parkUntilFurtherNotice();
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of AutomowerActions");
+        }
     }
 
-    @Override
     @RuleAction(label = "@text/action-park-label", description = "@text/action-park-desc")
     public void park(
             @ActionInput(name = "duration", label = "@text/action-input-duration-label", description = "@text/action-input-duration-desc") int durationMin) {
@@ -120,10 +128,13 @@ public class AutomowerActions implements ThingActions, IAutomowerActions {
     }
 
     public static void park(@Nullable ThingActions actions, int durationMin) {
-        invokeMethodOf(actions).park(durationMin);
+        if (actions instanceof AutomowerActions) {
+            ((AutomowerActions) actions).park(durationMin);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of AutomowerActions");
+        }
     }
 
-    @Override
     @RuleAction(label = "@text/action-resumeschedule-label", description = "@text/action-resumeschedule-desc")
     public void resumeSchedule() {
         AutomowerHandler automowerHandler = handler;
@@ -135,25 +146,10 @@ public class AutomowerActions implements ThingActions, IAutomowerActions {
     }
 
     public static void resumeSchedule(@Nullable ThingActions actions) {
-        invokeMethodOf(actions).resumeSchedule();
-    }
-
-    private static IAutomowerActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(AutomowerActions.class.getName())) {
-            if (actions instanceof AutomowerActions) {
-                return (IAutomowerActions) actions;
-            } else {
-                return (IAutomowerActions) Proxy.newProxyInstance(IAutomowerActions.class.getClassLoader(),
-                        new Class[] { IAutomowerActions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
+        if (actions instanceof AutomowerActions) {
+            ((AutomowerActions) actions).resumeSchedule();
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of AutomowerActions");
         }
-        throw new IllegalArgumentException("Actions is not an instance of IAutomowerActions");
     }
 }
diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/IAutomowerActions.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/IAutomowerActions.java
deleted file mode 100644 (file)
index 553401b..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.automower.internal.actions;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * Actions that can be executed for an automower
- *
- * @author Markus Pfleger - Initial contribution
- */
-@NonNullByDefault
-public interface IAutomowerActions {
-
-    void resumeSchedule();
-
-    void park(int durationMin);
-
-    void parkUntilFurtherNotice();
-
-    void parkUntilNextSchedule();
-
-    void pause();
-
-    void start(int durationMin);
-}
diff --git a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/actions/AVMFritzHeatingActions.java b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/actions/AVMFritzHeatingActions.java
deleted file mode 100644 (file)
index 662beef..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.avmfritz.actions;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.avmfritz.internal.actions.IAVMFritzHeatingActions;
-import org.openhab.binding.avmfritz.internal.handler.AVMFritzHeatingActionsHandler;
-import org.openhab.core.automation.annotation.ActionInput;
-import org.openhab.core.automation.annotation.RuleAction;
-import org.openhab.core.thing.binding.ThingActions;
-import org.openhab.core.thing.binding.ThingActionsScope;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link AVMFritzHeatingActions} defines thing actions for heating devices / groups of the avmfritz binding.
- *
- * @author Christoph Weitkamp - Initial contribution
- */
-@ThingActionsScope(name = "avmfritz")
-@NonNullByDefault
-public class AVMFritzHeatingActions implements ThingActions, IAVMFritzHeatingActions {
-
-    private final Logger logger = LoggerFactory.getLogger(AVMFritzHeatingActions.class);
-
-    private @Nullable AVMFritzHeatingActionsHandler handler;
-
-    @Override
-    public void setThingHandler(@Nullable ThingHandler handler) {
-        this.handler = (AVMFritzHeatingActionsHandler) handler;
-    }
-
-    @Override
-    public @Nullable ThingHandler getThingHandler() {
-        return handler;
-    }
-
-    @Override
-    @RuleAction(label = "@text/setBoostModeModeActionLabel", description = "@text/setBoostModeActionDescription")
-    public void setBoostMode(
-            @ActionInput(name = "Duration", label = "@text/setBoostModeDurationInputLabel", description = "@text/setBoostModeDurationInputDescription", type = "java.lang.Long", required = true) @Nullable Long duration) {
-        AVMFritzHeatingActionsHandler actionsHandler = handler;
-        if (actionsHandler == null) {
-            throw new IllegalArgumentException("AVMFritzHeatingActions ThingHandler is null!");
-        }
-        if (duration == null) {
-            throw new IllegalArgumentException("Cannot set Boost mode as 'duration' is null!");
-        }
-        actionsHandler.setBoostMode(duration.longValue());
-    }
-
-    public static void setBoostMode(@Nullable ThingActions actions, @Nullable Long duration) {
-        invokeMethodOf(actions).setBoostMode(duration);
-    }
-
-    @Override
-    @RuleAction(label = "@text/setWindowOpenModeActionLabel", description = "@text/setWindowOpenModeActionDescription")
-    public void setWindowOpenMode(
-            @ActionInput(name = "Duration", label = "@text/setWindowOpenModeDurationInputLabel", description = "@text/setWindowOpenModeDurationInputDescription", type = "java.lang.Long", required = true) @Nullable Long duration) {
-        AVMFritzHeatingActionsHandler actionsHandler = handler;
-        if (actionsHandler == null) {
-            throw new IllegalArgumentException("AVMFritzHeatingActions ThingHandler is null!");
-        }
-        if (duration == null) {
-            throw new IllegalArgumentException("Cannot set Window Open mode as 'duration' is null!");
-        }
-        actionsHandler.setWindowOpenMode(duration.longValue());
-    }
-
-    public static void setWindowOpenMode(@Nullable ThingActions actions, @Nullable Long duration) {
-        invokeMethodOf(actions).setWindowOpenMode(duration);
-    }
-
-    private static IAVMFritzHeatingActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(AVMFritzHeatingActions.class.getName())) {
-            if (actions instanceof IAVMFritzHeatingActions) {
-                return (IAVMFritzHeatingActions) actions;
-            } else {
-                return (IAVMFritzHeatingActions) Proxy.newProxyInstance(IAVMFritzHeatingActions.class.getClassLoader(),
-                        new Class[] { IAVMFritzHeatingActions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
-        }
-        throw new IllegalArgumentException("Actions is not an instance of AVMFritzHeatingActions");
-    }
-}
diff --git a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/actions/AVMFritzHeatingActions.java b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/actions/AVMFritzHeatingActions.java
new file mode 100644 (file)
index 0000000..ef5530b
--- /dev/null
@@ -0,0 +1,86 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.avmfritz.internal.actions;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.avmfritz.internal.handler.AVMFritzHeatingActionsHandler;
+import org.openhab.core.automation.annotation.ActionInput;
+import org.openhab.core.automation.annotation.RuleAction;
+import org.openhab.core.thing.binding.ThingActions;
+import org.openhab.core.thing.binding.ThingActionsScope;
+import org.openhab.core.thing.binding.ThingHandler;
+
+/**
+ * The {@link AVMFritzHeatingActions} defines thing actions for heating devices / groups of the avmfritz binding.
+ *
+ * @author Christoph Weitkamp - Initial contribution
+ */
+@ThingActionsScope(name = "avmfritz")
+@NonNullByDefault
+public class AVMFritzHeatingActions implements ThingActions {
+
+    private @Nullable AVMFritzHeatingActionsHandler handler;
+
+    @Override
+    public void setThingHandler(@Nullable ThingHandler handler) {
+        this.handler = (AVMFritzHeatingActionsHandler) handler;
+    }
+
+    @Override
+    public @Nullable ThingHandler getThingHandler() {
+        return handler;
+    }
+
+    @RuleAction(label = "@text/setBoostModeModeActionLabel", description = "@text/setBoostModeActionDescription")
+    public void setBoostMode(
+            @ActionInput(name = "Duration", label = "@text/setBoostModeDurationInputLabel", description = "@text/setBoostModeDurationInputDescription", type = "java.lang.Long", required = true) @Nullable Long duration) {
+        AVMFritzHeatingActionsHandler actionsHandler = handler;
+        if (actionsHandler == null) {
+            throw new IllegalArgumentException("AVMFritzHeatingActions ThingHandler is null!");
+        }
+        if (duration == null) {
+            throw new IllegalArgumentException("Cannot set Boost mode as 'duration' is null!");
+        }
+        actionsHandler.setBoostMode(duration.longValue());
+    }
+
+    public static void setBoostMode(@Nullable ThingActions actions, @Nullable Long duration) {
+        if (actions instanceof AVMFritzHeatingActions) {
+            ((AVMFritzHeatingActions) actions).setBoostMode(duration);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of AVMFritzHeatingActions");
+        }
+    }
+
+    @RuleAction(label = "@text/setWindowOpenModeActionLabel", description = "@text/setWindowOpenModeActionDescription")
+    public void setWindowOpenMode(
+            @ActionInput(name = "Duration", label = "@text/setWindowOpenModeDurationInputLabel", description = "@text/setWindowOpenModeDurationInputDescription", type = "java.lang.Long", required = true) @Nullable Long duration) {
+        AVMFritzHeatingActionsHandler actionsHandler = handler;
+        if (actionsHandler == null) {
+            throw new IllegalArgumentException("AVMFritzHeatingActions ThingHandler is null!");
+        }
+        if (duration == null) {
+            throw new IllegalArgumentException("Cannot set Window Open mode as 'duration' is null!");
+        }
+        actionsHandler.setWindowOpenMode(duration.longValue());
+    }
+
+    public static void setWindowOpenMode(@Nullable ThingActions actions, @Nullable Long duration) {
+        if (actions instanceof AVMFritzHeatingActions) {
+            ((AVMFritzHeatingActions) actions).setWindowOpenMode(duration);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of AVMFritzHeatingActions");
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/actions/IAVMFritzHeatingActions.java b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/actions/IAVMFritzHeatingActions.java
deleted file mode 100644 (file)
index 0c30df9..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.avmfritz.internal.actions;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.avmfritz.actions.AVMFritzHeatingActions;
-
-/**
- * The {@link IAVMFritzHeatingActions} defines the interface for all thing actions supported by the binding.
- * These methods, parameters, and return types are explained in {@link AVMFritzHeatingActions}.
- *
- * @author Christoph Weitkamp - Initial contribution
- */
-@NonNullByDefault
-public interface IAVMFritzHeatingActions {
-
-    void setBoostMode(@Nullable Long duration);
-
-    void setWindowOpenMode(@Nullable Long duration);
-}
index 8be206f50c9d169f18af0e50161c6cc1f0fbef36..7f1cf572e1d1f07f1f33b837fb020f322b9a22c6 100644 (file)
@@ -16,7 +16,7 @@ import java.util.Collection;
 import java.util.Collections;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.avmfritz.actions.AVMFritzHeatingActions;
+import org.openhab.binding.avmfritz.internal.actions.AVMFritzHeatingActions;
 import org.openhab.core.thing.binding.ThingHandler;
 import org.openhab.core.thing.binding.ThingHandlerService;
 
diff --git a/bundles/org.openhab.binding.avmfritz/src/test/java/org/openhab/binding/avmfritz/actions/AVMFritzHeatingActionsTest.java b/bundles/org.openhab.binding.avmfritz/src/test/java/org/openhab/binding/avmfritz/actions/AVMFritzHeatingActionsTest.java
deleted file mode 100644 (file)
index a18edcc..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.avmfritz.actions;
-
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.openhab.binding.avmfritz.internal.handler.AVMFritzHeatingActionsHandler;
-import org.openhab.core.thing.binding.ThingActions;
-import org.openhab.core.thing.binding.ThingHandler;
-
-/**
- * Unit tests for {@link AVMFritzHeatingActions}.
- *
- * @author Christoph Weitkamp - Initial contribution
- */
-@ExtendWith(MockitoExtension.class)
-public class AVMFritzHeatingActionsTest {
-
-    private final ThingActions thingActionsStub = new ThingActions() {
-        @Override
-        public void setThingHandler(ThingHandler handler) {
-        }
-
-        @Override
-        public ThingHandler getThingHandler() {
-            return null;
-        }
-    };
-
-    private @Mock AVMFritzHeatingActionsHandler heatingActionsHandler;
-
-    private AVMFritzHeatingActions heatingActions;
-
-    @BeforeEach
-    public void setUp() {
-        heatingActions = new AVMFritzHeatingActions();
-    }
-
-    @Test
-    public void testSetBoostModeThingActionsIsNull() {
-        assertThrows(IllegalArgumentException.class, () -> AVMFritzHeatingActions.setBoostMode(null, Long.valueOf(5L)));
-    }
-
-    @Test
-    public void testSetBoostModeThingActionsIsNotPushoverThingActions() {
-        assertThrows(IllegalArgumentException.class,
-                () -> AVMFritzHeatingActions.setBoostMode(thingActionsStub, Long.valueOf(5L)));
-    }
-
-    @Test
-    public void testSetBoostModeThingHandlerIsNull() {
-        assertThrows(IllegalArgumentException.class,
-                () -> AVMFritzHeatingActions.setBoostMode(heatingActions, Long.valueOf(5L)));
-    }
-
-    @Test
-    public void testSetBoostModeDurationNull() {
-        heatingActions.setThingHandler(heatingActionsHandler);
-        assertThrows(IllegalArgumentException.class, () -> AVMFritzHeatingActions.setBoostMode(heatingActions, null));
-    }
-
-    @Test
-    public void testSetBoostMode() {
-        heatingActions.setThingHandler(heatingActionsHandler);
-        AVMFritzHeatingActions.setBoostMode(heatingActions, Long.valueOf(5L));
-    }
-
-    @Test
-    public void testSetWindowOpenModeThingActionsIsNull() {
-        assertThrows(IllegalArgumentException.class,
-                () -> AVMFritzHeatingActions.setWindowOpenMode(null, Long.valueOf(5L)));
-    }
-
-    @Test
-    public void testSetWindowOpenModeThingActionsIsNotPushoverThingActions() {
-        assertThrows(IllegalArgumentException.class,
-                () -> AVMFritzHeatingActions.setWindowOpenMode(thingActionsStub, Long.valueOf(5L)));
-    }
-
-    @Test
-    public void testSetWindowOpenModeThingHandlerIsNull() {
-        assertThrows(IllegalArgumentException.class,
-                () -> AVMFritzHeatingActions.setWindowOpenMode(heatingActions, Long.valueOf(5L)));
-    }
-
-    @Test
-    public void testSetWindowOpenModeDurationNull() {
-        heatingActions.setThingHandler(heatingActionsHandler);
-        assertThrows(IllegalArgumentException.class,
-                () -> AVMFritzHeatingActions.setWindowOpenMode(heatingActions, null));
-    }
-
-    @Test
-    public void testSetWindowOpenMode() {
-        heatingActions.setThingHandler(heatingActionsHandler);
-        AVMFritzHeatingActions.setWindowOpenMode(heatingActions, Long.valueOf(5L));
-    }
-}
diff --git a/bundles/org.openhab.binding.avmfritz/src/test/java/org/openhab/binding/avmfritz/internal/actions/AVMFritzHeatingActionsTest.java b/bundles/org.openhab.binding.avmfritz/src/test/java/org/openhab/binding/avmfritz/internal/actions/AVMFritzHeatingActionsTest.java
new file mode 100644 (file)
index 0000000..f0f5865
--- /dev/null
@@ -0,0 +1,113 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.avmfritz.internal.actions;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.openhab.binding.avmfritz.internal.handler.AVMFritzHeatingActionsHandler;
+import org.openhab.core.thing.binding.ThingActions;
+import org.openhab.core.thing.binding.ThingHandler;
+
+/**
+ * Unit tests for {@link AVMFritzHeatingActions}.
+ *
+ * @author Christoph Weitkamp - Initial contribution
+ */
+@ExtendWith(MockitoExtension.class)
+public class AVMFritzHeatingActionsTest {
+
+    private final ThingActions thingActionsStub = new ThingActions() {
+        @Override
+        public void setThingHandler(ThingHandler handler) {
+        }
+
+        @Override
+        public ThingHandler getThingHandler() {
+            return null;
+        }
+    };
+
+    private @Mock AVMFritzHeatingActionsHandler heatingActionsHandler;
+
+    private AVMFritzHeatingActions heatingActions;
+
+    @BeforeEach
+    public void setUp() {
+        heatingActions = new AVMFritzHeatingActions();
+    }
+
+    @Test
+    public void testSetBoostModeThingActionsIsNull() {
+        assertThrows(IllegalArgumentException.class, () -> AVMFritzHeatingActions.setBoostMode(null, Long.valueOf(5L)));
+    }
+
+    @Test
+    public void testSetBoostModeThingActionsIsNotPushoverThingActions() {
+        assertThrows(IllegalArgumentException.class,
+                () -> AVMFritzHeatingActions.setBoostMode(thingActionsStub, Long.valueOf(5L)));
+    }
+
+    @Test
+    public void testSetBoostModeThingHandlerIsNull() {
+        assertThrows(IllegalArgumentException.class,
+                () -> AVMFritzHeatingActions.setBoostMode(heatingActions, Long.valueOf(5L)));
+    }
+
+    @Test
+    public void testSetBoostModeDurationNull() {
+        heatingActions.setThingHandler(heatingActionsHandler);
+        assertThrows(IllegalArgumentException.class, () -> AVMFritzHeatingActions.setBoostMode(heatingActions, null));
+    }
+
+    @Test
+    public void testSetBoostMode() {
+        heatingActions.setThingHandler(heatingActionsHandler);
+        AVMFritzHeatingActions.setBoostMode(heatingActions, Long.valueOf(5L));
+    }
+
+    @Test
+    public void testSetWindowOpenModeThingActionsIsNull() {
+        assertThrows(IllegalArgumentException.class,
+                () -> AVMFritzHeatingActions.setWindowOpenMode(null, Long.valueOf(5L)));
+    }
+
+    @Test
+    public void testSetWindowOpenModeThingActionsIsNotPushoverThingActions() {
+        assertThrows(IllegalArgumentException.class,
+                () -> AVMFritzHeatingActions.setWindowOpenMode(thingActionsStub, Long.valueOf(5L)));
+    }
+
+    @Test
+    public void testSetWindowOpenModeThingHandlerIsNull() {
+        assertThrows(IllegalArgumentException.class,
+                () -> AVMFritzHeatingActions.setWindowOpenMode(heatingActions, Long.valueOf(5L)));
+    }
+
+    @Test
+    public void testSetWindowOpenModeDurationNull() {
+        heatingActions.setThingHandler(heatingActionsHandler);
+        assertThrows(IllegalArgumentException.class,
+                () -> AVMFritzHeatingActions.setWindowOpenMode(heatingActions, null));
+    }
+
+    @Test
+    public void testSetWindowOpenMode() {
+        heatingActions.setThingHandler(heatingActionsHandler);
+        AVMFritzHeatingActions.setWindowOpenMode(heatingActions, Long.valueOf(5L));
+    }
+}
diff --git a/bundles/org.openhab.binding.dmx/src/main/java/org/openhab/binding/dmx/action/DmxActions.java b/bundles/org.openhab.binding.dmx/src/main/java/org/openhab/binding/dmx/action/DmxActions.java
deleted file mode 100644 (file)
index cbbf729..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.dmx.action;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.dmx.internal.DmxBridgeHandler;
-import org.openhab.core.automation.annotation.ActionInput;
-import org.openhab.core.automation.annotation.RuleAction;
-import org.openhab.core.thing.binding.ThingActions;
-import org.openhab.core.thing.binding.ThingActionsScope;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link DmxActions} provides actions for DMX Bridges
- * <p>
- * <b>Note:</b>The static method <b>invokeMethodOf</b> handles the case where
- * the test <i>actions instanceof DmxActions</i> fails. This test can fail
- * due to an issue in openHAB core v2.5.0 where the {@link DmxActions} class
- * can be loaded by a different classloader than the <i>actions</i> instance.
- *
- * @author Jan N. Klug - Initial contribution
- */
-
-@ThingActionsScope(name = "dmx")
-@NonNullByDefault
-public class DmxActions implements ThingActions, IDmxActions {
-
-    private final Logger logger = LoggerFactory.getLogger(DmxActions.class);
-
-    private @Nullable DmxBridgeHandler handler;
-
-    @Override
-    @RuleAction(label = "DMX Output", description = "immediately performs fade on selected DMX channels")
-    public void sendFade(@ActionInput(name = "channels") @Nullable String channels,
-            @ActionInput(name = "fade") @Nullable String fade,
-            @ActionInput(name = "resumeAfter") @Nullable Boolean resumeAfter) {
-        logger.debug("thingHandlerAction called with inputs: {} {} {}", channels, fade, resumeAfter);
-
-        if (handler == null) {
-            logger.warn("DMX Action service ThingHandler is null!");
-            return;
-        }
-
-        if (channels == null) {
-            logger.debug("skipping immediate DMX action {} due to missing channel(s)", fade);
-            return;
-        }
-
-        if (fade == null) {
-            logger.debug("skipping immediate DMX action channel(s) {} due to missing fade", channels);
-            return;
-        }
-
-        if (resumeAfter == null) {
-            logger.debug("DMX action {} to channel(s) {} with default resumeAfter=false", fade, channels);
-            handler.immediateFade(channels, fade, false);
-        } else {
-            handler.immediateFade(channels, fade, resumeAfter);
-        }
-    }
-
-    public static void sendFade(@Nullable ThingActions actions, @Nullable String channels, @Nullable String fade,
-            @Nullable Boolean resumeAfter) {
-        invokeMethodOf(actions).sendFade(channels, fade, resumeAfter);
-    }
-
-    private static IDmxActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(DmxActions.class.getName())) {
-            if (actions instanceof IDmxActions) {
-                return (IDmxActions) actions;
-            } else {
-                return (IDmxActions) Proxy.newProxyInstance(IDmxActions.class.getClassLoader(),
-                        new Class[] { IDmxActions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
-        }
-        throw new IllegalArgumentException("Actions is not an instance of DmxActions");
-    }
-
-    @Override
-    public void setThingHandler(@Nullable ThingHandler handler) {
-        if (handler instanceof DmxBridgeHandler) {
-            this.handler = (DmxBridgeHandler) handler;
-        }
-    }
-
-    @Override
-    public @Nullable ThingHandler getThingHandler() {
-        return this.handler;
-    }
-}
diff --git a/bundles/org.openhab.binding.dmx/src/main/java/org/openhab/binding/dmx/action/IDmxActions.java b/bundles/org.openhab.binding.dmx/src/main/java/org/openhab/binding/dmx/action/IDmxActions.java
deleted file mode 100644 (file)
index 9444044..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.dmx.action;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * The {@link IDmxActions} defines the actions for DMX Bridges
- *
- * @author Laurent Garnier - Initial contribution
- */
-@NonNullByDefault
-public interface IDmxActions {
-
-    public void sendFade(@Nullable String channels, @Nullable String fade, @Nullable Boolean resumeAfter);
-}
index 59a74d60abe70fc333882ca551a6fd32a0c44d36..bf06dd2ac5ad18d6874524d595a9b09d660e4433 100644 (file)
@@ -21,7 +21,7 @@ import java.util.List;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 
-import org.openhab.binding.dmx.action.DmxActions;
+import org.openhab.binding.dmx.internal.action.DmxActions;
 import org.openhab.binding.dmx.internal.action.FadeAction;
 import org.openhab.binding.dmx.internal.action.ResumeAction;
 import org.openhab.binding.dmx.internal.config.DmxBridgeHandlerConfiguration;
diff --git a/bundles/org.openhab.binding.dmx/src/main/java/org/openhab/binding/dmx/internal/action/DmxActions.java b/bundles/org.openhab.binding.dmx/src/main/java/org/openhab/binding/dmx/internal/action/DmxActions.java
new file mode 100644 (file)
index 0000000..0e92e88
--- /dev/null
@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.dmx.internal.action;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.dmx.internal.DmxBridgeHandler;
+import org.openhab.core.automation.annotation.ActionInput;
+import org.openhab.core.automation.annotation.RuleAction;
+import org.openhab.core.thing.binding.ThingActions;
+import org.openhab.core.thing.binding.ThingActionsScope;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link DmxActions} provides actions for DMX Bridges
+ *
+ * @author Jan N. Klug - Initial contribution
+ */
+@ThingActionsScope(name = "dmx")
+@NonNullByDefault
+public class DmxActions implements ThingActions {
+
+    private final Logger logger = LoggerFactory.getLogger(DmxActions.class);
+
+    private @Nullable DmxBridgeHandler handler;
+
+    @RuleAction(label = "immediately fade channels", description = "Immediately performs fade on selected DMX channels.")
+    public void sendFade(@ActionInput(name = "channels") @Nullable String channels,
+            @ActionInput(name = "fade") @Nullable String fade,
+            @ActionInput(name = "resumeAfter") @Nullable Boolean resumeAfter) {
+        logger.debug("thingHandlerAction called with inputs: {} {} {}", channels, fade, resumeAfter);
+        DmxBridgeHandler handler = this.handler;
+
+        if (handler == null) {
+            logger.warn("DMX Action service ThingHandler is null!");
+            return;
+        }
+
+        if (channels == null) {
+            logger.debug("skipping immediate DMX action {} due to missing channel(s)", fade);
+            return;
+        }
+
+        if (fade == null) {
+            logger.debug("skipping immediate DMX action channel(s) {} due to missing fade", channels);
+            return;
+        }
+
+        if (resumeAfter == null) {
+            logger.debug("DMX action {} to channel(s) {} with default resumeAfter=false", fade, channels);
+            handler.immediateFade(channels, fade, false);
+        } else {
+            handler.immediateFade(channels, fade, resumeAfter);
+        }
+    }
+
+    public static void sendFade(@Nullable ThingActions actions, @Nullable String channels, @Nullable String fade,
+            @Nullable Boolean resumeAfter) {
+        if (actions instanceof DmxActions) {
+            ((DmxActions) actions).sendFade(channels, fade, resumeAfter);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of DmxActions");
+        }
+    }
+
+    @Override
+    public void setThingHandler(@Nullable ThingHandler handler) {
+        if (handler instanceof DmxBridgeHandler) {
+            this.handler = (DmxBridgeHandler) handler;
+        }
+    }
+
+    @Override
+    public @Nullable ThingHandler getThingHandler() {
+        return handler;
+    }
+}
diff --git a/bundles/org.openhab.binding.doorbird/src/main/java/org/openhab/binding/doorbird/action/DoorbirdActions.java b/bundles/org.openhab.binding.doorbird/src/main/java/org/openhab/binding/doorbird/action/DoorbirdActions.java
deleted file mode 100644 (file)
index d6095a8..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.doorbird.action;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.doorbird.internal.handler.DoorbellHandler;
-import org.openhab.core.automation.annotation.ActionOutput;
-import org.openhab.core.automation.annotation.RuleAction;
-import org.openhab.core.thing.binding.ThingActions;
-import org.openhab.core.thing.binding.ThingActionsScope;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link DoorbirdActions} defines rule actions for the doorbird binding.
- *
- * @author Mark Hilbush - Initial contribution
- */
-@ThingActionsScope(name = "doorbird")
-@NonNullByDefault
-public class DoorbirdActions implements ThingActions {
-    private static final Logger LOGGER = LoggerFactory.getLogger(DoorbirdActions.class);
-
-    private @Nullable DoorbellHandler handler;
-
-    public DoorbirdActions() {
-        LOGGER.debug("DoorbirdActions service created");
-    }
-
-    @Override
-    public void setThingHandler(@Nullable ThingHandler handler) {
-        if (handler instanceof DoorbellHandler) {
-            this.handler = (DoorbellHandler) handler;
-        }
-    }
-
-    @Override
-    public @Nullable ThingHandler getThingHandler() {
-        return this.handler;
-    }
-
-    private static IDoorbirdActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(DoorbirdActions.class.getName())) {
-            if (actions instanceof IDoorbirdActions) {
-                return (IDoorbirdActions) actions;
-            } else {
-                return (IDoorbirdActions) Proxy.newProxyInstance(IDoorbirdActions.class.getClassLoader(),
-                        new Class[] { IDoorbirdActions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
-        }
-        throw new IllegalArgumentException("actions is not an instance of DoorbirdActions");
-    }
-
-    @RuleAction(label = "Restart Doorbird", description = "Restarts the Doorbird device")
-    public void restart() {
-        LOGGER.debug("Doorbird action 'restart' called");
-        if (handler != null) {
-            handler.actionRestart();
-        } else {
-            LOGGER.info("Doorbird Action service ThingHandler is null!");
-        }
-    }
-
-    public static void restart(@Nullable ThingActions actions) {
-        invokeMethodOf(actions).restart();
-    }
-
-    @RuleAction(label = "SIP Hangup", description = "Hangup SIP call")
-    public void sipHangup() {
-        LOGGER.debug("Doorbird action 'sipHangup' called");
-        if (handler != null) {
-            handler.actionSIPHangup();
-        } else {
-            LOGGER.info("Doorbird Action service ThingHandler is null!");
-        }
-    }
-
-    public static void sipHangup(@Nullable ThingActions actions) {
-        invokeMethodOf(actions).sipHangup();
-    }
-
-    @RuleAction(label = "Get Ring Time Limit", description = "Get the value of RING_TIME_LIMIT")
-    public @ActionOutput(name = "getRingTimeLimit", type = "java.lang.String") String getRingTimeLimit() {
-        LOGGER.debug("Doorbird action 'getRingTimeLimit' called");
-        if (handler != null) {
-            return handler.actionGetRingTimeLimit();
-        } else {
-            LOGGER.info("Doorbird Action service ThingHandler is null!");
-            return "";
-        }
-    }
-
-    public static String getRingTimeLimit(@Nullable ThingActions actions) {
-        return invokeMethodOf(actions).getRingTimeLimit();
-    }
-
-    @RuleAction(label = "Get Call Time Limit", description = "Get the value of CALL_TIME_LIMIT")
-    public @ActionOutput(name = "getCallTimeLimit", type = "java.lang.String") String getCallTimeLimit() {
-        LOGGER.debug("Doorbird action 'getCallTimeLimit' called");
-        if (handler != null) {
-            return handler.actionGetCallTimeLimit();
-        } else {
-            LOGGER.info("Doorbird Action service ThingHandler is null!");
-            return "";
-        }
-    }
-
-    public static String getCallTimeLimit(@Nullable ThingActions actions) {
-        return invokeMethodOf(actions).getCallTimeLimit();
-    }
-
-    @RuleAction(label = "Get Last Error Code", description = "Get the value of LASTERRORCODE")
-    public @ActionOutput(name = "getLastErrorCode", type = "java.lang.String") String getLastErrorCode() {
-        LOGGER.debug("Doorbird action 'getLastErrorCode' called");
-        if (handler != null) {
-            return handler.actionGetLastErrorCode();
-        } else {
-            LOGGER.info("Doorbird Action service ThingHandler is null!");
-            return "";
-        }
-    }
-
-    public static String getLastErrorCode(@Nullable ThingActions actions) {
-        return invokeMethodOf(actions).getLastErrorCode();
-    }
-
-    @RuleAction(label = "Get Last Error Text", description = "Get the value of LASTERRORTEXT")
-    public @ActionOutput(name = "getLastErrorText", type = "java.lang.String") String getLastErrorText() {
-        LOGGER.debug("Doorbird action 'getLastErrorText' called");
-        if (handler != null) {
-            return handler.actionGetLastErrorText();
-        } else {
-            LOGGER.info("Doorbird Action service ThingHandler is null!");
-            return "";
-        }
-    }
-
-    public static String getLastErrorText(@Nullable ThingActions actions) {
-        return invokeMethodOf(actions).getLastErrorText();
-    }
-}
diff --git a/bundles/org.openhab.binding.doorbird/src/main/java/org/openhab/binding/doorbird/action/IDoorbirdActions.java b/bundles/org.openhab.binding.doorbird/src/main/java/org/openhab/binding/doorbird/action/IDoorbirdActions.java
deleted file mode 100644 (file)
index 9286046..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.doorbird.action;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * The {@link IDoorbirdActions} defines the interface for all thing actions supported by the binding.
- * These methods, parameters, and return types are explained in {@link DoorbirdActions}.
- *
- * @author Mark Hilbush - Initial contribution
- */
-@NonNullByDefault
-public interface IDoorbirdActions {
-
-    public void restart();
-
-    public void sipHangup();
-
-    public String getRingTimeLimit();
-
-    public String getCallTimeLimit();
-
-    public String getLastErrorCode();
-
-    public String getLastErrorText();
-}
diff --git a/bundles/org.openhab.binding.doorbird/src/main/java/org/openhab/binding/doorbird/internal/action/DoorbirdActions.java b/bundles/org.openhab.binding.doorbird/src/main/java/org/openhab/binding/doorbird/internal/action/DoorbirdActions.java
new file mode 100644 (file)
index 0000000..389a7bd
--- /dev/null
@@ -0,0 +1,165 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.doorbird.internal.action;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.doorbird.internal.handler.DoorbellHandler;
+import org.openhab.core.automation.annotation.ActionOutput;
+import org.openhab.core.automation.annotation.RuleAction;
+import org.openhab.core.thing.binding.ThingActions;
+import org.openhab.core.thing.binding.ThingActionsScope;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link DoorbirdActions} defines rule actions for the doorbird binding.
+ *
+ * @author Mark Hilbush - Initial contribution
+ */
+@ThingActionsScope(name = "doorbird")
+@NonNullByDefault
+public class DoorbirdActions implements ThingActions {
+    private final Logger logger = LoggerFactory.getLogger(DoorbirdActions.class);
+
+    private @Nullable DoorbellHandler handler;
+
+    public DoorbirdActions() {
+        logger.debug("DoorbirdActions service created");
+    }
+
+    @Override
+    public void setThingHandler(@Nullable ThingHandler handler) {
+        if (handler instanceof DoorbellHandler) {
+            this.handler = (DoorbellHandler) handler;
+        }
+    }
+
+    @Override
+    public @Nullable ThingHandler getThingHandler() {
+        return handler;
+    }
+
+    @RuleAction(label = "restart the Doorbird", description = "Restarts the Doorbird device.")
+    public void restart() {
+        logger.debug("Doorbird action 'restart' called");
+        if (handler != null) {
+            handler.actionRestart();
+        } else {
+            logger.info("Doorbird Action service ThingHandler is null!");
+        }
+    }
+
+    public static void restart(@Nullable ThingActions actions) {
+        if (actions instanceof DoorbirdActions) {
+            ((DoorbirdActions) actions).restart();
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of DoorbirdActions");
+        }
+    }
+
+    @RuleAction(label = "hangup a SIP call", description = "Hangup SIP call.")
+    public void sipHangup() {
+        logger.debug("Doorbird action 'sipHangup' called");
+        if (handler != null) {
+            handler.actionSIPHangup();
+        } else {
+            logger.info("Doorbird Action service ThingHandler is null!");
+        }
+    }
+
+    public static void sipHangup(@Nullable ThingActions actions) {
+        if (actions instanceof DoorbirdActions) {
+            ((DoorbirdActions) actions).sipHangup();
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of DoorbirdActions");
+        }
+    }
+
+    @RuleAction(label = "get the ring time limit", description = "Get the value of RING_TIME_LIMIT.")
+    public @ActionOutput(name = "getRingTimeLimit", type = "java.lang.String") String getRingTimeLimit() {
+        logger.debug("Doorbird action 'getRingTimeLimit' called");
+        if (handler != null) {
+            return handler.actionGetRingTimeLimit();
+        } else {
+            logger.info("Doorbird Action service ThingHandler is null!");
+            return "";
+        }
+    }
+
+    public static String getRingTimeLimit(@Nullable ThingActions actions) {
+        if (actions instanceof DoorbirdActions) {
+            return ((DoorbirdActions) actions).getRingTimeLimit();
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of DoorbirdActions");
+        }
+    }
+
+    @RuleAction(label = "get the call time limit", description = "Get the value of CALL_TIME_LIMIT.")
+    public @ActionOutput(name = "getCallTimeLimit", type = "java.lang.String") String getCallTimeLimit() {
+        logger.debug("Doorbird action 'getCallTimeLimit' called");
+        if (handler != null) {
+            return handler.actionGetCallTimeLimit();
+        } else {
+            logger.info("Doorbird Action service ThingHandler is null!");
+            return "";
+        }
+    }
+
+    public static String getCallTimeLimit(@Nullable ThingActions actions) {
+        if (actions instanceof DoorbirdActions) {
+            return ((DoorbirdActions) actions).getCallTimeLimit();
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of DoorbirdActions");
+        }
+    }
+
+    @RuleAction(label = "get the last error code", description = "Get the value of LASTERRORCODE.")
+    public @ActionOutput(name = "getLastErrorCode", type = "java.lang.String") String getLastErrorCode() {
+        logger.debug("Doorbird action 'getLastErrorCode' called");
+        if (handler != null) {
+            return handler.actionGetLastErrorCode();
+        } else {
+            logger.info("Doorbird Action service ThingHandler is null!");
+            return "";
+        }
+    }
+
+    public static String getLastErrorCode(@Nullable ThingActions actions) {
+        if (actions instanceof DoorbirdActions) {
+            return ((DoorbirdActions) actions).getLastErrorCode();
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of DoorbirdActions");
+        }
+    }
+
+    @RuleAction(label = "get the last error text", description = "Get the value of LASTERRORTEXT.")
+    public @ActionOutput(name = "getLastErrorText", type = "java.lang.String") String getLastErrorText() {
+        logger.debug("Doorbird action 'getLastErrorText' called");
+        if (handler != null) {
+            return handler.actionGetLastErrorText();
+        } else {
+            logger.info("Doorbird Action service ThingHandler is null!");
+            return "";
+        }
+    }
+
+    public static String getLastErrorText(@Nullable ThingActions actions) {
+        if (actions instanceof DoorbirdActions) {
+            return ((DoorbirdActions) actions).getLastErrorText();
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of DoorbirdActions");
+        }
+    }
+}
index 1db3f49245545c148608c136434c40a6b68d5080..f5017578d110eb61b0f8565dca55442e5a84559b 100644 (file)
@@ -33,7 +33,7 @@ import javax.imageio.ImageIO;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.jetty.client.HttpClient;
-import org.openhab.binding.doorbird.action.DoorbirdActions;
+import org.openhab.binding.doorbird.internal.action.DoorbirdActions;
 import org.openhab.binding.doorbird.internal.api.DoorbirdAPI;
 import org.openhab.binding.doorbird.internal.api.DoorbirdImage;
 import org.openhab.binding.doorbird.internal.api.SipStatus;
diff --git a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/action/EcobeeActions.java b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/action/EcobeeActions.java
deleted file mode 100644 (file)
index eea101b..0000000
+++ /dev/null
@@ -1,644 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.ecobee.action;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.measure.quantity.Temperature;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.ecobee.internal.dto.thermostat.EventDTO;
-import org.openhab.binding.ecobee.internal.enums.AckType;
-import org.openhab.binding.ecobee.internal.enums.FanMode;
-import org.openhab.binding.ecobee.internal.enums.HoldType;
-import org.openhab.binding.ecobee.internal.enums.PlugState;
-import org.openhab.binding.ecobee.internal.enums.VentilatorMode;
-import org.openhab.binding.ecobee.internal.function.AcknowledgeFunction;
-import org.openhab.binding.ecobee.internal.function.ControlPlugFunction;
-import org.openhab.binding.ecobee.internal.function.CreateVacationFunction;
-import org.openhab.binding.ecobee.internal.function.DeleteVacationFunction;
-import org.openhab.binding.ecobee.internal.function.ResetPreferencesFunction;
-import org.openhab.binding.ecobee.internal.function.ResumeProgramFunction;
-import org.openhab.binding.ecobee.internal.function.SendMessageFunction;
-import org.openhab.binding.ecobee.internal.function.SetHoldFunction;
-import org.openhab.binding.ecobee.internal.function.SetOccupiedFunction;
-import org.openhab.binding.ecobee.internal.function.UpdateSensorFunction;
-import org.openhab.binding.ecobee.internal.handler.EcobeeThermostatBridgeHandler;
-import org.openhab.binding.ecobee.internal.handler.EcobeeUtils;
-import org.openhab.core.automation.annotation.ActionInput;
-import org.openhab.core.automation.annotation.ActionOutput;
-import org.openhab.core.automation.annotation.RuleAction;
-import org.openhab.core.library.types.QuantityType;
-import org.openhab.core.thing.binding.ThingActions;
-import org.openhab.core.thing.binding.ThingActionsScope;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link EcobeeActions} defines the thing actions for the Ecobee binding.
- * <p>
- * <b>Note:</b>The static method <b>invokeMethod</b> handles the case where
- * the test <i>actions instanceof EcobeeActions</i> fails. This test can fail
- * due to an issue in openHAB core v2.5.0 where the {@link EcobeeActions} class
- * can be loaded by a different classloader than the <i>actions</i> instance.
- *
- * @author John Cocula - Initial contribution
- * @author Mark Hilbush - Adapted for OH2/3
- * @author Connor Petty - Proxy method for invoking actions
- */
-@ThingActionsScope(name = "ecobee")
-@NonNullByDefault
-public class EcobeeActions implements ThingActions, IEcobeeActions {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(EcobeeActions.class);
-
-    private @Nullable EcobeeThermostatBridgeHandler handler;
-
-    public EcobeeActions() {
-        LOGGER.debug("EcobeeActions: EcobeeActions: Actions service created");
-    }
-
-    @Override
-    public void setThingHandler(@Nullable ThingHandler handler) {
-        if (handler instanceof EcobeeThermostatBridgeHandler) {
-            this.handler = (EcobeeThermostatBridgeHandler) handler;
-        }
-    }
-
-    @Override
-    public @Nullable ThingHandler getThingHandler() {
-        return this.handler;
-    }
-
-    private static IEcobeeActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(EcobeeActions.class.getName())) {
-            if (actions instanceof IEcobeeActions) {
-                return (IEcobeeActions) actions;
-            } else {
-                return (IEcobeeActions) Proxy.newProxyInstance(IEcobeeActions.class.getClassLoader(),
-                        new Class[] { IEcobeeActions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
-        }
-        throw new IllegalArgumentException("Actions is not an instance of EcobeeActions");
-    }
-
-    /**
-     * The acknowledge function allows an alert to be acknowledged.
-     *
-     * @see <a
-     *      href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/Acknowledge.shtml">Acknowledge
-     *      </a>
-     */
-    @Override
-    @RuleAction(label = "Acknowledge", description = "Acknowledges an alert.")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean acknowledge(
-            @ActionInput(name = "ackRef", description = "The acknowledge ref of alert") @Nullable String ackRef,
-            @ActionInput(name = "ackType", description = "The type of acknowledgement. Valid values: accept, decline, defer, unacknowledged") @Nullable String ackType,
-            @ActionInput(name = "remindMeLater", description = "(opt) Whether to remind at a later date, if this is a defer acknowledgement") @Nullable Boolean remindMeLater) {
-        LOGGER.debug("EcobeeActions: Action 'Acknowledge' called");
-        EcobeeThermostatBridgeHandler localHandler = handler;
-        if (localHandler == null) {
-            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
-            return false;
-        }
-        AcknowledgeFunction function = new AcknowledgeFunction(localHandler.getThermostatId(), ackRef,
-                AckType.forValue(ackType), remindMeLater);
-        return localHandler.actionPerformFunction(function);
-    }
-
-    public static boolean acknowledge(@Nullable ThingActions actions, @Nullable String ackRef, @Nullable String ackType,
-            @Nullable Boolean remindMeLater) {
-        return invokeMethodOf(actions).acknowledge(ackRef, ackType, remindMeLater);
-    }
-
-    /**
-     * Control the on/off state of a plug by setting a hold on the plug.
-     *
-     * @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/ControlPlug.shtml">Control
-     *      Plug</a>
-     */
-    @Override
-    @RuleAction(label = "Control Plug", description = "Control the on/off state of a plug by setting a hold on the plug.")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean controlPlug(
-            @ActionInput(name = "plugName", description = "The name of the plug. Ensure each plug has a unique name.") @Nullable String plugName,
-            @ActionInput(name = "plugState", description = "The state to put the plug into. Valid values: on, off, resume.") @Nullable String plugState,
-            @ActionInput(name = "startDateTime", description = "(opt) The start date/time in thermostat time.") @Nullable Date startDateTime,
-            @ActionInput(name = "endDateTime", description = "(opt) The end date/time in thermostat time.") @Nullable Date endDateTime,
-            @ActionInput(name = "holdType", description = "(opt) The hold duration type. Valid values: dateTime, nextTransition, indefinite, holdHours.") @Nullable String holdType,
-            @ActionInput(name = "holdHours", description = "(opt) The number of hours to hold for, used and required if holdType='holdHours'.") @Nullable Number holdHours) {
-        LOGGER.debug("EcobeeActions: Action 'Control Plug' called");
-        EcobeeThermostatBridgeHandler localHandler = handler;
-        if (localHandler == null) {
-            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
-            return false;
-        }
-        ControlPlugFunction function = (new ControlPlugFunction(plugName, PlugState.forValue(plugState), startDateTime,
-                endDateTime, (holdType == null) ? null : HoldType.forValue(holdType),
-                (holdHours == null) ? null : Integer.valueOf(holdHours.intValue())));
-        return localHandler.actionPerformFunction(function);
-    }
-
-    public static boolean controlPlug(@Nullable ThingActions actions, @Nullable String plugName,
-            @Nullable String plugState, @Nullable Date startDateTime, @Nullable Date endDateTime,
-            @Nullable String holdType, @Nullable Number holdHours) {
-        return invokeMethodOf(actions).controlPlug(plugName, plugState, startDateTime, endDateTime, holdType,
-                holdHours);
-    }
-
-    /**
-     * The create vacation function creates a vacation event on the thermostat.
-     *
-     * @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/CreateVacation.shtml">Create
-     *      Vacation</a>
-     */
-    @Override
-    @RuleAction(label = "Create Vacation", description = "The create vacation function creates a vacation event on the thermostat.")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean createVacation(
-            @ActionInput(name = "name", description = "The vacation event name. It must be unique.") @Nullable String name,
-            @ActionInput(name = "coolHoldTemp", description = "The temperature at which to set the cool vacation hold.") @Nullable QuantityType<Temperature> coolHoldTemp,
-            @ActionInput(name = "heatHoldTemp", description = "The temperature at which to set the heat vacation hold.") @Nullable QuantityType<Temperature> heatHoldTemp,
-            @ActionInput(name = "startDateTime", description = "(opt) The start date/time in thermostat time.") @Nullable Date startDateTime,
-            @ActionInput(name = "endDateTime", description = "(opt) The end date in thermostat time.") @Nullable Date endDateTime,
-            @ActionInput(name = "fan", description = "(opt) The fan mode during the vacation. Values: auto, on Default: auto") @Nullable String fan,
-            @ActionInput(name = "fanMinOnTime", description = "(opt) The minimum number of minutes to run the fan each hour. Range: 0-60, Default: 0") @Nullable Number fanMinOnTime) {
-        LOGGER.debug("EcobeeActions: Action 'Create Vacation' called");
-        EcobeeThermostatBridgeHandler localHandler = handler;
-        if (localHandler == null) {
-            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
-            return false;
-        }
-        CreateVacationFunction function = new CreateVacationFunction(name, coolHoldTemp, heatHoldTemp, startDateTime,
-                endDateTime, (fan == null) ? null : FanMode.forValue(fan),
-                (fanMinOnTime == null) ? null : Integer.valueOf(fanMinOnTime.intValue()));
-        return localHandler.actionPerformFunction(function);
-    }
-
-    public static boolean createVacation(@Nullable ThingActions actions, @Nullable String name,
-            @Nullable QuantityType<Temperature> coolHoldTemp, @Nullable QuantityType<Temperature> heatHoldTemp,
-            @Nullable Date startDateTime, @Nullable Date endDateTime, @Nullable String fan,
-            @Nullable Number fanMinOnTime) {
-        return invokeMethodOf(actions).createVacation(name, coolHoldTemp, heatHoldTemp, startDateTime, endDateTime, fan,
-                fanMinOnTime);
-    }
-
-    /**
-     * The delete vacation function deletes a vacation event from a thermostat.
-     *
-     * @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/DeleteVacation.shtml">Delete
-     *      Vacation</a>
-     */
-    @Override
-    @RuleAction(label = "Delete Vacation", description = "The delete vacation function deletes a vacation event from a thermostat.")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean deleteVacation(
-            @ActionInput(name = "name", description = "The vacation event name to delete.") @Nullable String name) {
-        LOGGER.debug("EcobeeActions: Action 'Delete Vacation' called");
-        EcobeeThermostatBridgeHandler localHandler = handler;
-        if (localHandler == null) {
-            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
-            return false;
-        }
-        DeleteVacationFunction function = new DeleteVacationFunction(name);
-        return localHandler.actionPerformFunction(function);
-    }
-
-    public static boolean deleteVacation(@Nullable ThingActions actions, @Nullable String name) {
-        return invokeMethodOf(actions).deleteVacation(name);
-    }
-
-    /**
-     * The reset preferences function sets all of the user configurable settings back to the factory default values.
-     *
-     * @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/ResetPreferences.shtml">Reset
-     *      Preferences</a>
-     */
-    @Override
-    @RuleAction(label = "Reset Preferences", description = "The reset preferences function sets all of the user configurable settings back to the factory default values.")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean resetPreferences() {
-        LOGGER.debug("EcobeeActions: Action 'Reset Preferences' called");
-        EcobeeThermostatBridgeHandler localHandler = handler;
-        if (localHandler == null) {
-            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
-            return false;
-        }
-        ResetPreferencesFunction function = new ResetPreferencesFunction();
-        return localHandler.actionPerformFunction(function);
-    }
-
-    public static boolean resetPreferences(@Nullable ThingActions actions) {
-        return invokeMethodOf(actions).resetPreferences();
-    }
-
-    /**
-     * The resume program function removes the currently running event.
-     *
-     * @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/ResumeProgram.shtml">Resume
-     *      Program</a>
-     */
-    @Override
-    @RuleAction(label = "Resume Program", description = "Removes the currently running event providing the event is not a mandatory demand response event")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean resumeProgram(
-            @ActionInput(name = "resumeAll", description = "(opt) Should the thermostat be resumed to next event (false) or to its program (true)") @Nullable Boolean resumeAll) {
-        LOGGER.debug("EcobeeActions: Action 'Resume Program' called");
-        EcobeeThermostatBridgeHandler localHandler = handler;
-        if (localHandler == null) {
-            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
-            return false;
-        }
-        ResumeProgramFunction function = new ResumeProgramFunction(resumeAll);
-        return localHandler.actionPerformFunction(function);
-    }
-
-    public static boolean resumeProgram(@Nullable ThingActions actions, @Nullable Boolean resumeAll) {
-        return invokeMethodOf(actions).resumeProgram(resumeAll);
-    }
-
-    /**
-     * The send message function allows an alert message to be sent to the thermostat.
-     *
-     * @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/SendMessage.shtml">Send
-     *      Message</a>
-     */
-    @Override
-    @RuleAction(label = "Send Message", description = "The send message function allows an alert message to be sent to the thermostat.")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendMessage(
-            @ActionInput(name = "text", description = "The message text to send. Text will be truncated to 500 characters if longer") @Nullable String text) {
-        LOGGER.debug("EcobeeActions: Action 'SendMessage' called");
-        EcobeeThermostatBridgeHandler localHandler = handler;
-        if (localHandler == null) {
-            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
-            return false;
-        }
-        SendMessageFunction function = new SendMessageFunction(text);
-        return localHandler.actionPerformFunction(function);
-    }
-
-    public static boolean sendMessage(@Nullable ThingActions actions, @Nullable String text) {
-        return invokeMethodOf(actions).sendMessage(text);
-    }
-
-    /**
-     * Set an indefinite hold using the supplied the cool and heat hold temperatures
-     *
-     * @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/SetHold.shtml">Set Hold</a>
-     */
-    @Override
-    @RuleAction(label = "Set Hold", description = "The set hold function sets the thermostat into a hold with the specified temperatures.")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean setHold(
-            @ActionInput(name = "coolHoldTemp", description = "The temperature at which to set the cool hold.") @Nullable QuantityType<Temperature> coolHoldTemp,
-            @ActionInput(name = "heatHoldTemp", description = "The temperature at which to set the heat hold.") @Nullable QuantityType<Temperature> heatHoldTemp) {
-        if (coolHoldTemp == null || heatHoldTemp == null) {
-            throw new IllegalArgumentException("hold temperatures cannot be null");
-        }
-        Map<String, Object> params = new HashMap<String, Object>();
-        params.put("coolHoldTemp", coolHoldTemp);
-        params.put("heatHoldTemp", heatHoldTemp);
-        return setHold(params, null, null, null, null);
-    }
-
-    public static boolean setHold(@Nullable ThingActions actions, @Nullable QuantityType<Temperature> coolHoldTemp,
-            @Nullable QuantityType<Temperature> heatHoldTemp) {
-        return invokeMethodOf(actions).setHold(coolHoldTemp, heatHoldTemp);
-    }
-
-    /**
-     * Set a hold by providing the cool and heat temperatures and the number of hours.
-     */
-    @Override
-    @RuleAction(label = "Set Hold", description = "The set hold function sets the thermostat into a hold for the specified number of hours.")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean setHold(
-            @ActionInput(name = "coolHoldTemp", description = "The temperature at which to set the cool hold.") @Nullable QuantityType<Temperature> coolHoldTemp,
-            @ActionInput(name = "heatHoldTemp", description = "The temperature at which to set the heat hold.") @Nullable QuantityType<Temperature> heatHoldTemp,
-            @ActionInput(name = "holdHours", description = "The number of hours for the hold.") @Nullable Number holdHours) {
-        if (coolHoldTemp == null || heatHoldTemp == null) {
-            throw new IllegalArgumentException("hold temperatures cannot be null");
-        }
-        if (holdHours == null) {
-            throw new IllegalArgumentException("number of hold hours is missing");
-        }
-        Map<String, Object> params = new HashMap<String, Object>();
-        params.put("coolHoldTemp", coolHoldTemp);
-        params.put("heatHoldTemp", heatHoldTemp);
-        params.put("holdType", HoldType.HOLD_HOURS);
-        params.put("holdHours", Integer.valueOf(holdHours.intValue()));
-        return setHold(params, null, null, null, null);
-    }
-
-    public static boolean setHold(@Nullable ThingActions actions, @Nullable QuantityType<Temperature> coolHoldTemp,
-            @Nullable QuantityType<Temperature> heatHoldTemp, @Nullable Number holdHours) {
-        return invokeMethodOf(actions).setHold(coolHoldTemp, heatHoldTemp, holdHours);
-    }
-
-    /**
-     * Set an indefinite hold using the supplied climateRef
-     */
-    @Override
-    @RuleAction(label = "Set Hold", description = "The set hold function sets the thermostat into a hold with the specified climate ref.")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean setHold(
-            @ActionInput(name = "holdClimateRef", description = "The holdClimateRef used to set the hold.") @Nullable String holdClimateRef) {
-        EcobeeThermostatBridgeHandler localHandler = handler;
-        if (localHandler == null) {
-            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
-            return false;
-        }
-        if (holdClimateRef == null || !localHandler.isValidClimateRef(holdClimateRef)) {
-            throw new IllegalArgumentException("hold climate ref is missing or invalid");
-        }
-        Map<String, Object> params = new HashMap<String, Object>();
-        params.put("holdClimateRef", holdClimateRef);
-        return setHold(params, null, null, null, null);
-    }
-
-    public static boolean setHold(@Nullable ThingActions actions, @Nullable String holdClimateRef) {
-        return invokeMethodOf(actions).setHold(holdClimateRef);
-    }
-
-    /**
-     * Set a hold using the supplied climateRef for the supplied number of hours.
-     */
-    @Override
-    @RuleAction(label = "Set Hold", description = "The set hold function sets the thermostat into a hold with the specified climate ref.")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean setHold(
-            @ActionInput(name = "holdClimateRef", description = "The holdClimateRef used to set the hold.") @Nullable String holdClimateRef,
-            @ActionInput(name = "holdHours", description = "The number of hours for the hold.") @Nullable Number holdHours) {
-        if (holdHours == null) {
-            throw new IllegalArgumentException("number of hold hours is missing");
-        }
-        EcobeeThermostatBridgeHandler localHandler = handler;
-        if (localHandler == null) {
-            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
-            return false;
-        }
-        if (holdClimateRef == null || !localHandler.isValidClimateRef(holdClimateRef)) {
-            throw new IllegalArgumentException("hold climate ref is missing or invalid");
-        }
-        Map<String, Object> params = new HashMap<String, Object>();
-        params.put("holdClimateRef", holdClimateRef);
-        params.put("holdType", HoldType.HOLD_HOURS);
-        params.put("holdHours", Integer.valueOf(holdHours.intValue()));
-        return setHold(params, null, null, null, null);
-    }
-
-    public static boolean setHold(@Nullable ThingActions actions, @Nullable String holdClimateRef,
-            @Nullable Number holdHours) {
-        return invokeMethodOf(actions).setHold(holdClimateRef, holdHours);
-    }
-
-    /**
-     * Set a hold
-     */
-    @Override
-    @RuleAction(label = "Set Hold", description = "The set hold function sets the thermostat into a hold with the specified temperature or climate ref.")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean setHold(
-            @ActionInput(name = "coolHoldTemp", description = "(opt) The temperature at which to set the cool hold.") @Nullable QuantityType<Temperature> coolHoldTemp,
-            @ActionInput(name = "heatHoldTemp", description = "(opt) The temperature at which to set the heat hold.") @Nullable QuantityType<Temperature> heatHoldTemp,
-            @ActionInput(name = "holdClimateRef", description = "(opt) The Climate to use as reference for setting the coolHoldTemp, heatHoldTemp and fan settings for this hold. If this value is passed the coolHoldTemp and heatHoldTemp are not required.") @Nullable String holdClimateRef,
-            @ActionInput(name = "startDateTime", description = "(opt) The start date in thermostat time.") @Nullable Date startDateTime,
-            @ActionInput(name = "endDateTime", description = "(opt) The end date in thermostat time.") @Nullable Date endDateTime,
-            @ActionInput(name = "holdType", description = "(opt) The hold duration type. Valid values: dateTime, nextTransition, indefinite, holdHours.") @Nullable String holdType,
-            @ActionInput(name = "holdHours", description = "(opt) The number of hours to hold for, used and required if holdType='holdHours'.") @Nullable Number holdHours) {
-        Map<String, Object> params = new HashMap<String, Object>();
-        if (coolHoldTemp != null) {
-            params.put("coolHoldTemp", coolHoldTemp);
-        }
-        if (heatHoldTemp != null) {
-            params.put("heatHoldTemp", heatHoldTemp);
-        }
-        if (holdClimateRef != null) {
-            params.put("holdClimateRef", holdClimateRef);
-        }
-        return setHold(params, holdType, holdHours, startDateTime, endDateTime);
-    }
-
-    public static boolean setHold(@Nullable ThingActions actions, @Nullable QuantityType<Temperature> coolHoldTemp,
-            @Nullable QuantityType<Temperature> heatHoldTemp, @Nullable String holdClimateRef,
-            @Nullable Date startDateTime, @Nullable Date endDateTime, @Nullable String holdType,
-            @Nullable Number holdHours) {
-        return invokeMethodOf(actions).setHold(coolHoldTemp, heatHoldTemp, holdClimateRef, startDateTime, endDateTime,
-                holdType, holdHours);
-    }
-
-    /**
-     * Set a hold by providing a parameter map
-     */
-    @Override
-    @RuleAction(label = "Set Hold", description = "The set hold function sets the thermostat into a hold with the specified event parameters.")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean setHold(
-            @ActionInput(name = "params", description = "The map of hold parameters.") @Nullable Map<String, Object> params,
-            @ActionInput(name = "holdType", description = "(opt) The hold duration type. Valid values: dateTime, nextTransition, indefinite, holdHours.") @Nullable String holdType,
-            @ActionInput(name = "holdHours", description = "(opt) The number of hours to hold for, used and required if holdType='holdHours'.") @Nullable Number holdHours,
-            @ActionInput(name = "startDateTime", description = "(opt) The start date in thermostat time.") @Nullable Date startDateTime,
-            @ActionInput(name = "endDateTime", description = "(opt) The end date in thermostat time.") @Nullable Date endDateTime) {
-        LOGGER.debug("EcobeeActions: Action 'SetHold' called");
-        if (params == null) {
-            throw new IllegalArgumentException("params cannot be null");
-        }
-        EventDTO event = new EventDTO();
-        for (String key : params.keySet()) {
-            Object value = params.get(key);
-            switch (key) {
-                case "isOccupied":
-                    event.isOccupied = ((Boolean) value);
-                    break;
-                case "isCoolOff":
-                    event.isCoolOff = ((Boolean) value);
-                    break;
-                case "isHeatOff":
-                    event.isHeatOff = ((Boolean) value);
-                    break;
-                case "coolHoldTemp":
-                    event.coolHoldTemp = EcobeeUtils.convertQuantityTypeToEcobeeTemp(value);
-                    break;
-                case "heatHoldTemp":
-                    event.heatHoldTemp = EcobeeUtils.convertQuantityTypeToEcobeeTemp(value);
-                    break;
-                case "fan":
-                    event.fan = FanMode.forValue((String) value).toString();
-                    break;
-                case "vent":
-                    event.vent = VentilatorMode.forValue((String) value).toString();
-                    break;
-                case "ventilatorMinOnTime":
-                    event.ventilatorMinOnTime = ((Integer) value);
-                    break;
-                case "isOptional":
-                    event.isOptional = ((Boolean) value);
-                    break;
-                case "isTemperatureRelative":
-                    event.isTemperatureRelative = ((Boolean) value);
-                    break;
-                case "coolRelativeTemp":
-                    event.coolRelativeTemp = EcobeeUtils.convertQuantityTypeToEcobeeTemp(value);
-                    break;
-                case "heatRelativeTemp":
-                    event.heatRelativeTemp = EcobeeUtils.convertQuantityTypeToEcobeeTemp(value);
-                    break;
-                case "isTemperatureAbsolute":
-                    event.isTemperatureAbsolute = ((Boolean) value);
-                    break;
-                case "fanMinOnTime":
-                    event.fanMinOnTime = ((Integer) value);
-                    break;
-                case "holdClimateRef":
-                    event.holdClimateRef = ((String) value);
-                    break;
-                default:
-                    LOGGER.warn("Unrecognized event field '{}' with value '{}' ignored.", key, value);
-                    break;
-            }
-        }
-        EcobeeThermostatBridgeHandler localHandler = handler;
-        if (localHandler == null) {
-            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
-            return false;
-        }
-        SetHoldFunction function = new SetHoldFunction(event, (holdType == null) ? null : HoldType.forValue(holdType),
-                (holdHours == null) ? null : holdHours.intValue(), startDateTime, endDateTime);
-        return localHandler.actionPerformFunction(function);
-    }
-
-    public static boolean setHold(@Nullable ThingActions actions, @Nullable Map<String, Object> params,
-            @Nullable String holdType, @Nullable Number holdHours, @Nullable Date startDateTime,
-            @Nullable Date endDateTime) {
-        return invokeMethodOf(actions).setHold(params, holdType, holdHours, startDateTime, endDateTime);
-    }
-
-    /**
-     * The set occupied function may only be used by EMS thermostats. The function switches a thermostat from occupied
-     * mode to unoccupied, or vice versa.
-     *
-     * @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/SetOccupied.shtml">Set
-     *      Occupied</a>
-     */
-    @Override
-    @RuleAction(label = "Set Occupied", description = "The function switches a thermostat from occupied mode to unoccupied, or vice versa (EMS MODELS ONLY).")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean setOccupied(
-            @ActionInput(name = "occupied", description = "The climate to use for the temperature, occupied (true) or unoccupied (false).") @Nullable Boolean occupied,
-            @ActionInput(name = "startDateTime", description = "(opt) The start date in thermostat time.") @Nullable Date startDateTime,
-            @ActionInput(name = "endDateTime", description = "(opt) The end date in thermostat time.") @Nullable Date endDateTime,
-            @ActionInput(name = "holdType", description = "(opt) The hold duration type. Valid values: dateTime, nextTransition, indefinite, holdHours.") @Nullable String holdType,
-            @ActionInput(name = "holdHours", description = "(opt) The number of hours to hold for, used and required if holdType='holdHours'.") @Nullable Number holdHours) {
-        LOGGER.debug("EcobeeActions: Action 'Set Occupied' called");
-        EcobeeThermostatBridgeHandler localHandler = handler;
-        if (localHandler == null) {
-            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
-            return false;
-        }
-        SetOccupiedFunction function = new SetOccupiedFunction(occupied, startDateTime, endDateTime,
-                (holdType == null) ? null : HoldType.forValue(holdType),
-                (holdHours == null) ? null : Integer.valueOf(holdHours.intValue()));
-        return localHandler.actionPerformFunction(function);
-    }
-
-    public static boolean setOccupied(@Nullable ThingActions actions, @Nullable Boolean occupied,
-            @Nullable Date startDateTime, @Nullable Date endDateTime, @Nullable String holdType,
-            @Nullable Number holdHours) {
-        return invokeMethodOf(actions).setOccupied(occupied, startDateTime, endDateTime, holdType, holdHours);
-    }
-
-    /**
-     * The update sensor function allows the caller to update the name of an ecobee3 remote sensor.
-     *
-     * @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/UpdateSensor.shtml">Update
-     *      Sensor</a>
-     */
-    @Override
-    @RuleAction(label = "Update Sensor", description = "The update sensor function allows the caller to update the name of an ecobee3 remote sensor.")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean updateSensor(
-            @ActionInput(name = "name", description = "The updated name to give the sensor. Has a max length of 32, but shorter is recommended.") @Nullable String name,
-            @ActionInput(name = "deviceId", description = "The deviceId for the sensor, typically this indicates the enclosure and corresponds to the ThermostatRemoteSensor.id field. For example: rs:100") @Nullable String deviceId,
-            @ActionInput(name = "sensorId", description = "The identifier for the sensor within the enclosure. Corresponds to the RemoteSensorCapability.id. For example: 1") @Nullable String sensorId) {
-        LOGGER.debug("EcobeeActions: Action 'UpdateSensor' called");
-        EcobeeThermostatBridgeHandler localHandler = handler;
-        if (localHandler == null) {
-            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
-            return false;
-        }
-        UpdateSensorFunction function = new UpdateSensorFunction(name, deviceId, sensorId);
-        return localHandler.actionPerformFunction(function);
-    }
-
-    public static boolean updateSensor(@Nullable ThingActions actions, @Nullable String name, @Nullable String deviceId,
-            @Nullable String sensorId) {
-        return invokeMethodOf(actions).updateSensor(name, deviceId, sensorId);
-    }
-
-    /**
-     * Get the alerts list. Returns a JSON string containing all the alerts.
-     */
-    @Override
-    @RuleAction(label = "Get Alerts", description = "Get the alerts list")
-    public @ActionOutput(name = "alerts", type = "java.lang.String") @Nullable String getAlerts() {
-        LOGGER.debug("EcobeeActions: Action 'Get Alerts' called");
-        EcobeeThermostatBridgeHandler localHandler = handler;
-        if (localHandler == null) {
-            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
-            return null;
-        }
-        return localHandler.getAlerts();
-    }
-
-    public static @Nullable String getAlerts(@Nullable ThingActions actions) {
-        return invokeMethodOf(actions).getAlerts();
-    }
-
-    /**
-     * Get the events list. Returns a JSON string contains all events.
-     */
-    @Override
-    @RuleAction(label = "Get Events", description = "Get the events list")
-    public @ActionOutput(name = "events", type = "java.lang.String") @Nullable String getEvents() {
-        LOGGER.debug("EcobeeActions: Action 'Get Events' called");
-        EcobeeThermostatBridgeHandler localHandler = handler;
-        if (localHandler == null) {
-            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
-            return null;
-        }
-        return localHandler.getEvents();
-    }
-
-    public static @Nullable String getEvents(@Nullable ThingActions actions) {
-        return invokeMethodOf(actions).getEvents();
-    }
-
-    /**
-     * Get a list of climates. Returns a JSON string contains all climates.
-     */
-    @Override
-    @RuleAction(label = "Get Climates", description = "Get a list of climates")
-    public @ActionOutput(name = "climates", type = "java.lang.String") @Nullable String getClimates() {
-        LOGGER.debug("EcobeeActions: Action 'Get Climates' called");
-        EcobeeThermostatBridgeHandler localHandler = handler;
-        if (localHandler == null) {
-            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
-            return null;
-        }
-        return localHandler.getClimates();
-    }
-
-    public static @Nullable String getClimates(@Nullable ThingActions actions) {
-        return invokeMethodOf(actions).getClimates();
-    }
-}
diff --git a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/action/IEcobeeActions.java b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/action/IEcobeeActions.java
deleted file mode 100644 (file)
index 3ed103c..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.ecobee.action;
-
-import java.util.Date;
-import java.util.Map;
-
-import javax.measure.quantity.Temperature;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.core.library.types.QuantityType;
-
-/**
- * The {@link IEcobeeActions} defines the interface for all thing actions supported by the binding.
- * These methods, parameters, and return types are explained in {@link EcobeeActions}.
- *
- * @author Mark Hilbush - Initial contribution
- */
-@NonNullByDefault
-public interface IEcobeeActions {
-
-    public Boolean acknowledge(@Nullable String ackRef, @Nullable String ackType, @Nullable Boolean remindMeLater);
-
-    public Boolean controlPlug(@Nullable String plugName, @Nullable String plugState, @Nullable Date startDateTime,
-            @Nullable Date endDateTime, @Nullable String holdType, @Nullable Number holdHours);
-
-    public Boolean sendMessage(@Nullable String text);
-
-    public Boolean createVacation(@Nullable String name, @Nullable QuantityType<Temperature> coolHoldTemp,
-            @Nullable QuantityType<Temperature> heatHoldTemp, @Nullable Date startDateTime, @Nullable Date endDateTime,
-            @Nullable String fan, @Nullable Number fanMinOnTime);
-
-    public Boolean deleteVacation(@Nullable String name);
-
-    public Boolean resetPreferences();
-
-    public Boolean resumeProgram(@Nullable Boolean resumeAll);
-
-    public Boolean setHold(@Nullable QuantityType<Temperature> coolHoldTemp,
-            @Nullable QuantityType<Temperature> heatHoldTemp);
-
-    public Boolean setHold(@Nullable QuantityType<Temperature> coolHoldTemp,
-            @Nullable QuantityType<Temperature> heatHoldTemp, @Nullable Number holdHours);
-
-    public Boolean setHold(@Nullable String holdClimateRef);
-
-    public Boolean setHold(@Nullable String holdClimateRef, @Nullable Number holdHours);
-
-    public Boolean setHold(@Nullable QuantityType<Temperature> coolHoldTemp,
-            @Nullable QuantityType<Temperature> heatHoldTemp, @Nullable String holdClimateRef,
-            @Nullable Date startDateTime, @Nullable Date endDateTime, @Nullable String holdType,
-            @Nullable Number holdHours);
-
-    public Boolean setHold(@Nullable Map<String, Object> params, @Nullable String holdType, @Nullable Number holdHours,
-            @Nullable Date startDateTime, @Nullable Date endDateTime);
-
-    public Boolean setOccupied(@Nullable Boolean occupied, @Nullable Date startDateTime, @Nullable Date endDateTime,
-            @Nullable String holdType, @Nullable Number holdHours);
-
-    public Boolean updateSensor(@Nullable String name, @Nullable String deviceId, @Nullable String sensorId);
-
-    public @Nullable String getAlerts();
-
-    public @Nullable String getEvents();
-
-    public @Nullable String getClimates();
-}
diff --git a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/action/EcobeeActions.java b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/action/EcobeeActions.java
new file mode 100644 (file)
index 0000000..fbf788f
--- /dev/null
@@ -0,0 +1,672 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.ecobee.internal.action;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.measure.quantity.Temperature;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.ecobee.internal.dto.thermostat.EventDTO;
+import org.openhab.binding.ecobee.internal.enums.AckType;
+import org.openhab.binding.ecobee.internal.enums.FanMode;
+import org.openhab.binding.ecobee.internal.enums.HoldType;
+import org.openhab.binding.ecobee.internal.enums.PlugState;
+import org.openhab.binding.ecobee.internal.enums.VentilatorMode;
+import org.openhab.binding.ecobee.internal.function.AcknowledgeFunction;
+import org.openhab.binding.ecobee.internal.function.ControlPlugFunction;
+import org.openhab.binding.ecobee.internal.function.CreateVacationFunction;
+import org.openhab.binding.ecobee.internal.function.DeleteVacationFunction;
+import org.openhab.binding.ecobee.internal.function.ResetPreferencesFunction;
+import org.openhab.binding.ecobee.internal.function.ResumeProgramFunction;
+import org.openhab.binding.ecobee.internal.function.SendMessageFunction;
+import org.openhab.binding.ecobee.internal.function.SetHoldFunction;
+import org.openhab.binding.ecobee.internal.function.SetOccupiedFunction;
+import org.openhab.binding.ecobee.internal.function.UpdateSensorFunction;
+import org.openhab.binding.ecobee.internal.handler.EcobeeThermostatBridgeHandler;
+import org.openhab.binding.ecobee.internal.handler.EcobeeUtils;
+import org.openhab.core.automation.annotation.ActionInput;
+import org.openhab.core.automation.annotation.ActionOutput;
+import org.openhab.core.automation.annotation.RuleAction;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.thing.binding.ThingActions;
+import org.openhab.core.thing.binding.ThingActionsScope;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link EcobeeActions} defines the thing actions for the Ecobee binding.
+ *
+ * @author John Cocula - Initial contribution
+ * @author Mark Hilbush - Adapted for OH2/3
+ * @author Connor Petty - Proxy method for invoking actions
+ */
+@ThingActionsScope(name = "ecobee")
+@NonNullByDefault
+public class EcobeeActions implements ThingActions {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(EcobeeActions.class);
+
+    private @Nullable EcobeeThermostatBridgeHandler handler;
+
+    public EcobeeActions() {
+        LOGGER.debug("EcobeeActions: EcobeeActions: Actions service created");
+    }
+
+    @Override
+    public void setThingHandler(@Nullable ThingHandler handler) {
+        if (handler instanceof EcobeeThermostatBridgeHandler) {
+            this.handler = (EcobeeThermostatBridgeHandler) handler;
+        }
+    }
+
+    @Override
+    public @Nullable ThingHandler getThingHandler() {
+        return handler;
+    }
+
+    /**
+     * The acknowledge function allows an alert to be acknowledged.
+     *
+     * @see <a
+     *      href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/Acknowledge.shtml">Acknowledge
+     *      </a>
+     */
+    @RuleAction(label = "acknowledge an alert", description = "Acknowledges an alert.")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean acknowledge(
+            @ActionInput(name = "ackRef", description = "The acknowledge ref of alert") @Nullable String ackRef,
+            @ActionInput(name = "ackType", description = "The type of acknowledgement. Valid values: accept, decline, defer, unacknowledged") @Nullable String ackType,
+            @ActionInput(name = "remindMeLater", description = "(opt) Whether to remind at a later date, if this is a defer acknowledgement") @Nullable Boolean remindMeLater) {
+        LOGGER.debug("EcobeeActions: Action 'Acknowledge' called");
+        EcobeeThermostatBridgeHandler localHandler = handler;
+        if (localHandler == null) {
+            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
+            return false;
+        }
+        AcknowledgeFunction function = new AcknowledgeFunction(localHandler.getThermostatId(), ackRef,
+                AckType.forValue(ackType), remindMeLater);
+        return localHandler.actionPerformFunction(function);
+    }
+
+    public static boolean acknowledge(@Nullable ThingActions actions, @Nullable String ackRef, @Nullable String ackType,
+            @Nullable Boolean remindMeLater) {
+        if (actions instanceof EcobeeActions) {
+            return ((EcobeeActions) actions).acknowledge(ackRef, ackType, remindMeLater);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of EcobeeActions");
+        }
+    }
+
+    /**
+     * Control the on/off state of a plug by setting a hold on the plug.
+     *
+     * @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/ControlPlug.shtml">Control
+     *      Plug</a>
+     */
+    @RuleAction(label = "control a plug", description = "Control the on/off state of a plug by setting a hold on the plug.")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean controlPlug(
+            @ActionInput(name = "plugName", description = "The name of the plug. Ensure each plug has a unique name.") @Nullable String plugName,
+            @ActionInput(name = "plugState", description = "The state to put the plug into. Valid values: on, off, resume.") @Nullable String plugState,
+            @ActionInput(name = "startDateTime", description = "(opt) The start date/time in thermostat time.") @Nullable Date startDateTime,
+            @ActionInput(name = "endDateTime", description = "(opt) The end date/time in thermostat time.") @Nullable Date endDateTime,
+            @ActionInput(name = "holdType", description = "(opt) The hold duration type. Valid values: dateTime, nextTransition, indefinite, holdHours.") @Nullable String holdType,
+            @ActionInput(name = "holdHours", description = "(opt) The number of hours to hold for, used and required if holdType='holdHours'.") @Nullable Number holdHours) {
+        LOGGER.debug("EcobeeActions: Action 'Control Plug' called");
+        EcobeeThermostatBridgeHandler localHandler = handler;
+        if (localHandler == null) {
+            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
+            return false;
+        }
+        ControlPlugFunction function = (new ControlPlugFunction(plugName, PlugState.forValue(plugState), startDateTime,
+                endDateTime, (holdType == null) ? null : HoldType.forValue(holdType),
+                (holdHours == null) ? null : Integer.valueOf(holdHours.intValue())));
+        return localHandler.actionPerformFunction(function);
+    }
+
+    public static boolean controlPlug(@Nullable ThingActions actions, @Nullable String plugName,
+            @Nullable String plugState, @Nullable Date startDateTime, @Nullable Date endDateTime,
+            @Nullable String holdType, @Nullable Number holdHours) {
+        if (actions instanceof EcobeeActions) {
+            return ((EcobeeActions) actions).controlPlug(plugName, plugState, startDateTime, endDateTime, holdType,
+                    holdHours);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of EcobeeActions");
+        }
+    }
+
+    /**
+     * The create vacation function creates a vacation event on the thermostat.
+     *
+     * @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/CreateVacation.shtml">Create
+     *      Vacation</a>
+     */
+    @RuleAction(label = "create a vacation", description = "The create vacation function creates a vacation event on the thermostat.")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean createVacation(
+            @ActionInput(name = "name", description = "The vacation event name. It must be unique.") @Nullable String name,
+            @ActionInput(name = "coolHoldTemp", description = "The temperature at which to set the cool vacation hold.") @Nullable QuantityType<Temperature> coolHoldTemp,
+            @ActionInput(name = "heatHoldTemp", description = "The temperature at which to set the heat vacation hold.") @Nullable QuantityType<Temperature> heatHoldTemp,
+            @ActionInput(name = "startDateTime", description = "(opt) The start date/time in thermostat time.") @Nullable Date startDateTime,
+            @ActionInput(name = "endDateTime", description = "(opt) The end date in thermostat time.") @Nullable Date endDateTime,
+            @ActionInput(name = "fan", description = "(opt) The fan mode during the vacation. Values: auto, on Default: auto") @Nullable String fan,
+            @ActionInput(name = "fanMinOnTime", description = "(opt) The minimum number of minutes to run the fan each hour. Range: 0-60, Default: 0") @Nullable Number fanMinOnTime) {
+        LOGGER.debug("EcobeeActions: Action 'Create Vacation' called");
+        EcobeeThermostatBridgeHandler localHandler = handler;
+        if (localHandler == null) {
+            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
+            return false;
+        }
+        CreateVacationFunction function = new CreateVacationFunction(name, coolHoldTemp, heatHoldTemp, startDateTime,
+                endDateTime, (fan == null) ? null : FanMode.forValue(fan),
+                (fanMinOnTime == null) ? null : Integer.valueOf(fanMinOnTime.intValue()));
+        return localHandler.actionPerformFunction(function);
+    }
+
+    public static boolean createVacation(@Nullable ThingActions actions, @Nullable String name,
+            @Nullable QuantityType<Temperature> coolHoldTemp, @Nullable QuantityType<Temperature> heatHoldTemp,
+            @Nullable Date startDateTime, @Nullable Date endDateTime, @Nullable String fan,
+            @Nullable Number fanMinOnTime) {
+        if (actions instanceof EcobeeActions) {
+            return ((EcobeeActions) actions).createVacation(name, coolHoldTemp, heatHoldTemp, startDateTime,
+                    endDateTime, fan, fanMinOnTime);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of EcobeeActions");
+        }
+    }
+
+    /**
+     * The delete vacation function deletes a vacation event from a thermostat.
+     *
+     * @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/DeleteVacation.shtml">Delete
+     *      Vacation</a>
+     */
+    @RuleAction(label = "delete a vacation", description = "The delete vacation function deletes a vacation event from a thermostat.")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean deleteVacation(
+            @ActionInput(name = "name", description = "The vacation event name to delete.") @Nullable String name) {
+        LOGGER.debug("EcobeeActions: Action 'Delete Vacation' called");
+        EcobeeThermostatBridgeHandler localHandler = handler;
+        if (localHandler == null) {
+            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
+            return false;
+        }
+        DeleteVacationFunction function = new DeleteVacationFunction(name);
+        return localHandler.actionPerformFunction(function);
+    }
+
+    public static boolean deleteVacation(@Nullable ThingActions actions, @Nullable String name) {
+        if (actions instanceof EcobeeActions) {
+            return ((EcobeeActions) actions).deleteVacation(name);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of EcobeeActions");
+        }
+    }
+
+    /**
+     * The reset preferences function sets all of the user configurable settings back to the factory default values.
+     *
+     * @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/ResetPreferences.shtml">Reset
+     *      Preferences</a>
+     */
+    @RuleAction(label = "reset the preferences", description = "The reset preferences function sets all of the user configurable settings back to the factory default values.")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean resetPreferences() {
+        LOGGER.debug("EcobeeActions: Action 'Reset Preferences' called");
+        EcobeeThermostatBridgeHandler localHandler = handler;
+        if (localHandler == null) {
+            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
+            return false;
+        }
+        ResetPreferencesFunction function = new ResetPreferencesFunction();
+        return localHandler.actionPerformFunction(function);
+    }
+
+    public static boolean resetPreferences(@Nullable ThingActions actions) {
+        if (actions instanceof EcobeeActions) {
+            return ((EcobeeActions) actions).resetPreferences();
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of EcobeeActions");
+        }
+    }
+
+    /**
+     * The resume program function removes the currently running event.
+     *
+     * @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/ResumeProgram.shtml">Resume
+     *      Program</a>
+     */
+    @RuleAction(label = "resume the program", description = "Removes the currently running event providing the event is not a mandatory demand response event")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean resumeProgram(
+            @ActionInput(name = "resumeAll", description = "(opt) Should the thermostat be resumed to next event (false) or to its program (true)") @Nullable Boolean resumeAll) {
+        LOGGER.debug("EcobeeActions: Action 'Resume Program' called");
+        EcobeeThermostatBridgeHandler localHandler = handler;
+        if (localHandler == null) {
+            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
+            return false;
+        }
+        ResumeProgramFunction function = new ResumeProgramFunction(resumeAll);
+        return localHandler.actionPerformFunction(function);
+    }
+
+    public static boolean resumeProgram(@Nullable ThingActions actions, @Nullable Boolean resumeAll) {
+        if (actions instanceof EcobeeActions) {
+            return ((EcobeeActions) actions).resumeProgram(resumeAll);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of EcobeeActions");
+        }
+    }
+
+    /**
+     * The send message function allows an alert message to be sent to the thermostat.
+     *
+     * @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/SendMessage.shtml">Send
+     *      Message</a>
+     */
+    @RuleAction(label = "send a message", description = "The send message function allows an alert message to be sent to the thermostat.")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendMessage(
+            @ActionInput(name = "text", description = "The message text to send. Text will be truncated to 500 characters if longer") @Nullable String text) {
+        LOGGER.debug("EcobeeActions: Action 'SendMessage' called");
+        EcobeeThermostatBridgeHandler localHandler = handler;
+        if (localHandler == null) {
+            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
+            return false;
+        }
+        SendMessageFunction function = new SendMessageFunction(text);
+        return localHandler.actionPerformFunction(function);
+    }
+
+    public static boolean sendMessage(@Nullable ThingActions actions, @Nullable String text) {
+        if (actions instanceof EcobeeActions) {
+            return ((EcobeeActions) actions).sendMessage(text);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of EcobeeActions");
+        }
+    }
+
+    /**
+     * Set an indefinite hold using the supplied the cool and heat hold temperatures
+     *
+     * @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/SetHold.shtml">Set Hold</a>
+     */
+    @RuleAction(label = "set the thermostat into hold", description = "The set hold function sets the thermostat into a hold with the specified temperatures.")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean setHold(
+            @ActionInput(name = "coolHoldTemp", description = "The temperature at which to set the cool hold.") @Nullable QuantityType<Temperature> coolHoldTemp,
+            @ActionInput(name = "heatHoldTemp", description = "The temperature at which to set the heat hold.") @Nullable QuantityType<Temperature> heatHoldTemp) {
+        if (coolHoldTemp == null || heatHoldTemp == null) {
+            throw new IllegalArgumentException("hold temperatures cannot be null");
+        }
+        Map<String, Object> params = new HashMap<String, Object>();
+        params.put("coolHoldTemp", coolHoldTemp);
+        params.put("heatHoldTemp", heatHoldTemp);
+        return setHold(params, null, null, null, null);
+    }
+
+    public static boolean setHold(@Nullable ThingActions actions, @Nullable QuantityType<Temperature> coolHoldTemp,
+            @Nullable QuantityType<Temperature> heatHoldTemp) {
+        if (actions instanceof EcobeeActions) {
+            return ((EcobeeActions) actions).setHold(coolHoldTemp, heatHoldTemp);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of EcobeeActions");
+        }
+    }
+
+    /**
+     * Set a hold by providing the cool and heat temperatures and the number of hours.
+     */
+    @RuleAction(label = "set the thermostat into hold", description = "The set hold function sets the thermostat into a hold for the specified number of hours.")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean setHold(
+            @ActionInput(name = "coolHoldTemp", description = "The temperature at which to set the cool hold.") @Nullable QuantityType<Temperature> coolHoldTemp,
+            @ActionInput(name = "heatHoldTemp", description = "The temperature at which to set the heat hold.") @Nullable QuantityType<Temperature> heatHoldTemp,
+            @ActionInput(name = "holdHours", description = "The number of hours for the hold.") @Nullable Number holdHours) {
+        if (coolHoldTemp == null || heatHoldTemp == null) {
+            throw new IllegalArgumentException("hold temperatures cannot be null");
+        }
+        if (holdHours == null) {
+            throw new IllegalArgumentException("number of hold hours is missing");
+        }
+        Map<String, Object> params = new HashMap<String, Object>();
+        params.put("coolHoldTemp", coolHoldTemp);
+        params.put("heatHoldTemp", heatHoldTemp);
+        params.put("holdType", HoldType.HOLD_HOURS);
+        params.put("holdHours", Integer.valueOf(holdHours.intValue()));
+        return setHold(params, null, null, null, null);
+    }
+
+    public static boolean setHold(@Nullable ThingActions actions, @Nullable QuantityType<Temperature> coolHoldTemp,
+            @Nullable QuantityType<Temperature> heatHoldTemp, @Nullable Number holdHours) {
+        if (actions instanceof EcobeeActions) {
+            return ((EcobeeActions) actions).setHold(coolHoldTemp, heatHoldTemp, holdHours);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of EcobeeActions");
+        }
+    }
+
+    /**
+     * Set an indefinite hold using the supplied climateRef
+     */
+    @RuleAction(label = "set the thermostat into hold", description = "The set hold function sets the thermostat into a hold with the specified climate ref.")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean setHold(
+            @ActionInput(name = "holdClimateRef", description = "The holdClimateRef used to set the hold.") @Nullable String holdClimateRef) {
+        EcobeeThermostatBridgeHandler localHandler = handler;
+        if (localHandler == null) {
+            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
+            return false;
+        }
+        if (holdClimateRef == null || !localHandler.isValidClimateRef(holdClimateRef)) {
+            throw new IllegalArgumentException("hold climate ref is missing or invalid");
+        }
+        Map<String, Object> params = new HashMap<String, Object>();
+        params.put("holdClimateRef", holdClimateRef);
+        return setHold(params, null, null, null, null);
+    }
+
+    public static boolean setHold(@Nullable ThingActions actions, @Nullable String holdClimateRef) {
+        if (actions instanceof EcobeeActions) {
+            return ((EcobeeActions) actions).setHold(holdClimateRef);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of EcobeeActions");
+        }
+    }
+
+    /**
+     * Set a hold using the supplied climateRef for the supplied number of hours.
+     */
+    @RuleAction(label = "set the thermostat into hold", description = "The set hold function sets the thermostat into a hold with the specified climate ref.")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean setHold(
+            @ActionInput(name = "holdClimateRef", description = "The holdClimateRef used to set the hold.") @Nullable String holdClimateRef,
+            @ActionInput(name = "holdHours", description = "The number of hours for the hold.") @Nullable Number holdHours) {
+        if (holdHours == null) {
+            throw new IllegalArgumentException("number of hold hours is missing");
+        }
+        EcobeeThermostatBridgeHandler localHandler = handler;
+        if (localHandler == null) {
+            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
+            return false;
+        }
+        if (holdClimateRef == null || !localHandler.isValidClimateRef(holdClimateRef)) {
+            throw new IllegalArgumentException("hold climate ref is missing or invalid");
+        }
+        Map<String, Object> params = new HashMap<String, Object>();
+        params.put("holdClimateRef", holdClimateRef);
+        params.put("holdType", HoldType.HOLD_HOURS);
+        params.put("holdHours", Integer.valueOf(holdHours.intValue()));
+        return setHold(params, null, null, null, null);
+    }
+
+    public static boolean setHold(@Nullable ThingActions actions, @Nullable String holdClimateRef,
+            @Nullable Number holdHours) {
+        if (actions instanceof EcobeeActions) {
+            return ((EcobeeActions) actions).setHold(holdClimateRef, holdHours);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of EcobeeActions");
+        }
+    }
+
+    /**
+     * Set a hold
+     */
+    @RuleAction(label = "set the thermostat into hold", description = "The set hold function sets the thermostat into a hold with the specified temperature or climate ref.")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean setHold(
+            @ActionInput(name = "coolHoldTemp", description = "(opt) The temperature at which to set the cool hold.") @Nullable QuantityType<Temperature> coolHoldTemp,
+            @ActionInput(name = "heatHoldTemp", description = "(opt) The temperature at which to set the heat hold.") @Nullable QuantityType<Temperature> heatHoldTemp,
+            @ActionInput(name = "holdClimateRef", description = "(opt) The Climate to use as reference for setting the coolHoldTemp, heatHoldTemp and fan settings for this hold. If this value is passed the coolHoldTemp and heatHoldTemp are not required.") @Nullable String holdClimateRef,
+            @ActionInput(name = "startDateTime", description = "(opt) The start date in thermostat time.") @Nullable Date startDateTime,
+            @ActionInput(name = "endDateTime", description = "(opt) The end date in thermostat time.") @Nullable Date endDateTime,
+            @ActionInput(name = "holdType", description = "(opt) The hold duration type. Valid values: dateTime, nextTransition, indefinite, holdHours.") @Nullable String holdType,
+            @ActionInput(name = "holdHours", description = "(opt) The number of hours to hold for, used and required if holdType='holdHours'.") @Nullable Number holdHours) {
+        Map<String, Object> params = new HashMap<String, Object>();
+        if (coolHoldTemp != null) {
+            params.put("coolHoldTemp", coolHoldTemp);
+        }
+        if (heatHoldTemp != null) {
+            params.put("heatHoldTemp", heatHoldTemp);
+        }
+        if (holdClimateRef != null) {
+            params.put("holdClimateRef", holdClimateRef);
+        }
+        return setHold(params, holdType, holdHours, startDateTime, endDateTime);
+    }
+
+    public static boolean setHold(@Nullable ThingActions actions, @Nullable QuantityType<Temperature> coolHoldTemp,
+            @Nullable QuantityType<Temperature> heatHoldTemp, @Nullable String holdClimateRef,
+            @Nullable Date startDateTime, @Nullable Date endDateTime, @Nullable String holdType,
+            @Nullable Number holdHours) {
+        if (actions instanceof EcobeeActions) {
+            return ((EcobeeActions) actions).setHold(coolHoldTemp, heatHoldTemp, holdClimateRef, startDateTime,
+                    endDateTime, holdType, holdHours);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of EcobeeActions");
+        }
+    }
+
+    /**
+     * Set a hold by providing a parameter map
+     */
+    @RuleAction(label = "set the thermostat into hold", description = "The set hold function sets the thermostat into a hold with the specified event parameters.")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean setHold(
+            @ActionInput(name = "params", description = "The map of hold parameters.") @Nullable Map<String, Object> params,
+            @ActionInput(name = "holdType", description = "(opt) The hold duration type. Valid values: dateTime, nextTransition, indefinite, holdHours.") @Nullable String holdType,
+            @ActionInput(name = "holdHours", description = "(opt) The number of hours to hold for, used and required if holdType='holdHours'.") @Nullable Number holdHours,
+            @ActionInput(name = "startDateTime", description = "(opt) The start date in thermostat time.") @Nullable Date startDateTime,
+            @ActionInput(name = "endDateTime", description = "(opt) The end date in thermostat time.") @Nullable Date endDateTime) {
+        LOGGER.debug("EcobeeActions: Action 'SetHold' called");
+        if (params == null) {
+            throw new IllegalArgumentException("params cannot be null");
+        }
+        EventDTO event = new EventDTO();
+        for (String key : params.keySet()) {
+            Object value = params.get(key);
+            switch (key) {
+                case "isOccupied":
+                    event.isOccupied = ((Boolean) value);
+                    break;
+                case "isCoolOff":
+                    event.isCoolOff = ((Boolean) value);
+                    break;
+                case "isHeatOff":
+                    event.isHeatOff = ((Boolean) value);
+                    break;
+                case "coolHoldTemp":
+                    event.coolHoldTemp = EcobeeUtils.convertQuantityTypeToEcobeeTemp(value);
+                    break;
+                case "heatHoldTemp":
+                    event.heatHoldTemp = EcobeeUtils.convertQuantityTypeToEcobeeTemp(value);
+                    break;
+                case "fan":
+                    event.fan = FanMode.forValue((String) value).toString();
+                    break;
+                case "vent":
+                    event.vent = VentilatorMode.forValue((String) value).toString();
+                    break;
+                case "ventilatorMinOnTime":
+                    event.ventilatorMinOnTime = ((Integer) value);
+                    break;
+                case "isOptional":
+                    event.isOptional = ((Boolean) value);
+                    break;
+                case "isTemperatureRelative":
+                    event.isTemperatureRelative = ((Boolean) value);
+                    break;
+                case "coolRelativeTemp":
+                    event.coolRelativeTemp = EcobeeUtils.convertQuantityTypeToEcobeeTemp(value);
+                    break;
+                case "heatRelativeTemp":
+                    event.heatRelativeTemp = EcobeeUtils.convertQuantityTypeToEcobeeTemp(value);
+                    break;
+                case "isTemperatureAbsolute":
+                    event.isTemperatureAbsolute = ((Boolean) value);
+                    break;
+                case "fanMinOnTime":
+                    event.fanMinOnTime = ((Integer) value);
+                    break;
+                case "holdClimateRef":
+                    event.holdClimateRef = ((String) value);
+                    break;
+                default:
+                    LOGGER.warn("Unrecognized event field '{}' with value '{}' ignored.", key, value);
+                    break;
+            }
+        }
+        EcobeeThermostatBridgeHandler localHandler = handler;
+        if (localHandler == null) {
+            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
+            return false;
+        }
+        SetHoldFunction function = new SetHoldFunction(event, (holdType == null) ? null : HoldType.forValue(holdType),
+                (holdHours == null) ? null : holdHours.intValue(), startDateTime, endDateTime);
+        return localHandler.actionPerformFunction(function);
+    }
+
+    public static boolean setHold(@Nullable ThingActions actions, @Nullable Map<String, Object> params,
+            @Nullable String holdType, @Nullable Number holdHours, @Nullable Date startDateTime,
+            @Nullable Date endDateTime) {
+        if (actions instanceof EcobeeActions) {
+            return ((EcobeeActions) actions).setHold(params, holdType, holdHours, startDateTime, endDateTime);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of EcobeeActions");
+        }
+    }
+
+    /**
+     * The set occupied function may only be used by EMS thermostats. The function switches a thermostat from occupied
+     * mode to unoccupied, or vice versa.
+     *
+     * @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/SetOccupied.shtml">Set
+     *      Occupied</a>
+     */
+    @RuleAction(label = "switch the thermostat occupancy", description = "The function switches a thermostat from occupied mode to unoccupied, or vice versa (EMS MODELS ONLY).")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean setOccupied(
+            @ActionInput(name = "occupied", description = "The climate to use for the temperature, occupied (true) or unoccupied (false).") @Nullable Boolean occupied,
+            @ActionInput(name = "startDateTime", description = "(opt) The start date in thermostat time.") @Nullable Date startDateTime,
+            @ActionInput(name = "endDateTime", description = "(opt) The end date in thermostat time.") @Nullable Date endDateTime,
+            @ActionInput(name = "holdType", description = "(opt) The hold duration type. Valid values: dateTime, nextTransition, indefinite, holdHours.") @Nullable String holdType,
+            @ActionInput(name = "holdHours", description = "(opt) The number of hours to hold for, used and required if holdType='holdHours'.") @Nullable Number holdHours) {
+        LOGGER.debug("EcobeeActions: Action 'Set Occupied' called");
+        EcobeeThermostatBridgeHandler localHandler = handler;
+        if (localHandler == null) {
+            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
+            return false;
+        }
+        SetOccupiedFunction function = new SetOccupiedFunction(occupied, startDateTime, endDateTime,
+                (holdType == null) ? null : HoldType.forValue(holdType),
+                (holdHours == null) ? null : Integer.valueOf(holdHours.intValue()));
+        return localHandler.actionPerformFunction(function);
+    }
+
+    public static boolean setOccupied(@Nullable ThingActions actions, @Nullable Boolean occupied,
+            @Nullable Date startDateTime, @Nullable Date endDateTime, @Nullable String holdType,
+            @Nullable Number holdHours) {
+        if (actions instanceof EcobeeActions) {
+            return ((EcobeeActions) actions).setOccupied(occupied, startDateTime, endDateTime, holdType, holdHours);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of EcobeeActions");
+        }
+    }
+
+    /**
+     * The update sensor function allows the caller to update the name of an ecobee3 remote sensor.
+     *
+     * @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/UpdateSensor.shtml">Update
+     *      Sensor</a>
+     */
+    @RuleAction(label = "update a remote sensor name", description = "The update sensor function allows the caller to update the name of an ecobee3 remote sensor.")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean updateSensor(
+            @ActionInput(name = "name", description = "The updated name to give the sensor. Has a max length of 32, but shorter is recommended.") @Nullable String name,
+            @ActionInput(name = "deviceId", description = "The deviceId for the sensor, typically this indicates the enclosure and corresponds to the ThermostatRemoteSensor.id field. For example: rs:100") @Nullable String deviceId,
+            @ActionInput(name = "sensorId", description = "The identifier for the sensor within the enclosure. Corresponds to the RemoteSensorCapability.id. For example: 1") @Nullable String sensorId) {
+        LOGGER.debug("EcobeeActions: Action 'UpdateSensor' called");
+        EcobeeThermostatBridgeHandler localHandler = handler;
+        if (localHandler == null) {
+            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
+            return false;
+        }
+        UpdateSensorFunction function = new UpdateSensorFunction(name, deviceId, sensorId);
+        return localHandler.actionPerformFunction(function);
+    }
+
+    public static boolean updateSensor(@Nullable ThingActions actions, @Nullable String name, @Nullable String deviceId,
+            @Nullable String sensorId) {
+        if (actions instanceof EcobeeActions) {
+            return ((EcobeeActions) actions).updateSensor(name, deviceId, sensorId);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of EcobeeActions");
+        }
+    }
+
+    /**
+     * Get the alerts list. Returns a JSON string containing all the alerts.
+     */
+    @RuleAction(label = "get the alerts", description = "Get the alerts list.")
+    public @ActionOutput(name = "alerts", type = "java.lang.String") @Nullable String getAlerts() {
+        LOGGER.debug("EcobeeActions: Action 'Get Alerts' called");
+        EcobeeThermostatBridgeHandler localHandler = handler;
+        if (localHandler == null) {
+            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
+            return null;
+        }
+        return localHandler.getAlerts();
+    }
+
+    public static @Nullable String getAlerts(@Nullable ThingActions actions) {
+        if (actions instanceof EcobeeActions) {
+            return ((EcobeeActions) actions).getAlerts();
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of EcobeeActions");
+        }
+    }
+
+    /**
+     * Get the events list. Returns a JSON string contains all events.
+     */
+    @RuleAction(label = "get the events", description = "Get the events list.")
+    public @ActionOutput(name = "events", type = "java.lang.String") @Nullable String getEvents() {
+        LOGGER.debug("EcobeeActions: Action 'Get Events' called");
+        EcobeeThermostatBridgeHandler localHandler = handler;
+        if (localHandler == null) {
+            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
+            return null;
+        }
+        return localHandler.getEvents();
+    }
+
+    public static @Nullable String getEvents(@Nullable ThingActions actions) {
+        if (actions instanceof EcobeeActions) {
+            return ((EcobeeActions) actions).getEvents();
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of EcobeeActions");
+        }
+    }
+
+    /**
+     * Get a list of climates. Returns a JSON string contains all climates.
+     */
+    @RuleAction(label = "get the climates", description = "Get a list of climates.")
+    public @ActionOutput(name = "climates", type = "java.lang.String") @Nullable String getClimates() {
+        LOGGER.debug("EcobeeActions: Action 'Get Climates' called");
+        EcobeeThermostatBridgeHandler localHandler = handler;
+        if (localHandler == null) {
+            LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
+            return null;
+        }
+        return localHandler.getClimates();
+    }
+
+    public static @Nullable String getClimates(@Nullable ThingActions actions) {
+        if (actions instanceof EcobeeActions) {
+            return ((EcobeeActions) actions).getClimates();
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of EcobeeActions");
+        }
+    }
+}
index 1e2b2d17f0895026e09e2b607ae99b7f9f39c566..a6df47edbc289a4fb179bffeccc46d69ac1fd6bc 100644 (file)
@@ -32,7 +32,7 @@ import javax.measure.Unit;
 import org.apache.commons.lang.WordUtils;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.ecobee.action.EcobeeActions;
+import org.openhab.binding.ecobee.internal.action.EcobeeActions;
 import org.openhab.binding.ecobee.internal.api.EcobeeApi;
 import org.openhab.binding.ecobee.internal.config.EcobeeThermostatConfiguration;
 import org.openhab.binding.ecobee.internal.discovery.SensorDiscoveryService;
diff --git a/bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/actions/Enigma2Actions.java b/bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/actions/Enigma2Actions.java
deleted file mode 100644 (file)
index e2806fa..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.enigma2.actions;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.enigma2.handler.Enigma2Handler;
-import org.openhab.binding.enigma2.internal.Enigma2BindingConstants;
-import org.openhab.core.automation.annotation.ActionInput;
-import org.openhab.core.automation.annotation.RuleAction;
-import org.openhab.core.thing.binding.ThingActions;
-import org.openhab.core.thing.binding.ThingActionsScope;
-import org.openhab.core.thing.binding.ThingHandler;
-
-/**
- * This is the automation engine actions handler service for the
- * enigma2 actions.
- *
- * @author Guido Dolfen - Initial contribution
- */
-@ThingActionsScope(name = "enigma2")
-@NonNullByDefault
-public class Enigma2Actions implements ThingActions, IEnigma2Actions {
-    private @Nullable Enigma2Handler handler;
-
-    @Override
-    public void setThingHandler(@Nullable ThingHandler handler) {
-        this.handler = (Enigma2Handler) handler;
-    }
-
-    @Override
-    public @Nullable Enigma2Handler getThingHandler() {
-        return this.handler;
-    }
-
-    @Override
-    @RuleAction(label = "@text/actions.enigma2.send-rc-button.label", description = "@text/actions.enigma2.send-rc-button.description")
-    @SuppressWarnings("null")
-    public void sendRcCommand(
-            @ActionInput(name = "rcButton", label = "@text/actions-input.enigma2.rc-button.label", description = "@text/actions-input.enigma2.rc-button.description") String rcButton) {
-        handler.sendRcCommand(rcButton);
-    }
-
-    @Override
-    @RuleAction(label = "@text/actions.enigma2.send-info.label", description = "@text/actions.enigma2.send-info.description")
-    @SuppressWarnings("null")
-    public void sendInfo(
-            @ActionInput(name = "text", label = "@text/actions-input.enigma2.text.label", description = "@text/actions-input.enigma2.text.description") String text) {
-        handler.sendInfo(Enigma2BindingConstants.MESSAGE_TIMEOUT, text);
-    }
-
-    @Override
-    @RuleAction(label = "@text/actions.enigma2.send-info.label", description = "@text/actions.enigma2.send-info.description")
-    @SuppressWarnings("null")
-    public void sendInfo(
-            @ActionInput(name = "text", label = "@text/actions-input.enigma2.text.label", description = "@text/actions-input.enigma2.text.description") String text,
-            @ActionInput(name = "timeout", label = "@text/actions-input.enigma2.timeout.label", description = "@text/actions-input.enigma2.timeout.description") int timeout) {
-        handler.sendInfo(timeout, text);
-    }
-
-    @Override
-    @RuleAction(label = "@text/actions.enigma2.send-warning.label", description = "@text/actions.enigma2.send-warning.description")
-    @SuppressWarnings("null")
-    public void sendWarning(
-            @ActionInput(name = "text", label = "@text/actions-input.enigma2.text.label", description = "@text/actions-input.enigma2.text.description") String text) {
-        handler.sendWarning(Enigma2BindingConstants.MESSAGE_TIMEOUT, text);
-    }
-
-    @Override
-    @RuleAction(label = "@text/actions.enigma2.send-warning.label", description = "@text/actions.enigma2.send-warning.description")
-    @SuppressWarnings("null")
-    public void sendWarning(
-            @ActionInput(name = "text", label = "@text/actions-input.enigma2.text.label", description = "@text/actions-input.enigma2.text.description") String text,
-            @ActionInput(name = "timeout", label = "@text/actions-input.enigma2.timeout.label", description = "@text/actions-input.enigma2.timeout.description") int timeout) {
-        handler.sendWarning(timeout, text);
-    }
-
-    @Override
-    @RuleAction(label = "@text/actions.enigma2.send-error.label", description = "@text/actions.enigma2.send-error.description")
-    @SuppressWarnings("null")
-    public void sendError(
-            @ActionInput(name = "text", label = "@text/actions-input.enigma2.text.label", description = "@text/actions-input.enigma2.text.description") String text) {
-        handler.sendError(Enigma2BindingConstants.MESSAGE_TIMEOUT, text);
-    }
-
-    @Override
-    @RuleAction(label = "@text/actions.enigma2.send-error.label", description = "@text/actions.enigma2.send-error.description")
-    @SuppressWarnings("null")
-    public void sendError(
-            @ActionInput(name = "text", label = "@text/actions-input.enigma2.text.label", description = "@text/actions-input.enigma2.text.description") String text,
-            @ActionInput(name = "timeout", label = "@text/actions-input.enigma2.timeout.label", description = "@text/actions-input.enigma2.timeout.description") int timeout) {
-        handler.sendError(timeout, text);
-    }
-
-    @Override
-    @RuleAction(label = "@text/actions.enigma2.send-error.label", description = "@text/actions.enigma2.send-question.description")
-    @SuppressWarnings("null")
-    public void sendQuestion(
-            @ActionInput(name = "text", label = "@text/actions-input.enigma2.text.label", description = "@text/actions-input.enigma2.text.description") String text) {
-        handler.sendQuestion(Enigma2BindingConstants.MESSAGE_TIMEOUT, text);
-    }
-
-    @Override
-    @RuleAction(label = "@text/actions.enigma2.send-error.label", description = "@text/actions.enigma2.send-question.description")
-    @SuppressWarnings("null")
-    public void sendQuestion(
-            @ActionInput(name = "text", label = "@text/actions-input.enigma2.text.label", description = "@text/actions-input.enigma2.text.description") String text,
-            @ActionInput(name = "timeout", label = "@text/actions-input.enigma2.timeout.label", description = "@text/actions-input.enigma2.timeout.description") int timeout) {
-        handler.sendQuestion(timeout, text);
-    }
-
-    // delegation methods for "legacy" rule support
-    public static void sendRcCommand(@Nullable ThingActions actions, String rcButton) {
-        invokeMethodOf(actions).sendRcCommand(rcButton);
-    }
-
-    public static void sendInfo(@Nullable ThingActions actions, String info) {
-        invokeMethodOf(actions).sendInfo(info);
-    }
-
-    public static void sendInfo(@Nullable ThingActions actions, String info, int timeout) {
-        invokeMethodOf(actions).sendInfo(info, timeout);
-    }
-
-    public static void sendWarning(@Nullable ThingActions actions, String warning) {
-        invokeMethodOf(actions).sendWarning(warning);
-    }
-
-    public static void sendWarning(@Nullable ThingActions actions, String warning, int timeout) {
-        invokeMethodOf(actions).sendWarning(warning, timeout);
-    }
-
-    public static void sendError(@Nullable ThingActions actions, String error) {
-        invokeMethodOf(actions).sendError(error);
-    }
-
-    public static void sendError(@Nullable ThingActions actions, String error, int timeout) {
-        invokeMethodOf(actions).sendError(error, timeout);
-    }
-
-    public static void sendQuestion(@Nullable ThingActions actions, String text) {
-        invokeMethodOf(actions).sendQuestion(text);
-    }
-
-    public static void sendQuestion(@Nullable ThingActions actions, String text, int timeout) {
-        invokeMethodOf(actions).sendQuestion(text, timeout);
-    }
-
-    private static IEnigma2Actions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(Enigma2Actions.class.getName())) {
-            if (actions instanceof IEnigma2Actions) {
-                return (IEnigma2Actions) actions;
-            } else {
-                return (IEnigma2Actions) Proxy.newProxyInstance(IEnigma2Actions.class.getClassLoader(),
-                        new Class[] { IEnigma2Actions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
-        }
-        throw new IllegalArgumentException("Actions is not an instance of Enigma2Actions");
-    }
-}
diff --git a/bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/actions/IEnigma2Actions.java b/bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/actions/IEnigma2Actions.java
deleted file mode 100644 (file)
index 591d256..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.enigma2.actions;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * The {@link IEnigma2Actions} defines the interface for all thing actions supported by the binding.
- *
- * @author Guido Dolfen - Initial contribution
- */
-@NonNullByDefault
-public interface IEnigma2Actions {
-    void sendRcCommand(String rcButton);
-
-    void sendInfo(String text);
-
-    void sendInfo(String text, int timeout);
-
-    void sendWarning(String text);
-
-    void sendWarning(String text, int timeout);
-
-    void sendError(String text);
-
-    void sendError(String text, int timeout);
-
-    void sendQuestion(String text);
-
-    void sendQuestion(String text, int timeout);
-}
diff --git a/bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/handler/Enigma2Handler.java b/bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/handler/Enigma2Handler.java
deleted file mode 100644 (file)
index 8bf4294..0000000
+++ /dev/null
@@ -1,302 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.enigma2.handler;
-
-import static org.openhab.binding.enigma2.internal.Enigma2BindingConstants.*;
-
-import java.time.LocalDateTime;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Optional;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.enigma2.actions.Enigma2Actions;
-import org.openhab.binding.enigma2.internal.Enigma2Client;
-import org.openhab.binding.enigma2.internal.Enigma2Configuration;
-import org.openhab.binding.enigma2.internal.Enigma2RemoteKey;
-import org.openhab.core.library.types.*;
-import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingStatus;
-import org.openhab.core.thing.ThingStatusDetail;
-import org.openhab.core.thing.binding.BaseThingHandler;
-import org.openhab.core.thing.binding.ThingHandlerService;
-import org.openhab.core.types.Command;
-import org.openhab.core.types.RefreshType;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link Enigma2Handler} is responsible for handling commands, which are
- * sent to one of the channels.
- *
- * @author Guido Dolfen - Initial contribution
- */
-@NonNullByDefault
-public class Enigma2Handler extends BaseThingHandler {
-    private final Logger logger = LoggerFactory.getLogger(Enigma2Handler.class);
-    private Enigma2Configuration configuration = new Enigma2Configuration();
-    private Optional<Enigma2Client> enigma2Client = Optional.empty();
-    private @Nullable ScheduledFuture<?> refreshJob;
-    private LocalDateTime lastAnswerTime = LocalDateTime.now();
-
-    public Enigma2Handler(Thing thing) {
-        super(thing);
-    }
-
-    @Override
-    public void initialize() {
-        configuration = getConfigAs(Enigma2Configuration.class);
-        if (configuration.host.isEmpty()) {
-            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "host must not be empty");
-        } else if (configuration.timeout <= 0 || configuration.timeout > 300) {
-            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
-                    "timeout must be between 0 and 300 seconds");
-        } else if (configuration.refreshInterval <= 0 || configuration.refreshInterval > 3600) {
-            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
-                    "refreshInterval must be between 0 and 3600 seconds");
-        }
-        enigma2Client = Optional.of(new Enigma2Client(configuration.host, configuration.user, configuration.password,
-                configuration.timeout));
-        refreshJob = scheduler.scheduleWithFixedDelay(this::refresh, 2, configuration.refreshInterval,
-                TimeUnit.SECONDS);
-    }
-
-    private void refresh() {
-        getEnigma2Client().ifPresent(client -> {
-            boolean online = client.refresh();
-            if (online) {
-                updateStatus(ThingStatus.ONLINE);
-                updateState(CHANNEL_POWER, client.isPower() ? OnOffType.ON : OnOffType.OFF);
-                updateState(CHANNEL_MUTE, client.isMute() ? OnOffType.ON : OnOffType.OFF);
-                updateState(CHANNEL_VOLUME, new PercentType(client.getVolume()));
-                updateState(CHANNEL_CHANNEL, new StringType(client.getChannel()));
-                updateState(CHANNEL_TITLE, new StringType(client.getTitle()));
-                updateState(CHANNEL_DESCRIPTION, new StringType(client.getDescription()));
-                if (lastAnswerTime.isBefore(client.getLastAnswerTime())) {
-                    lastAnswerTime = client.getLastAnswerTime();
-                    updateState(CHANNEL_ANSWER, new StringType(client.getAnswer()));
-                }
-            } else {
-                updateStatus(ThingStatus.OFFLINE);
-            }
-        });
-    }
-
-    @Override
-    public void dispose() {
-        ScheduledFuture<?> job = this.refreshJob;
-        if (job != null) {
-            job.cancel(true);
-        }
-        this.refreshJob = null;
-    }
-
-    @Override
-    public void handleCommand(ChannelUID channelUID, Command command) {
-        logger.debug("handleCommand({},{})", channelUID, command);
-        getEnigma2Client().ifPresent(client -> {
-            switch (channelUID.getId()) {
-                case CHANNEL_POWER:
-                    handlePower(channelUID, command, client);
-                    break;
-                case CHANNEL_CHANNEL:
-                    handleChannel(channelUID, command, client);
-                    break;
-                case CHANNEL_MEDIA_PLAYER:
-                    handleMediaPlayer(channelUID, command);
-                    break;
-                case CHANNEL_MEDIA_STOP:
-                    handleMediaStop(channelUID, command);
-                    break;
-                case CHANNEL_MUTE:
-                    handleMute(channelUID, command, client);
-                    break;
-                case CHANNEL_VOLUME:
-                    handleVolume(channelUID, command, client);
-                    break;
-                case CHANNEL_TITLE:
-                    handleTitle(channelUID, command, client);
-                    break;
-                case CHANNEL_DESCRIPTION:
-                    handleDescription(channelUID, command, client);
-                    break;
-                case CHANNEL_ANSWER:
-                    handleAnswer(channelUID, command, client);
-                    break;
-                default:
-                    logger.debug("Channel {} is not supported", channelUID);
-                    break;
-            }
-        });
-    }
-
-    private void handleVolume(ChannelUID channelUID, Command command, Enigma2Client client) {
-        if (command instanceof RefreshType) {
-            client.refreshVolume();
-            updateState(channelUID, new PercentType(client.getVolume()));
-        } else if (command instanceof PercentType) {
-            client.setVolume(((PercentType) command).intValue());
-        } else if (command instanceof DecimalType) {
-            client.setVolume(((DecimalType) command).intValue());
-        } else {
-            logger.info("Channel {} only accepts PercentType, DecimalType, RefreshType. Type was {}.", channelUID,
-                    command.getClass());
-        }
-    }
-
-    private void handleMute(ChannelUID channelUID, Command command, Enigma2Client client) {
-        if (command instanceof RefreshType) {
-            client.refreshVolume();
-            updateState(channelUID, client.isMute() ? OnOffType.ON : OnOffType.OFF);
-        } else if (OnOffType.ON.equals(command)) {
-            client.setMute(true);
-        } else if (OnOffType.OFF.equals(command)) {
-            client.setMute(false);
-        } else {
-            logger.info("Channel {} only accepts OnOffType, RefreshType. Type was {}.", channelUID, command.getClass());
-        }
-    }
-
-    private void handleAnswer(ChannelUID channelUID, Command command, Enigma2Client client) {
-        if (command instanceof RefreshType) {
-            client.refreshAnswer();
-            if (lastAnswerTime.isBefore(client.getLastAnswerTime())) {
-                lastAnswerTime = client.getLastAnswerTime();
-                updateState(channelUID, new StringType(client.getAnswer()));
-            }
-        } else {
-            logger.info("Channel {} only accepts RefreshType. Type was {}.", channelUID, command.getClass());
-        }
-    }
-
-    private void handleMediaStop(ChannelUID channelUID, Command command) {
-        if (command instanceof RefreshType) {
-            return;
-        } else if (command instanceof OnOffType) {
-            sendRcCommand(Enigma2RemoteKey.STOP);
-        } else {
-            logger.info("Channel {} only accepts OnOffType, RefreshType. Type was {}.", channelUID, command.getClass());
-        }
-    }
-
-    private void handleMediaPlayer(ChannelUID channelUID, Command command) {
-        if (RefreshType.REFRESH == command) {
-            return;
-        } else if (PlayPauseType.PLAY == command) {
-            sendRcCommand(Enigma2RemoteKey.PLAY);
-        } else if (PlayPauseType.PAUSE == command) {
-            sendRcCommand(Enigma2RemoteKey.PAUSE);
-        } else if (NextPreviousType.NEXT == command) {
-            sendRcCommand(Enigma2RemoteKey.FAST_FORWARD);
-        } else if (NextPreviousType.PREVIOUS == command) {
-            sendRcCommand(Enigma2RemoteKey.FAST_BACKWARD);
-        } else {
-            logger.info("Channel {} only accepts PlayPauseType, NextPreviousType, RefreshType. Type was {}.",
-                    channelUID, command.getClass());
-        }
-    }
-
-    private void handleChannel(ChannelUID channelUID, Command command, Enigma2Client client) {
-        if (command instanceof RefreshType) {
-            client.refreshChannel();
-            updateState(channelUID, new StringType(client.getChannel()));
-        } else if (command instanceof StringType) {
-            client.setChannel(command.toString());
-        } else {
-            logger.info("Channel {} only accepts StringType, RefreshType. Type was {}.", channelUID,
-                    command.getClass());
-        }
-    }
-
-    private void handleTitle(ChannelUID channelUID, Command command, Enigma2Client client) {
-        if (command instanceof RefreshType) {
-            client.refreshEpg();
-            updateState(channelUID, new StringType(client.getTitle()));
-        } else {
-            logger.info("Channel {} only accepts RefreshType. Type was {}.", channelUID, command.getClass());
-        }
-    }
-
-    private void handleDescription(ChannelUID channelUID, Command command, Enigma2Client client) {
-        if (command instanceof RefreshType) {
-            client.refreshEpg();
-            updateState(channelUID, new StringType(client.getDescription()));
-        } else {
-            logger.info("Channel {} only accepts RefreshType. Type was {}.", channelUID, command.getClass());
-        }
-    }
-
-    private void handlePower(ChannelUID channelUID, Command command, Enigma2Client client) {
-        if (RefreshType.REFRESH == command) {
-            client.refreshPower();
-            updateState(channelUID, client.isPower() ? OnOffType.ON : OnOffType.OFF);
-        } else if (OnOffType.ON == command) {
-            client.setPower(true);
-        } else if (OnOffType.OFF == command) {
-            client.setPower(false);
-        } else {
-            logger.info("Channel {} only accepts OnOffType, RefreshType. Type was {}.", channelUID, command.getClass());
-        }
-    }
-
-    public void sendRcCommand(String rcButton) {
-        logger.debug("sendRcCommand({})", rcButton);
-        try {
-            Enigma2RemoteKey remoteKey = Enigma2RemoteKey.valueOf(rcButton);
-            sendRcCommand(remoteKey);
-        } catch (IllegalArgumentException ex) {
-            logger.warn("{} is not a valid value for button - available are: {}", rcButton,
-                    Stream.of(Enigma2RemoteKey.values()).map(b -> b.name()).collect(Collectors.joining(", ")));
-        }
-    }
-
-    private void sendRcCommand(Enigma2RemoteKey remoteKey) {
-        getEnigma2Client().ifPresent(client -> client.sendRcCommand(remoteKey.getValue()));
-    }
-
-    public void sendInfo(int timeout, String text) {
-        getEnigma2Client().ifPresent(client -> client.sendInfo(timeout, text));
-    }
-
-    public void sendWarning(int timeout, String text) {
-        getEnigma2Client().ifPresent(client -> client.sendWarning(timeout, text));
-    }
-
-    public void sendError(int timeout, String text) {
-        getEnigma2Client().ifPresent(client -> client.sendError(timeout, text));
-    }
-
-    public void sendQuestion(int timeout, String text) {
-        getEnigma2Client().ifPresent(client -> client.sendQuestion(timeout, text));
-    }
-
-    @Override
-    public Collection<Class<? extends ThingHandlerService>> getServices() {
-        return Collections.singleton(Enigma2Actions.class);
-    }
-
-    /**
-     * Getter for Test-Injection
-     * 
-     * @return Enigma2Client.
-     */
-    Optional<Enigma2Client> getEnigma2Client() {
-        return enigma2Client;
-    }
-}
index 002faee91a1c5d72084881fc8e7ad9b5fffddb9a..1e8a6a7b0d142cc0371dbb6bfc374596b9ba2f6a 100644 (file)
@@ -16,7 +16,7 @@ import static org.openhab.binding.enigma2.internal.Enigma2BindingConstants.*;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.enigma2.handler.Enigma2Handler;
+import org.openhab.binding.enigma2.internal.handler.Enigma2Handler;
 import org.openhab.core.thing.Thing;
 import org.openhab.core.thing.ThingTypeUID;
 import org.openhab.core.thing.binding.BaseThingHandlerFactory;
diff --git a/bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/internal/actions/Enigma2Actions.java b/bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/internal/actions/Enigma2Actions.java
new file mode 100644 (file)
index 0000000..258fd23
--- /dev/null
@@ -0,0 +1,185 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.enigma2.internal.actions;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.enigma2.internal.Enigma2BindingConstants;
+import org.openhab.binding.enigma2.internal.handler.Enigma2Handler;
+import org.openhab.core.automation.annotation.ActionInput;
+import org.openhab.core.automation.annotation.RuleAction;
+import org.openhab.core.thing.binding.ThingActions;
+import org.openhab.core.thing.binding.ThingActionsScope;
+import org.openhab.core.thing.binding.ThingHandler;
+
+/**
+ * This is the automation engine actions handler service for the
+ * enigma2 actions.
+ *
+ * @author Guido Dolfen - Initial contribution
+ */
+@ThingActionsScope(name = "enigma2")
+@NonNullByDefault
+public class Enigma2Actions implements ThingActions {
+    private @Nullable Enigma2Handler handler;
+
+    @Override
+    public void setThingHandler(@Nullable ThingHandler handler) {
+        this.handler = (Enigma2Handler) handler;
+    }
+
+    @Override
+    public @Nullable Enigma2Handler getThingHandler() {
+        return handler;
+    }
+
+    @RuleAction(label = "@text/actions.enigma2.send-rc-button.label", description = "@text/actions.enigma2.send-rc-button.description")
+    @SuppressWarnings("null")
+    public void sendRcCommand(
+            @ActionInput(name = "rcButton", label = "@text/actions-input.enigma2.rc-button.label", description = "@text/actions-input.enigma2.rc-button.description") String rcButton) {
+        handler.sendRcCommand(rcButton);
+    }
+
+    @RuleAction(label = "@text/actions.enigma2.send-info.label", description = "@text/actions.enigma2.send-info.description")
+    @SuppressWarnings("null")
+    public void sendInfo(
+            @ActionInput(name = "text", label = "@text/actions-input.enigma2.text.label", description = "@text/actions-input.enigma2.text.description") String text) {
+        handler.sendInfo(Enigma2BindingConstants.MESSAGE_TIMEOUT, text);
+    }
+
+    @RuleAction(label = "@text/actions.enigma2.send-info.label", description = "@text/actions.enigma2.send-info.description")
+    @SuppressWarnings("null")
+    public void sendInfo(
+            @ActionInput(name = "text", label = "@text/actions-input.enigma2.text.label", description = "@text/actions-input.enigma2.text.description") String text,
+            @ActionInput(name = "timeout", label = "@text/actions-input.enigma2.timeout.label", description = "@text/actions-input.enigma2.timeout.description") int timeout) {
+        handler.sendInfo(timeout, text);
+    }
+
+    @RuleAction(label = "@text/actions.enigma2.send-warning.label", description = "@text/actions.enigma2.send-warning.description")
+    @SuppressWarnings("null")
+    public void sendWarning(
+            @ActionInput(name = "text", label = "@text/actions-input.enigma2.text.label", description = "@text/actions-input.enigma2.text.description") String text) {
+        handler.sendWarning(Enigma2BindingConstants.MESSAGE_TIMEOUT, text);
+    }
+
+    @RuleAction(label = "@text/actions.enigma2.send-warning.label", description = "@text/actions.enigma2.send-warning.description")
+    @SuppressWarnings("null")
+    public void sendWarning(
+            @ActionInput(name = "text", label = "@text/actions-input.enigma2.text.label", description = "@text/actions-input.enigma2.text.description") String text,
+            @ActionInput(name = "timeout", label = "@text/actions-input.enigma2.timeout.label", description = "@text/actions-input.enigma2.timeout.description") int timeout) {
+        handler.sendWarning(timeout, text);
+    }
+
+    @RuleAction(label = "@text/actions.enigma2.send-error.label", description = "@text/actions.enigma2.send-error.description")
+    @SuppressWarnings("null")
+    public void sendError(
+            @ActionInput(name = "text", label = "@text/actions-input.enigma2.text.label", description = "@text/actions-input.enigma2.text.description") String text) {
+        handler.sendError(Enigma2BindingConstants.MESSAGE_TIMEOUT, text);
+    }
+
+    @RuleAction(label = "@text/actions.enigma2.send-error.label", description = "@text/actions.enigma2.send-error.description")
+    @SuppressWarnings("null")
+    public void sendError(
+            @ActionInput(name = "text", label = "@text/actions-input.enigma2.text.label", description = "@text/actions-input.enigma2.text.description") String text,
+            @ActionInput(name = "timeout", label = "@text/actions-input.enigma2.timeout.label", description = "@text/actions-input.enigma2.timeout.description") int timeout) {
+        handler.sendError(timeout, text);
+    }
+
+    @RuleAction(label = "@text/actions.enigma2.send-error.label", description = "@text/actions.enigma2.send-question.description")
+    @SuppressWarnings("null")
+    public void sendQuestion(
+            @ActionInput(name = "text", label = "@text/actions-input.enigma2.text.label", description = "@text/actions-input.enigma2.text.description") String text) {
+        handler.sendQuestion(Enigma2BindingConstants.MESSAGE_TIMEOUT, text);
+    }
+
+    @RuleAction(label = "@text/actions.enigma2.send-error.label", description = "@text/actions.enigma2.send-question.description")
+    @SuppressWarnings("null")
+    public void sendQuestion(
+            @ActionInput(name = "text", label = "@text/actions-input.enigma2.text.label", description = "@text/actions-input.enigma2.text.description") String text,
+            @ActionInput(name = "timeout", label = "@text/actions-input.enigma2.timeout.label", description = "@text/actions-input.enigma2.timeout.description") int timeout) {
+        handler.sendQuestion(timeout, text);
+    }
+
+    // delegation methods for "legacy" rule support
+    public static void sendRcCommand(@Nullable ThingActions actions, String rcButton) {
+        if (actions instanceof Enigma2Actions) {
+            ((Enigma2Actions) actions).sendRcCommand(rcButton);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of Enigma2Actions");
+        }
+    }
+
+    public static void sendInfo(@Nullable ThingActions actions, String info) {
+        if (actions instanceof Enigma2Actions) {
+            ((Enigma2Actions) actions).sendInfo(info);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of Enigma2Actions");
+        }
+    }
+
+    public static void sendInfo(@Nullable ThingActions actions, String info, int timeout) {
+        if (actions instanceof Enigma2Actions) {
+            ((Enigma2Actions) actions).sendInfo(info, timeout);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of Enigma2Actions");
+        }
+    }
+
+    public static void sendWarning(@Nullable ThingActions actions, String warning) {
+        if (actions instanceof Enigma2Actions) {
+            ((Enigma2Actions) actions).sendWarning(warning);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of Enigma2Actions");
+        }
+    }
+
+    public static void sendWarning(@Nullable ThingActions actions, String warning, int timeout) {
+        if (actions instanceof Enigma2Actions) {
+            ((Enigma2Actions) actions).sendWarning(warning, timeout);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of Enigma2Actions");
+        }
+    }
+
+    public static void sendError(@Nullable ThingActions actions, String error) {
+        if (actions instanceof Enigma2Actions) {
+            ((Enigma2Actions) actions).sendError(error);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of Enigma2Actions");
+        }
+    }
+
+    public static void sendError(@Nullable ThingActions actions, String error, int timeout) {
+        if (actions instanceof Enigma2Actions) {
+            ((Enigma2Actions) actions).sendError(error, timeout);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of Enigma2Actions");
+        }
+    }
+
+    public static void sendQuestion(@Nullable ThingActions actions, String text) {
+        if (actions instanceof Enigma2Actions) {
+            ((Enigma2Actions) actions).sendQuestion(text);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of Enigma2Actions");
+        }
+    }
+
+    public static void sendQuestion(@Nullable ThingActions actions, String text, int timeout) {
+        if (actions instanceof Enigma2Actions) {
+            ((Enigma2Actions) actions).sendQuestion(text, timeout);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of Enigma2Actions");
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/internal/handler/Enigma2Handler.java b/bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/internal/handler/Enigma2Handler.java
new file mode 100644 (file)
index 0000000..1b7171a
--- /dev/null
@@ -0,0 +1,302 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.enigma2.internal.handler;
+
+import static org.openhab.binding.enigma2.internal.Enigma2BindingConstants.*;
+
+import java.time.LocalDateTime;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Optional;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.enigma2.internal.Enigma2Client;
+import org.openhab.binding.enigma2.internal.Enigma2Configuration;
+import org.openhab.binding.enigma2.internal.Enigma2RemoteKey;
+import org.openhab.binding.enigma2.internal.actions.Enigma2Actions;
+import org.openhab.core.library.types.*;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.binding.BaseThingHandler;
+import org.openhab.core.thing.binding.ThingHandlerService;
+import org.openhab.core.types.Command;
+import org.openhab.core.types.RefreshType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link Enigma2Handler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author Guido Dolfen - Initial contribution
+ */
+@NonNullByDefault
+public class Enigma2Handler extends BaseThingHandler {
+    private final Logger logger = LoggerFactory.getLogger(Enigma2Handler.class);
+    private Enigma2Configuration configuration = new Enigma2Configuration();
+    private Optional<Enigma2Client> enigma2Client = Optional.empty();
+    private @Nullable ScheduledFuture<?> refreshJob;
+    private LocalDateTime lastAnswerTime = LocalDateTime.now();
+
+    public Enigma2Handler(Thing thing) {
+        super(thing);
+    }
+
+    @Override
+    public void initialize() {
+        configuration = getConfigAs(Enigma2Configuration.class);
+        if (configuration.host.isEmpty()) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "host must not be empty");
+        } else if (configuration.timeout <= 0 || configuration.timeout > 300) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+                    "timeout must be between 0 and 300 seconds");
+        } else if (configuration.refreshInterval <= 0 || configuration.refreshInterval > 3600) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+                    "refreshInterval must be between 0 and 3600 seconds");
+        }
+        enigma2Client = Optional.of(new Enigma2Client(configuration.host, configuration.user, configuration.password,
+                configuration.timeout));
+        refreshJob = scheduler.scheduleWithFixedDelay(this::refresh, 2, configuration.refreshInterval,
+                TimeUnit.SECONDS);
+    }
+
+    private void refresh() {
+        getEnigma2Client().ifPresent(client -> {
+            boolean online = client.refresh();
+            if (online) {
+                updateStatus(ThingStatus.ONLINE);
+                updateState(CHANNEL_POWER, client.isPower() ? OnOffType.ON : OnOffType.OFF);
+                updateState(CHANNEL_MUTE, client.isMute() ? OnOffType.ON : OnOffType.OFF);
+                updateState(CHANNEL_VOLUME, new PercentType(client.getVolume()));
+                updateState(CHANNEL_CHANNEL, new StringType(client.getChannel()));
+                updateState(CHANNEL_TITLE, new StringType(client.getTitle()));
+                updateState(CHANNEL_DESCRIPTION, new StringType(client.getDescription()));
+                if (lastAnswerTime.isBefore(client.getLastAnswerTime())) {
+                    lastAnswerTime = client.getLastAnswerTime();
+                    updateState(CHANNEL_ANSWER, new StringType(client.getAnswer()));
+                }
+            } else {
+                updateStatus(ThingStatus.OFFLINE);
+            }
+        });
+    }
+
+    @Override
+    public void dispose() {
+        ScheduledFuture<?> job = this.refreshJob;
+        if (job != null) {
+            job.cancel(true);
+        }
+        this.refreshJob = null;
+    }
+
+    @Override
+    public void handleCommand(ChannelUID channelUID, Command command) {
+        logger.debug("handleCommand({},{})", channelUID, command);
+        getEnigma2Client().ifPresent(client -> {
+            switch (channelUID.getId()) {
+                case CHANNEL_POWER:
+                    handlePower(channelUID, command, client);
+                    break;
+                case CHANNEL_CHANNEL:
+                    handleChannel(channelUID, command, client);
+                    break;
+                case CHANNEL_MEDIA_PLAYER:
+                    handleMediaPlayer(channelUID, command);
+                    break;
+                case CHANNEL_MEDIA_STOP:
+                    handleMediaStop(channelUID, command);
+                    break;
+                case CHANNEL_MUTE:
+                    handleMute(channelUID, command, client);
+                    break;
+                case CHANNEL_VOLUME:
+                    handleVolume(channelUID, command, client);
+                    break;
+                case CHANNEL_TITLE:
+                    handleTitle(channelUID, command, client);
+                    break;
+                case CHANNEL_DESCRIPTION:
+                    handleDescription(channelUID, command, client);
+                    break;
+                case CHANNEL_ANSWER:
+                    handleAnswer(channelUID, command, client);
+                    break;
+                default:
+                    logger.debug("Channel {} is not supported", channelUID);
+                    break;
+            }
+        });
+    }
+
+    private void handleVolume(ChannelUID channelUID, Command command, Enigma2Client client) {
+        if (command instanceof RefreshType) {
+            client.refreshVolume();
+            updateState(channelUID, new PercentType(client.getVolume()));
+        } else if (command instanceof PercentType) {
+            client.setVolume(((PercentType) command).intValue());
+        } else if (command instanceof DecimalType) {
+            client.setVolume(((DecimalType) command).intValue());
+        } else {
+            logger.info("Channel {} only accepts PercentType, DecimalType, RefreshType. Type was {}.", channelUID,
+                    command.getClass());
+        }
+    }
+
+    private void handleMute(ChannelUID channelUID, Command command, Enigma2Client client) {
+        if (command instanceof RefreshType) {
+            client.refreshVolume();
+            updateState(channelUID, client.isMute() ? OnOffType.ON : OnOffType.OFF);
+        } else if (OnOffType.ON.equals(command)) {
+            client.setMute(true);
+        } else if (OnOffType.OFF.equals(command)) {
+            client.setMute(false);
+        } else {
+            logger.info("Channel {} only accepts OnOffType, RefreshType. Type was {}.", channelUID, command.getClass());
+        }
+    }
+
+    private void handleAnswer(ChannelUID channelUID, Command command, Enigma2Client client) {
+        if (command instanceof RefreshType) {
+            client.refreshAnswer();
+            if (lastAnswerTime.isBefore(client.getLastAnswerTime())) {
+                lastAnswerTime = client.getLastAnswerTime();
+                updateState(channelUID, new StringType(client.getAnswer()));
+            }
+        } else {
+            logger.info("Channel {} only accepts RefreshType. Type was {}.", channelUID, command.getClass());
+        }
+    }
+
+    private void handleMediaStop(ChannelUID channelUID, Command command) {
+        if (command instanceof RefreshType) {
+            return;
+        } else if (command instanceof OnOffType) {
+            sendRcCommand(Enigma2RemoteKey.STOP);
+        } else {
+            logger.info("Channel {} only accepts OnOffType, RefreshType. Type was {}.", channelUID, command.getClass());
+        }
+    }
+
+    private void handleMediaPlayer(ChannelUID channelUID, Command command) {
+        if (RefreshType.REFRESH == command) {
+            return;
+        } else if (PlayPauseType.PLAY == command) {
+            sendRcCommand(Enigma2RemoteKey.PLAY);
+        } else if (PlayPauseType.PAUSE == command) {
+            sendRcCommand(Enigma2RemoteKey.PAUSE);
+        } else if (NextPreviousType.NEXT == command) {
+            sendRcCommand(Enigma2RemoteKey.FAST_FORWARD);
+        } else if (NextPreviousType.PREVIOUS == command) {
+            sendRcCommand(Enigma2RemoteKey.FAST_BACKWARD);
+        } else {
+            logger.info("Channel {} only accepts PlayPauseType, NextPreviousType, RefreshType. Type was {}.",
+                    channelUID, command.getClass());
+        }
+    }
+
+    private void handleChannel(ChannelUID channelUID, Command command, Enigma2Client client) {
+        if (command instanceof RefreshType) {
+            client.refreshChannel();
+            updateState(channelUID, new StringType(client.getChannel()));
+        } else if (command instanceof StringType) {
+            client.setChannel(command.toString());
+        } else {
+            logger.info("Channel {} only accepts StringType, RefreshType. Type was {}.", channelUID,
+                    command.getClass());
+        }
+    }
+
+    private void handleTitle(ChannelUID channelUID, Command command, Enigma2Client client) {
+        if (command instanceof RefreshType) {
+            client.refreshEpg();
+            updateState(channelUID, new StringType(client.getTitle()));
+        } else {
+            logger.info("Channel {} only accepts RefreshType. Type was {}.", channelUID, command.getClass());
+        }
+    }
+
+    private void handleDescription(ChannelUID channelUID, Command command, Enigma2Client client) {
+        if (command instanceof RefreshType) {
+            client.refreshEpg();
+            updateState(channelUID, new StringType(client.getDescription()));
+        } else {
+            logger.info("Channel {} only accepts RefreshType. Type was {}.", channelUID, command.getClass());
+        }
+    }
+
+    private void handlePower(ChannelUID channelUID, Command command, Enigma2Client client) {
+        if (RefreshType.REFRESH == command) {
+            client.refreshPower();
+            updateState(channelUID, client.isPower() ? OnOffType.ON : OnOffType.OFF);
+        } else if (OnOffType.ON == command) {
+            client.setPower(true);
+        } else if (OnOffType.OFF == command) {
+            client.setPower(false);
+        } else {
+            logger.info("Channel {} only accepts OnOffType, RefreshType. Type was {}.", channelUID, command.getClass());
+        }
+    }
+
+    public void sendRcCommand(String rcButton) {
+        logger.debug("sendRcCommand({})", rcButton);
+        try {
+            Enigma2RemoteKey remoteKey = Enigma2RemoteKey.valueOf(rcButton);
+            sendRcCommand(remoteKey);
+        } catch (IllegalArgumentException ex) {
+            logger.warn("{} is not a valid value for button - available are: {}", rcButton,
+                    Stream.of(Enigma2RemoteKey.values()).map(b -> b.name()).collect(Collectors.joining(", ")));
+        }
+    }
+
+    private void sendRcCommand(Enigma2RemoteKey remoteKey) {
+        getEnigma2Client().ifPresent(client -> client.sendRcCommand(remoteKey.getValue()));
+    }
+
+    public void sendInfo(int timeout, String text) {
+        getEnigma2Client().ifPresent(client -> client.sendInfo(timeout, text));
+    }
+
+    public void sendWarning(int timeout, String text) {
+        getEnigma2Client().ifPresent(client -> client.sendWarning(timeout, text));
+    }
+
+    public void sendError(int timeout, String text) {
+        getEnigma2Client().ifPresent(client -> client.sendError(timeout, text));
+    }
+
+    public void sendQuestion(int timeout, String text) {
+        getEnigma2Client().ifPresent(client -> client.sendQuestion(timeout, text));
+    }
+
+    @Override
+    public Collection<Class<? extends ThingHandlerService>> getServices() {
+        return Collections.singleton(Enigma2Actions.class);
+    }
+
+    /**
+     * Getter for Test-Injection
+     * 
+     * @return Enigma2Client.
+     */
+    Optional<Enigma2Client> getEnigma2Client() {
+        return enigma2Client;
+    }
+}
diff --git a/bundles/org.openhab.binding.enigma2/src/test/java/org/openhab/binding/enigma2/actions/Enigma2ActionsTest.java b/bundles/org.openhab.binding.enigma2/src/test/java/org/openhab/binding/enigma2/actions/Enigma2ActionsTest.java
deleted file mode 100644 (file)
index 827c76a..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.enigma2.actions;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.mockito.Mockito.*;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.openhab.binding.enigma2.handler.Enigma2Handler;
-import org.openhab.binding.enigma2.internal.Enigma2BindingConstants;
-
-/**
- * The {@link Enigma2ActionsTest} class is responsible for testing {@link Enigma2Actions}.
- *
- * @author Guido Dolfen - Initial contribution
- */
-@SuppressWarnings("null")
-@NonNullByDefault
-public class Enigma2ActionsTest {
-    @Nullable
-    private Enigma2Actions enigma2Actions;
-    @Nullable
-    private Enigma2Handler enigma2Handler;
-    public static final String SOME_TEXT = "some Text";
-
-    @BeforeEach
-    public void setUp() {
-        enigma2Handler = mock(Enigma2Handler.class);
-        enigma2Actions = new Enigma2Actions();
-        enigma2Actions.setThingHandler(enigma2Handler);
-    }
-
-    @Test
-    public void testGetThingHandler() {
-        assertThat(enigma2Actions.getThingHandler(), is(enigma2Handler));
-    }
-
-    @Test
-    public void testSendRcCommand() {
-        enigma2Actions.sendRcCommand("KEY_1");
-        verify(enigma2Handler).sendRcCommand("KEY_1");
-    }
-
-    @Test
-    public void testSendInfo() {
-        enigma2Actions.sendInfo(SOME_TEXT);
-        verify(enigma2Handler).sendInfo(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
-    }
-
-    @Test
-    public void testSendInfoTimeout() {
-        enigma2Actions.sendInfo(SOME_TEXT, 10);
-        verify(enigma2Handler).sendInfo(10, SOME_TEXT);
-    }
-
-    @Test
-    public void testSendError() {
-        enigma2Actions.sendError(SOME_TEXT);
-        verify(enigma2Handler).sendError(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
-    }
-
-    @Test
-    public void testSendErrorTimeout() {
-        enigma2Actions.sendError(SOME_TEXT, 10);
-        verify(enigma2Handler).sendError(10, SOME_TEXT);
-    }
-
-    @Test
-    public void testSendWarning() {
-        enigma2Actions.sendWarning(SOME_TEXT);
-        verify(enigma2Handler).sendWarning(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
-    }
-
-    @Test
-    public void testSendWarningTimeout() {
-        enigma2Actions.sendWarning(SOME_TEXT, 10);
-        verify(enigma2Handler).sendWarning(10, SOME_TEXT);
-    }
-
-    @Test
-    public void testSendQuestion() {
-        enigma2Actions.sendQuestion(SOME_TEXT);
-        verify(enigma2Handler).sendQuestion(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
-    }
-
-    @Test
-    public void testSendQuestionTimeout() {
-        enigma2Actions.sendQuestion(SOME_TEXT, 10);
-        verify(enigma2Handler).sendQuestion(10, SOME_TEXT);
-    }
-
-    @Test
-    public void testSendRcCommandStatic() {
-        Enigma2Actions.sendRcCommand(enigma2Actions, "KEY_1");
-        verify(enigma2Handler).sendRcCommand("KEY_1");
-    }
-
-    @Test
-    public void testSendRcCommandStaticWithException() {
-        assertThrows(IllegalArgumentException.class, () -> Enigma2Actions.sendRcCommand(null, "KEY_1"));
-    }
-
-    @Test
-    public void testSendInfoStatic() {
-        Enigma2Actions.sendInfo(enigma2Actions, SOME_TEXT);
-        verify(enigma2Handler).sendInfo(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
-    }
-
-    @Test
-    public void testSendInfoTimeoutStatic() {
-        Enigma2Actions.sendInfo(enigma2Actions, SOME_TEXT, 10);
-        verify(enigma2Handler).sendInfo(10, SOME_TEXT);
-    }
-
-    @Test
-    public void testSendInfoStaticWithException() {
-        assertThrows(IllegalArgumentException.class, () -> Enigma2Actions.sendInfo(null, SOME_TEXT));
-    }
-
-    @Test
-    public void testSendErrorStatic() {
-        Enigma2Actions.sendError(enigma2Actions, SOME_TEXT);
-        verify(enigma2Handler).sendError(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
-    }
-
-    @Test
-    public void testSendErrorTimeoutStatic() {
-        Enigma2Actions.sendError(enigma2Actions, SOME_TEXT, 10);
-        verify(enigma2Handler).sendError(10, SOME_TEXT);
-    }
-
-    @Test
-    public void testSendErrorStaticWithException() {
-        assertThrows(IllegalArgumentException.class, () -> Enigma2Actions.sendError(null, SOME_TEXT));
-    }
-
-    @Test
-    public void testSendWarningStatic() {
-        Enigma2Actions.sendWarning(enigma2Actions, SOME_TEXT);
-        verify(enigma2Handler).sendWarning(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
-    }
-
-    @Test
-    public void testSendWarningTimeoutStatic() {
-        Enigma2Actions.sendWarning(enigma2Actions, SOME_TEXT, 10);
-        verify(enigma2Handler).sendWarning(10, SOME_TEXT);
-    }
-
-    @Test
-    public void testSendWarningStaticWithException() {
-        assertThrows(IllegalArgumentException.class, () -> Enigma2Actions.sendWarning(null, SOME_TEXT));
-    }
-
-    @Test
-    public void testSendQuestionStatic() {
-        Enigma2Actions.sendQuestion(enigma2Actions, SOME_TEXT);
-        verify(enigma2Handler).sendQuestion(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
-    }
-
-    @Test
-    public void testSendQuestionTimeoutStatic() {
-        Enigma2Actions.sendQuestion(enigma2Actions, SOME_TEXT, 10);
-        verify(enigma2Handler).sendQuestion(10, SOME_TEXT);
-    }
-
-    @Test
-    public void testSendQuestionStaticWithException() {
-        assertThrows(IllegalArgumentException.class, () -> Enigma2Actions.sendQuestion(null, SOME_TEXT));
-    }
-}
diff --git a/bundles/org.openhab.binding.enigma2/src/test/java/org/openhab/binding/enigma2/handler/Enigma2HandlerTest.java b/bundles/org.openhab.binding.enigma2/src/test/java/org/openhab/binding/enigma2/handler/Enigma2HandlerTest.java
deleted file mode 100644 (file)
index f5b2e63..0000000
+++ /dev/null
@@ -1,386 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.enigma2.handler;
-
-import static org.eclipse.jdt.annotation.Checks.requireNonNull;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.*;
-import static org.mockito.Mockito.*;
-
-import java.time.LocalDateTime;
-import java.time.temporal.ChronoUnit;
-import java.util.Optional;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.openhab.binding.enigma2.actions.Enigma2Actions;
-import org.openhab.binding.enigma2.internal.Enigma2BindingConstants;
-import org.openhab.binding.enigma2.internal.Enigma2Client;
-import org.openhab.binding.enigma2.internal.Enigma2Configuration;
-import org.openhab.binding.enigma2.internal.Enigma2RemoteKey;
-import org.openhab.core.config.core.Configuration;
-import org.openhab.core.library.types.DecimalType;
-import org.openhab.core.library.types.NextPreviousType;
-import org.openhab.core.library.types.OnOffType;
-import org.openhab.core.library.types.PercentType;
-import org.openhab.core.library.types.PlayPauseType;
-import org.openhab.core.library.types.StringType;
-import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.binding.ThingHandlerCallback;
-import org.openhab.core.types.RefreshType;
-
-/**
- * The {@link Enigma2HandlerTest} class is responsible for testing {@link Enigma2Handler}.
- *
- * @author Guido Dolfen - Initial contribution
- */
-@SuppressWarnings({ "null", "unchecked" })
-@NonNullByDefault
-public class Enigma2HandlerTest {
-    public static final String CHANNEL_UID_PREFIX = "enigma2:device:192_168_0_3:";
-    public static final String SOME_TEXT = "some Text";
-    @Nullable
-    private Enigma2Handler enigma2Handler;
-    @Nullable
-    private Enigma2Client enigma2Client;
-    @Nullable
-    private Thing thing;
-    @Nullable
-    private Configuration configuration;
-    @Nullable
-    private ThingHandlerCallback callback;
-
-    @BeforeEach
-    public void setUp() {
-        enigma2Client = mock(Enigma2Client.class);
-        thing = mock(Thing.class);
-        callback = mock(ThingHandlerCallback.class);
-        configuration = mock(Configuration.class);
-        when(thing.getConfiguration()).thenReturn(requireNonNull(configuration));
-        when(configuration.as(Enigma2Configuration.class)).thenReturn(new Enigma2Configuration());
-        enigma2Handler = spy(new Enigma2Handler(requireNonNull(thing)));
-        enigma2Handler.setCallback(callback);
-        when(enigma2Handler.getEnigma2Client()).thenReturn(Optional.of(requireNonNull(enigma2Client)));
-    }
-
-    @Test
-    public void testSendRcCommand() {
-        enigma2Handler.sendRcCommand("KEY_1");
-        verify(enigma2Client).sendRcCommand(Enigma2RemoteKey.KEY_1.getValue());
-    }
-
-    @Test
-    public void testSendInfo() {
-        enigma2Handler.sendInfo(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
-        verify(enigma2Client).sendInfo(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
-    }
-
-    @Test
-    public void testSendWarning() {
-        enigma2Handler.sendWarning(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
-        verify(enigma2Client).sendWarning(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
-    }
-
-    @Test
-    public void testSendError() {
-        enigma2Handler.sendError(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
-        verify(enigma2Client).sendError(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
-    }
-
-    @Test
-    public void testSendQuestion() {
-        enigma2Handler.sendQuestion(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
-        verify(enigma2Client).sendQuestion(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
-    }
-
-    @Test
-    public void testGetEnigma2Client() {
-        enigma2Handler = new Enigma2Handler(requireNonNull(thing));
-        assertThat(enigma2Handler.getEnigma2Client(), is(Optional.empty()));
-    }
-
-    @Test
-    public void testGetServices() {
-        enigma2Handler = new Enigma2Handler(requireNonNull(thing));
-        assertThat(enigma2Handler.getServices(), contains(Enigma2Actions.class));
-    }
-
-    @Test
-    public void testSendRcCommandUnsupported() {
-        enigma2Handler.sendRcCommand("KEY_X");
-        verifyNoInteractions(enigma2Client);
-    }
-
-    @Test
-    public void testHandleCommandPowerRefreshFalse() {
-        when(enigma2Client.isPower()).thenReturn(false);
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_POWER);
-        enigma2Handler.handleCommand(channelUID, RefreshType.REFRESH);
-        verify(enigma2Client).refreshPower();
-        verify(callback).stateUpdated(channelUID, OnOffType.OFF);
-    }
-
-    @Test
-    public void testHandleCommandPowerRefreshTrue() {
-        when(enigma2Client.isPower()).thenReturn(true);
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_POWER);
-        enigma2Handler.handleCommand(channelUID, RefreshType.REFRESH);
-        verify(enigma2Client).refreshPower();
-        verify(callback).stateUpdated(channelUID, OnOffType.ON);
-    }
-
-    @Test
-    public void testHandleCommandPowerOn() {
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_POWER);
-        enigma2Handler.handleCommand(channelUID, OnOffType.ON);
-        verify(enigma2Client).setPower(true);
-    }
-
-    @Test
-    public void testHandleCommandPowerOff() {
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_POWER);
-        enigma2Handler.handleCommand(channelUID, OnOffType.OFF);
-        verify(enigma2Client).setPower(false);
-    }
-
-    @Test
-    public void testHandleCommandPowerUnsupported() {
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_POWER);
-        enigma2Handler.handleCommand(channelUID, PlayPauseType.PAUSE);
-        verifyNoInteractions(enigma2Client);
-    }
-
-    @Test
-    public void testHandleCommandChannelRefresh() {
-        when(enigma2Client.getChannel()).thenReturn(SOME_TEXT);
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_CHANNEL);
-        enigma2Handler.handleCommand(channelUID, RefreshType.REFRESH);
-        verify(enigma2Client).refreshChannel();
-        verify(callback).stateUpdated(channelUID, new StringType(SOME_TEXT));
-    }
-
-    @Test
-    public void testHandleCommandMuteRefreshFalse() {
-        when(enigma2Client.isMute()).thenReturn(false);
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MUTE);
-        enigma2Handler.handleCommand(channelUID, RefreshType.REFRESH);
-        verify(enigma2Client).refreshVolume();
-        verify(callback).stateUpdated(channelUID, OnOffType.OFF);
-    }
-
-    @Test
-    public void testHandleCommandMuteRefreshTrue() {
-        when(enigma2Client.isMute()).thenReturn(true);
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MUTE);
-        enigma2Handler.handleCommand(channelUID, RefreshType.REFRESH);
-        verify(enigma2Client).refreshVolume();
-        verify(callback).stateUpdated(channelUID, OnOffType.ON);
-    }
-
-    @Test
-    public void testHandleCommandMuteOn() {
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MUTE);
-        enigma2Handler.handleCommand(channelUID, OnOffType.ON);
-        verify(enigma2Client).setMute(true);
-    }
-
-    @Test
-    public void testHandleCommandMuteOff() {
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MUTE);
-        enigma2Handler.handleCommand(channelUID, OnOffType.OFF);
-        verify(enigma2Client).setMute(false);
-    }
-
-    @Test
-    public void testHandleCommandMuteUnsupported() {
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MUTE);
-        enigma2Handler.handleCommand(channelUID, PlayPauseType.PAUSE);
-        verifyNoInteractions(enigma2Client);
-    }
-
-    @Test
-    public void testHandleCommandChannelString() {
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_CHANNEL);
-        enigma2Handler.handleCommand(channelUID, new StringType(SOME_TEXT));
-        verify(enigma2Client).setChannel(SOME_TEXT);
-    }
-
-    @Test
-    public void testHandleCommandChannelUnsupported() {
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_CHANNEL);
-        enigma2Handler.handleCommand(channelUID, PlayPauseType.PAUSE);
-        verifyNoInteractions(enigma2Client);
-    }
-
-    @Test
-    public void testHandleCommandMediaPlayerRefresh() {
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MEDIA_PLAYER);
-        enigma2Handler.handleCommand(channelUID, RefreshType.REFRESH);
-        verifyNoInteractions(enigma2Client);
-    }
-
-    @Test
-    public void testHandleCommandMediaPlay() {
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MEDIA_PLAYER);
-        enigma2Handler.handleCommand(channelUID, PlayPauseType.PLAY);
-        verify(enigma2Client).sendRcCommand(Enigma2RemoteKey.PLAY.getValue());
-    }
-
-    @Test
-    public void testHandleCommandMediaPause() {
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MEDIA_PLAYER);
-        enigma2Handler.handleCommand(channelUID, PlayPauseType.PAUSE);
-        verify(enigma2Client).sendRcCommand(Enigma2RemoteKey.PAUSE.getValue());
-    }
-
-    @Test
-    public void testHandleCommandMediaNext() {
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MEDIA_PLAYER);
-        enigma2Handler.handleCommand(channelUID, NextPreviousType.NEXT);
-        verify(enigma2Client).sendRcCommand(Enigma2RemoteKey.FAST_FORWARD.getValue());
-    }
-
-    @Test
-    public void testHandleCommandMediaPrevious() {
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MEDIA_PLAYER);
-        enigma2Handler.handleCommand(channelUID, NextPreviousType.PREVIOUS);
-        verify(enigma2Client).sendRcCommand(Enigma2RemoteKey.FAST_BACKWARD.getValue());
-    }
-
-    @Test
-    public void testHandleCommandMediaPlayerUnsupported() {
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MEDIA_PLAYER);
-        enigma2Handler.handleCommand(channelUID, OnOffType.ON);
-        verifyNoInteractions(enigma2Client);
-    }
-
-    @Test
-    public void testHandleCommandMediaStopRefresh() {
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MEDIA_STOP);
-        enigma2Handler.handleCommand(channelUID, RefreshType.REFRESH);
-        verifyNoInteractions(enigma2Client);
-    }
-
-    @Test
-    public void testHandleCommandMediaStopOn() {
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MEDIA_STOP);
-        enigma2Handler.handleCommand(channelUID, OnOffType.ON);
-        verify(enigma2Client).sendRcCommand(Enigma2RemoteKey.STOP.getValue());
-    }
-
-    @Test
-    public void testHandleCommandMediaStopOff() {
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MEDIA_STOP);
-        enigma2Handler.handleCommand(channelUID, OnOffType.OFF);
-        verify(enigma2Client).sendRcCommand(Enigma2RemoteKey.STOP.getValue());
-    }
-
-    @Test
-    public void testHandleCommandMediaStopUnsupported() {
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MEDIA_STOP);
-        enigma2Handler.handleCommand(channelUID, PlayPauseType.PAUSE);
-        verifyNoInteractions(enigma2Client);
-    }
-
-    @Test
-    public void testHandleCommandTitleUnsupported() {
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_TITLE);
-        enigma2Handler.handleCommand(channelUID, PlayPauseType.PAUSE);
-        verifyNoInteractions(enigma2Client);
-    }
-
-    @Test
-    public void testHandleCommandTitleRefresh() {
-        when(enigma2Client.getTitle()).thenReturn(SOME_TEXT);
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_TITLE);
-        enigma2Handler.handleCommand(channelUID, RefreshType.REFRESH);
-        verify(enigma2Client).refreshEpg();
-        verify(callback).stateUpdated(channelUID, new StringType(SOME_TEXT));
-    }
-
-    @Test
-    public void testHandleCommandAnswerUnsupported() {
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_ANSWER);
-        enigma2Handler.handleCommand(channelUID, PlayPauseType.PAUSE);
-        verifyNoInteractions(enigma2Client);
-    }
-
-    @Test
-    public void testHandleCommandAnswerRefresh() {
-        when(enigma2Client.getAnswer()).thenReturn(SOME_TEXT);
-        when(enigma2Client.getLastAnswerTime()).thenReturn(LocalDateTime.now().plus(1, ChronoUnit.SECONDS));
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_ANSWER);
-        enigma2Handler.handleCommand(channelUID, RefreshType.REFRESH);
-        verify(enigma2Client).refreshAnswer();
-        verify(callback).stateUpdated(channelUID, new StringType(SOME_TEXT));
-    }
-
-    @Test
-    public void testHandleCommandAnswerRefreshFalse() {
-        when(enigma2Client.getAnswer()).thenReturn(SOME_TEXT);
-        when(enigma2Client.getLastAnswerTime()).thenReturn(LocalDateTime.of(2020, 1, 1, 0, 0));
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_ANSWER);
-        enigma2Handler.handleCommand(channelUID, RefreshType.REFRESH);
-        verify(enigma2Client).refreshAnswer();
-        verifyNoInteractions(callback);
-    }
-
-    @Test
-    public void testHandleCommandDescriptionUnsupported() {
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_DESCRIPTION);
-        enigma2Handler.handleCommand(channelUID, PlayPauseType.PAUSE);
-        verifyNoInteractions(enigma2Client);
-    }
-
-    @Test
-    public void testHandleCommandDescriptionRefresh() {
-        when(enigma2Client.getDescription()).thenReturn(SOME_TEXT);
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_DESCRIPTION);
-        enigma2Handler.handleCommand(channelUID, RefreshType.REFRESH);
-        verify(enigma2Client).refreshEpg();
-        verify(callback).stateUpdated(channelUID, new StringType(SOME_TEXT));
-    }
-
-    @Test
-    public void testHandleCommandVolumeRefresh() {
-        when(enigma2Client.getVolume()).thenReturn(35);
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_VOLUME);
-        enigma2Handler.handleCommand(channelUID, RefreshType.REFRESH);
-        verify(enigma2Client).refreshVolume();
-        verify(callback).stateUpdated(channelUID, new PercentType(35));
-    }
-
-    @Test
-    public void testHandleCommandVolumePercent() {
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_VOLUME);
-        enigma2Handler.handleCommand(channelUID, new PercentType(30));
-        verify(enigma2Client).setVolume(30);
-    }
-
-    @Test
-    public void testHandleCommandVolumeDecimal() {
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_VOLUME);
-        enigma2Handler.handleCommand(channelUID, new DecimalType(40));
-        verify(enigma2Client).setVolume(40);
-    }
-
-    @Test
-    public void testHandleCommandVolumeUnsupported() {
-        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_VOLUME);
-        enigma2Handler.handleCommand(channelUID, PlayPauseType.PAUSE);
-        verifyNoInteractions(enigma2Client);
-    }
-}
diff --git a/bundles/org.openhab.binding.enigma2/src/test/java/org/openhab/binding/enigma2/internal/actions/Enigma2ActionsTest.java b/bundles/org.openhab.binding.enigma2/src/test/java/org/openhab/binding/enigma2/internal/actions/Enigma2ActionsTest.java
new file mode 100644 (file)
index 0000000..2a9a7cf
--- /dev/null
@@ -0,0 +1,185 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.enigma2.internal.actions;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.*;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.openhab.binding.enigma2.internal.Enigma2BindingConstants;
+import org.openhab.binding.enigma2.internal.handler.Enigma2Handler;
+
+/**
+ * The {@link Enigma2ActionsTest} class is responsible for testing {@link Enigma2Actions}.
+ *
+ * @author Guido Dolfen - Initial contribution
+ */
+@SuppressWarnings("null")
+@NonNullByDefault
+public class Enigma2ActionsTest {
+    @Nullable
+    private Enigma2Actions enigma2Actions;
+    @Nullable
+    private Enigma2Handler enigma2Handler;
+    public static final String SOME_TEXT = "some Text";
+
+    @BeforeEach
+    public void setUp() {
+        enigma2Handler = mock(Enigma2Handler.class);
+        enigma2Actions = new Enigma2Actions();
+        enigma2Actions.setThingHandler(enigma2Handler);
+    }
+
+    @Test
+    public void testGetThingHandler() {
+        assertThat(enigma2Actions.getThingHandler(), is(enigma2Handler));
+    }
+
+    @Test
+    public void testSendRcCommand() {
+        enigma2Actions.sendRcCommand("KEY_1");
+        verify(enigma2Handler).sendRcCommand("KEY_1");
+    }
+
+    @Test
+    public void testSendInfo() {
+        enigma2Actions.sendInfo(SOME_TEXT);
+        verify(enigma2Handler).sendInfo(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
+    }
+
+    @Test
+    public void testSendInfoTimeout() {
+        enigma2Actions.sendInfo(SOME_TEXT, 10);
+        verify(enigma2Handler).sendInfo(10, SOME_TEXT);
+    }
+
+    @Test
+    public void testSendError() {
+        enigma2Actions.sendError(SOME_TEXT);
+        verify(enigma2Handler).sendError(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
+    }
+
+    @Test
+    public void testSendErrorTimeout() {
+        enigma2Actions.sendError(SOME_TEXT, 10);
+        verify(enigma2Handler).sendError(10, SOME_TEXT);
+    }
+
+    @Test
+    public void testSendWarning() {
+        enigma2Actions.sendWarning(SOME_TEXT);
+        verify(enigma2Handler).sendWarning(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
+    }
+
+    @Test
+    public void testSendWarningTimeout() {
+        enigma2Actions.sendWarning(SOME_TEXT, 10);
+        verify(enigma2Handler).sendWarning(10, SOME_TEXT);
+    }
+
+    @Test
+    public void testSendQuestion() {
+        enigma2Actions.sendQuestion(SOME_TEXT);
+        verify(enigma2Handler).sendQuestion(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
+    }
+
+    @Test
+    public void testSendQuestionTimeout() {
+        enigma2Actions.sendQuestion(SOME_TEXT, 10);
+        verify(enigma2Handler).sendQuestion(10, SOME_TEXT);
+    }
+
+    @Test
+    public void testSendRcCommandStatic() {
+        Enigma2Actions.sendRcCommand(enigma2Actions, "KEY_1");
+        verify(enigma2Handler).sendRcCommand("KEY_1");
+    }
+
+    @Test
+    public void testSendRcCommandStaticWithException() {
+        assertThrows(IllegalArgumentException.class, () -> Enigma2Actions.sendRcCommand(null, "KEY_1"));
+    }
+
+    @Test
+    public void testSendInfoStatic() {
+        Enigma2Actions.sendInfo(enigma2Actions, SOME_TEXT);
+        verify(enigma2Handler).sendInfo(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
+    }
+
+    @Test
+    public void testSendInfoTimeoutStatic() {
+        Enigma2Actions.sendInfo(enigma2Actions, SOME_TEXT, 10);
+        verify(enigma2Handler).sendInfo(10, SOME_TEXT);
+    }
+
+    @Test
+    public void testSendInfoStaticWithException() {
+        assertThrows(IllegalArgumentException.class, () -> Enigma2Actions.sendInfo(null, SOME_TEXT));
+    }
+
+    @Test
+    public void testSendErrorStatic() {
+        Enigma2Actions.sendError(enigma2Actions, SOME_TEXT);
+        verify(enigma2Handler).sendError(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
+    }
+
+    @Test
+    public void testSendErrorTimeoutStatic() {
+        Enigma2Actions.sendError(enigma2Actions, SOME_TEXT, 10);
+        verify(enigma2Handler).sendError(10, SOME_TEXT);
+    }
+
+    @Test
+    public void testSendErrorStaticWithException() {
+        assertThrows(IllegalArgumentException.class, () -> Enigma2Actions.sendError(null, SOME_TEXT));
+    }
+
+    @Test
+    public void testSendWarningStatic() {
+        Enigma2Actions.sendWarning(enigma2Actions, SOME_TEXT);
+        verify(enigma2Handler).sendWarning(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
+    }
+
+    @Test
+    public void testSendWarningTimeoutStatic() {
+        Enigma2Actions.sendWarning(enigma2Actions, SOME_TEXT, 10);
+        verify(enigma2Handler).sendWarning(10, SOME_TEXT);
+    }
+
+    @Test
+    public void testSendWarningStaticWithException() {
+        assertThrows(IllegalArgumentException.class, () -> Enigma2Actions.sendWarning(null, SOME_TEXT));
+    }
+
+    @Test
+    public void testSendQuestionStatic() {
+        Enigma2Actions.sendQuestion(enigma2Actions, SOME_TEXT);
+        verify(enigma2Handler).sendQuestion(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
+    }
+
+    @Test
+    public void testSendQuestionTimeoutStatic() {
+        Enigma2Actions.sendQuestion(enigma2Actions, SOME_TEXT, 10);
+        verify(enigma2Handler).sendQuestion(10, SOME_TEXT);
+    }
+
+    @Test
+    public void testSendQuestionStaticWithException() {
+        assertThrows(IllegalArgumentException.class, () -> Enigma2Actions.sendQuestion(null, SOME_TEXT));
+    }
+}
diff --git a/bundles/org.openhab.binding.enigma2/src/test/java/org/openhab/binding/enigma2/internal/handler/Enigma2HandlerTest.java b/bundles/org.openhab.binding.enigma2/src/test/java/org/openhab/binding/enigma2/internal/handler/Enigma2HandlerTest.java
new file mode 100644 (file)
index 0000000..f0c52bb
--- /dev/null
@@ -0,0 +1,386 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.enigma2.internal.handler;
+
+import static org.eclipse.jdt.annotation.Checks.requireNonNull;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.*;
+import static org.mockito.Mockito.*;
+
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.Optional;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.openhab.binding.enigma2.internal.Enigma2BindingConstants;
+import org.openhab.binding.enigma2.internal.Enigma2Client;
+import org.openhab.binding.enigma2.internal.Enigma2Configuration;
+import org.openhab.binding.enigma2.internal.Enigma2RemoteKey;
+import org.openhab.binding.enigma2.internal.actions.Enigma2Actions;
+import org.openhab.core.config.core.Configuration;
+import org.openhab.core.library.types.DecimalType;
+import org.openhab.core.library.types.NextPreviousType;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.library.types.PercentType;
+import org.openhab.core.library.types.PlayPauseType;
+import org.openhab.core.library.types.StringType;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.binding.ThingHandlerCallback;
+import org.openhab.core.types.RefreshType;
+
+/**
+ * The {@link Enigma2HandlerTest} class is responsible for testing {@link Enigma2Handler}.
+ *
+ * @author Guido Dolfen - Initial contribution
+ */
+@SuppressWarnings({ "null", "unchecked" })
+@NonNullByDefault
+public class Enigma2HandlerTest {
+    public static final String CHANNEL_UID_PREFIX = "enigma2:device:192_168_0_3:";
+    public static final String SOME_TEXT = "some Text";
+    @Nullable
+    private Enigma2Handler enigma2Handler;
+    @Nullable
+    private Enigma2Client enigma2Client;
+    @Nullable
+    private Thing thing;
+    @Nullable
+    private Configuration configuration;
+    @Nullable
+    private ThingHandlerCallback callback;
+
+    @BeforeEach
+    public void setUp() {
+        enigma2Client = mock(Enigma2Client.class);
+        thing = mock(Thing.class);
+        callback = mock(ThingHandlerCallback.class);
+        configuration = mock(Configuration.class);
+        when(thing.getConfiguration()).thenReturn(requireNonNull(configuration));
+        when(configuration.as(Enigma2Configuration.class)).thenReturn(new Enigma2Configuration());
+        enigma2Handler = spy(new Enigma2Handler(requireNonNull(thing)));
+        enigma2Handler.setCallback(callback);
+        when(enigma2Handler.getEnigma2Client()).thenReturn(Optional.of(requireNonNull(enigma2Client)));
+    }
+
+    @Test
+    public void testSendRcCommand() {
+        enigma2Handler.sendRcCommand("KEY_1");
+        verify(enigma2Client).sendRcCommand(Enigma2RemoteKey.KEY_1.getValue());
+    }
+
+    @Test
+    public void testSendInfo() {
+        enigma2Handler.sendInfo(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
+        verify(enigma2Client).sendInfo(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
+    }
+
+    @Test
+    public void testSendWarning() {
+        enigma2Handler.sendWarning(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
+        verify(enigma2Client).sendWarning(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
+    }
+
+    @Test
+    public void testSendError() {
+        enigma2Handler.sendError(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
+        verify(enigma2Client).sendError(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
+    }
+
+    @Test
+    public void testSendQuestion() {
+        enigma2Handler.sendQuestion(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
+        verify(enigma2Client).sendQuestion(Enigma2BindingConstants.MESSAGE_TIMEOUT, SOME_TEXT);
+    }
+
+    @Test
+    public void testGetEnigma2Client() {
+        enigma2Handler = new Enigma2Handler(requireNonNull(thing));
+        assertThat(enigma2Handler.getEnigma2Client(), is(Optional.empty()));
+    }
+
+    @Test
+    public void testGetServices() {
+        enigma2Handler = new Enigma2Handler(requireNonNull(thing));
+        assertThat(enigma2Handler.getServices(), contains(Enigma2Actions.class));
+    }
+
+    @Test
+    public void testSendRcCommandUnsupported() {
+        enigma2Handler.sendRcCommand("KEY_X");
+        verifyNoInteractions(enigma2Client);
+    }
+
+    @Test
+    public void testHandleCommandPowerRefreshFalse() {
+        when(enigma2Client.isPower()).thenReturn(false);
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_POWER);
+        enigma2Handler.handleCommand(channelUID, RefreshType.REFRESH);
+        verify(enigma2Client).refreshPower();
+        verify(callback).stateUpdated(channelUID, OnOffType.OFF);
+    }
+
+    @Test
+    public void testHandleCommandPowerRefreshTrue() {
+        when(enigma2Client.isPower()).thenReturn(true);
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_POWER);
+        enigma2Handler.handleCommand(channelUID, RefreshType.REFRESH);
+        verify(enigma2Client).refreshPower();
+        verify(callback).stateUpdated(channelUID, OnOffType.ON);
+    }
+
+    @Test
+    public void testHandleCommandPowerOn() {
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_POWER);
+        enigma2Handler.handleCommand(channelUID, OnOffType.ON);
+        verify(enigma2Client).setPower(true);
+    }
+
+    @Test
+    public void testHandleCommandPowerOff() {
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_POWER);
+        enigma2Handler.handleCommand(channelUID, OnOffType.OFF);
+        verify(enigma2Client).setPower(false);
+    }
+
+    @Test
+    public void testHandleCommandPowerUnsupported() {
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_POWER);
+        enigma2Handler.handleCommand(channelUID, PlayPauseType.PAUSE);
+        verifyNoInteractions(enigma2Client);
+    }
+
+    @Test
+    public void testHandleCommandChannelRefresh() {
+        when(enigma2Client.getChannel()).thenReturn(SOME_TEXT);
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_CHANNEL);
+        enigma2Handler.handleCommand(channelUID, RefreshType.REFRESH);
+        verify(enigma2Client).refreshChannel();
+        verify(callback).stateUpdated(channelUID, new StringType(SOME_TEXT));
+    }
+
+    @Test
+    public void testHandleCommandMuteRefreshFalse() {
+        when(enigma2Client.isMute()).thenReturn(false);
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MUTE);
+        enigma2Handler.handleCommand(channelUID, RefreshType.REFRESH);
+        verify(enigma2Client).refreshVolume();
+        verify(callback).stateUpdated(channelUID, OnOffType.OFF);
+    }
+
+    @Test
+    public void testHandleCommandMuteRefreshTrue() {
+        when(enigma2Client.isMute()).thenReturn(true);
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MUTE);
+        enigma2Handler.handleCommand(channelUID, RefreshType.REFRESH);
+        verify(enigma2Client).refreshVolume();
+        verify(callback).stateUpdated(channelUID, OnOffType.ON);
+    }
+
+    @Test
+    public void testHandleCommandMuteOn() {
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MUTE);
+        enigma2Handler.handleCommand(channelUID, OnOffType.ON);
+        verify(enigma2Client).setMute(true);
+    }
+
+    @Test
+    public void testHandleCommandMuteOff() {
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MUTE);
+        enigma2Handler.handleCommand(channelUID, OnOffType.OFF);
+        verify(enigma2Client).setMute(false);
+    }
+
+    @Test
+    public void testHandleCommandMuteUnsupported() {
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MUTE);
+        enigma2Handler.handleCommand(channelUID, PlayPauseType.PAUSE);
+        verifyNoInteractions(enigma2Client);
+    }
+
+    @Test
+    public void testHandleCommandChannelString() {
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_CHANNEL);
+        enigma2Handler.handleCommand(channelUID, new StringType(SOME_TEXT));
+        verify(enigma2Client).setChannel(SOME_TEXT);
+    }
+
+    @Test
+    public void testHandleCommandChannelUnsupported() {
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_CHANNEL);
+        enigma2Handler.handleCommand(channelUID, PlayPauseType.PAUSE);
+        verifyNoInteractions(enigma2Client);
+    }
+
+    @Test
+    public void testHandleCommandMediaPlayerRefresh() {
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MEDIA_PLAYER);
+        enigma2Handler.handleCommand(channelUID, RefreshType.REFRESH);
+        verifyNoInteractions(enigma2Client);
+    }
+
+    @Test
+    public void testHandleCommandMediaPlay() {
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MEDIA_PLAYER);
+        enigma2Handler.handleCommand(channelUID, PlayPauseType.PLAY);
+        verify(enigma2Client).sendRcCommand(Enigma2RemoteKey.PLAY.getValue());
+    }
+
+    @Test
+    public void testHandleCommandMediaPause() {
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MEDIA_PLAYER);
+        enigma2Handler.handleCommand(channelUID, PlayPauseType.PAUSE);
+        verify(enigma2Client).sendRcCommand(Enigma2RemoteKey.PAUSE.getValue());
+    }
+
+    @Test
+    public void testHandleCommandMediaNext() {
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MEDIA_PLAYER);
+        enigma2Handler.handleCommand(channelUID, NextPreviousType.NEXT);
+        verify(enigma2Client).sendRcCommand(Enigma2RemoteKey.FAST_FORWARD.getValue());
+    }
+
+    @Test
+    public void testHandleCommandMediaPrevious() {
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MEDIA_PLAYER);
+        enigma2Handler.handleCommand(channelUID, NextPreviousType.PREVIOUS);
+        verify(enigma2Client).sendRcCommand(Enigma2RemoteKey.FAST_BACKWARD.getValue());
+    }
+
+    @Test
+    public void testHandleCommandMediaPlayerUnsupported() {
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MEDIA_PLAYER);
+        enigma2Handler.handleCommand(channelUID, OnOffType.ON);
+        verifyNoInteractions(enigma2Client);
+    }
+
+    @Test
+    public void testHandleCommandMediaStopRefresh() {
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MEDIA_STOP);
+        enigma2Handler.handleCommand(channelUID, RefreshType.REFRESH);
+        verifyNoInteractions(enigma2Client);
+    }
+
+    @Test
+    public void testHandleCommandMediaStopOn() {
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MEDIA_STOP);
+        enigma2Handler.handleCommand(channelUID, OnOffType.ON);
+        verify(enigma2Client).sendRcCommand(Enigma2RemoteKey.STOP.getValue());
+    }
+
+    @Test
+    public void testHandleCommandMediaStopOff() {
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MEDIA_STOP);
+        enigma2Handler.handleCommand(channelUID, OnOffType.OFF);
+        verify(enigma2Client).sendRcCommand(Enigma2RemoteKey.STOP.getValue());
+    }
+
+    @Test
+    public void testHandleCommandMediaStopUnsupported() {
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_MEDIA_STOP);
+        enigma2Handler.handleCommand(channelUID, PlayPauseType.PAUSE);
+        verifyNoInteractions(enigma2Client);
+    }
+
+    @Test
+    public void testHandleCommandTitleUnsupported() {
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_TITLE);
+        enigma2Handler.handleCommand(channelUID, PlayPauseType.PAUSE);
+        verifyNoInteractions(enigma2Client);
+    }
+
+    @Test
+    public void testHandleCommandTitleRefresh() {
+        when(enigma2Client.getTitle()).thenReturn(SOME_TEXT);
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_TITLE);
+        enigma2Handler.handleCommand(channelUID, RefreshType.REFRESH);
+        verify(enigma2Client).refreshEpg();
+        verify(callback).stateUpdated(channelUID, new StringType(SOME_TEXT));
+    }
+
+    @Test
+    public void testHandleCommandAnswerUnsupported() {
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_ANSWER);
+        enigma2Handler.handleCommand(channelUID, PlayPauseType.PAUSE);
+        verifyNoInteractions(enigma2Client);
+    }
+
+    @Test
+    public void testHandleCommandAnswerRefresh() {
+        when(enigma2Client.getAnswer()).thenReturn(SOME_TEXT);
+        when(enigma2Client.getLastAnswerTime()).thenReturn(LocalDateTime.now().plus(1, ChronoUnit.SECONDS));
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_ANSWER);
+        enigma2Handler.handleCommand(channelUID, RefreshType.REFRESH);
+        verify(enigma2Client).refreshAnswer();
+        verify(callback).stateUpdated(channelUID, new StringType(SOME_TEXT));
+    }
+
+    @Test
+    public void testHandleCommandAnswerRefreshFalse() {
+        when(enigma2Client.getAnswer()).thenReturn(SOME_TEXT);
+        when(enigma2Client.getLastAnswerTime()).thenReturn(LocalDateTime.of(2020, 1, 1, 0, 0));
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_ANSWER);
+        enigma2Handler.handleCommand(channelUID, RefreshType.REFRESH);
+        verify(enigma2Client).refreshAnswer();
+        verifyNoInteractions(callback);
+    }
+
+    @Test
+    public void testHandleCommandDescriptionUnsupported() {
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_DESCRIPTION);
+        enigma2Handler.handleCommand(channelUID, PlayPauseType.PAUSE);
+        verifyNoInteractions(enigma2Client);
+    }
+
+    @Test
+    public void testHandleCommandDescriptionRefresh() {
+        when(enigma2Client.getDescription()).thenReturn(SOME_TEXT);
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_DESCRIPTION);
+        enigma2Handler.handleCommand(channelUID, RefreshType.REFRESH);
+        verify(enigma2Client).refreshEpg();
+        verify(callback).stateUpdated(channelUID, new StringType(SOME_TEXT));
+    }
+
+    @Test
+    public void testHandleCommandVolumeRefresh() {
+        when(enigma2Client.getVolume()).thenReturn(35);
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_VOLUME);
+        enigma2Handler.handleCommand(channelUID, RefreshType.REFRESH);
+        verify(enigma2Client).refreshVolume();
+        verify(callback).stateUpdated(channelUID, new PercentType(35));
+    }
+
+    @Test
+    public void testHandleCommandVolumePercent() {
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_VOLUME);
+        enigma2Handler.handleCommand(channelUID, new PercentType(30));
+        verify(enigma2Client).setVolume(30);
+    }
+
+    @Test
+    public void testHandleCommandVolumeDecimal() {
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_VOLUME);
+        enigma2Handler.handleCommand(channelUID, new DecimalType(40));
+        verify(enigma2Client).setVolume(40);
+    }
+
+    @Test
+    public void testHandleCommandVolumeUnsupported() {
+        ChannelUID channelUID = new ChannelUID(CHANNEL_UID_PREFIX + Enigma2BindingConstants.CHANNEL_VOLUME);
+        enigma2Handler.handleCommand(channelUID, PlayPauseType.PAUSE);
+        verifyNoInteractions(enigma2Client);
+    }
+}
diff --git a/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/action/IIpx800Actions.java b/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/action/IIpx800Actions.java
deleted file mode 100644 (file)
index 03cd5d1..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.gce.internal.action;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * The {@link IIpx800Actions} defines the interface for all thing actions supported by the binding.
- *
- * @author Gaël L'hopital - Initial contribution
- */
-@NonNullByDefault
-public interface IIpx800Actions {
-    public void resetCounter(Integer counter);
-
-    public void reset(@Nullable Integer placeholder);
-}
index acdb937a1b81848565229b2cc2e8c7bdcd726655..d27f8494d75c41c3daba4f7490c98705d7aa1555 100644 (file)
@@ -12,9 +12,6 @@
  */
 package org.openhab.binding.gce.internal.action;
 
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.gce.internal.handler.Ipx800v3Handler;
@@ -27,18 +24,13 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * The {Ipx800Actions } defines rule actions for the GCE binding.
- * <p>
- * <b>Note:</b>The static method <b>invokeMethodOf</b> handles the case where
- * the test <i>actions instanceof Ipx800Actions</i> fails. This test can fail
- * due to an issue in openHAB core v2.5.0 where the {@link Ipx800Actions} class
- * can be loaded by a different classloader than the <i>actions</i> instance.
+ * Defines rule actions for the GCE binding.
  *
  * @author Gaël L'hopital - Initial contribution
  */
 @ThingActionsScope(name = "gce")
 @NonNullByDefault
-public class Ipx800Actions implements ThingActions, IIpx800Actions {
+public class Ipx800Actions implements ThingActions {
     private final Logger logger = LoggerFactory.getLogger(Ipx800Actions.class);
 
     protected @Nullable Ipx800v3Handler handler;
@@ -56,11 +48,10 @@ public class Ipx800Actions implements ThingActions, IIpx800Actions {
 
     @Override
     public @Nullable ThingHandler getThingHandler() {
-        return this.handler;
+        return handler;
     }
 
-    @Override
-    @RuleAction(label = "GCE : Reset counter", description = "Resets to 0 value of a given counter")
+    @RuleAction(label = "reset a counter", description = "Resets to 0 value of a given counter.")
     public void resetCounter(
             @ActionInput(name = "counter", label = "Counter", required = true, description = "Id of the counter", type = "java.lang.Integer") Integer counter) {
         logger.debug("IPX800 action 'resetCounter' called");
@@ -72,8 +63,7 @@ public class Ipx800Actions implements ThingActions, IIpx800Actions {
         }
     }
 
-    @Override
-    @RuleAction(label = "GCE : Reset PLC", description = "Restarts the IPX800")
+    @RuleAction(label = "reset the PLC", description = "Restarts the IPX800.")
     public void reset(
             @ActionInput(name = "placeholder", label = "Placeholder", required = false, description = "This parameter is not used", type = "java.lang.Integer") @Nullable Integer placeholder) {
         logger.debug("IPX800 action 'reset' called");
@@ -86,29 +76,18 @@ public class Ipx800Actions implements ThingActions, IIpx800Actions {
     }
 
     public static void resetCounter(@Nullable ThingActions actions, Integer counter) {
-        invokeMethodOf(actions).resetCounter(counter);
+        if (actions instanceof Ipx800Actions) {
+            ((Ipx800Actions) actions).resetCounter(counter);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of Ipx800Actions");
+        }
     }
 
     public static void reset(@Nullable ThingActions actions, @Nullable Integer placeholder) {
-        invokeMethodOf(actions).reset(placeholder);
-    }
-
-    private static IIpx800Actions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(Ipx800Actions.class.getName())) {
-            if (actions instanceof IIpx800Actions) {
-                return (IIpx800Actions) actions;
-            } else {
-                return (IIpx800Actions) Proxy.newProxyInstance(IIpx800Actions.class.getClassLoader(),
-                        new Class[] { IIpx800Actions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
+        if (actions instanceof Ipx800Actions) {
+            ((Ipx800Actions) actions).reset(placeholder);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of Ipx800Actions");
         }
-        throw new IllegalArgumentException("Actions is not an instance of Ipx800Actions");
     }
 }
index e4e6e9c74c3ea6a65d9e2ff2dda512c2da082d52..72dbe8e7b2278a1e14bcff2bf964aab9458137c9 100644 (file)
@@ -13,8 +13,6 @@
 package org.openhab.binding.heos.internal.action;
 
 import java.io.IOException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
@@ -32,17 +30,12 @@ import org.slf4j.LoggerFactory;
 
 /**
  * The class is responsible to call corresponding action on HEOS Handler
- * <p>
- * <b>Note:</b>The static method <b>invokeMethodOf</b> handles the case where
- * the test <i>actions instanceof HeosActions</i> fails. This test can fail
- * due to an issue in openHAB core v2.5.0 where the {@link HeosActions} class
- * can be loaded by a different classloader than the <i>actions</i> instance.
  *
  * @author Martin van Wingerden - Initial contribution
  */
 @ThingActionsScope(name = "heos")
 @NonNullByDefault
-public class HeosActions implements ThingActions, IHeosActions {
+public class HeosActions implements ThingActions {
 
     private final static Logger logger = LoggerFactory.getLogger(HeosActions.class);
 
@@ -57,10 +50,11 @@ public class HeosActions implements ThingActions, IHeosActions {
 
     @Override
     public @Nullable ThingHandler getThingHandler() {
-        return this.handler;
+        return handler;
     }
 
     private @Nullable HeosFacade getConnection() throws HeosNotConnectedException {
+        HeosBridgeHandler handler = this.handler;
         if (handler == null) {
             return null;
         }
@@ -68,7 +62,6 @@ public class HeosActions implements ThingActions, IHeosActions {
         return handler.getApiConnection();
     }
 
-    @Override
     @RuleAction(label = "Play Input", description = "Play an input from another device")
     public void playInputFromPlayer(
             @ActionInput(name = "source", label = "Source Player", description = "Player used for input") @Nullable Integer sourcePlayer,
@@ -97,25 +90,10 @@ public class HeosActions implements ThingActions, IHeosActions {
 
     public static void playInputFromPlayer(@Nullable ThingActions actions, @Nullable Integer sourcePlayer,
             @Nullable String input, @Nullable Integer destinationPlayer) {
-        invokeMethodOf(actions).playInputFromPlayer(sourcePlayer, input, destinationPlayer);
-    }
-
-    private static IHeosActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(HeosActions.class.getName())) {
-            if (actions instanceof IHeosActions) {
-                return (IHeosActions) actions;
-            } else {
-                return (IHeosActions) Proxy.newProxyInstance(IHeosActions.class.getClassLoader(),
-                        new Class[] { IHeosActions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
+        if (actions instanceof HeosActions) {
+            ((HeosActions) actions).playInputFromPlayer(sourcePlayer, input, destinationPlayer);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of HeosActions");
         }
-        throw new IllegalArgumentException("Actions is not an instance of HeosActions");
     }
 }
diff --git a/bundles/org.openhab.binding.heos/src/main/java/org/openhab/binding/heos/internal/action/IHeosActions.java b/bundles/org.openhab.binding.heos/src/main/java/org/openhab/binding/heos/internal/action/IHeosActions.java
deleted file mode 100644 (file)
index 3739292..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.heos.internal.action;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * The {@link IHeosActions} defines the interface for all thing actions supported by the binding.
- *
- * @author Laurent Garnier - Initial contribution
- */
-@NonNullByDefault
-public interface IHeosActions {
-
-    public void playInputFromPlayer(@Nullable Integer sourcePlayer, @Nullable String input,
-            @Nullable Integer destinationPlayer);
-}
diff --git a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/action/ILightActions.java b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/action/ILightActions.java
deleted file mode 100644 (file)
index 912791b..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.hue.internal.action;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.core.library.types.DecimalType;
-import org.openhab.core.types.Command;
-
-/**
- * The {@link ILightActions} defines the interface for all thing actions supported by the binding.
- *
- * @author Laurent Garnier - Initial contribution
- */
-@NonNullByDefault
-public interface ILightActions {
-
-    public void fadingLightCommand(@Nullable String channel, @Nullable Command command, @Nullable DecimalType fadeTime);
-}
index 6d4942e8229089621db8ac9e791d3a52752a87fc..2e994368e772510569d623e524873d6aa1c388eb 100644 (file)
@@ -12,9 +12,6 @@
  */
 package org.openhab.binding.hue.internal.action;
 
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.hue.internal.handler.HueLightHandler;
@@ -30,18 +27,13 @@ import org.slf4j.LoggerFactory;
 
 /**
  * The {@link LightActions} defines the thing actions for the hue binding.
- * <p>
- * <b>Note:</b>The static method <b>invokeMethodOf</b> handles the case where
- * the test <i>actions instanceof LightActions</i> fails. This test can fail
- * due to an issue in openHAB core v2.5.0 where the {@link LightActions} class
- * can be loaded by a different classloader than the <i>actions</i> instance.
  *
  * @author Jochen Leopold - Initial contribution
  * @author Laurent Garnier - new method invokeMethodOf + interface ILightActions
  */
 @ThingActionsScope(name = "hue")
 @NonNullByDefault
-public class LightActions implements ThingActions, ILightActions {
+public class LightActions implements ThingActions {
     private final Logger logger = LoggerFactory.getLogger(LightActions.class);
     private @Nullable HueLightHandler handler;
 
@@ -52,10 +44,9 @@ public class LightActions implements ThingActions, ILightActions {
 
     @Override
     public @Nullable ThingHandler getThingHandler() {
-        return this.handler;
+        return handler;
     }
 
-    @Override
     @RuleAction(label = "@text/actionLabel", description = "@text/actionDesc")
     public void fadingLightCommand(
             @ActionInput(name = "channel", label = "@text/actionInputChannelLabel", description = "@text/actionInputChannelDesc") @Nullable String channel,
@@ -85,27 +76,12 @@ public class LightActions implements ThingActions, ILightActions {
         logger.debug("send LightAction to {} with {}ms of fadeTime", channel, fadeTime);
     }
 
-    private static ILightActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(LightActions.class.getName())) {
-            if (actions instanceof ILightActions) {
-                return (ILightActions) actions;
-            } else {
-                return (ILightActions) Proxy.newProxyInstance(ILightActions.class.getClassLoader(),
-                        new Class[] { ILightActions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
-        }
-        throw new IllegalArgumentException("Actions is not an instance of LightActions");
-    }
-
     public static void fadingLightCommand(@Nullable ThingActions actions, @Nullable String channel,
             @Nullable Command command, @Nullable DecimalType fadeTime) {
-        invokeMethodOf(actions).fadingLightCommand(channel, command, fadeTime);
+        if (actions instanceof LightActions) {
+            ((LightActions) actions).fadingLightCommand(channel, command, fadeTime);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of LightActions");
+        }
     }
 }
diff --git a/bundles/org.openhab.binding.kaleidescape/src/main/java/org/openhab/binding/kaleidescape/internal/IKaleidescapeThingActions.java b/bundles/org.openhab.binding.kaleidescape/src/main/java/org/openhab/binding/kaleidescape/internal/IKaleidescapeThingActions.java
deleted file mode 100644 (file)
index 3643273..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.kaleidescape.internal;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * The {@link IKaleidescapeThingActions} defines the interface for all thing actions supported by the binding.
- * These methods, parameters, and return types are explained in {@link KaleidescapeThingActions}.
- *
- * @author Michael Lobstein - Initial contribution
- */
-@NonNullByDefault
-public interface IKaleidescapeThingActions {
-
-    void sendKCommand(String kCommand);
-}
index 577422439c42a81ae2380ad9a317a1e05eb2ed70..6a5fad50bb925ecf2d8d4379058884840a31fcff 100644 (file)
@@ -12,9 +12,6 @@
  */
 package org.openhab.binding.kaleidescape.internal;
 
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.kaleidescape.internal.handler.KaleidescapeHandler;
@@ -30,16 +27,15 @@ import org.slf4j.LoggerFactory;
  * Some automation actions to be used with a {@link KaleidescapeThingActions}
  *
  * @author Michael Lobstein - initial contribution
- *
  */
 @ThingActionsScope(name = "kaleidescape")
 @NonNullByDefault
-public class KaleidescapeThingActions implements ThingActions, IKaleidescapeThingActions {
+public class KaleidescapeThingActions implements ThingActions {
     private final Logger logger = LoggerFactory.getLogger(KaleidescapeThingActions.class);
 
     private @Nullable KaleidescapeHandler handler;
 
-    @RuleAction(label = "sendKCommand", description = "Action that sends raw command to the kaleidescape zone")
+    @RuleAction(label = "send a raw command", description = "Action that sends raw command to the kaleidescape zone.")
     public void sendKCommand(@ActionInput(name = "sendKCommand") String kCommand) {
         KaleidescapeHandler localHandler = handler;
         if (localHandler != null) {
@@ -52,7 +48,11 @@ public class KaleidescapeThingActions implements ThingActions, IKaleidescapeThin
 
     /** Static alias to support the old DSL rules engine and make the action available there. */
     public static void sendKCommand(@Nullable ThingActions actions, String kCommand) throws IllegalArgumentException {
-        invokeMethodOf(actions).sendKCommand(kCommand);
+        if (actions instanceof KaleidescapeThingActions) {
+            ((KaleidescapeThingActions) actions).sendKCommand(kCommand);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of KaleidescapeThingActions");
+        }
     }
 
     @Override
@@ -62,27 +62,6 @@ public class KaleidescapeThingActions implements ThingActions, IKaleidescapeThin
 
     @Override
     public @Nullable ThingHandler getThingHandler() {
-        return this.handler;
-    }
-
-    private static IKaleidescapeThingActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(KaleidescapeThingActions.class.getName())) {
-            if (actions instanceof KaleidescapeThingActions) {
-                return (IKaleidescapeThingActions) actions;
-            } else {
-                return (IKaleidescapeThingActions) Proxy.newProxyInstance(
-                        IKaleidescapeThingActions.class.getClassLoader(),
-                        new Class[] { IKaleidescapeThingActions.class },
-                        (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
-        }
-        throw new IllegalArgumentException("Actions is not an instance of KaleidescapeThingActions");
+        return handler;
     }
 }
diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/ILcnModuleActions.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/ILcnModuleActions.java
deleted file mode 100644 (file)
index e35924a..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.lcn.internal;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * The {@link ILcnModuleActions} defines the interface for all thing actions supported by the binding.
- * These methods, parameters, and return types are explained in {@link LcnModuleActions}.
- *
- * @author Fabian Wolter - Initial contribution
- */
-@NonNullByDefault
-public interface ILcnModuleActions {
-    void hitKey(@Nullable String table, int key, @Nullable String action);
-
-    void flickerOutput(int output, int depth, int ramp, int count);
-
-    void sendDynamicText(int row, @Nullable String textInput);
-
-    void startRelayTimer(int relaynumber, double duration);
-}
index d023fcc48cfd4b3c41a2f234b6bbff7fd0ea6c8a..cad59e55746dbd9ae69dfd61c35580a3767f9fd5 100644 (file)
@@ -12,8 +12,6 @@
  */
 package org.openhab.binding.lcn.internal;
 
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
 import java.nio.ByteBuffer;
 import java.util.Arrays;
 
@@ -40,7 +38,7 @@ import org.slf4j.LoggerFactory;
  */
 @ThingActionsScope(name = "lcn")
 @NonNullByDefault
-public class LcnModuleActions implements ThingActions, ILcnModuleActions {
+public class LcnModuleActions implements ThingActions {
     private final Logger logger = LoggerFactory.getLogger(LcnModuleActions.class);
     private static final int DYN_TEXT_CHUNK_COUNT = 5;
     private static final int DYN_TEXT_HEADER_LENGTH = 6;
@@ -57,8 +55,7 @@ public class LcnModuleActions implements ThingActions, ILcnModuleActions {
         return moduleHandler;
     }
 
-    @Override
-    @RuleAction(label = "LCN Hit Key", description = "Sends a \"hit key\" command to an LCN module")
+    @RuleAction(label = "send a hit key command", description = "Sends a \"hit key\" command to an LCN module.")
     public void hitKey(
             @ActionInput(name = "table", required = true, type = "java.lang.String", label = "Table", description = "The key table (A-D)") @Nullable String table,
             @ActionInput(name = "key", required = true, type = "java.lang.Integer", label = "Key", description = "The key number (1-8)") int key,
@@ -104,8 +101,7 @@ public class LcnModuleActions implements ThingActions, ILcnModuleActions {
         }
     }
 
-    @Override
-    @RuleAction(label = "LCN Flicker Output", description = "Let a dimmer output flicker for a given count of flashes")
+    @RuleAction(label = "flicker a dimmer output", description = "Let a dimmer output flicker for a given count of flashes.")
     public void flickerOutput(
             @ActionInput(name = "output", type = "java.lang.Integer", required = true, label = "Output", description = "The output number (1-4)") int output,
             @ActionInput(name = "depth", type = "java.lang.Integer", label = "Depth", description = "0=25% 1=50% 2=100%") int depth,
@@ -118,8 +114,7 @@ public class LcnModuleActions implements ThingActions, ILcnModuleActions {
         }
     }
 
-    @Override
-    @RuleAction(label = "LCN Dynamic Text", description = "Send custom text to an LCN-GTxD display")
+    @RuleAction(label = "send a custom text", description = "Send custom text to an LCN-GTxD display.")
     public void sendDynamicText(
             @ActionInput(name = "row", type = "java.lang.Integer", required = true, label = "Row", description = "Display the text on the LCN-GTxD in the given row number (1-4)") int row,
             @ActionInput(name = "text", type = "java.lang.String", label = "Text", description = "The text to display (max. 60 chars/bytes)") @Nullable String textInput) {
@@ -162,8 +157,7 @@ public class LcnModuleActions implements ThingActions, ILcnModuleActions {
      * @param relaynumber 1-based number of the relay to use
      * @param duration duration of the relay timer in milliseconds
      */
-    @Override
-    @RuleAction(label = "LCN Relay Timer", description = "Start an LCN relay timer")
+    @RuleAction(label = "start a relay timer", description = "Start an LCN relay timer.")
     public void startRelayTimer(
             @ActionInput(name = "relaynumber", required = true, type = "java.lang.Integer", label = "Relay Number", description = "The relay number (1-8)") int relayNumber,
             @ActionInput(name = "duration", required = true, type = "java.lang.Double", label = "Duration [ms]", description = "The timer duration in milliseconds") double duration) {
@@ -174,44 +168,41 @@ public class LcnModuleActions implements ThingActions, ILcnModuleActions {
         }
     }
 
-    private static ILcnModuleActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(LcnModuleActions.class.getName())) {
-            if (actions instanceof LcnModuleActions) {
-                return (ILcnModuleActions) actions;
-            } else {
-                return (ILcnModuleActions) Proxy.newProxyInstance(ILcnModuleActions.class.getClassLoader(),
-                        new Class[] { ILcnModuleActions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
-        }
-        throw new IllegalArgumentException("Actions is not an instance of LcnModuleActions");
-    }
-
     /** Static alias to support the old DSL rules engine and make the action available there. */
     public static void hitKey(@Nullable ThingActions actions, @Nullable String table, int key,
             @Nullable String action) {
-        invokeMethodOf(actions).hitKey(table, key, action);
+        if (actions instanceof LcnModuleHandler) {
+            ((LcnModuleActions) actions).hitKey(table, key, action);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of LcnModuleActions");
+        }
     }
 
     /** Static alias to support the old DSL rules engine and make the action available there. */
     public static void flickerOutput(@Nullable ThingActions actions, int output, int depth, int ramp, int count) {
-        invokeMethodOf(actions).flickerOutput(output, depth, ramp, count);
+        if (actions instanceof LcnModuleHandler) {
+            ((LcnModuleActions) actions).flickerOutput(output, depth, ramp, count);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of LcnModuleActions");
+        }
     }
 
     /** Static alias to support the old DSL rules engine and make the action available there. */
     public static void sendDynamicText(@Nullable ThingActions actions, int row, @Nullable String text) {
-        invokeMethodOf(actions).sendDynamicText(row, text);
+        if (actions instanceof LcnModuleHandler) {
+            ((LcnModuleActions) actions).sendDynamicText(row, text);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of LcnModuleActions");
+        }
     }
 
     /** Static alias to support the old DSL rules engine and make the action available there. */
     public static void startRelayTimer(@Nullable ThingActions actions, int relaynumber, double duration) {
-        invokeMethodOf(actions).startRelayTimer(relaynumber, duration);
+        if (actions instanceof LcnModuleHandler) {
+            ((LcnModuleActions) actions).startRelayTimer(relaynumber, duration);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of LcnModuleActions");
+        }
     }
 
     private LcnModuleHandler getHandler() throws LcnException {
diff --git a/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/action/ILGWebOSActions.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/action/ILGWebOSActions.java
deleted file mode 100644 (file)
index 5eb004b..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.lgwebos.action;
-
-import java.io.IOException;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * The {@link ILGWebOSActions} defines the interface for all thing actions supported by the binding.
- *
- * @author Laurent Garnier - Initial contribution
- */
-@NonNullByDefault
-public interface ILGWebOSActions {
-
-    public void showToast(String text) throws IOException;
-
-    public void showToast(String icon, String text) throws IOException;
-
-    public void launchBrowser(String url);
-
-    public void launchApplication(String appId);
-
-    public void launchApplication(String appId, String params);
-
-    public void sendText(String text);
-
-    public void sendButton(String button);
-
-    public void increaseChannel();
-
-    public void decreaseChannel();
-
-    public void sendRCButton(String rcButton);
-}
diff --git a/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/action/LGWebOSActions.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/action/LGWebOSActions.java
deleted file mode 100644 (file)
index 6cf60a3..0000000
+++ /dev/null
@@ -1,359 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.lgwebos.action;
-
-import java.awt.image.BufferedImage;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.util.Base64;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import javax.imageio.ImageIO;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.lgwebos.internal.handler.LGWebOSHandler;
-import org.openhab.binding.lgwebos.internal.handler.LGWebOSTVMouseSocket.ButtonType;
-import org.openhab.binding.lgwebos.internal.handler.LGWebOSTVSocket;
-import org.openhab.binding.lgwebos.internal.handler.LGWebOSTVSocket.State;
-import org.openhab.binding.lgwebos.internal.handler.command.ServiceSubscription;
-import org.openhab.binding.lgwebos.internal.handler.core.AppInfo;
-import org.openhab.binding.lgwebos.internal.handler.core.ResponseListener;
-import org.openhab.binding.lgwebos.internal.handler.core.TextInputStatusInfo;
-import org.openhab.core.automation.annotation.ActionInput;
-import org.openhab.core.automation.annotation.RuleAction;
-import org.openhab.core.thing.binding.ThingActions;
-import org.openhab.core.thing.binding.ThingActionsScope;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParseException;
-import com.google.gson.JsonParser;
-
-/**
- * The {@link LGWebOSActions} defines the thing actions for the LGwebOS binding.
- * <p>
- * <b>Note:</b>The static method <b>invokeMethodOf</b> handles the case where
- * the test <i>actions instanceof LGWebOSActions</i> fails. This test can fail
- * due to an issue in openHAB core v2.5.0 where the {@link LGWebOSActions} class
- * can be loaded by a different classloader than the <i>actions</i> instance.
- *
- * @author Sebastian Prehn - Initial contribution
- * @author Laurent Garnier - new method invokeMethodOf + interface ILGWebOSActions
- */
-@ThingActionsScope(name = "lgwebos")
-@NonNullByDefault
-public class LGWebOSActions implements ThingActions, ILGWebOSActions {
-    private final Logger logger = LoggerFactory.getLogger(LGWebOSActions.class);
-    private final ResponseListener<TextInputStatusInfo> textInputListener = createTextInputStatusListener();
-    private @Nullable LGWebOSHandler handler;
-
-    @Override
-    public void setThingHandler(@Nullable ThingHandler handler) {
-        this.handler = (LGWebOSHandler) handler;
-    }
-
-    @Override
-    public @Nullable ThingHandler getThingHandler() {
-        return this.handler;
-    }
-
-    // a NonNull getter for handler
-    private LGWebOSHandler getLGWebOSHandler() {
-        LGWebOSHandler lgWebOSHandler = this.handler;
-        if (lgWebOSHandler == null) {
-            throw new IllegalStateException(
-                    "ThingHandler must be set before any action may be invoked on LGWebOSActions.");
-        }
-        return lgWebOSHandler;
-    }
-
-    private enum Button {
-        UP,
-        DOWN,
-        LEFT,
-        RIGHT,
-        BACK,
-        DELETE,
-        ENTER,
-        HOME,
-        OK
-    }
-
-    @Override
-    @RuleAction(label = "@text/actionShowToastLabel", description = "@text/actionShowToastDesc")
-    public void showToast(
-            @ActionInput(name = "text", label = "@text/actionShowToastInputTextLabel", description = "@text/actionShowToastInputTextDesc") String text)
-            throws IOException {
-        getConnectedSocket().ifPresent(control -> control.showToast(text, createResponseListener()));
-    }
-
-    @Override
-    @RuleAction(label = "@text/actionShowToastWithIconLabel", description = "@text/actionShowToastWithIconLabel")
-    public void showToast(
-            @ActionInput(name = "icon", label = "@text/actionShowToastInputIconLabel", description = "@text/actionShowToastInputIconDesc") String icon,
-            @ActionInput(name = "text", label = "@text/actionShowToastInputTextLabel", description = "@text/actionShowToastInputTextDesc") String text)
-            throws IOException {
-        BufferedImage bi = ImageIO.read(new URL(icon));
-        try (ByteArrayOutputStream os = new ByteArrayOutputStream(); OutputStream b64 = Base64.getEncoder().wrap(os)) {
-            ImageIO.write(bi, "png", b64);
-            String string = os.toString(StandardCharsets.UTF_8.name());
-            getConnectedSocket().ifPresent(control -> control.showToast(text, string, "png", createResponseListener()));
-        }
-    }
-
-    @Override
-    @RuleAction(label = "@text/actionLaunchBrowserLabel", description = "@text/actionLaunchBrowserDesc")
-    public void launchBrowser(
-            @ActionInput(name = "url", label = "@text/actionLaunchBrowserInputUrlLabel", description = "@text/actionLaunchBrowserInputUrlDesc") String url) {
-        getConnectedSocket().ifPresent(control -> control.launchBrowser(url, createResponseListener()));
-    }
-
-    private List<AppInfo> getAppInfos() {
-        LGWebOSHandler lgWebOSHandler = getLGWebOSHandler();
-
-        if (!this.getConnectedSocket().isPresent()) {
-            return Collections.emptyList();
-        }
-
-        List<AppInfo> appInfos = lgWebOSHandler.getLauncherApplication()
-                .getAppInfos(lgWebOSHandler.getThing().getUID());
-        if (appInfos == null) {
-            logger.warn("No AppInfos found for device with ThingID {}.", lgWebOSHandler.getThing().getUID());
-            return Collections.emptyList();
-        }
-        return appInfos;
-    }
-
-    @Override
-    @RuleAction(label = "@text/actionLaunchApplicationLabel", description = "@text/actionLaunchApplicationDesc")
-    public void launchApplication(
-            @ActionInput(name = "appId", label = "@text/actionLaunchApplicationInputAppIDLabel", description = "@text/actionLaunchApplicationInputAppIDDesc") String appId) {
-        Optional<AppInfo> appInfo = getAppInfos().stream().filter(a -> a.getId().equals(appId)).findFirst();
-        if (appInfo.isPresent()) {
-            getConnectedSocket()
-                    .ifPresent(control -> control.launchAppWithInfo(appInfo.get(), createResponseListener()));
-        } else {
-            logger.warn("Device with ThingID {} does not support any app with id: {}.",
-                    getLGWebOSHandler().getThing().getUID(), appId);
-        }
-    }
-
-    @Override
-    @RuleAction(label = "@text/actionLaunchApplicationWithParamsLabel", description = "@text/actionLaunchApplicationWithParamsDesc")
-    public void launchApplication(
-            @ActionInput(name = "appId", label = "@text/actionLaunchApplicationInputAppIDLabel", description = "@text/actionLaunchApplicationInputAppIDDesc") String appId,
-            @ActionInput(name = "params", label = "@text/actionLaunchApplicationInputParamsLabel", description = "@text/actionLaunchApplicationInputParamsDesc") String params) {
-        try {
-            JsonParser parser = new JsonParser();
-            JsonObject payload = (JsonObject) parser.parse(params);
-
-            Optional<AppInfo> appInfo = getAppInfos().stream().filter(a -> a.getId().equals(appId)).findFirst();
-            if (appInfo.isPresent()) {
-                getConnectedSocket().ifPresent(
-                        control -> control.launchAppWithInfo(appInfo.get(), payload, createResponseListener()));
-            } else {
-                logger.warn("Device with ThingID {} does not support any app with id: {}.",
-                        getLGWebOSHandler().getThing().getUID(), appId);
-            }
-
-        } catch (JsonParseException ex) {
-            logger.warn("Parameters value ({}) is not in a valid JSON format. {}", params, ex.getMessage());
-            return;
-        }
-    }
-
-    @Override
-    @RuleAction(label = "@text/actionSendTextLabel", description = "@text/actionSendTextDesc")
-    public void sendText(
-            @ActionInput(name = "text", label = "@text/actionSendTextInputTextLabel", description = "@text/actionSendTextInputTextDesc") String text) {
-        getConnectedSocket().ifPresent(control -> {
-            ServiceSubscription<TextInputStatusInfo> subscription = control.subscribeTextInputStatus(textInputListener);
-            control.sendText(text);
-            control.unsubscribe(subscription);
-        });
-    }
-
-    @Override
-    @RuleAction(label = "@text/actionSendButtonLabel", description = "@text/actionSendButtonDesc")
-    public void sendButton(
-            @ActionInput(name = "text", label = "@text/actionSendButtonInputButtonLabel", description = "@text/actionSendButtonInputButtonDesc") String button) {
-        try {
-            switch (Button.valueOf(button)) {
-                case UP:
-                    getConnectedSocket().ifPresent(control -> control.executeMouse(s -> s.button(ButtonType.UP)));
-                    break;
-                case DOWN:
-                    getConnectedSocket().ifPresent(control -> control.executeMouse(s -> s.button(ButtonType.DOWN)));
-                    break;
-                case LEFT:
-                    getConnectedSocket().ifPresent(control -> control.executeMouse(s -> s.button(ButtonType.LEFT)));
-                    break;
-                case RIGHT:
-                    getConnectedSocket().ifPresent(control -> control.executeMouse(s -> s.button(ButtonType.RIGHT)));
-                    break;
-                case BACK:
-                    getConnectedSocket().ifPresent(control -> control.executeMouse(s -> s.button(ButtonType.BACK)));
-                    break;
-                case DELETE:
-                    getConnectedSocket().ifPresent(control -> control.sendDelete());
-                    break;
-                case ENTER:
-                    getConnectedSocket().ifPresent(control -> control.sendEnter());
-                    break;
-                case HOME:
-                    getConnectedSocket().ifPresent(control -> control.executeMouse(s -> s.button("HOME")));
-                    break;
-                case OK:
-                    getConnectedSocket().ifPresent(control -> control.executeMouse(s -> s.click()));
-                    break;
-            }
-        } catch (IllegalArgumentException ex) {
-            logger.warn("{} is not a valid value for button - available are: {}", button,
-                    Stream.of(Button.values()).map(b -> b.name()).collect(Collectors.joining(", ")));
-        }
-    }
-
-    @Override
-    @RuleAction(label = "@text/actionIncreaseChannelLabel", description = "@text/actionIncreaseChannelDesc")
-    public void increaseChannel() {
-        getConnectedSocket().ifPresent(control -> control.channelUp(createResponseListener()));
-    }
-
-    @Override
-    @RuleAction(label = "@text/actionDecreaseChannelLabel", description = "@text/actionDecreaseChannelDesc")
-    public void decreaseChannel() {
-        getConnectedSocket().ifPresent(control -> control.channelDown(createResponseListener()));
-    }
-
-    @Override
-    @RuleAction(label = "@text/actionSendRCButtonLabel", description = "@text/actionSendRCButtonDesc")
-    public void sendRCButton(
-            @ActionInput(name = "text", label = "@text/actionSendRCButtonInputTextLabel", description = "@text/actionSendRCButtonInputTextDesc") String rcButton) {
-        getConnectedSocket().ifPresent(control -> control.executeMouse(s -> s.button(rcButton)));
-    }
-
-    private Optional<LGWebOSTVSocket> getConnectedSocket() {
-        LGWebOSHandler lgWebOSHandler = getLGWebOSHandler();
-        final LGWebOSTVSocket socket = lgWebOSHandler.getSocket();
-
-        if (socket.getState() != State.REGISTERED) {
-            logger.warn("Device with ThingID {} is currently not connected.", lgWebOSHandler.getThing().getUID());
-            return Optional.empty();
-        }
-
-        return Optional.of(socket);
-    }
-
-    private ResponseListener<TextInputStatusInfo> createTextInputStatusListener() {
-        return new ResponseListener<TextInputStatusInfo>() {
-
-            @Override
-            public void onError(@Nullable String error) {
-                logger.warn("Response: {}", error);
-            }
-
-            @Override
-            public void onSuccess(@Nullable TextInputStatusInfo info) {
-                logger.debug("Response: {}", info == null ? "OK" : info.getRawData());
-            }
-        };
-    }
-
-    private <O> ResponseListener<O> createResponseListener() {
-        return new ResponseListener<O>() {
-
-            @Override
-            public void onError(@Nullable String error) {
-                logger.warn("Response: {}", error);
-            }
-
-            @Override
-            public void onSuccess(@Nullable O object) {
-                logger.debug("Response: {}", object == null ? "OK" : object.toString());
-            }
-        };
-    }
-
-    // delegation methods for "legacy" rule support
-
-    private static ILGWebOSActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(LGWebOSActions.class.getName())) {
-            if (actions instanceof ILGWebOSActions) {
-                return (ILGWebOSActions) actions;
-            } else {
-                return (ILGWebOSActions) Proxy.newProxyInstance(ILGWebOSActions.class.getClassLoader(),
-                        new Class[] { ILGWebOSActions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
-        }
-        throw new IllegalArgumentException("Actions is not an instance of LGWebOSActions");
-    }
-
-    public static void showToast(@Nullable ThingActions actions, String text) throws IOException {
-        invokeMethodOf(actions).showToast(text);
-    }
-
-    public static void showToast(@Nullable ThingActions actions, String icon, String text) throws IOException {
-        invokeMethodOf(actions).showToast(icon, text);
-    }
-
-    public static void launchBrowser(@Nullable ThingActions actions, String url) {
-        invokeMethodOf(actions).launchBrowser(url);
-    }
-
-    public static void launchApplication(@Nullable ThingActions actions, String appId) {
-        invokeMethodOf(actions).launchApplication(appId);
-    }
-
-    public static void launchApplication(@Nullable ThingActions actions, String appId, String param) {
-        invokeMethodOf(actions).launchApplication(appId, param);
-    }
-
-    public static void sendText(@Nullable ThingActions actions, String text) {
-        invokeMethodOf(actions).sendText(text);
-    }
-
-    public static void sendButton(@Nullable ThingActions actions, String button) {
-        invokeMethodOf(actions).sendButton(button);
-    }
-
-    public static void increaseChannel(@Nullable ThingActions actions) {
-        invokeMethodOf(actions).increaseChannel();
-    }
-
-    public static void decreaseChannel(@Nullable ThingActions actions) {
-        invokeMethodOf(actions).decreaseChannel();
-    }
-
-    public static void sendRCButton(@Nullable ThingActions actions, String rcButton) {
-        invokeMethodOf(actions).sendRCButton(rcButton);
-    }
-}
diff --git a/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/action/LGWebOSActions.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/action/LGWebOSActions.java
new file mode 100644 (file)
index 0000000..954a097
--- /dev/null
@@ -0,0 +1,363 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.lgwebos.internal.action;
+
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.imageio.ImageIO;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.lgwebos.internal.handler.LGWebOSHandler;
+import org.openhab.binding.lgwebos.internal.handler.LGWebOSTVMouseSocket.ButtonType;
+import org.openhab.binding.lgwebos.internal.handler.LGWebOSTVSocket;
+import org.openhab.binding.lgwebos.internal.handler.LGWebOSTVSocket.State;
+import org.openhab.binding.lgwebos.internal.handler.command.ServiceSubscription;
+import org.openhab.binding.lgwebos.internal.handler.core.AppInfo;
+import org.openhab.binding.lgwebos.internal.handler.core.ResponseListener;
+import org.openhab.binding.lgwebos.internal.handler.core.TextInputStatusInfo;
+import org.openhab.core.automation.annotation.ActionInput;
+import org.openhab.core.automation.annotation.RuleAction;
+import org.openhab.core.thing.binding.ThingActions;
+import org.openhab.core.thing.binding.ThingActionsScope;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonParser;
+
+/**
+ * The {@link LGWebOSActions} defines the thing actions for the LGwebOS binding.
+ *
+ * @author Sebastian Prehn - Initial contribution
+ * @author Laurent Garnier - new method invokeMethodOf + interface ILGWebOSActions
+ */
+@ThingActionsScope(name = "lgwebos")
+@NonNullByDefault
+public class LGWebOSActions implements ThingActions {
+    private final Logger logger = LoggerFactory.getLogger(LGWebOSActions.class);
+    private final ResponseListener<TextInputStatusInfo> textInputListener = createTextInputStatusListener();
+    private @Nullable LGWebOSHandler handler;
+
+    @Override
+    public void setThingHandler(@Nullable ThingHandler handler) {
+        this.handler = (LGWebOSHandler) handler;
+    }
+
+    @Override
+    public @Nullable ThingHandler getThingHandler() {
+        return handler;
+    }
+
+    // a NonNull getter for handler
+    private LGWebOSHandler getLGWebOSHandler() {
+        LGWebOSHandler lgWebOSHandler = this.handler;
+        if (lgWebOSHandler == null) {
+            throw new IllegalStateException(
+                    "ThingHandler must be set before any action may be invoked on LGWebOSActions.");
+        }
+        return lgWebOSHandler;
+    }
+
+    private enum Button {
+        UP,
+        DOWN,
+        LEFT,
+        RIGHT,
+        BACK,
+        DELETE,
+        ENTER,
+        HOME,
+        OK
+    }
+
+    @RuleAction(label = "@text/actionShowToastLabel", description = "@text/actionShowToastDesc")
+    public void showToast(
+            @ActionInput(name = "text", label = "@text/actionShowToastInputTextLabel", description = "@text/actionShowToastInputTextDesc") String text)
+            throws IOException {
+        getConnectedSocket().ifPresent(control -> control.showToast(text, createResponseListener()));
+    }
+
+    @RuleAction(label = "@text/actionShowToastWithIconLabel", description = "@text/actionShowToastWithIconLabel")
+    public void showToast(
+            @ActionInput(name = "icon", label = "@text/actionShowToastInputIconLabel", description = "@text/actionShowToastInputIconDesc") String icon,
+            @ActionInput(name = "text", label = "@text/actionShowToastInputTextLabel", description = "@text/actionShowToastInputTextDesc") String text)
+            throws IOException {
+        BufferedImage bi = ImageIO.read(new URL(icon));
+        try (ByteArrayOutputStream os = new ByteArrayOutputStream(); OutputStream b64 = Base64.getEncoder().wrap(os)) {
+            ImageIO.write(bi, "png", b64);
+            String string = os.toString(StandardCharsets.UTF_8.name());
+            getConnectedSocket().ifPresent(control -> control.showToast(text, string, "png", createResponseListener()));
+        }
+    }
+
+    @RuleAction(label = "@text/actionLaunchBrowserLabel", description = "@text/actionLaunchBrowserDesc")
+    public void launchBrowser(
+            @ActionInput(name = "url", label = "@text/actionLaunchBrowserInputUrlLabel", description = "@text/actionLaunchBrowserInputUrlDesc") String url) {
+        getConnectedSocket().ifPresent(control -> control.launchBrowser(url, createResponseListener()));
+    }
+
+    private List<AppInfo> getAppInfos() {
+        LGWebOSHandler lgWebOSHandler = getLGWebOSHandler();
+
+        if (!this.getConnectedSocket().isPresent()) {
+            return Collections.emptyList();
+        }
+
+        List<AppInfo> appInfos = lgWebOSHandler.getLauncherApplication()
+                .getAppInfos(lgWebOSHandler.getThing().getUID());
+        if (appInfos == null) {
+            logger.warn("No AppInfos found for device with ThingID {}.", lgWebOSHandler.getThing().getUID());
+            return Collections.emptyList();
+        }
+        return appInfos;
+    }
+
+    @RuleAction(label = "@text/actionLaunchApplicationLabel", description = "@text/actionLaunchApplicationDesc")
+    public void launchApplication(
+            @ActionInput(name = "appId", label = "@text/actionLaunchApplicationInputAppIDLabel", description = "@text/actionLaunchApplicationInputAppIDDesc") String appId) {
+        Optional<AppInfo> appInfo = getAppInfos().stream().filter(a -> a.getId().equals(appId)).findFirst();
+        if (appInfo.isPresent()) {
+            getConnectedSocket()
+                    .ifPresent(control -> control.launchAppWithInfo(appInfo.get(), createResponseListener()));
+        } else {
+            logger.warn("Device with ThingID {} does not support any app with id: {}.",
+                    getLGWebOSHandler().getThing().getUID(), appId);
+        }
+    }
+
+    @RuleAction(label = "@text/actionLaunchApplicationWithParamsLabel", description = "@text/actionLaunchApplicationWithParamsDesc")
+    public void launchApplication(
+            @ActionInput(name = "appId", label = "@text/actionLaunchApplicationInputAppIDLabel", description = "@text/actionLaunchApplicationInputAppIDDesc") String appId,
+            @ActionInput(name = "params", label = "@text/actionLaunchApplicationInputParamsLabel", description = "@text/actionLaunchApplicationInputParamsDesc") String params) {
+        try {
+            JsonParser parser = new JsonParser();
+            JsonObject payload = (JsonObject) parser.parse(params);
+
+            Optional<AppInfo> appInfo = getAppInfos().stream().filter(a -> a.getId().equals(appId)).findFirst();
+            if (appInfo.isPresent()) {
+                getConnectedSocket().ifPresent(
+                        control -> control.launchAppWithInfo(appInfo.get(), payload, createResponseListener()));
+            } else {
+                logger.warn("Device with ThingID {} does not support any app with id: {}.",
+                        getLGWebOSHandler().getThing().getUID(), appId);
+            }
+
+        } catch (JsonParseException ex) {
+            logger.warn("Parameters value ({}) is not in a valid JSON format. {}", params, ex.getMessage());
+            return;
+        }
+    }
+
+    @RuleAction(label = "@text/actionSendTextLabel", description = "@text/actionSendTextDesc")
+    public void sendText(
+            @ActionInput(name = "text", label = "@text/actionSendTextInputTextLabel", description = "@text/actionSendTextInputTextDesc") String text) {
+        getConnectedSocket().ifPresent(control -> {
+            ServiceSubscription<TextInputStatusInfo> subscription = control.subscribeTextInputStatus(textInputListener);
+            control.sendText(text);
+            control.unsubscribe(subscription);
+        });
+    }
+
+    @RuleAction(label = "@text/actionSendButtonLabel", description = "@text/actionSendButtonDesc")
+    public void sendButton(
+            @ActionInput(name = "text", label = "@text/actionSendButtonInputButtonLabel", description = "@text/actionSendButtonInputButtonDesc") String button) {
+        try {
+            switch (Button.valueOf(button)) {
+                case UP:
+                    getConnectedSocket().ifPresent(control -> control.executeMouse(s -> s.button(ButtonType.UP)));
+                    break;
+                case DOWN:
+                    getConnectedSocket().ifPresent(control -> control.executeMouse(s -> s.button(ButtonType.DOWN)));
+                    break;
+                case LEFT:
+                    getConnectedSocket().ifPresent(control -> control.executeMouse(s -> s.button(ButtonType.LEFT)));
+                    break;
+                case RIGHT:
+                    getConnectedSocket().ifPresent(control -> control.executeMouse(s -> s.button(ButtonType.RIGHT)));
+                    break;
+                case BACK:
+                    getConnectedSocket().ifPresent(control -> control.executeMouse(s -> s.button(ButtonType.BACK)));
+                    break;
+                case DELETE:
+                    getConnectedSocket().ifPresent(control -> control.sendDelete());
+                    break;
+                case ENTER:
+                    getConnectedSocket().ifPresent(control -> control.sendEnter());
+                    break;
+                case HOME:
+                    getConnectedSocket().ifPresent(control -> control.executeMouse(s -> s.button("HOME")));
+                    break;
+                case OK:
+                    getConnectedSocket().ifPresent(control -> control.executeMouse(s -> s.click()));
+                    break;
+            }
+        } catch (IllegalArgumentException ex) {
+            logger.warn("{} is not a valid value for button - available are: {}", button,
+                    Stream.of(Button.values()).map(b -> b.name()).collect(Collectors.joining(", ")));
+        }
+    }
+
+    @RuleAction(label = "@text/actionIncreaseChannelLabel", description = "@text/actionIncreaseChannelDesc")
+    public void increaseChannel() {
+        getConnectedSocket().ifPresent(control -> control.channelUp(createResponseListener()));
+    }
+
+    @RuleAction(label = "@text/actionDecreaseChannelLabel", description = "@text/actionDecreaseChannelDesc")
+    public void decreaseChannel() {
+        getConnectedSocket().ifPresent(control -> control.channelDown(createResponseListener()));
+    }
+
+    @RuleAction(label = "@text/actionSendRCButtonLabel", description = "@text/actionSendRCButtonDesc")
+    public void sendRCButton(
+            @ActionInput(name = "text", label = "@text/actionSendRCButtonInputTextLabel", description = "@text/actionSendRCButtonInputTextDesc") String rcButton) {
+        getConnectedSocket().ifPresent(control -> control.executeMouse(s -> s.button(rcButton)));
+    }
+
+    private Optional<LGWebOSTVSocket> getConnectedSocket() {
+        LGWebOSHandler lgWebOSHandler = getLGWebOSHandler();
+        final LGWebOSTVSocket socket = lgWebOSHandler.getSocket();
+
+        if (socket.getState() != State.REGISTERED) {
+            logger.warn("Device with ThingID {} is currently not connected.", lgWebOSHandler.getThing().getUID());
+            return Optional.empty();
+        }
+
+        return Optional.of(socket);
+    }
+
+    private ResponseListener<TextInputStatusInfo> createTextInputStatusListener() {
+        return new ResponseListener<TextInputStatusInfo>() {
+
+            @Override
+            public void onError(@Nullable String error) {
+                logger.warn("Response: {}", error);
+            }
+
+            @Override
+            public void onSuccess(@Nullable TextInputStatusInfo info) {
+                logger.debug("Response: {}", info == null ? "OK" : info.getRawData());
+            }
+        };
+    }
+
+    private <O> ResponseListener<O> createResponseListener() {
+        return new ResponseListener<O>() {
+
+            @Override
+            public void onError(@Nullable String error) {
+                logger.warn("Response: {}", error);
+            }
+
+            @Override
+            public void onSuccess(@Nullable O object) {
+                logger.debug("Response: {}", object == null ? "OK" : object.toString());
+            }
+        };
+    }
+
+    // delegation methods for "legacy" rule support
+
+    public static void showToast(@Nullable ThingActions actions, String text) throws IOException {
+        if (actions instanceof LGWebOSActions) {
+            ((LGWebOSActions) actions).showToast(text);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of LGWebOSActions");
+        }
+    }
+
+    public static void showToast(@Nullable ThingActions actions, String icon, String text) throws IOException {
+        if (actions instanceof LGWebOSActions) {
+            ((LGWebOSActions) actions).showToast(icon, text);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of LGWebOSActions");
+        }
+    }
+
+    public static void launchBrowser(@Nullable ThingActions actions, String url) {
+        if (actions instanceof LGWebOSActions) {
+            ((LGWebOSActions) actions).launchBrowser(url);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of LGWebOSActions");
+        }
+    }
+
+    public static void launchApplication(@Nullable ThingActions actions, String appId) {
+        if (actions instanceof LGWebOSActions) {
+            ((LGWebOSActions) actions).launchApplication(appId);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of LGWebOSActions");
+        }
+    }
+
+    public static void launchApplication(@Nullable ThingActions actions, String appId, String param) {
+        if (actions instanceof LGWebOSActions) {
+            ((LGWebOSActions) actions).launchApplication(appId, param);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of LGWebOSActions");
+        }
+    }
+
+    public static void sendText(@Nullable ThingActions actions, String text) {
+        if (actions instanceof LGWebOSActions) {
+            ((LGWebOSActions) actions).sendText(text);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of LGWebOSActions");
+        }
+    }
+
+    public static void sendButton(@Nullable ThingActions actions, String button) {
+        if (actions instanceof LGWebOSActions) {
+            ((LGWebOSActions) actions).sendButton(button);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of LGWebOSActions");
+        }
+    }
+
+    public static void increaseChannel(@Nullable ThingActions actions) {
+        if (actions instanceof LGWebOSActions) {
+            ((LGWebOSActions) actions).increaseChannel();
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of LGWebOSActions");
+        }
+    }
+
+    public static void decreaseChannel(@Nullable ThingActions actions) {
+        if (actions instanceof LGWebOSActions) {
+            ((LGWebOSActions) actions).decreaseChannel();
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of LGWebOSActions");
+        }
+    }
+
+    public static void sendRCButton(@Nullable ThingActions actions, String rcButton) {
+        if (actions instanceof LGWebOSActions) {
+            ((LGWebOSActions) actions).sendRCButton(rcButton);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of LGWebOSActions");
+        }
+    }
+}
index 26a1d4bd4cbd668eec2863a41e97ab58ff8e873c..cf40ac53d3b3b60e1c6eff9a443c5f217abfe6dd 100644 (file)
@@ -27,7 +27,6 @@ import java.util.concurrent.TimeUnit;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.jetty.websocket.client.WebSocketClient;
-import org.openhab.binding.lgwebos.action.LGWebOSActions;
 import org.openhab.binding.lgwebos.internal.ChannelHandler;
 import org.openhab.binding.lgwebos.internal.LGWebOSBindingConstants;
 import org.openhab.binding.lgwebos.internal.LGWebOSStateDescriptionOptionProvider;
@@ -41,6 +40,7 @@ import org.openhab.binding.lgwebos.internal.ToastControlToast;
 import org.openhab.binding.lgwebos.internal.VolumeControlMute;
 import org.openhab.binding.lgwebos.internal.VolumeControlVolume;
 import org.openhab.binding.lgwebos.internal.WakeOnLanUtility;
+import org.openhab.binding.lgwebos.internal.action.LGWebOSActions;
 import org.openhab.binding.lgwebos.internal.handler.LGWebOSTVSocket.WebOSTVSocketListener;
 import org.openhab.binding.lgwebos.internal.handler.core.AppInfo;
 import org.openhab.binding.lgwebos.internal.handler.core.ResponseListener;
diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/action/DimmerActions.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/action/DimmerActions.java
deleted file mode 100644 (file)
index b7d4f40..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.lutron.action;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.lutron.internal.handler.DimmerHandler;
-import org.openhab.binding.lutron.internal.protocol.LutronDuration;
-import org.openhab.core.automation.annotation.ActionInput;
-import org.openhab.core.automation.annotation.RuleAction;
-import org.openhab.core.thing.binding.ThingActions;
-import org.openhab.core.thing.binding.ThingActionsScope;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link DimmerActions} defines thing actions for DimmerHandler.
- *
- * @author Bob Adair - Initial contribution
- */
-@ThingActionsScope(name = "lutron")
-@NonNullByDefault
-public class DimmerActions implements ThingActions, IDimmerActions {
-    private final Logger logger = LoggerFactory.getLogger(DimmerActions.class);
-
-    private @Nullable DimmerHandler handler;
-
-    public DimmerActions() {
-        logger.trace("Lutron Dimmer actions service created");
-    }
-
-    @Override
-    public void setThingHandler(@Nullable ThingHandler handler) {
-        if (handler instanceof DimmerHandler) {
-            this.handler = (DimmerHandler) handler;
-        }
-    }
-
-    @Override
-    public @Nullable ThingHandler getThingHandler() {
-        return handler;
-    }
-
-    /**
-     * The setLevel dimmer thing action
-     */
-    @Override
-    @RuleAction(label = "setLevel", description = "Send set level command with fade and delay times")
-    public void setLevel(
-            @ActionInput(name = "level", label = "Dimmer Level", description = "New dimmer level (0-100)") @Nullable Double level,
-            @ActionInput(name = "fadeTime", label = "Fade Time", description = "Time to fade to new level (seconds)") @Nullable Double fadeTime,
-            @ActionInput(name = "delayTime", label = "Delay Time", description = "Delay before starting fade (seconds)") @Nullable Double delayTime) {
-        DimmerHandler dimmerHandler = handler;
-        if (dimmerHandler == null) {
-            logger.debug("Handler not set for Dimmer thing actions.");
-            return;
-        }
-        if (level == null) {
-            logger.debug("Ignoring setLevel command due to null level value.");
-            return;
-        }
-        if (fadeTime == null) {
-            logger.debug("Ignoring setLevel command due to null value for fadeTime.");
-            return;
-        }
-        if (delayTime == null) {
-            logger.debug("Ignoring setLevel command due to null value for delayTime.");
-            return;
-        }
-
-        Double lightLevel = level;
-        if (lightLevel > 100.0) {
-            lightLevel = 100.0;
-        } else if (lightLevel < 0.0) {
-            lightLevel = 0.0;
-        }
-        try {
-            dimmerHandler.setLightLevel(new BigDecimal(lightLevel).setScale(2, RoundingMode.HALF_UP),
-                    new LutronDuration(fadeTime), new LutronDuration(delayTime));
-        } catch (IllegalArgumentException e) {
-            logger.debug("Ignoring setLevel command due to illegal argument exception: {}", e.getMessage());
-        }
-    }
-
-    /**
-     * Static setLevel method for Rules DSL backward compatibility
-     */
-    public static void setLevel(@Nullable ThingActions actions, @Nullable Double level, @Nullable Double fadeTime,
-            @Nullable Double delayTime) {
-        invokeMethodOf(actions).setLevel(level, fadeTime, delayTime); // Replace when core issue #1536 is fixed
-    }
-
-    /**
-     * This is only necessary to work around a bug in openhab-core (issue #1536). It should be removed once that is
-     * resolved.
-     */
-    private static IDimmerActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(DimmerActions.class.getName())) {
-            if (actions instanceof IDimmerActions) {
-                return (IDimmerActions) actions;
-            } else {
-                return (IDimmerActions) Proxy.newProxyInstance(IDimmerActions.class.getClassLoader(),
-                        new Class[] { IDimmerActions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
-        }
-        throw new IllegalArgumentException("Actions is not an instance of DimmerActions");
-    }
-}
diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/action/IDimmerActions.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/action/IDimmerActions.java
deleted file mode 100644 (file)
index 551e8a6..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.lutron.action;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * The {@link IDimmerActions} interface defines the interface for all thing actions supported by the dimmer thing.
- * This is only necessary to work around a bug in openhab-core (issue #1536). It should be removed once that is
- * resolved.
- *
- * @author Bob Adair - Initial contribution
- *
- */
-@NonNullByDefault
-public interface IDimmerActions {
-
-    public void setLevel(@Nullable Double level, @Nullable Double fadeTime, @Nullable Double delayTime);
-}
diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/action/DimmerActions.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/action/DimmerActions.java
new file mode 100644 (file)
index 0000000..48fb70e
--- /dev/null
@@ -0,0 +1,109 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.lutron.internal.action;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.lutron.internal.handler.DimmerHandler;
+import org.openhab.binding.lutron.internal.protocol.LutronDuration;
+import org.openhab.core.automation.annotation.ActionInput;
+import org.openhab.core.automation.annotation.RuleAction;
+import org.openhab.core.thing.binding.ThingActions;
+import org.openhab.core.thing.binding.ThingActionsScope;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link DimmerActions} defines thing actions for DimmerHandler.
+ *
+ * @author Bob Adair - Initial contribution
+ */
+@ThingActionsScope(name = "lutron")
+@NonNullByDefault
+public class DimmerActions implements ThingActions {
+    private final Logger logger = LoggerFactory.getLogger(DimmerActions.class);
+
+    private @Nullable DimmerHandler handler;
+
+    public DimmerActions() {
+        logger.trace("Lutron Dimmer actions service created");
+    }
+
+    @Override
+    public void setThingHandler(@Nullable ThingHandler handler) {
+        if (handler instanceof DimmerHandler) {
+            this.handler = (DimmerHandler) handler;
+        }
+    }
+
+    @Override
+    public @Nullable ThingHandler getThingHandler() {
+        return handler;
+    }
+
+    /**
+     * The setLevel dimmer thing action
+     */
+    @RuleAction(label = "send a set level command", description = "Send set level command with fade and delay times.")
+    public void setLevel(
+            @ActionInput(name = "level", label = "Dimmer Level", description = "New dimmer level (0-100)") @Nullable Double level,
+            @ActionInput(name = "fadeTime", label = "Fade Time", description = "Time to fade to new level (seconds)") @Nullable Double fadeTime,
+            @ActionInput(name = "delayTime", label = "Delay Time", description = "Delay before starting fade (seconds)") @Nullable Double delayTime) {
+        DimmerHandler dimmerHandler = handler;
+        if (dimmerHandler == null) {
+            logger.debug("Handler not set for Dimmer thing actions.");
+            return;
+        }
+        if (level == null) {
+            logger.debug("Ignoring setLevel command due to null level value.");
+            return;
+        }
+        if (fadeTime == null) {
+            logger.debug("Ignoring setLevel command due to null value for fadeTime.");
+            return;
+        }
+        if (delayTime == null) {
+            logger.debug("Ignoring setLevel command due to null value for delayTime.");
+            return;
+        }
+
+        Double lightLevel = level;
+        if (lightLevel > 100.0) {
+            lightLevel = 100.0;
+        } else if (lightLevel < 0.0) {
+            lightLevel = 0.0;
+        }
+        try {
+            dimmerHandler.setLightLevel(new BigDecimal(lightLevel).setScale(2, RoundingMode.HALF_UP),
+                    new LutronDuration(fadeTime), new LutronDuration(delayTime));
+        } catch (IllegalArgumentException e) {
+            logger.debug("Ignoring setLevel command due to illegal argument exception: {}", e.getMessage());
+        }
+    }
+
+    /**
+     * Static setLevel method for Rules DSL backward compatibility
+     */
+    public static void setLevel(@Nullable ThingActions actions, @Nullable Double level, @Nullable Double fadeTime,
+            @Nullable Double delayTime) {
+        if (actions instanceof DimmerActions) {
+            ((DimmerActions) actions).setLevel(level, fadeTime, delayTime);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of DimmerActions");
+        }
+    }
+}
index 35c01d2b15fb2e168db2cac975391f3c603d50e1..84f002cbca09975ca108a0118105629b2d609587 100644 (file)
@@ -19,7 +19,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.concurrent.atomic.AtomicReference;
 
-import org.openhab.binding.lutron.action.DimmerActions;
+import org.openhab.binding.lutron.internal.action.DimmerActions;
 import org.openhab.binding.lutron.internal.config.DimmerConfig;
 import org.openhab.binding.lutron.internal.protocol.LutronCommandType;
 import org.openhab.binding.lutron.internal.protocol.LutronDuration;
diff --git a/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/action/ISendMailActions.java b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/action/ISendMailActions.java
deleted file mode 100644 (file)
index 1a0b3a3..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.mail.action;
-
-import java.util.List;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * The {@link ISendMailActions} interface defines rule actions for sending mail
- *
- * @author Jan N. Klug - Initial contribution
- */
-@NonNullByDefault
-public interface ISendMailActions {
-    Boolean sendMail(@Nullable String recipient, @Nullable String subject, @Nullable String text,
-            @Nullable List<String> urlStringList);
-
-    Boolean sendHtmlMail(@Nullable String recipient, @Nullable String subject, @Nullable String html,
-            @Nullable List<String> urlStringList);
-}
diff --git a/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/action/SendMailActions.java b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/action/SendMailActions.java
deleted file mode 100644 (file)
index 1513025..0000000
+++ /dev/null
@@ -1,236 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.mail.action;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.net.MalformedURLException;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.mail.internet.AddressException;
-
-import org.apache.commons.mail.EmailException;
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.mail.internal.MailBuilder;
-import org.openhab.binding.mail.internal.SMTPHandler;
-import org.openhab.core.automation.annotation.ActionInput;
-import org.openhab.core.automation.annotation.ActionOutput;
-import org.openhab.core.automation.annotation.RuleAction;
-import org.openhab.core.thing.binding.ThingActions;
-import org.openhab.core.thing.binding.ThingActionsScope;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link SendMailActions} class defines rule actions for sending mail
- *
- * @author Jan N. Klug - Initial contribution
- */
-@ThingActionsScope(name = "mail")
-@NonNullByDefault
-public class SendMailActions implements ThingActions, ISendMailActions {
-
-    private final Logger logger = LoggerFactory.getLogger(SendMailActions.class);
-
-    private @Nullable SMTPHandler handler;
-
-    @RuleAction(label = "Send Text Mail", description = "sends a text mail")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendMail(
-            @ActionInput(name = "recipient") @Nullable String recipient,
-            @ActionInput(name = "subject") @Nullable String subject,
-            @ActionInput(name = "text") @Nullable String text) {
-        return sendMail(recipient, subject, text, new ArrayList<>());
-    }
-
-    @RuleAction(label = "Send Text Mail", description = "sends a text mail with URL attachment")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendMail(
-            @ActionInput(name = "recipient") @Nullable String recipient,
-            @ActionInput(name = "subject") @Nullable String subject, @ActionInput(name = "text") @Nullable String text,
-            @ActionInput(name = "url") @Nullable String urlString) {
-        List<String> urlList = new ArrayList<>();
-        if (urlString != null) {
-            urlList.add(urlString);
-        }
-        return sendMail(recipient, subject, text, urlList);
-    }
-
-    @Override
-    @RuleAction(label = "Send Text Mail", description = "sends a text mail with several URL attachments")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendMail(
-            @ActionInput(name = "recipient") @Nullable String recipient,
-            @ActionInput(name = "subject") @Nullable String subject, @ActionInput(name = "text") @Nullable String text,
-            @ActionInput(name = "urlList") @Nullable List<String> urlStringList) {
-        if (recipient == null) {
-            logger.warn("Cannot send mail as recipient is missing.");
-            return false;
-        }
-
-        try {
-            MailBuilder builder = new MailBuilder(recipient);
-
-            if (subject != null && !subject.isEmpty()) {
-                builder.withSubject(subject);
-            }
-            if (text != null && !text.isEmpty()) {
-                builder.withText(text);
-            }
-            if (urlStringList != null) {
-                for (String urlString : urlStringList) {
-                    builder.withURLAttachment(urlString);
-                }
-            }
-
-            final SMTPHandler handler = this.handler;
-            if (handler == null) {
-                logger.info("Handler is null, cannot send mail.");
-                return false;
-            } else {
-                return handler.sendMail(builder.build());
-            }
-        } catch (AddressException | MalformedURLException | EmailException e) {
-            logger.warn("Could not send mail: {}", e.getMessage());
-            return false;
-        }
-    }
-
-    public static boolean sendMail(@Nullable ThingActions actions, @Nullable String recipient, @Nullable String subject,
-            @Nullable String text) {
-        return SendMailActions.sendMail(actions, recipient, subject, text, new ArrayList<>());
-    }
-
-    public static boolean sendMail(@Nullable ThingActions actions, @Nullable String recipient, @Nullable String subject,
-            @Nullable String text, @Nullable String urlString) {
-        List<String> urlList = new ArrayList<>();
-        if (urlString != null) {
-            urlList.add(urlString);
-        }
-        return SendMailActions.sendMail(actions, recipient, subject, text, urlList);
-    }
-
-    public static boolean sendMail(@Nullable ThingActions actions, @Nullable String recipient, @Nullable String subject,
-            @Nullable String text, @Nullable List<String> urlStringList) {
-        return invokeMethodOf(actions).sendMail(recipient, subject, text, urlStringList);
-    }
-
-    @RuleAction(label = "Send HTML Mail", description = "sends a HTML mail")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendHtmlMail(
-            @ActionInput(name = "recipient") @Nullable String recipient,
-            @ActionInput(name = "subject") @Nullable String subject,
-            @ActionInput(name = "html") @Nullable String html) {
-        return sendHtmlMail(recipient, subject, html, new ArrayList<>());
-    }
-
-    @RuleAction(label = "Send HTML Mail", description = "sends a HTML mail with URL attachment")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendHtmlMail(
-            @ActionInput(name = "recipient") @Nullable String recipient,
-            @ActionInput(name = "subject") @Nullable String subject, @ActionInput(name = "html") @Nullable String html,
-            @ActionInput(name = "url") @Nullable String urlString) {
-        List<String> urlList = new ArrayList<>();
-        if (urlString != null) {
-            urlList.add(urlString);
-        }
-        return sendHtmlMail(recipient, subject, html, urlList);
-    }
-
-    @Override
-    @RuleAction(label = "Send HTML Mail", description = "sends a HTML mail with several URL attachments")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendHtmlMail(
-            @ActionInput(name = "recipient") @Nullable String recipient,
-            @ActionInput(name = "subject") @Nullable String subject, @ActionInput(name = "html") @Nullable String html,
-            @ActionInput(name = "urlList") @Nullable List<String> urlStringList) {
-        if (recipient == null) {
-            logger.warn("Cannot send mail as recipient is missing.");
-            return false;
-        }
-
-        try {
-            MailBuilder builder = new MailBuilder(recipient);
-
-            if (subject != null && !subject.isEmpty()) {
-                builder.withSubject(subject);
-            }
-            if (html != null && !html.isEmpty()) {
-                builder.withHtml(html);
-            }
-            if (urlStringList != null) {
-                for (String urlString : urlStringList) {
-                    builder.withURLAttachment(urlString);
-                }
-            }
-
-            final SMTPHandler handler = this.handler;
-            if (handler == null) {
-                logger.warn("Handler is null, cannot send mail.");
-                return false;
-            } else {
-                return handler.sendMail(builder.build());
-            }
-        } catch (AddressException | MalformedURLException | EmailException e) {
-            logger.warn("Could not send mail: {}", e.getMessage());
-            return false;
-        }
-    }
-
-    public static boolean sendHtmlMail(@Nullable ThingActions actions, @Nullable String recipient,
-            @Nullable String subject, @Nullable String html) {
-        return SendMailActions.sendHtmlMail(actions, recipient, subject, html, new ArrayList<>());
-    }
-
-    public static boolean sendHtmlMail(@Nullable ThingActions actions, @Nullable String recipient,
-            @Nullable String subject, @Nullable String html, @Nullable String urlString) {
-        List<String> urlList = new ArrayList<>();
-        if (urlString != null) {
-            urlList.add(urlString);
-        }
-        return SendMailActions.sendHtmlMail(actions, recipient, subject, html, urlList);
-    }
-
-    public static boolean sendHtmlMail(@Nullable ThingActions actions, @Nullable String recipient,
-            @Nullable String subject, @Nullable String html, @Nullable List<String> urlStringList) {
-        return invokeMethodOf(actions).sendHtmlMail(recipient, subject, html, urlStringList);
-    }
-
-    @Override
-    public void setThingHandler(@Nullable ThingHandler handler) {
-        if (handler instanceof SMTPHandler) {
-            this.handler = (SMTPHandler) handler;
-        }
-    }
-
-    @Override
-    public @Nullable ThingHandler getThingHandler() {
-        return this.handler;
-    }
-
-    private static ISendMailActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(SendMailActions.class.getName())) {
-            if (actions instanceof ISendMailActions) {
-                return (ISendMailActions) actions;
-            } else {
-                return (ISendMailActions) Proxy.newProxyInstance(ISendMailActions.class.getClassLoader(),
-                        new Class[] { ISendMailActions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
-        }
-        throw new IllegalArgumentException("Actions is not an instance of SendMailActions");
-    }
-}
index 69b36f6af0235edbadf4fb5b317e098cfcc74fb1..476cdceabc45f0a3f3c6fdfa239881859637546b 100644 (file)
@@ -19,7 +19,7 @@ import org.apache.commons.mail.DefaultAuthenticator;
 import org.apache.commons.mail.Email;
 import org.apache.commons.mail.EmailException;
 import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.mail.action.SendMailActions;
+import org.openhab.binding.mail.internal.action.SendMailActions;
 import org.openhab.binding.mail.internal.config.SMTPConfig;
 import org.openhab.core.thing.ChannelUID;
 import org.openhab.core.thing.Thing;
diff --git a/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/action/SendMailActions.java b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/action/SendMailActions.java
new file mode 100644 (file)
index 0000000..2c86e18
--- /dev/null
@@ -0,0 +1,221 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.mail.internal.action;
+
+import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.mail.internet.AddressException;
+
+import org.apache.commons.mail.EmailException;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.mail.internal.MailBuilder;
+import org.openhab.binding.mail.internal.SMTPHandler;
+import org.openhab.core.automation.annotation.ActionInput;
+import org.openhab.core.automation.annotation.ActionOutput;
+import org.openhab.core.automation.annotation.RuleAction;
+import org.openhab.core.thing.binding.ThingActions;
+import org.openhab.core.thing.binding.ThingActionsScope;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link SendMailActions} class defines rule actions for sending mail
+ *
+ * @author Jan N. Klug - Initial contribution
+ */
+@ThingActionsScope(name = "mail")
+@NonNullByDefault
+public class SendMailActions implements ThingActions {
+
+    private final Logger logger = LoggerFactory.getLogger(SendMailActions.class);
+
+    private @Nullable SMTPHandler handler;
+
+    @RuleAction(label = "send a text mail", description = "Sends a text mail.")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendMail(
+            @ActionInput(name = "recipient") @Nullable String recipient,
+            @ActionInput(name = "subject") @Nullable String subject,
+            @ActionInput(name = "text") @Nullable String text) {
+        return sendMail(recipient, subject, text, new ArrayList<>());
+    }
+
+    @RuleAction(label = "send a text mail", description = "Sends a text mail with URL attachment.")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendMail(
+            @ActionInput(name = "recipient") @Nullable String recipient,
+            @ActionInput(name = "subject") @Nullable String subject, @ActionInput(name = "text") @Nullable String text,
+            @ActionInput(name = "url") @Nullable String urlString) {
+        List<String> urlList = new ArrayList<>();
+        if (urlString != null) {
+            urlList.add(urlString);
+        }
+        return sendMail(recipient, subject, text, urlList);
+    }
+
+    @RuleAction(label = "send a text mail", description = "Sends a text mail with several URL attachments.")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendMail(
+            @ActionInput(name = "recipient") @Nullable String recipient,
+            @ActionInput(name = "subject") @Nullable String subject, @ActionInput(name = "text") @Nullable String text,
+            @ActionInput(name = "urlList") @Nullable List<String> urlStringList) {
+        if (recipient == null) {
+            logger.warn("Cannot send mail as recipient is missing.");
+            return false;
+        }
+
+        try {
+            MailBuilder builder = new MailBuilder(recipient);
+
+            if (subject != null && !subject.isEmpty()) {
+                builder.withSubject(subject);
+            }
+            if (text != null && !text.isEmpty()) {
+                builder.withText(text);
+            }
+            if (urlStringList != null) {
+                for (String urlString : urlStringList) {
+                    builder.withURLAttachment(urlString);
+                }
+            }
+
+            final SMTPHandler handler = this.handler;
+            if (handler == null) {
+                logger.info("Handler is null, cannot send mail.");
+                return false;
+            } else {
+                return handler.sendMail(builder.build());
+            }
+        } catch (AddressException | MalformedURLException | EmailException e) {
+            logger.warn("Could not send mail: {}", e.getMessage());
+            return false;
+        }
+    }
+
+    public static boolean sendMail(@Nullable ThingActions actions, @Nullable String recipient, @Nullable String subject,
+            @Nullable String text) {
+        return SendMailActions.sendMail(actions, recipient, subject, text, new ArrayList<>());
+    }
+
+    public static boolean sendMail(@Nullable ThingActions actions, @Nullable String recipient, @Nullable String subject,
+            @Nullable String text, @Nullable String urlString) {
+        List<String> urlList = new ArrayList<>();
+        if (urlString != null) {
+            urlList.add(urlString);
+        }
+        return SendMailActions.sendMail(actions, recipient, subject, text, urlList);
+    }
+
+    public static boolean sendMail(@Nullable ThingActions actions, @Nullable String recipient, @Nullable String subject,
+            @Nullable String text, @Nullable List<String> urlStringList) {
+        if (actions instanceof SendMailActions) {
+            return ((SendMailActions) actions).sendMail(recipient, subject, text, urlStringList);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of SendMailActions");
+        }
+    }
+
+    @RuleAction(label = "send a HTML mail", description = "Sends a HTML mail.")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendHtmlMail(
+            @ActionInput(name = "recipient") @Nullable String recipient,
+            @ActionInput(name = "subject") @Nullable String subject,
+            @ActionInput(name = "html") @Nullable String html) {
+        return sendHtmlMail(recipient, subject, html, new ArrayList<>());
+    }
+
+    @RuleAction(label = "send a HTML mail", description = "Sends a HTML mail with URL attachment.")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendHtmlMail(
+            @ActionInput(name = "recipient") @Nullable String recipient,
+            @ActionInput(name = "subject") @Nullable String subject, @ActionInput(name = "html") @Nullable String html,
+            @ActionInput(name = "url") @Nullable String urlString) {
+        List<String> urlList = new ArrayList<>();
+        if (urlString != null) {
+            urlList.add(urlString);
+        }
+        return sendHtmlMail(recipient, subject, html, urlList);
+    }
+
+    @RuleAction(label = "send a HTML mail", description = "Sends a HTML mail with several URL attachments.")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendHtmlMail(
+            @ActionInput(name = "recipient") @Nullable String recipient,
+            @ActionInput(name = "subject") @Nullable String subject, @ActionInput(name = "html") @Nullable String html,
+            @ActionInput(name = "urlList") @Nullable List<String> urlStringList) {
+        if (recipient == null) {
+            logger.warn("Cannot send mail as recipient is missing.");
+            return false;
+        }
+
+        try {
+            MailBuilder builder = new MailBuilder(recipient);
+
+            if (subject != null && !subject.isEmpty()) {
+                builder.withSubject(subject);
+            }
+            if (html != null && !html.isEmpty()) {
+                builder.withHtml(html);
+            }
+            if (urlStringList != null) {
+                for (String urlString : urlStringList) {
+                    builder.withURLAttachment(urlString);
+                }
+            }
+
+            final SMTPHandler handler = this.handler;
+            if (handler == null) {
+                logger.warn("Handler is null, cannot send mail.");
+                return false;
+            } else {
+                return handler.sendMail(builder.build());
+            }
+        } catch (AddressException | MalformedURLException | EmailException e) {
+            logger.warn("Could not send mail: {}", e.getMessage());
+            return false;
+        }
+    }
+
+    public static boolean sendHtmlMail(@Nullable ThingActions actions, @Nullable String recipient,
+            @Nullable String subject, @Nullable String html) {
+        return SendMailActions.sendHtmlMail(actions, recipient, subject, html, new ArrayList<>());
+    }
+
+    public static boolean sendHtmlMail(@Nullable ThingActions actions, @Nullable String recipient,
+            @Nullable String subject, @Nullable String html, @Nullable String urlString) {
+        List<String> urlList = new ArrayList<>();
+        if (urlString != null) {
+            urlList.add(urlString);
+        }
+        return SendMailActions.sendHtmlMail(actions, recipient, subject, html, urlList);
+    }
+
+    public static boolean sendHtmlMail(@Nullable ThingActions actions, @Nullable String recipient,
+            @Nullable String subject, @Nullable String html, @Nullable List<String> urlStringList) {
+        if (actions instanceof SendMailActions) {
+            return ((SendMailActions) actions).sendHtmlMail(recipient, subject, html, urlStringList);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of SendMailActions");
+        }
+    }
+
+    @Override
+    public void setThingHandler(@Nullable ThingHandler handler) {
+        if (handler instanceof SMTPHandler) {
+            this.handler = (SMTPHandler) handler;
+        }
+    }
+
+    @Override
+    public @Nullable ThingHandler getThingHandler() {
+        return handler;
+    }
+}
diff --git a/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/actions/MaxCubeActions.java b/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/actions/MaxCubeActions.java
deleted file mode 100644 (file)
index 1f709b4..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.max.actions;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.max.internal.actions.IMaxCubeActions;
-import org.openhab.binding.max.internal.handler.MaxCubeBridgeHandler;
-import org.openhab.core.automation.annotation.ActionOutput;
-import org.openhab.core.automation.annotation.RuleAction;
-import org.openhab.core.thing.binding.ThingActions;
-import org.openhab.core.thing.binding.ThingActionsScope;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link MaxCubeActions} class defines rule actions for MAX! Cube
- *
- * @author Christoph Weitkamp - Initial contribution
- */
-@ThingActionsScope(name = "max-cube")
-@NonNullByDefault
-public class MaxCubeActions implements ThingActions, IMaxCubeActions {
-
-    private final Logger logger = LoggerFactory.getLogger(MaxCubeActions.class);
-
-    private @Nullable MaxCubeBridgeHandler handler;
-
-    @Override
-    public void setThingHandler(@Nullable ThingHandler handler) {
-        if (handler instanceof MaxCubeBridgeHandler) {
-            this.handler = (MaxCubeBridgeHandler) handler;
-        }
-    }
-
-    @Override
-    public @Nullable ThingHandler getThingHandler() {
-        return this.handler;
-    }
-
-    @Override
-    @RuleAction(label = "Backup Cube Data", description = "Creates a backup of the MAX! Cube data.")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean backup() {
-        MaxCubeBridgeHandler actionsHandler = handler;
-        if (actionsHandler == null) {
-            logger.info("MaxCubeActions: Action service ThingHandler is null!");
-            return false;
-        }
-        actionsHandler.backup();
-        return true;
-    }
-
-    public static boolean backup(@Nullable ThingActions actions) {
-        return invokeMethodOf(actions).backup();
-    }
-
-    @Override
-    @RuleAction(label = "Reset Cube Configuration", description = "Resets the MAX! Cube room and device information. Devices will need to be included again!")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean resetConfig() {
-        MaxCubeBridgeHandler actionsHandler = handler;
-        if (actionsHandler == null) {
-            logger.info("MaxCubeActions: Action service ThingHandler is null!");
-            return false;
-        }
-        actionsHandler.cubeConfigReset();
-        return true;
-    }
-
-    public static boolean reset(@Nullable ThingActions actions) {
-        return invokeMethodOf(actions).resetConfig();
-    }
-
-    @Override
-    @RuleAction(label = "Restart Cube", description = "Restarts the MAX! Cube.")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean reboot() {
-        MaxCubeBridgeHandler actionsHandler = handler;
-        if (actionsHandler == null) {
-            logger.info("MaxCubeActions: Action service ThingHandler is null!");
-            return false;
-        }
-        actionsHandler.cubeReboot();
-        return true;
-    }
-
-    public static boolean reboot(@Nullable ThingActions actions) {
-        return invokeMethodOf(actions).reboot();
-    }
-
-    private static IMaxCubeActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(MaxCubeActions.class.getName())) {
-            if (actions instanceof IMaxCubeActions) {
-                return (IMaxCubeActions) actions;
-            } else {
-                return (IMaxCubeActions) Proxy.newProxyInstance(IMaxCubeActions.class.getClassLoader(),
-                        new Class[] { IMaxCubeActions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
-        }
-        throw new IllegalArgumentException("Actions is not an instance of MaxCubeActions");
-    }
-}
diff --git a/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/actions/MaxDevicesActions.java b/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/actions/MaxDevicesActions.java
deleted file mode 100644 (file)
index d9291fa..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.max.actions;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.max.internal.actions.IMaxDevicesActions;
-import org.openhab.binding.max.internal.handler.MaxDevicesHandler;
-import org.openhab.core.automation.annotation.ActionOutput;
-import org.openhab.core.automation.annotation.RuleAction;
-import org.openhab.core.thing.binding.ThingActions;
-import org.openhab.core.thing.binding.ThingActionsScope;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link MaxDevicesActions} class defines rule actions for MAX! devices
- *
- * @author Christoph Weitkamp - Initial contribution
- */
-@ThingActionsScope(name = "max-devices")
-@NonNullByDefault
-public class MaxDevicesActions implements ThingActions, IMaxDevicesActions {
-
-    private final Logger logger = LoggerFactory.getLogger(MaxDevicesActions.class);
-
-    private @Nullable MaxDevicesHandler handler;
-
-    @Override
-    public void setThingHandler(@Nullable ThingHandler handler) {
-        if (handler instanceof MaxDevicesHandler) {
-            this.handler = (MaxDevicesHandler) handler;
-        }
-    }
-
-    @Override
-    public @Nullable ThingHandler getThingHandler() {
-        return this.handler;
-    }
-
-    @Override
-    @RuleAction(label = "Delete Device from Cube", description = "Deletes the device from the MAX! Cube. Device will need to be included again!")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean deleteFromCube() {
-        MaxDevicesHandler actionsHandler = handler;
-        if (actionsHandler == null) {
-            logger.info("MaxDevicesActions: Action service ThingHandler is null!");
-            return false;
-        }
-        actionsHandler.deviceDelete();
-        return true;
-    }
-
-    public static boolean deleteFromCube(@Nullable ThingActions actions) {
-        return invokeMethodOf(actions).deleteFromCube();
-    }
-
-    private static IMaxDevicesActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(MaxDevicesActions.class.getName())) {
-            if (actions instanceof IMaxDevicesActions) {
-                return (IMaxDevicesActions) actions;
-            } else {
-                return (IMaxDevicesActions) Proxy.newProxyInstance(IMaxDevicesActions.class.getClassLoader(),
-                        new Class[] { IMaxDevicesActions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
-        }
-        throw new IllegalArgumentException("Actions is not an instance of MaxDevicesActions");
-    }
-}
diff --git a/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/actions/IMaxCubeActions.java b/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/actions/IMaxCubeActions.java
deleted file mode 100644 (file)
index 69d57b5..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.max.internal.actions;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.max.actions.MaxCubeActions;
-
-/**
- * The {@link IMaxCubeActions} defines the interface for all thing actions supported by the binding.
- * These methods, parameters, and return types are explained in {@link MaxCubeActions}.
- *
- * @author Christoph Weitkamp - Initial contribution
- */
-@NonNullByDefault
-public interface IMaxCubeActions {
-
-    Boolean backup();
-
-    Boolean resetConfig();
-
-    Boolean reboot();
-}
diff --git a/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/actions/IMaxDevicesActions.java b/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/actions/IMaxDevicesActions.java
deleted file mode 100644 (file)
index 0fcb61e..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.max.internal.actions;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.max.actions.MaxDevicesActions;
-
-/**
- * The {@link IMaxDevicesActions} defines the interface for all thing actions supported by the binding.
- * These methods, parameters, and return types are explained in {@link MaxDevicesActions}.
- *
- * @author Christoph Weitkamp - Initial contribution
- */
-@NonNullByDefault
-public interface IMaxDevicesActions {
-
-    Boolean deleteFromCube();
-}
diff --git a/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/actions/MaxCubeActions.java b/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/actions/MaxCubeActions.java
new file mode 100644 (file)
index 0000000..0814fe1
--- /dev/null
@@ -0,0 +1,107 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.max.internal.actions;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.max.internal.handler.MaxCubeBridgeHandler;
+import org.openhab.core.automation.annotation.ActionOutput;
+import org.openhab.core.automation.annotation.RuleAction;
+import org.openhab.core.thing.binding.ThingActions;
+import org.openhab.core.thing.binding.ThingActionsScope;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link MaxCubeActions} class defines rule actions for MAX! Cube
+ *
+ * @author Christoph Weitkamp - Initial contribution
+ */
+@ThingActionsScope(name = "max-cube")
+@NonNullByDefault
+public class MaxCubeActions implements ThingActions {
+
+    private final Logger logger = LoggerFactory.getLogger(MaxCubeActions.class);
+
+    private @Nullable MaxCubeBridgeHandler handler;
+
+    @Override
+    public void setThingHandler(@Nullable ThingHandler handler) {
+        if (handler instanceof MaxCubeBridgeHandler) {
+            this.handler = (MaxCubeBridgeHandler) handler;
+        }
+    }
+
+    @Override
+    public @Nullable ThingHandler getThingHandler() {
+        return handler;
+    }
+
+    @RuleAction(label = "backup the Cube data", description = "Creates a backup of the MAX! Cube data.")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean backup() {
+        MaxCubeBridgeHandler actionsHandler = handler;
+        if (actionsHandler == null) {
+            logger.info("MaxCubeActions: Action service ThingHandler is null!");
+            return false;
+        }
+        actionsHandler.backup();
+        return true;
+    }
+
+    public static boolean backup(@Nullable ThingActions actions) {
+        if (actions instanceof MaxCubeActions) {
+            return ((MaxCubeActions) actions).backup();
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of MaxCubeActions");
+        }
+    }
+
+    @RuleAction(label = "reset the Cube configuration", description = "Resets the MAX! Cube room and device information. Devices will need to be included again!")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean resetConfig() {
+        MaxCubeBridgeHandler actionsHandler = handler;
+        if (actionsHandler == null) {
+            logger.info("MaxCubeActions: Action service ThingHandler is null!");
+            return false;
+        }
+        actionsHandler.cubeConfigReset();
+        return true;
+    }
+
+    public static boolean reset(@Nullable ThingActions actions) {
+        if (actions instanceof MaxCubeActions) {
+            return ((MaxCubeActions) actions).resetConfig();
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of MaxCubeActions");
+        }
+    }
+
+    @RuleAction(label = "restart the Cube", description = "Restarts the MAX! Cube.")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean reboot() {
+        MaxCubeBridgeHandler actionsHandler = handler;
+        if (actionsHandler == null) {
+            logger.info("MaxCubeActions: Action service ThingHandler is null!");
+            return false;
+        }
+        actionsHandler.cubeReboot();
+        return true;
+    }
+
+    public static boolean reboot(@Nullable ThingActions actions) {
+        if (actions instanceof MaxCubeActions) {
+            return ((MaxCubeActions) actions).reboot();
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of MaxCubeActions");
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/actions/MaxDevicesActions.java b/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/actions/MaxDevicesActions.java
new file mode 100644 (file)
index 0000000..f14fdbd
--- /dev/null
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.max.internal.actions;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.max.internal.handler.MaxDevicesHandler;
+import org.openhab.core.automation.annotation.ActionOutput;
+import org.openhab.core.automation.annotation.RuleAction;
+import org.openhab.core.thing.binding.ThingActions;
+import org.openhab.core.thing.binding.ThingActionsScope;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link MaxDevicesActions} class defines rule actions for MAX! devices
+ *
+ * @author Christoph Weitkamp - Initial contribution
+ */
+@ThingActionsScope(name = "max-devices")
+@NonNullByDefault
+public class MaxDevicesActions implements ThingActions {
+
+    private final Logger logger = LoggerFactory.getLogger(MaxDevicesActions.class);
+
+    private @Nullable MaxDevicesHandler handler;
+
+    @Override
+    public void setThingHandler(@Nullable ThingHandler handler) {
+        if (handler instanceof MaxDevicesHandler) {
+            this.handler = (MaxDevicesHandler) handler;
+        }
+    }
+
+    @Override
+    public @Nullable ThingHandler getThingHandler() {
+        return handler;
+    }
+
+    @RuleAction(label = "delete the device from the Cube", description = "Deletes the device from the MAX! Cube. Device will need to be included again!")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean deleteFromCube() {
+        MaxDevicesHandler actionsHandler = handler;
+        if (actionsHandler == null) {
+            logger.info("MaxDevicesActions: Action service ThingHandler is null!");
+            return false;
+        }
+        actionsHandler.deviceDelete();
+        return true;
+    }
+
+    public static boolean deleteFromCube(@Nullable ThingActions actions) {
+        if (actions instanceof MaxDevicesActions) {
+            return ((MaxDevicesActions) actions).deleteFromCube();
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of MaxDevicesActions");
+        }
+    }
+}
index e2deb6025dc47c00e7a089223a9bd8fa9a85e43d..ca7aa63c99e7c3284d6aaefdd791c3fcdc2b9d48 100644 (file)
@@ -49,9 +49,9 @@ import java.util.stream.Stream;
 
 import javax.measure.quantity.Temperature;
 
-import org.openhab.binding.max.actions.MaxCubeActions;
 import org.openhab.binding.max.internal.MaxBackupUtils;
 import org.openhab.binding.max.internal.MaxBindingConstants;
+import org.openhab.binding.max.internal.actions.MaxCubeActions;
 import org.openhab.binding.max.internal.command.ACommand;
 import org.openhab.binding.max.internal.command.CCommand;
 import org.openhab.binding.max.internal.command.CubeCommand;
index 860a641a231b427f55cc2d9f8733b5c4f5f6f916..753427ad80165db1568aaa53eb3b9a265360b0f8 100644 (file)
@@ -27,7 +27,7 @@ import java.util.Map.Entry;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 
-import org.openhab.binding.max.actions.MaxDevicesActions;
+import org.openhab.binding.max.internal.actions.MaxDevicesActions;
 import org.openhab.binding.max.internal.command.CCommand;
 import org.openhab.binding.max.internal.command.QCommand;
 import org.openhab.binding.max.internal.command.SConfigCommand;
diff --git a/bundles/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/internal/action/IMPDActions.java b/bundles/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/internal/action/IMPDActions.java
deleted file mode 100644 (file)
index 6accb2c..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.mpd.internal.action;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * The {@link IMPDActions} interface defines rule actions for sending commands to a Music Player Daemon
- *
- * @author Stefan Röllin - Initial contribution
- */
-@NonNullByDefault
-public interface IMPDActions {
-
-    public void sendCommand(@Nullable String command, @Nullable String parameter);
-
-    public void sendCommand(@Nullable String command);
-}
index 85450853798c3859de24b205de82acd734a6176d..060ea6d37b9a382992f7835d4beb599fbfad2ead 100644 (file)
@@ -12,9 +12,6 @@
  */
 package org.openhab.binding.mpd.internal.action;
 
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.mpd.internal.handler.MPDHandler;
@@ -33,7 +30,7 @@ import org.slf4j.LoggerFactory;
  */
 @ThingActionsScope(name = "mpd")
 @NonNullByDefault
-public class MPDActions implements ThingActions, IMPDActions {
+public class MPDActions implements ThingActions {
 
     private final Logger logger = LoggerFactory.getLogger(MPDActions.class);
 
@@ -51,8 +48,7 @@ public class MPDActions implements ThingActions, IMPDActions {
         return handler;
     }
 
-    @Override
-    @RuleAction(label = "MPD : Send command", description = "Send a command to the Music Player Daemon.")
+    @RuleAction(label = "send a command with a parameter", description = "Send a command to the Music Player Daemon.")
     public void sendCommand(@ActionInput(name = "command") @Nullable String command,
             @ActionInput(name = "parameter") @Nullable String parameter) {
         logger.debug("sendCommand called with {}", command);
@@ -65,8 +61,7 @@ public class MPDActions implements ThingActions, IMPDActions {
         }
     }
 
-    @Override
-    @RuleAction(label = "MPD : Send command", description = "Send a command to the Music Player Daemon.")
+    @RuleAction(label = "send a command", description = "Send a command to the Music Player Daemon.")
     public void sendCommand(@ActionInput(name = "command") @Nullable String command) {
         logger.debug("sendCommand called with {}", command);
 
@@ -78,31 +73,20 @@ public class MPDActions implements ThingActions, IMPDActions {
         }
     }
 
-    private static IMPDActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(MPDActions.class.getName())) {
-            if (actions instanceof IMPDActions) {
-                return (IMPDActions) actions;
-            } else {
-                return (IMPDActions) Proxy.newProxyInstance(IMPDActions.class.getClassLoader(),
-                        new Class[] { IMPDActions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
-        }
-        throw new IllegalArgumentException("Actions is not an instance of MPDActions");
-    }
-
     public static void sendCommand(@Nullable ThingActions actions, @Nullable String command,
             @Nullable String parameter) {
-        invokeMethodOf(actions).sendCommand(command, parameter);
+        if (actions instanceof MPDActions) {
+            ((MPDActions) actions).sendCommand(command, parameter);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of MPDActions");
+        }
     }
 
     public static void sendCommand(@Nullable ThingActions actions, @Nullable String command) {
-        invokeMethodOf(actions).sendCommand(command);
+        if (actions instanceof MPDActions) {
+            ((MPDActions) actions).sendCommand(command);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of MPDActions");
+        }
     }
 }
diff --git a/bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/action/IMQTTActions.java b/bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/action/IMQTTActions.java
deleted file mode 100644 (file)
index 9cc1463..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.mqtt.action;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * The {@link IMQTTActions} defines the interface for all thing actions supported by the binding.
- *
- * @author Laurent Garnier - Initial contribution
- */
-@NonNullByDefault
-public interface IMQTTActions {
-
-    public void publishMQTT(@Nullable String topic, @Nullable String value, @Nullable Boolean retain);
-}
diff --git a/bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/action/MQTTActions.java b/bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/action/MQTTActions.java
deleted file mode 100644 (file)
index 339236a..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.mqtt.action;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.mqtt.handler.AbstractBrokerHandler;
-import org.openhab.core.automation.annotation.ActionInput;
-import org.openhab.core.automation.annotation.RuleAction;
-import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
-import org.openhab.core.thing.binding.ThingActions;
-import org.openhab.core.thing.binding.ThingActionsScope;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * This is the automation engine action handler service for the publishMQTT action.
- * <p>
- * <b>Note:</b>The static method <b>invokeMethodOf</b> handles the case where
- * the test <i>actions instanceof MQTTActions</i> fails. This test can fail
- * due to an issue in openHAB core v2.5.0 where the {@link MQTTActions} class
- * can be loaded by a different classloader than the <i>actions</i> instance.
- *
- * @author David Graeff - Initial contribution
- */
-@ThingActionsScope(name = "mqtt")
-@NonNullByDefault
-public class MQTTActions implements ThingActions, IMQTTActions {
-    private final Logger logger = LoggerFactory.getLogger(MQTTActions.class);
-    private @Nullable AbstractBrokerHandler handler;
-
-    @Override
-    public void setThingHandler(@Nullable ThingHandler handler) {
-        this.handler = (AbstractBrokerHandler) handler;
-    }
-
-    @Override
-    public @Nullable ThingHandler getThingHandler() {
-        return this.handler;
-    }
-
-    @RuleAction(label = "@text/actionLabel", description = "@text/actionDesc")
-    public void publishMQTT(
-            @ActionInput(name = "topic", label = "@text/actionInputTopicLabel", description = "@text/actionInputTopicDesc") @Nullable String topic,
-            @ActionInput(name = "value", label = "@text/actionInputValueLabel", description = "@text/actionInputValueDesc") @Nullable String value) {
-        publishMQTT(topic, value, null);
-    }
-
-    @Override
-    @RuleAction(label = "@text/actionLabel", description = "@text/actionDesc")
-    public void publishMQTT(
-            @ActionInput(name = "topic", label = "@text/actionInputTopicLabel", description = "@text/actionInputTopicDesc") @Nullable final String topic,
-            @ActionInput(name = "value", label = "@text/actionInputValueLabel", description = "@text/actionInputValueDesc") @Nullable final String value,
-            @ActionInput(name = "retain", label = "@text/actionInputRetainlabel", description = "@text/actionInputRetainDesc") @Nullable final Boolean retain) {
-        AbstractBrokerHandler brokerHandler = handler;
-        if (brokerHandler == null) {
-            logger.warn("MQTT Action service ThingHandler is null!");
-            return;
-        }
-        MqttBrokerConnection connection = brokerHandler.getConnection();
-        if (connection == null) {
-            logger.warn("MQTT Action service ThingHandler connection is null!");
-            return;
-        }
-        if (value == null) {
-            logger.debug("skipping MQTT publishing to topic '{}' due to null value.", topic);
-            return;
-        }
-        if (topic == null) {
-            logger.debug("skipping MQTT publishing of value '{}' as topic is null.", value);
-            return;
-        }
-
-        connection.publish(topic, value.getBytes(), connection.getQos(), retain != null && retain.booleanValue())
-                .thenRun(() -> {
-                    logger.debug("MQTT publish to {} performed", topic);
-                }).exceptionally(e -> {
-                    logger.warn("MQTT publish to {} failed!", topic);
-                    return null;
-                });
-    }
-
-    public static void publishMQTT(@Nullable ThingActions actions, @Nullable String topic, @Nullable String value) {
-        publishMQTT(actions, topic, value, null);
-    }
-
-    public static void publishMQTT(@Nullable ThingActions actions, @Nullable String topic, @Nullable String value,
-            @Nullable Boolean retain) {
-        invokeMethodOf(actions).publishMQTT(topic, value, retain);
-    }
-
-    private static IMQTTActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(MQTTActions.class.getName())) {
-            if (actions instanceof IMQTTActions) {
-                return (IMQTTActions) actions;
-            } else {
-                return (IMQTTActions) Proxy.newProxyInstance(IMQTTActions.class.getClassLoader(),
-                        new Class[] { IMQTTActions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
-        }
-        throw new IllegalArgumentException("Actions is not an instance of MQTTActions");
-    }
-}
index 9b58a7cab8f98d84b8670bd999f18798b652bd05..6468e3437470ffc38abccf14d608f72096d9f24c 100644 (file)
@@ -21,9 +21,9 @@ import java.util.concurrent.TimeoutException;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.mqtt.action.MQTTActions;
 import org.openhab.binding.mqtt.discovery.MQTTTopicDiscoveryParticipant;
 import org.openhab.binding.mqtt.discovery.TopicSubscribe;
+import org.openhab.binding.mqtt.internal.action.MQTTActions;
 import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
 import org.openhab.core.io.transport.mqtt.MqttConnectionObserver;
 import org.openhab.core.io.transport.mqtt.MqttConnectionState;
diff --git a/bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/internal/action/MQTTActions.java b/bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/internal/action/MQTTActions.java
new file mode 100644 (file)
index 0000000..75b748c
--- /dev/null
@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.mqtt.internal.action;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.mqtt.handler.AbstractBrokerHandler;
+import org.openhab.core.automation.annotation.ActionInput;
+import org.openhab.core.automation.annotation.RuleAction;
+import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
+import org.openhab.core.thing.binding.ThingActions;
+import org.openhab.core.thing.binding.ThingActionsScope;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This is the automation engine action handler service for the publishMQTT action.
+ *
+ * @author David Graeff - Initial contribution
+ */
+@ThingActionsScope(name = "mqtt")
+@NonNullByDefault
+public class MQTTActions implements ThingActions {
+    private final Logger logger = LoggerFactory.getLogger(MQTTActions.class);
+    private @Nullable AbstractBrokerHandler handler;
+
+    @Override
+    public void setThingHandler(@Nullable ThingHandler handler) {
+        this.handler = (AbstractBrokerHandler) handler;
+    }
+
+    @Override
+    public @Nullable ThingHandler getThingHandler() {
+        return handler;
+    }
+
+    @RuleAction(label = "@text/actionLabel", description = "@text/actionDesc")
+    public void publishMQTT(
+            @ActionInput(name = "topic", label = "@text/actionInputTopicLabel", description = "@text/actionInputTopicDesc") @Nullable String topic,
+            @ActionInput(name = "value", label = "@text/actionInputValueLabel", description = "@text/actionInputValueDesc") @Nullable String value) {
+        publishMQTT(topic, value, null);
+    }
+
+    @RuleAction(label = "@text/actionLabel", description = "@text/actionDesc")
+    public void publishMQTT(
+            @ActionInput(name = "topic", label = "@text/actionInputTopicLabel", description = "@text/actionInputTopicDesc") @Nullable final String topic,
+            @ActionInput(name = "value", label = "@text/actionInputValueLabel", description = "@text/actionInputValueDesc") @Nullable final String value,
+            @ActionInput(name = "retain", label = "@text/actionInputRetainlabel", description = "@text/actionInputRetainDesc") @Nullable final Boolean retain) {
+        AbstractBrokerHandler brokerHandler = handler;
+        if (brokerHandler == null) {
+            logger.warn("MQTT Action service ThingHandler is null!");
+            return;
+        }
+        MqttBrokerConnection connection = brokerHandler.getConnection();
+        if (connection == null) {
+            logger.warn("MQTT Action service ThingHandler connection is null!");
+            return;
+        }
+        if (value == null) {
+            logger.debug("skipping MQTT publishing to topic '{}' due to null value.", topic);
+            return;
+        }
+        if (topic == null) {
+            logger.debug("skipping MQTT publishing of value '{}' as topic is null.", value);
+            return;
+        }
+
+        connection.publish(topic, value.getBytes(), connection.getQos(), retain != null && retain.booleanValue())
+                .thenRun(() -> {
+                    logger.debug("MQTT publish to {} performed", topic);
+                }).exceptionally(e -> {
+                    logger.warn("MQTT publish to {} failed!", topic);
+                    return null;
+                });
+    }
+
+    public static void publishMQTT(@Nullable ThingActions actions, @Nullable String topic, @Nullable String value) {
+        publishMQTT(actions, topic, value, null);
+    }
+
+    public static void publishMQTT(@Nullable ThingActions actions, @Nullable String topic, @Nullable String value,
+            @Nullable Boolean retain) {
+        if (actions instanceof MQTTActions) {
+            ((MQTTActions) actions).publishMQTT(topic, value, retain);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of MQTTActions");
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/action/INetworkActions.java b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/action/INetworkActions.java
deleted file mode 100644 (file)
index cc068ae..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.network.internal.action;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * The {@link INetworkActions} defines the interface for all thing actions supported by the binding.
- *
- * @author Wouter Born - Initial contribution
- */
-@NonNullByDefault
-public interface INetworkActions {
-
-    void sendWakeOnLanPacket();
-}
index ab2d6801f1f92b7a4a74910bee5719f4cb256dd0..a28837f0232f55e9897a58e48510b2a930d5e0f0 100644 (file)
@@ -12,9 +12,6 @@
  */
 package org.openhab.binding.network.internal.action;
 
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.network.internal.handler.NetworkHandler;
@@ -27,17 +24,12 @@ import org.slf4j.LoggerFactory;
 
 /**
  * The class is responsible to call corresponding actions on {@link NetworkHandler}.
- * <p>
- * <b>Note:</b>The static method <b>invokeMethodOf</b> handles the case where
- * the test <i>actions instanceof NetworkActions</i> fails. This test can fail
- * due to an issue in openHAB core v2.5.0 where the {@link NetworkActions} class
- * can be loaded by a different classloader than the <i>actions</i> instance.
  *
  * @author Wouter Born - Initial contribution
  */
 @ThingActionsScope(name = "network")
 @NonNullByDefault
-public class NetworkActions implements ThingActions, INetworkActions {
+public class NetworkActions implements ThingActions {
 
     private final Logger logger = LoggerFactory.getLogger(NetworkActions.class);
 
@@ -55,8 +47,7 @@ public class NetworkActions implements ThingActions, INetworkActions {
         return handler;
     }
 
-    @Override
-    @RuleAction(label = "Send WoL Packet", description = "Send a Wake-on-LAN packet to wake the device")
+    @RuleAction(label = "send a WoL packet", description = "Send a Wake-on-LAN packet to wake the device.")
     public void sendWakeOnLanPacket() {
         NetworkHandler localHandler = handler;
         if (localHandler != null) {
@@ -67,25 +58,10 @@ public class NetworkActions implements ThingActions, INetworkActions {
     }
 
     public static void sendWakeOnLanPacket(@Nullable ThingActions actions) {
-        invokeMethodOf(actions).sendWakeOnLanPacket();
-    }
-
-    private static INetworkActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(NetworkActions.class.getName())) {
-            if (actions instanceof INetworkActions) {
-                return (INetworkActions) actions;
-            } else {
-                return (INetworkActions) Proxy.newProxyInstance(INetworkActions.class.getClassLoader(),
-                        new Class[] { INetworkActions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
+        if (actions instanceof NetworkActions) {
+            ((NetworkActions) actions).sendWakeOnLanPacket();
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of NetworkActions");
         }
-        throw new IllegalArgumentException("Actions is not an instance of " + NetworkActions.class.getName());
     }
 }
diff --git a/bundles/org.openhab.binding.nuvo/src/main/java/org/openhab/binding/nuvo/internal/INuvoThingActions.java b/bundles/org.openhab.binding.nuvo/src/main/java/org/openhab/binding/nuvo/internal/INuvoThingActions.java
deleted file mode 100644 (file)
index 8bec3d2..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.nuvo.internal;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * The {@link INuvoThingActions} defines the interface for all thing actions supported by the binding.
- * These methods, parameters, and return types are explained in {@link NuvoThingActions}.
- *
- * @author Michael Lobstein - Initial contribution
- */
-@NonNullByDefault
-public interface INuvoThingActions {
-
-    void sendNuvoCommand(String rawCommand);
-}
index cf37669eb9090c8961928fafa261bca0bc484cb4..59716555bbf923c88e496941e7a3222b3dcc5942 100644 (file)
@@ -12,9 +12,6 @@
  */
 package org.openhab.binding.nuvo.internal;
 
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.nuvo.internal.handler.NuvoHandler;
@@ -30,17 +27,16 @@ import org.slf4j.LoggerFactory;
  * Some automation actions to be used with a {@link NuvoThingActions}
  *
  * @author Michael Lobstein - initial contribution
- *
  */
 @ThingActionsScope(name = "nuvo")
 @NonNullByDefault
-public class NuvoThingActions implements ThingActions, INuvoThingActions {
+public class NuvoThingActions implements ThingActions {
 
     private final Logger logger = LoggerFactory.getLogger(NuvoThingActions.class);
 
     private @Nullable NuvoHandler handler;
 
-    @RuleAction(label = "sendNuvoCommand", description = "Action that sends raw command to the amplifer")
+    @RuleAction(label = "send a raw command", description = "Send a raw command to the amplifier.")
     public void sendNuvoCommand(@ActionInput(name = "sendNuvoCommand") String rawCommand) {
         NuvoHandler localHandler = handler;
         if (localHandler != null) {
@@ -52,9 +48,12 @@ public class NuvoThingActions implements ThingActions, INuvoThingActions {
     }
 
     /** Static alias to support the old DSL rules engine and make the action available there. */
-    public static void sendNuvoCommand(@Nullable ThingActions actions, String rawCommand)
-            throws IllegalArgumentException {
-        invokeMethodOf(actions).sendNuvoCommand(rawCommand);
+    public static void sendNuvoCommand(@Nullable ThingActions actions, String rawCommand) {
+        if (actions instanceof NuvoThingActions) {
+            ((NuvoThingActions) actions).sendNuvoCommand(rawCommand);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of NuvoThingActions");
+        }
     }
 
     @Override
@@ -64,25 +63,6 @@ public class NuvoThingActions implements ThingActions, INuvoThingActions {
 
     @Override
     public @Nullable ThingHandler getThingHandler() {
-        return this.handler;
-    }
-
-    private static INuvoThingActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(NuvoThingActions.class.getName())) {
-            if (actions instanceof NuvoThingActions) {
-                return (INuvoThingActions) actions;
-            } else {
-                return (INuvoThingActions) Proxy.newProxyInstance(INuvoThingActions.class.getClassLoader(),
-                        new Class[] { INuvoThingActions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
-        }
-        throw new IllegalArgumentException("Actions is not an instance of NuvoThingActions");
+        return handler;
     }
 }
index 6386f7fe369ec74561a4221bab6c4513aa522ff6..9c84aa7e6d851e17fc2cddccf3d86113e46035b2 100644 (file)
@@ -14,14 +14,58 @@ package org.openhab.binding.onkyo.internal.automation.modules;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.onkyo.internal.handler.OnkyoHandler;
+import org.openhab.core.automation.annotation.ActionInput;
+import org.openhab.core.automation.annotation.RuleAction;
+import org.openhab.core.thing.binding.ThingActions;
+import org.openhab.core.thing.binding.ThingActionsScope;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
- * The {@link OnkyoThingActions} defines the interface for all thing actions supported by the binding.
+ * Some automation actions to be used with a {@link OnkyoThingActions}
  *
- * @author Laurent Garnier - initial contribution
+ * @author David Masshardt - initial contribution
  */
+@ThingActionsScope(name = "onkyo")
 @NonNullByDefault
-public interface OnkyoThingActions {
+public class OnkyoThingActions implements ThingActions {
 
-    public void sendRawCommand(@Nullable String command, @Nullable String value);
+    private final Logger logger = LoggerFactory.getLogger(OnkyoThingActions.class);
+
+    private @Nullable OnkyoHandler handler;
+
+    @SuppressWarnings("null")
+    @RuleAction(label = "send a raw command", description = "Send a raw command to the receiver.")
+    public void sendRawCommand(@ActionInput(name = "command") @Nullable String command,
+            @ActionInput(name = "command") @Nullable String value) {
+        logger.debug("sendRawCommand called with raw command: {} value: {}", command, value);
+        if (handler == null) {
+            logger.warn("Onkyo Action service ThingHandler is null!");
+            return;
+        }
+        handler.sendRawCommand(command, value);
+    }
+
+    public static void sendRawCommand(@Nullable ThingActions actions, @Nullable String command,
+            @Nullable String value) {
+        if (actions instanceof OnkyoThingActions) {
+            ((OnkyoThingActions) actions).sendRawCommand(command, value);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of OnkyoThingActions");
+        }
+    }
+
+    @Override
+    public void setThingHandler(@Nullable ThingHandler handler) {
+        if (handler instanceof OnkyoHandler) {
+            this.handler = (OnkyoHandler) handler;
+        }
+    }
+
+    @Override
+    public @Nullable ThingHandler getThingHandler() {
+        return handler;
+    }
 }
diff --git a/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/automation/modules/OnkyoThingActionsService.java b/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/automation/modules/OnkyoThingActionsService.java
deleted file mode 100644 (file)
index 3b2a0d8..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.onkyo.internal.automation.modules;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.onkyo.internal.handler.OnkyoHandler;
-import org.openhab.core.automation.annotation.ActionInput;
-import org.openhab.core.automation.annotation.RuleAction;
-import org.openhab.core.thing.binding.ThingActions;
-import org.openhab.core.thing.binding.ThingActionsScope;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Some automation actions to be used with a {@link OnkyoThingActionsService}
- * <p>
- * <b>Note:</b>The static method <b>invokeMethodOf</b> handles the case where
- * the test <i>actions instanceof OnkyoThingActionsService</i> fails. This test can fail
- * due to an issue in openHAB core v2.5.0 where the {@link OnkyoThingActionsService} class
- * can be loaded by a different classloader than the <i>actions</i> instance.
- *
- * @author David Masshardt - initial contribution
- *
- */
-@ThingActionsScope(name = "onkyo")
-@NonNullByDefault
-public class OnkyoThingActionsService implements ThingActions, OnkyoThingActions {
-
-    private final Logger logger = LoggerFactory.getLogger(OnkyoThingActionsService.class);
-
-    private @Nullable OnkyoHandler handler;
-
-    @Override
-    @SuppressWarnings("null")
-    @RuleAction(label = "Onkyo sendRawCommand", description = "Action that sends raw command to the receiver")
-    public void sendRawCommand(@ActionInput(name = "command") @Nullable String command,
-            @ActionInput(name = "command") @Nullable String value) {
-        logger.debug("sendRawCommand called with raw command: {} value: {}", command, value);
-        if (handler == null) {
-            logger.warn("Onkyo Action service ThingHandler is null!");
-            return;
-        }
-        handler.sendRawCommand(command, value);
-    }
-
-    public static void sendRawCommand(@Nullable ThingActions actions, @Nullable String command,
-            @Nullable String value) {
-        invokeMethodOf(actions).sendRawCommand(command, value);
-    }
-
-    private static OnkyoThingActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(OnkyoThingActionsService.class.getName())) {
-            if (actions instanceof OnkyoThingActions) {
-                return (OnkyoThingActions) actions;
-            } else {
-                return (OnkyoThingActions) Proxy.newProxyInstance(OnkyoThingActions.class.getClassLoader(),
-                        new Class[] { OnkyoThingActions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
-        }
-        throw new IllegalArgumentException("Actions is not an instance of OnkyoThingActionsService");
-    }
-
-    @Override
-    public void setThingHandler(@Nullable ThingHandler handler) {
-        if (handler instanceof OnkyoHandler) {
-            this.handler = (OnkyoHandler) handler;
-        }
-    }
-
-    @Override
-    public @Nullable ThingHandler getThingHandler() {
-        return this.handler;
-    }
-}
index 0e54d42927f8dec3f3f7e733d57ed3d857737b45..717873b3db822ec4d9a45ca0d663a7a59369edaa 100644 (file)
@@ -32,7 +32,7 @@ import org.openhab.binding.onkyo.internal.OnkyoConnection;
 import org.openhab.binding.onkyo.internal.OnkyoEventListener;
 import org.openhab.binding.onkyo.internal.OnkyoStateDescriptionProvider;
 import org.openhab.binding.onkyo.internal.ServiceType;
-import org.openhab.binding.onkyo.internal.automation.modules.OnkyoThingActionsService;
+import org.openhab.binding.onkyo.internal.automation.modules.OnkyoThingActions;
 import org.openhab.binding.onkyo.internal.config.OnkyoDeviceConfiguration;
 import org.openhab.binding.onkyo.internal.eiscp.EiscpCommand;
 import org.openhab.binding.onkyo.internal.eiscp.EiscpMessage;
@@ -910,6 +910,6 @@ public class OnkyoHandler extends UpnpAudioSinkHandler implements OnkyoEventList
 
     @Override
     public Collection<Class<? extends ThingHandlerService>> getServices() {
-        return Collections.singletonList(OnkyoThingActionsService.class);
+        return Collections.singletonList(OnkyoThingActions.class);
     }
 }
diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/action/IPushbulletActions.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/action/IPushbulletActions.java
deleted file mode 100644 (file)
index 46cbbc4..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.pushbullet.internal.action;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * The {@link IPushbulletActions} interface defines rule actions for sending notifications
- *
- * @author Laurent Garnier - Initial contribution
- */
-@NonNullByDefault
-public interface IPushbulletActions {
-
-    public Boolean sendPushbulletNote(@Nullable String recipient, @Nullable String title, @Nullable String message);
-
-    public Boolean sendPushbulletNote(@Nullable String recipient, @Nullable String message);
-}
index f1ae9e55f14f605de724f1a7a7800e90396ce3ae..103828e3785e1caa434afdc8b4fa725493a5f2dc 100644 (file)
@@ -12,9 +12,6 @@
  */
 package org.openhab.binding.pushbullet.internal.action;
 
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.pushbullet.internal.handler.PushbulletHandler;
@@ -29,17 +26,12 @@ import org.slf4j.LoggerFactory;
 
 /**
  * The {@link PushbulletActions} class defines rule actions for sending notifications
- * <p>
- * <b>Note:</b>The static method <b>invokeMethodOf</b> handles the case where
- * the test <i>actions instanceof PushbulletActions</i> fails. This test can fail
- * due to an issue in openHAB core v2.5.0 where the {@link PushbulletActions} class
- * can be loaded by a different classloader than the <i>actions</i> instance.
  *
  * @author Hakan Tandogan - Initial contribution
  */
 @ThingActionsScope(name = "pushbullet")
 @NonNullByDefault
-public class PushbulletActions implements ThingActions, IPushbulletActions {
+public class PushbulletActions implements ThingActions {
 
     private final Logger logger = LoggerFactory.getLogger(PushbulletActions.class);
 
@@ -52,10 +44,9 @@ public class PushbulletActions implements ThingActions, IPushbulletActions {
 
     @Override
     public @Nullable ThingHandler getThingHandler() {
-        return this.handler;
+        return handler;
     }
 
-    @Override
     @RuleAction(label = "@text/actionSendPushbulletNoteLabel", description = "@text/actionSendPushbulletNoteDesc")
     public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendPushbulletNote(
             @ActionInput(name = "recipient", label = "@text/actionSendPushbulletNoteInputRecipientLabel", description = "@text/actionSendPushbulletNoteInputRecipientDesc") @Nullable String recipient,
@@ -63,9 +54,7 @@ public class PushbulletActions implements ThingActions, IPushbulletActions {
             @ActionInput(name = "message", label = "@text/actionSendPushbulletNoteInputMessageLabel", description = "@text/actionSendPushbulletNoteInputMessageDesc") @Nullable String message) {
         logger.trace("sendPushbulletNote '{}', '{}', '{}'", recipient, title, message);
 
-        // Use local variable so the SAT check can do proper flow analysis
         PushbulletHandler localHandler = handler;
-
         if (localHandler == null) {
             logger.warn("Pushbullet service Handler is null!");
             return false;
@@ -76,19 +65,20 @@ public class PushbulletActions implements ThingActions, IPushbulletActions {
 
     public static boolean sendPushbulletNote(@Nullable ThingActions actions, @Nullable String recipient,
             @Nullable String title, @Nullable String message) {
-        return invokeMethodOf(actions).sendPushbulletNote(recipient, title, message);
+        if (actions instanceof PushbulletActions) {
+            return ((PushbulletActions) actions).sendPushbulletNote(recipient, title, message);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of PushbulletActions");
+        }
     }
 
-    @Override
     @RuleAction(label = "@text/actionSendPushbulletNoteLabel", description = "@text/actionSendPushbulletNoteDesc")
     public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendPushbulletNote(
             @ActionInput(name = "recipient", label = "@text/actionSendPushbulletNoteInputRecipientLabel", description = "@text/actionSendPushbulletNoteInputRecipientDesc") @Nullable String recipient,
             @ActionInput(name = "message", label = "@text/actionSendPushbulletNoteInputMessageLabel", description = "@text/actionSendPushbulletNoteInputMessageDesc") @Nullable String message) {
         logger.trace("sendPushbulletNote '{}', '{}'", recipient, message);
 
-        // Use local variable so the SAT check can do proper flow analysis
         PushbulletHandler localHandler = handler;
-
         if (localHandler == null) {
             logger.warn("Pushbullet service Handler is null!");
             return false;
@@ -99,25 +89,10 @@ public class PushbulletActions implements ThingActions, IPushbulletActions {
 
     public static boolean sendPushbulletNote(@Nullable ThingActions actions, @Nullable String recipient,
             @Nullable String message) {
-        return invokeMethodOf(actions).sendPushbulletNote(recipient, message);
-    }
-
-    private static IPushbulletActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(PushbulletActions.class.getName())) {
-            if (actions instanceof IPushbulletActions) {
-                return (IPushbulletActions) actions;
-            } else {
-                return (IPushbulletActions) Proxy.newProxyInstance(IPushbulletActions.class.getClassLoader(),
-                        new Class[] { IPushbulletActions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
+        if (actions instanceof PushbulletActions) {
+            return ((PushbulletActions) actions).sendPushbulletNote(recipient, message);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of PushbulletActions");
         }
-        throw new IllegalArgumentException("Actions is not an instance of PushbulletActions");
     }
 }
diff --git a/bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/IRadioThermostatThingActions.java b/bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/IRadioThermostatThingActions.java
deleted file mode 100644 (file)
index 101cef8..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.radiothermostat.internal;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * The {@link IRadioThermostatThingActions} defines the interface for all thing actions supported by the binding.
- * These methods, parameters, and return types are explained in {@link RadioThermostatThingActions}.
- *
- * @author Michael Lobstein - Initial contribution
- */
-@NonNullByDefault
-public interface IRadioThermostatThingActions {
-
-    void sendRawCommand(@Nullable String rawCommand);
-}
index 730dc4971a232be5295c43ec5db5c13408a8289e..997a71b27e7e5ebcf4b2c2f6f74bd9e7d567b494 100644 (file)
@@ -12,9 +12,6 @@
  */
 package org.openhab.binding.radiothermostat.internal;
 
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.radiothermostat.internal.handler.RadioThermostatHandler;
@@ -30,24 +27,22 @@ import org.slf4j.LoggerFactory;
  * Some automation actions to be used with a {@link RadioThermostatThingActions}
  *
  * @author Michael Lobstein - initial contribution
- *
  */
 @ThingActionsScope(name = "radiothermostat")
 @NonNullByDefault
-public class RadioThermostatThingActions implements ThingActions, IRadioThermostatThingActions {
+public class RadioThermostatThingActions implements ThingActions {
     private final Logger logger = LoggerFactory.getLogger(RadioThermostatThingActions.class);
 
     private @Nullable RadioThermostatHandler handler;
 
-    @Override
-    @RuleAction(label = "sendRawCommand", description = "Action that sends raw command to the thermostat")
+    @RuleAction(label = "send a raw command", description = "Send a raw command to the thermostat.")
     public void sendRawCommand(@ActionInput(name = "sendRawCommand") @Nullable String rawCommand) {
-        RadioThermostatHandler localHandler = handler;
         if (rawCommand == null) {
             logger.warn("sendRawCommand called with null command, ignoring");
             return;
         }
 
+        RadioThermostatHandler localHandler = handler;
         if (localHandler != null) {
             localHandler.handleRawCommand(rawCommand);
             logger.debug("sendRawCommand called with raw command: {}", rawCommand);
@@ -55,9 +50,12 @@ public class RadioThermostatThingActions implements ThingActions, IRadioThermost
     }
 
     /** Static alias to support the old DSL rules engine and make the action available there. */
-    public static void sendRawCommand(@Nullable ThingActions actions, @Nullable String rawCommand)
-            throws IllegalArgumentException {
-        invokeMethodOf(actions).sendRawCommand(rawCommand);
+    public static void sendRawCommand(@Nullable ThingActions actions, @Nullable String rawCommand) {
+        if (actions instanceof RadioThermostatThingActions) {
+            ((RadioThermostatThingActions) actions).sendRawCommand(rawCommand);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of RadioThermostatThingActions");
+        }
     }
 
     @Override
@@ -67,27 +65,6 @@ public class RadioThermostatThingActions implements ThingActions, IRadioThermost
 
     @Override
     public @Nullable ThingHandler getThingHandler() {
-        return this.handler;
-    }
-
-    private static IRadioThermostatThingActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(RadioThermostatThingActions.class.getName())) {
-            if (actions instanceof RadioThermostatThingActions) {
-                return (IRadioThermostatThingActions) actions;
-            } else {
-                return (IRadioThermostatThingActions) Proxy.newProxyInstance(
-                        IRadioThermostatThingActions.class.getClassLoader(),
-                        new Class[] { IRadioThermostatThingActions.class },
-                        (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
-        }
-        throw new IllegalArgumentException("Actions is not an instance of RadioThermostatThingActions");
+        return handler;
     }
 }
diff --git a/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/action/ISatelEventLogActions.java b/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/action/ISatelEventLogActions.java
deleted file mode 100644 (file)
index 53a8786..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.satel.action;
-
-import java.util.Map;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * Automation action handler interface for reading Satel event log.
- *
- * @author Krzysztof Goworek - Initial contribution
- */
-@NonNullByDefault
-public interface ISatelEventLogActions {
-
-    public Map<String, Object> readEvent(@Nullable Number index);
-}
diff --git a/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/action/SatelEventLogActions.java b/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/action/SatelEventLogActions.java
deleted file mode 100644 (file)
index 95d2a47..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.satel.action;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.satel.internal.handler.SatelEventLogHandler;
-import org.openhab.core.automation.annotation.ActionInput;
-import org.openhab.core.automation.annotation.ActionOutput;
-import org.openhab.core.automation.annotation.RuleAction;
-import org.openhab.core.thing.binding.ThingActions;
-import org.openhab.core.thing.binding.ThingActionsScope;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Automation action handler service for reading Satel event log.
- *
- * @author Krzysztof Goworek - Initial contribution
- * @see SatelEventLogHandler
- */
-@ThingActionsScope(name = "satel")
-@NonNullByDefault
-public class SatelEventLogActions implements ThingActions, ISatelEventLogActions {
-
-    private final Logger logger = LoggerFactory.getLogger(getClass());
-
-    private @Nullable SatelEventLogHandler handler;
-
-    @Override
-    public void setThingHandler(@Nullable ThingHandler handler) {
-        if (handler instanceof SatelEventLogHandler) {
-            this.handler = (SatelEventLogHandler) handler;
-        }
-    }
-
-    @Override
-    public @Nullable ThingHandler getThingHandler() {
-        return handler;
-    }
-
-    @Override
-    @RuleAction(label = "@text/actionReadEventLabel", description = "@text/actionReadEventDesc")
-    public @ActionOutput(name = "index", type = "java.lang.Integer", label = "@text/actionOutputIndexLabel", description = "@text/actionOutputIndexDesc") @ActionOutput(name = "prev_index", type = "java.lang.Integer", label = "@text/actionOutputPrevIndexLabel", description = "@text/actionOutputPrevIndexDesc") @ActionOutput(name = "timestamp", type = "java.time.ZonedDateTime", label = "@text/actionOutputTimestampLabel", description = "@text/actionOutputTimestampDesc") @ActionOutput(name = "description", type = "java.lang.String", label = "@text/actionOutputDescriptionLabel", description = "@text/actionOutputDescriptionDesc") @ActionOutput(name = "details", type = "java.lang.String", label = "@text/actionOutputDetailsLabel", description = "@text/actionOutputDetailsDesc") Map<String, Object> readEvent(
-            @ActionInput(name = "index", label = "@text/actionInputIndexLabel", description = "@text/actionInputIndexDesc") @Nullable Number index) {
-        logger.debug("satel.readEvent called with input: index={}", index);
-
-        Map<String, Object> result = new HashMap<>();
-        if (handler != null) {
-            handler.readEvent(index == null ? -1 : index.intValue()).ifPresent(event -> {
-                result.put("index", event.getIndex());
-                result.put("prev_index", event.getPrevIndex());
-                result.put("timestamp", event.getTimestamp());
-                result.put("description", event.getDescription());
-                result.put("details", event.getDetails());
-            });
-        }
-        return result;
-    }
-
-    public static Map<String, Object> readEvent(@Nullable ThingActions actions, @Nullable Number index) {
-        return invokeMethodOf(actions).readEvent(index);
-    }
-
-    private static ISatelEventLogActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        } else if (actions instanceof ISatelEventLogActions) {
-            return (ISatelEventLogActions) actions;
-        } else if (actions.getClass().getName().equals(SatelEventLogActions.class.getName())) {
-            return (ISatelEventLogActions) Proxy.newProxyInstance(ISatelEventLogActions.class.getClassLoader(),
-                    new Class[] { ISatelEventLogActions.class }, (Object proxy, Method method, Object[] args) -> {
-                        Method m = actions.getClass().getDeclaredMethod(method.getName(), method.getParameterTypes());
-                        return m.invoke(actions, args);
-                    });
-        }
-        throw new IllegalArgumentException("actions is not an instance of ISatelEventLogActions");
-    }
-}
diff --git a/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/action/SatelEventLogActions.java b/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/action/SatelEventLogActions.java
new file mode 100644 (file)
index 0000000..a2f49e1
--- /dev/null
@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.satel.internal.action;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.satel.internal.handler.SatelEventLogHandler;
+import org.openhab.core.automation.annotation.ActionInput;
+import org.openhab.core.automation.annotation.ActionOutput;
+import org.openhab.core.automation.annotation.RuleAction;
+import org.openhab.core.thing.binding.ThingActions;
+import org.openhab.core.thing.binding.ThingActionsScope;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Automation action handler service for reading Satel event log.
+ *
+ * @author Krzysztof Goworek - Initial contribution
+ * @see SatelEventLogHandler
+ */
+@ThingActionsScope(name = "satel")
+@NonNullByDefault
+public class SatelEventLogActions implements ThingActions {
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    private @Nullable SatelEventLogHandler handler;
+
+    @Override
+    public void setThingHandler(@Nullable ThingHandler handler) {
+        if (handler instanceof SatelEventLogHandler) {
+            this.handler = (SatelEventLogHandler) handler;
+        }
+    }
+
+    @Override
+    public @Nullable ThingHandler getThingHandler() {
+        return handler;
+    }
+
+    @RuleAction(label = "@text/actionReadEventLabel", description = "@text/actionReadEventDesc")
+    public @ActionOutput(name = "index", type = "java.lang.Integer", label = "@text/actionOutputIndexLabel", description = "@text/actionOutputIndexDesc") @ActionOutput(name = "prev_index", type = "java.lang.Integer", label = "@text/actionOutputPrevIndexLabel", description = "@text/actionOutputPrevIndexDesc") @ActionOutput(name = "timestamp", type = "java.time.ZonedDateTime", label = "@text/actionOutputTimestampLabel", description = "@text/actionOutputTimestampDesc") @ActionOutput(name = "description", type = "java.lang.String", label = "@text/actionOutputDescriptionLabel", description = "@text/actionOutputDescriptionDesc") @ActionOutput(name = "details", type = "java.lang.String", label = "@text/actionOutputDetailsLabel", description = "@text/actionOutputDetailsDesc") Map<String, Object> readEvent(
+            @ActionInput(name = "index", label = "@text/actionInputIndexLabel", description = "@text/actionInputIndexDesc") @Nullable Number index) {
+        logger.debug("satel.readEvent called with input: index={}", index);
+
+        Map<String, Object> result = new HashMap<>();
+        if (handler != null) {
+            handler.readEvent(index == null ? -1 : index.intValue()).ifPresent(event -> {
+                result.put("index", event.getIndex());
+                result.put("prev_index", event.getPrevIndex());
+                result.put("timestamp", event.getTimestamp());
+                result.put("description", event.getDescription());
+                result.put("details", event.getDetails());
+            });
+        }
+        return result;
+    }
+
+    public static Map<String, Object> readEvent(@Nullable ThingActions actions, @Nullable Number index) {
+        if (actions instanceof SatelEventLogActions) {
+            return ((SatelEventLogActions) actions).readEvent(index);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of SatelEventLogActions");
+        }
+    }
+}
index cca874f65782ba5898ce2516e2ec1c5564b85554..339bcedcf2779778c270f1326605edd8ffa25ded 100644 (file)
@@ -27,7 +27,7 @@ import java.util.concurrent.TimeUnit;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.satel.action.SatelEventLogActions;
+import org.openhab.binding.satel.internal.action.SatelEventLogActions;
 import org.openhab.binding.satel.internal.command.ReadDeviceInfoCommand;
 import org.openhab.binding.satel.internal.command.ReadDeviceInfoCommand.DeviceType;
 import org.openhab.binding.satel.internal.command.ReadEventCommand;
diff --git a/bundles/org.openhab.binding.telegram/src/main/java/org/openhab/binding/telegram/bot/ITelegramActions.java b/bundles/org.openhab.binding.telegram/src/main/java/org/openhab/binding/telegram/bot/ITelegramActions.java
deleted file mode 100644 (file)
index 9b6787a..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.telegram.bot;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * Provides the actions for the Telegram API.
- *
- * @author Laurent Garnier - Initial contribution
- */
-@NonNullByDefault
-public interface ITelegramActions {
-
-    public boolean sendTelegramAnswer(@Nullable Long chatId, @Nullable String replyId, @Nullable String message);
-
-    public boolean sendTelegramAnswer(@Nullable String replyId, @Nullable String message);
-
-    public boolean sendTelegram(@Nullable Long chatId, @Nullable String message);
-
-    public boolean sendTelegram(@Nullable String message);
-
-    public boolean sendTelegramQuery(@Nullable Long chatId, @Nullable String message, @Nullable String replyId,
-            @Nullable String... buttons);
-
-    public boolean sendTelegramQuery(@Nullable String message, @Nullable String replyId, @Nullable String... buttons);
-
-    public boolean sendTelegram(@Nullable Long chatId, @Nullable String message, @Nullable Object... args);
-
-    public boolean sendTelegram(@Nullable String message, @Nullable Object... args);
-
-    public boolean sendTelegramPhoto(@Nullable Long chatId, @Nullable String photoURL, @Nullable String caption,
-            @Nullable String username, @Nullable String password);
-
-    public boolean sendTelegramPhoto(@Nullable String photoURL, @Nullable String caption, @Nullable String username,
-            @Nullable String password);
-}
diff --git a/bundles/org.openhab.binding.telegram/src/main/java/org/openhab/binding/telegram/bot/TelegramActions.java b/bundles/org.openhab.binding.telegram/src/main/java/org/openhab/binding/telegram/bot/TelegramActions.java
deleted file mode 100644 (file)
index 9171dc5..0000000
+++ /dev/null
@@ -1,500 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.telegram.bot;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Paths;
-import java.util.Base64;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import org.apache.commons.io.IOUtils;
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.api.Authentication;
-import org.eclipse.jetty.client.api.AuthenticationStore;
-import org.eclipse.jetty.client.api.ContentResponse;
-import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.http.HttpMethod;
-import org.eclipse.jetty.util.B64Code;
-import org.openhab.binding.telegram.internal.TelegramHandler;
-import org.openhab.core.automation.annotation.ActionInput;
-import org.openhab.core.automation.annotation.RuleAction;
-import org.openhab.core.thing.binding.ThingActions;
-import org.openhab.core.thing.binding.ThingActionsScope;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.pengrad.telegrambot.model.request.InlineKeyboardButton;
-import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup;
-import com.pengrad.telegrambot.request.AnswerCallbackQuery;
-import com.pengrad.telegrambot.request.EditMessageReplyMarkup;
-import com.pengrad.telegrambot.request.SendMessage;
-import com.pengrad.telegrambot.request.SendPhoto;
-import com.pengrad.telegrambot.response.BaseResponse;
-import com.pengrad.telegrambot.response.SendResponse;
-
-/**
- * Provides the actions for the Telegram API.
- * <p>
- * <b>Note:</b>The static method <b>invokeMethodOf</b> handles the case where
- * the test <i>actions instanceof TelegramActions</i> fails. This test can fail
- * due to an issue in openHAB core v2.5.0 where the {@link TelegramActions} class
- * can be loaded by a different classloader than the <i>actions</i> instance.
- *
- * @author Alexander Krasnogolowy - Initial contribution
- *
- */
-@ThingActionsScope(name = "telegram")
-@NonNullByDefault
-public class TelegramActions implements ThingActions, ITelegramActions {
-    private final Logger logger = LoggerFactory.getLogger(TelegramActions.class);
-    private @Nullable TelegramHandler handler;
-
-    private boolean evaluateResponse(@Nullable BaseResponse response) {
-        if (response != null && !response.isOk()) {
-            logger.warn("Failed to send telegram message: {}", response.description());
-            return false;
-        }
-        return true;
-    }
-
-    @NonNullByDefault
-    private static class BasicResult implements Authentication.Result {
-
-        private final HttpHeader header;
-        private final URI uri;
-        private final String value;
-
-        public BasicResult(HttpHeader header, URI uri, String value) {
-            this.header = header;
-            this.uri = uri;
-            this.value = value;
-        }
-
-        @Override
-        public URI getURI() {
-            return this.uri;
-        }
-
-        @Override
-        public void apply(@Nullable Request request) {
-            if (request != null) {
-                request.header(this.header, this.value);
-            }
-        }
-
-        @Override
-        public String toString() {
-            return String.format("Basic authentication result for %s", this.uri);
-        }
-    }
-
-    @Override
-    @RuleAction(label = "Telegram answer", description = "Sends a Telegram answer via Telegram API")
-    public boolean sendTelegramAnswer(@ActionInput(name = "chatId") @Nullable Long chatId,
-            @ActionInput(name = "replyId") @Nullable String replyId,
-            @ActionInput(name = "message") @Nullable String message) {
-        if (replyId == null) {
-            logger.warn("ReplyId not defined; action skipped.");
-            return false;
-        }
-        if (chatId == null) {
-            logger.warn("chatId not defined; action skipped.");
-            return false;
-        }
-        TelegramHandler localHandler = handler;
-        if (localHandler != null) {
-            String callbackId = localHandler.getCallbackId(chatId, replyId);
-            if (callbackId != null) {
-                AnswerCallbackQuery answerCallbackQuery = new AnswerCallbackQuery(
-                        localHandler.getCallbackId(chatId, replyId));
-                logger.debug("AnswerCallbackQuery for chatId {} and replyId {} is the callbackId {}", chatId, replyId,
-                        localHandler.getCallbackId(chatId, replyId));
-                // we could directly set the text here, but this
-                // doesn't result in a real message only in a
-                // little popup or in an alert, so the only purpose
-                // is to stop the progress bar on client side
-                if (!evaluateResponse(localHandler.execute(answerCallbackQuery))) {
-                    return false;
-                }
-            }
-            Integer messageId = localHandler.removeMessageId(chatId, replyId);
-            logger.debug("remove messageId {} for chatId {} and replyId {}", messageId, chatId, replyId);
-
-            EditMessageReplyMarkup editReplyMarkup = new EditMessageReplyMarkup(chatId, messageId.intValue())
-                    .replyMarkup(new InlineKeyboardMarkup(new InlineKeyboardButton[0]));// remove reply markup from
-                                                                                        // old message
-            if (!evaluateResponse(localHandler.execute(editReplyMarkup))) {
-                return false;
-            }
-            return message != null ? sendTelegram(chatId, message) : true;
-        }
-        return false;
-    }
-
-    @Override
-    @RuleAction(label = "Telegram answer", description = "Sends a Telegram answer via Telegram API")
-    public boolean sendTelegramAnswer(@ActionInput(name = "replyId") @Nullable String replyId,
-            @ActionInput(name = "message") @Nullable String message) {
-        TelegramHandler localHandler = handler;
-        if (localHandler != null) {
-            for (Long chatId : localHandler.getReceiverChatIds()) {
-                if (!sendTelegramAnswer(chatId, replyId, message)) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    @Override
-    @RuleAction(label = "Telegram message", description = "Sends a Telegram via Telegram API")
-    public boolean sendTelegram(@ActionInput(name = "chatId") @Nullable Long chatId,
-            @ActionInput(name = "message") @Nullable String message) {
-        return sendTelegramGeneral(chatId, message, (String) null);
-    }
-
-    @Override
-    @RuleAction(label = "Telegram message", description = "Sends a Telegram via Telegram API")
-    public boolean sendTelegram(@ActionInput(name = "message") @Nullable String message) {
-        TelegramHandler localHandler = handler;
-        if (localHandler != null) {
-            for (Long chatId : localHandler.getReceiverChatIds()) {
-                if (!sendTelegram(chatId, message)) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    @Override
-    @RuleAction(label = "Telegram message", description = "Sends a Telegram via Telegram API")
-    public boolean sendTelegramQuery(@ActionInput(name = "chatId") @Nullable Long chatId,
-            @ActionInput(name = "message") @Nullable String message,
-            @ActionInput(name = "replyId") @Nullable String replyId,
-            @ActionInput(name = "buttons") @Nullable String... buttons) {
-        return sendTelegramGeneral(chatId, message, replyId, buttons);
-    }
-
-    @Override
-    @RuleAction(label = "Telegram message", description = "Sends a Telegram via Telegram API")
-    public boolean sendTelegramQuery(@ActionInput(name = "message") @Nullable String message,
-            @ActionInput(name = "replyId") @Nullable String replyId,
-            @ActionInput(name = "buttons") @Nullable String... buttons) {
-        TelegramHandler localHandler = handler;
-        if (localHandler != null) {
-            for (Long chatId : localHandler.getReceiverChatIds()) {
-                if (!sendTelegramQuery(chatId, message, replyId, buttons)) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    private boolean sendTelegramGeneral(@ActionInput(name = "chatId") @Nullable Long chatId, @Nullable String message,
-            @Nullable String replyId, @Nullable String... buttons) {
-        if (message == null) {
-            logger.warn("Message not defined; action skipped.");
-            return false;
-        }
-        if (chatId == null) {
-            logger.warn("chatId not defined; action skipped.");
-            return false;
-        }
-        TelegramHandler localHandler = handler;
-        if (localHandler != null) {
-            SendMessage sendMessage = new SendMessage(chatId, message);
-            if (localHandler.getParseMode() != null) {
-                sendMessage.parseMode(localHandler.getParseMode());
-            }
-            if (replyId != null) {
-                if (!replyId.contains(" ")) {
-                    if (buttons.length > 0) {
-                        InlineKeyboardButton[][] keyboard2D = new InlineKeyboardButton[1][];
-                        InlineKeyboardButton[] keyboard = new InlineKeyboardButton[buttons.length];
-                        keyboard2D[0] = keyboard;
-                        for (int i = 0; i < buttons.length; i++) {
-                            keyboard[i] = new InlineKeyboardButton(buttons[i]).callbackData(replyId + " " + buttons[i]);
-                        }
-                        InlineKeyboardMarkup keyBoardMarkup = new InlineKeyboardMarkup(keyboard2D);
-                        sendMessage.replyMarkup(keyBoardMarkup);
-                    } else {
-                        logger.warn(
-                                "The replyId {} for message {} is given, but no buttons are defined. ReplyMarkup will be ignored.",
-                                replyId, message);
-                    }
-                } else {
-                    logger.warn("replyId {} must not contain spaces. ReplyMarkup will be ignored.", replyId);
-                }
-            }
-            SendResponse retMessage = localHandler.execute(sendMessage);
-            if (!evaluateResponse(retMessage)) {
-                return false;
-            }
-            if (replyId != null && retMessage != null) {
-                logger.debug("Adding chatId {}, replyId {} and messageId {}", chatId, replyId,
-                        retMessage.message().messageId());
-                localHandler.addMessageId(chatId, replyId, retMessage.message().messageId());
-            }
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    @RuleAction(label = "Telegram message", description = "Sends a Telegram via Telegram API")
-    public boolean sendTelegram(@ActionInput(name = "chatId") @Nullable Long chatId,
-            @ActionInput(name = "message") @Nullable String message,
-            @ActionInput(name = "args") @Nullable Object... args) {
-        return sendTelegram(chatId, String.format(message, args));
-    }
-
-    @Override
-    @RuleAction(label = "Telegram message", description = "Sends a Telegram via Telegram API")
-    public boolean sendTelegram(@ActionInput(name = "message") @Nullable String message,
-            @ActionInput(name = "args") @Nullable Object... args) {
-        TelegramHandler localHandler = handler;
-        if (localHandler != null) {
-            for (Long chatId : localHandler.getReceiverChatIds()) {
-                if (!sendTelegram(chatId, message, args)) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    @RuleAction(label = "Telegram photo", description = "Sends a Picture via Telegram API")
-    public boolean sendTelegramPhoto(@ActionInput(name = "chatId") @Nullable Long chatId,
-            @ActionInput(name = "photoURL") @Nullable String photoURL,
-            @ActionInput(name = "caption") @Nullable String caption) {
-        return sendTelegramPhoto(chatId, photoURL, caption, null, null);
-    }
-
-    @Override
-    @RuleAction(label = "Telegram photo", description = "Sends a Picture via Telegram API")
-    public boolean sendTelegramPhoto(@ActionInput(name = "chatId") @Nullable Long chatId,
-            @ActionInput(name = "photoURL") @Nullable String photoURL,
-            @ActionInput(name = "caption") @Nullable String caption,
-            @ActionInput(name = "username") @Nullable String username,
-            @ActionInput(name = "password") @Nullable String password) {
-        if (photoURL == null) {
-            logger.warn("Photo URL not defined; unable to retrieve photo for sending.");
-            return false;
-        }
-        if (chatId == null) {
-            logger.warn("chatId not defined; action skipped.");
-            return false;
-        }
-
-        TelegramHandler localHandler = handler;
-        if (localHandler != null) {
-            final SendPhoto sendPhoto;
-
-            if (photoURL.toLowerCase().startsWith("http")) {
-                // load image from url
-                logger.debug("Photo URL provided.");
-                HttpClient client = localHandler.getClient();
-                if (client == null) {
-                    return false;
-                }
-                Request request = client.newRequest(photoURL).method(HttpMethod.GET).timeout(30, TimeUnit.SECONDS);
-                if (username != null && password != null) {
-                    AuthenticationStore auth = client.getAuthenticationStore();
-                    URI uri = URI.create(photoURL);
-                    auth.addAuthenticationResult(new BasicResult(HttpHeader.AUTHORIZATION, uri,
-                            "Basic " + B64Code.encode(username + ":" + password, StandardCharsets.ISO_8859_1)));
-                }
-                try {
-                    ContentResponse contentResponse = request.send();
-                    if (contentResponse.getStatus() == 200) {
-                        byte[] fileContent = contentResponse.getContent();
-                        sendPhoto = new SendPhoto(chatId, fileContent);
-                    } else {
-                        logger.warn("Download from {} failed with status: {}", photoURL, contentResponse.getStatus());
-                        return false;
-                    }
-                } catch (InterruptedException | TimeoutException | ExecutionException e) {
-                    logger.warn("Download from {} failed with exception: {}", photoURL, e.getMessage());
-                    return false;
-                }
-            } else if (photoURL.toLowerCase().startsWith("file")) {
-                // Load image from local file system
-                logger.debug("Read file from local file system: {}", photoURL);
-                try {
-                    URL url = new URL(photoURL);
-                    sendPhoto = new SendPhoto(chatId, Paths.get(url.getPath()).toFile());
-                } catch (MalformedURLException e) {
-                    logger.warn("Malformed URL: {}", photoURL);
-                    return false;
-                }
-            } else {
-                // Load image from provided base64 image
-                logger.debug("Photo base64 provided; converting to binary.");
-                final String photoB64Data;
-                if (photoURL.startsWith("data:")) { // support data URI scheme
-                    String[] photoURLParts = photoURL.split(",");
-                    if (photoURLParts.length > 1) {
-                        photoB64Data = photoURLParts[1];
-                    } else {
-                        logger.warn("The provided base64 string is not a valid data URI scheme");
-                        return false;
-                    }
-                } else {
-                    photoB64Data = photoURL;
-                }
-                InputStream is = Base64.getDecoder()
-                        .wrap(new ByteArrayInputStream(photoB64Data.getBytes(StandardCharsets.UTF_8)));
-                try {
-                    byte[] photoBytes = IOUtils.toByteArray(is);
-                    sendPhoto = new SendPhoto(chatId, photoBytes);
-                } catch (IOException e) {
-                    logger.warn("Malformed base64 string: {}", e.getMessage());
-                    return false;
-                }
-            }
-            sendPhoto.caption(caption);
-            if (localHandler.getParseMode() != null) {
-                sendPhoto.parseMode(localHandler.getParseMode());
-            }
-            return evaluateResponse(localHandler.execute(sendPhoto));
-        }
-        return false;
-    }
-
-    @Override
-    @RuleAction(label = "Telegram photo", description = "Sends a Picture via Telegram API")
-    public boolean sendTelegramPhoto(@ActionInput(name = "photoURL") @Nullable String photoURL,
-            @ActionInput(name = "caption") @Nullable String caption,
-            @ActionInput(name = "username") @Nullable String username,
-            @ActionInput(name = "password") @Nullable String password) {
-        TelegramHandler localHandler = handler;
-        if (localHandler != null) {
-            for (Long chatId : localHandler.getReceiverChatIds()) {
-                if (!sendTelegramPhoto(chatId, photoURL, caption, username, password)) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    @RuleAction(label = "Telegram photo", description = "Sends a Picture via Telegram API")
-    public boolean sendTelegramPhoto(@ActionInput(name = "photoURL") @Nullable String photoURL,
-            @ActionInput(name = "caption") @Nullable String caption) {
-        return sendTelegramPhoto(photoURL, caption, null, null);
-    }
-
-    // legacy delegate methods
-    /* APIs without chatId parameter */
-    public static boolean sendTelegram(@Nullable ThingActions actions, @Nullable String format,
-            @Nullable Object... args) {
-        return invokeMethodOf(actions).sendTelegram(format, args);
-    }
-
-    public static boolean sendTelegramQuery(@Nullable ThingActions actions, @Nullable String message,
-            @Nullable String replyId, @Nullable String... buttons) {
-        return invokeMethodOf(actions).sendTelegramQuery(message, replyId, buttons);
-    }
-
-    public static boolean sendTelegramPhoto(@Nullable ThingActions actions, @Nullable String photoURL,
-            @Nullable String caption) {
-        return invokeMethodOf(actions).sendTelegramPhoto(photoURL, caption, null, null);
-    }
-
-    public static boolean sendTelegramPhoto(@Nullable ThingActions actions, @Nullable String photoURL,
-            @Nullable String caption, @Nullable String username, @Nullable String password) {
-        return invokeMethodOf(actions).sendTelegramPhoto(photoURL, caption, username, password);
-    }
-
-    public static boolean sendTelegramAnswer(@Nullable ThingActions actions, @Nullable String replyId,
-            @Nullable String message) {
-        return invokeMethodOf(actions).sendTelegramAnswer(replyId, message);
-    }
-
-    /* APIs with chatId parameter */
-
-    public static boolean sendTelegram(@Nullable ThingActions actions, @Nullable Long chatId, @Nullable String format,
-            @Nullable Object... args) {
-        return invokeMethodOf(actions).sendTelegram(chatId, format, args);
-    }
-
-    public static boolean sendTelegramQuery(@Nullable ThingActions actions, @Nullable Long chatId,
-            @Nullable String message, @Nullable String replyId, @Nullable String... buttons) {
-        return invokeMethodOf(actions).sendTelegramQuery(chatId, message, replyId, buttons);
-    }
-
-    public static boolean sendTelegramPhoto(@Nullable ThingActions actions, @Nullable Long chatId,
-            @Nullable String photoURL, @Nullable String caption) {
-        return invokeMethodOf(actions).sendTelegramPhoto(chatId, photoURL, caption, null, null);
-    }
-
-    public static boolean sendTelegramPhoto(@Nullable ThingActions actions, @Nullable Long chatId,
-            @Nullable String photoURL, @Nullable String caption, @Nullable String username, @Nullable String password) {
-        return invokeMethodOf(actions).sendTelegramPhoto(chatId, photoURL, caption, username, password);
-    }
-
-    public static boolean sendTelegramAnswer(@Nullable ThingActions actions, @Nullable Long chatId,
-            @Nullable String replyId, @Nullable String message) {
-        return invokeMethodOf(actions).sendTelegramAnswer(chatId, replyId, message);
-    }
-
-    public static boolean sendTelegramAnswer(@Nullable ThingActions actions, @Nullable String chatId,
-            @Nullable String replyId, @Nullable String message) {
-        return invokeMethodOf(actions).sendTelegramAnswer(Long.valueOf(chatId), replyId, message);
-    }
-
-    private static ITelegramActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(TelegramActions.class.getName())) {
-            if (actions instanceof ITelegramActions) {
-                return (ITelegramActions) actions;
-            } else {
-                return (ITelegramActions) Proxy.newProxyInstance(ITelegramActions.class.getClassLoader(),
-                        new Class[] { ITelegramActions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
-        }
-        throw new IllegalArgumentException("Actions is not an instance of TelegramActions");
-    }
-
-    @Override
-    public void setThingHandler(@Nullable ThingHandler handler) {
-        this.handler = (TelegramHandler) handler;
-    }
-
-    @Override
-    public @Nullable ThingHandler getThingHandler() {
-        return handler;
-    }
-}
index c4c8c94d8aff6a2562d6b9fa9998250c0ecc4973..e28432b72565c9a68b7810c0e0ff300c6263da1d 100644 (file)
@@ -34,7 +34,7 @@ import java.util.concurrent.TimeUnit;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.jetty.client.HttpClient;
-import org.openhab.binding.telegram.bot.TelegramActions;
+import org.openhab.binding.telegram.internal.action.TelegramActions;
 import org.openhab.core.library.types.DateTimeType;
 import org.openhab.core.library.types.StringType;
 import org.openhab.core.thing.ChannelUID;
diff --git a/bundles/org.openhab.binding.telegram/src/main/java/org/openhab/binding/telegram/internal/action/TelegramActions.java b/bundles/org.openhab.binding.telegram/src/main/java/org/openhab/binding/telegram/internal/action/TelegramActions.java
new file mode 100644 (file)
index 0000000..42ca057
--- /dev/null
@@ -0,0 +1,506 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.telegram.internal.action;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Paths;
+import java.util.Base64;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.commons.io.IOUtils;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.Authentication;
+import org.eclipse.jetty.client.api.AuthenticationStore;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.util.B64Code;
+import org.openhab.binding.telegram.internal.TelegramHandler;
+import org.openhab.core.automation.annotation.ActionInput;
+import org.openhab.core.automation.annotation.RuleAction;
+import org.openhab.core.thing.binding.ThingActions;
+import org.openhab.core.thing.binding.ThingActionsScope;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.pengrad.telegrambot.model.request.InlineKeyboardButton;
+import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup;
+import com.pengrad.telegrambot.request.AnswerCallbackQuery;
+import com.pengrad.telegrambot.request.EditMessageReplyMarkup;
+import com.pengrad.telegrambot.request.SendMessage;
+import com.pengrad.telegrambot.request.SendPhoto;
+import com.pengrad.telegrambot.response.BaseResponse;
+import com.pengrad.telegrambot.response.SendResponse;
+
+/**
+ * Provides the actions for the Telegram API.
+ *
+ * @author Alexander Krasnogolowy - Initial contribution
+ */
+@ThingActionsScope(name = "telegram")
+@NonNullByDefault
+public class TelegramActions implements ThingActions {
+    private final Logger logger = LoggerFactory.getLogger(TelegramActions.class);
+    private @Nullable TelegramHandler handler;
+
+    private boolean evaluateResponse(@Nullable BaseResponse response) {
+        if (response != null && !response.isOk()) {
+            logger.warn("Failed to send telegram message: {}", response.description());
+            return false;
+        }
+        return true;
+    }
+
+    private static class BasicResult implements Authentication.Result {
+
+        private final HttpHeader header;
+        private final URI uri;
+        private final String value;
+
+        public BasicResult(HttpHeader header, URI uri, String value) {
+            this.header = header;
+            this.uri = uri;
+            this.value = value;
+        }
+
+        @Override
+        public URI getURI() {
+            return this.uri;
+        }
+
+        @Override
+        public void apply(@Nullable Request request) {
+            if (request != null) {
+                request.header(this.header, this.value);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return String.format("Basic authentication result for %s", this.uri);
+        }
+    }
+
+    @RuleAction(label = "send an answer", description = "Send a Telegram answer using the Telegram API.")
+    public boolean sendTelegramAnswer(@ActionInput(name = "chatId") @Nullable Long chatId,
+            @ActionInput(name = "replyId") @Nullable String replyId,
+            @ActionInput(name = "message") @Nullable String message) {
+        if (replyId == null) {
+            logger.warn("ReplyId not defined; action skipped.");
+            return false;
+        }
+        if (chatId == null) {
+            logger.warn("chatId not defined; action skipped.");
+            return false;
+        }
+        TelegramHandler localHandler = handler;
+        if (localHandler != null) {
+            String callbackId = localHandler.getCallbackId(chatId, replyId);
+            if (callbackId != null) {
+                AnswerCallbackQuery answerCallbackQuery = new AnswerCallbackQuery(
+                        localHandler.getCallbackId(chatId, replyId));
+                logger.debug("AnswerCallbackQuery for chatId {} and replyId {} is the callbackId {}", chatId, replyId,
+                        localHandler.getCallbackId(chatId, replyId));
+                // we could directly set the text here, but this
+                // doesn't result in a real message only in a
+                // little popup or in an alert, so the only purpose
+                // is to stop the progress bar on client side
+                if (!evaluateResponse(localHandler.execute(answerCallbackQuery))) {
+                    return false;
+                }
+            }
+            Integer messageId = localHandler.removeMessageId(chatId, replyId);
+            logger.debug("remove messageId {} for chatId {} and replyId {}", messageId, chatId, replyId);
+
+            EditMessageReplyMarkup editReplyMarkup = new EditMessageReplyMarkup(chatId, messageId.intValue())
+                    .replyMarkup(new InlineKeyboardMarkup(new InlineKeyboardButton[0]));// remove reply markup from
+                                                                                        // old message
+            if (!evaluateResponse(localHandler.execute(editReplyMarkup))) {
+                return false;
+            }
+            return message != null ? sendTelegram(chatId, message) : true;
+        }
+        return false;
+    }
+
+    @RuleAction(label = "send an answer", description = "Send a Telegram answer using the Telegram API.")
+    public boolean sendTelegramAnswer(@ActionInput(name = "replyId") @Nullable String replyId,
+            @ActionInput(name = "message") @Nullable String message) {
+        TelegramHandler localHandler = handler;
+        if (localHandler != null) {
+            for (Long chatId : localHandler.getReceiverChatIds()) {
+                if (!sendTelegramAnswer(chatId, replyId, message)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    @RuleAction(label = "send a message", description = "Send a Telegram message using the Telegram API.")
+    public boolean sendTelegram(@ActionInput(name = "chatId") @Nullable Long chatId,
+            @ActionInput(name = "message") @Nullable String message) {
+        return sendTelegramGeneral(chatId, message, (String) null);
+    }
+
+    @RuleAction(label = "send a message", description = "Send a Telegram message using the Telegram API.")
+    public boolean sendTelegram(@ActionInput(name = "message") @Nullable String message) {
+        TelegramHandler localHandler = handler;
+        if (localHandler != null) {
+            for (Long chatId : localHandler.getReceiverChatIds()) {
+                if (!sendTelegram(chatId, message)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    @RuleAction(label = "send a message", description = "Send a Telegram using the Telegram API.")
+    public boolean sendTelegramQuery(@ActionInput(name = "chatId") @Nullable Long chatId,
+            @ActionInput(name = "message") @Nullable String message,
+            @ActionInput(name = "replyId") @Nullable String replyId,
+            @ActionInput(name = "buttons") @Nullable String... buttons) {
+        return sendTelegramGeneral(chatId, message, replyId, buttons);
+    }
+
+    @RuleAction(label = "send a message", description = "Send a Telegram using the Telegram API.")
+    public boolean sendTelegramQuery(@ActionInput(name = "message") @Nullable String message,
+            @ActionInput(name = "replyId") @Nullable String replyId,
+            @ActionInput(name = "buttons") @Nullable String... buttons) {
+        TelegramHandler localHandler = handler;
+        if (localHandler != null) {
+            for (Long chatId : localHandler.getReceiverChatIds()) {
+                if (!sendTelegramQuery(chatId, message, replyId, buttons)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    private boolean sendTelegramGeneral(@ActionInput(name = "chatId") @Nullable Long chatId, @Nullable String message,
+            @Nullable String replyId, @Nullable String... buttons) {
+        if (message == null) {
+            logger.warn("Message not defined; action skipped.");
+            return false;
+        }
+        if (chatId == null) {
+            logger.warn("chatId not defined; action skipped.");
+            return false;
+        }
+        TelegramHandler localHandler = handler;
+        if (localHandler != null) {
+            SendMessage sendMessage = new SendMessage(chatId, message);
+            if (localHandler.getParseMode() != null) {
+                sendMessage.parseMode(localHandler.getParseMode());
+            }
+            if (replyId != null) {
+                if (!replyId.contains(" ")) {
+                    if (buttons.length > 0) {
+                        InlineKeyboardButton[][] keyboard2D = new InlineKeyboardButton[1][];
+                        InlineKeyboardButton[] keyboard = new InlineKeyboardButton[buttons.length];
+                        keyboard2D[0] = keyboard;
+                        for (int i = 0; i < buttons.length; i++) {
+                            keyboard[i] = new InlineKeyboardButton(buttons[i]).callbackData(replyId + " " + buttons[i]);
+                        }
+                        InlineKeyboardMarkup keyBoardMarkup = new InlineKeyboardMarkup(keyboard2D);
+                        sendMessage.replyMarkup(keyBoardMarkup);
+                    } else {
+                        logger.warn(
+                                "The replyId {} for message {} is given, but no buttons are defined. ReplyMarkup will be ignored.",
+                                replyId, message);
+                    }
+                } else {
+                    logger.warn("replyId {} must not contain spaces. ReplyMarkup will be ignored.", replyId);
+                }
+            }
+            SendResponse retMessage = localHandler.execute(sendMessage);
+            if (!evaluateResponse(retMessage)) {
+                return false;
+            }
+            if (replyId != null && retMessage != null) {
+                logger.debug("Adding chatId {}, replyId {} and messageId {}", chatId, replyId,
+                        retMessage.message().messageId());
+                localHandler.addMessageId(chatId, replyId, retMessage.message().messageId());
+            }
+            return true;
+        }
+        return false;
+    }
+
+    @RuleAction(label = "send a message", description = "Send a Telegram using the Telegram API.")
+    public boolean sendTelegram(@ActionInput(name = "chatId") @Nullable Long chatId,
+            @ActionInput(name = "message") @Nullable String message,
+            @ActionInput(name = "args") @Nullable Object... args) {
+        return sendTelegram(chatId, String.format(message, args));
+    }
+
+    @RuleAction(label = "send a message", description = "Send a Telegram using the Telegram API.")
+    public boolean sendTelegram(@ActionInput(name = "message") @Nullable String message,
+            @ActionInput(name = "args") @Nullable Object... args) {
+        TelegramHandler localHandler = handler;
+        if (localHandler != null) {
+            for (Long chatId : localHandler.getReceiverChatIds()) {
+                if (!sendTelegram(chatId, message, args)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    @RuleAction(label = "send a photo", description = "Send a picture using the Telegram API.")
+    public boolean sendTelegramPhoto(@ActionInput(name = "chatId") @Nullable Long chatId,
+            @ActionInput(name = "photoURL") @Nullable String photoURL,
+            @ActionInput(name = "caption") @Nullable String caption) {
+        return sendTelegramPhoto(chatId, photoURL, caption, null, null);
+    }
+
+    @RuleAction(label = "send a photo", description = "Send a picture using the Telegram API.")
+    public boolean sendTelegramPhoto(@ActionInput(name = "chatId") @Nullable Long chatId,
+            @ActionInput(name = "photoURL") @Nullable String photoURL,
+            @ActionInput(name = "caption") @Nullable String caption,
+            @ActionInput(name = "username") @Nullable String username,
+            @ActionInput(name = "password") @Nullable String password) {
+        if (photoURL == null) {
+            logger.warn("Photo URL not defined; unable to retrieve photo for sending.");
+            return false;
+        }
+        if (chatId == null) {
+            logger.warn("chatId not defined; action skipped.");
+            return false;
+        }
+
+        TelegramHandler localHandler = handler;
+        if (localHandler != null) {
+            final SendPhoto sendPhoto;
+
+            if (photoURL.toLowerCase().startsWith("http")) {
+                // load image from url
+                logger.debug("Photo URL provided.");
+                HttpClient client = localHandler.getClient();
+                if (client == null) {
+                    return false;
+                }
+                Request request = client.newRequest(photoURL).method(HttpMethod.GET).timeout(30, TimeUnit.SECONDS);
+                if (username != null && password != null) {
+                    AuthenticationStore auth = client.getAuthenticationStore();
+                    URI uri = URI.create(photoURL);
+                    auth.addAuthenticationResult(new BasicResult(HttpHeader.AUTHORIZATION, uri,
+                            "Basic " + B64Code.encode(username + ":" + password, StandardCharsets.ISO_8859_1)));
+                }
+                try {
+                    ContentResponse contentResponse = request.send();
+                    if (contentResponse.getStatus() == 200) {
+                        byte[] fileContent = contentResponse.getContent();
+                        sendPhoto = new SendPhoto(chatId, fileContent);
+                    } else {
+                        logger.warn("Download from {} failed with status: {}", photoURL, contentResponse.getStatus());
+                        return false;
+                    }
+                } catch (InterruptedException | TimeoutException | ExecutionException e) {
+                    logger.warn("Download from {} failed with exception: {}", photoURL, e.getMessage());
+                    return false;
+                }
+            } else if (photoURL.toLowerCase().startsWith("file")) {
+                // Load image from local file system
+                logger.debug("Read file from local file system: {}", photoURL);
+                try {
+                    URL url = new URL(photoURL);
+                    sendPhoto = new SendPhoto(chatId, Paths.get(url.getPath()).toFile());
+                } catch (MalformedURLException e) {
+                    logger.warn("Malformed URL: {}", photoURL);
+                    return false;
+                }
+            } else {
+                // Load image from provided base64 image
+                logger.debug("Photo base64 provided; converting to binary.");
+                final String photoB64Data;
+                if (photoURL.startsWith("data:")) { // support data URI scheme
+                    String[] photoURLParts = photoURL.split(",");
+                    if (photoURLParts.length > 1) {
+                        photoB64Data = photoURLParts[1];
+                    } else {
+                        logger.warn("The provided base64 string is not a valid data URI scheme");
+                        return false;
+                    }
+                } else {
+                    photoB64Data = photoURL;
+                }
+                InputStream is = Base64.getDecoder()
+                        .wrap(new ByteArrayInputStream(photoB64Data.getBytes(StandardCharsets.UTF_8)));
+                try {
+                    byte[] photoBytes = IOUtils.toByteArray(is);
+                    sendPhoto = new SendPhoto(chatId, photoBytes);
+                } catch (IOException e) {
+                    logger.warn("Malformed base64 string: {}", e.getMessage());
+                    return false;
+                }
+            }
+            sendPhoto.caption(caption);
+            if (localHandler.getParseMode() != null) {
+                sendPhoto.parseMode(localHandler.getParseMode());
+            }
+            return evaluateResponse(localHandler.execute(sendPhoto));
+        }
+        return false;
+    }
+
+    @RuleAction(label = "send a photo", description = "Send a Picture using the Telegram API.")
+    public boolean sendTelegramPhoto(@ActionInput(name = "photoURL") @Nullable String photoURL,
+            @ActionInput(name = "caption") @Nullable String caption,
+            @ActionInput(name = "username") @Nullable String username,
+            @ActionInput(name = "password") @Nullable String password) {
+        TelegramHandler localHandler = handler;
+        if (localHandler != null) {
+            for (Long chatId : localHandler.getReceiverChatIds()) {
+                if (!sendTelegramPhoto(chatId, photoURL, caption, username, password)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    @RuleAction(label = "send a photo", description = "Send a Picture using the Telegram API.")
+    public boolean sendTelegramPhoto(@ActionInput(name = "photoURL") @Nullable String photoURL,
+            @ActionInput(name = "caption") @Nullable String caption) {
+        return sendTelegramPhoto(photoURL, caption, null, null);
+    }
+
+    // legacy delegate methods
+    /* APIs without chatId parameter */
+    public static boolean sendTelegram(@Nullable ThingActions actions, @Nullable String format,
+            @Nullable Object... args) {
+        if (actions instanceof TelegramActions) {
+            return ((TelegramActions) actions).sendTelegram(format, args);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of TelegramActions");
+        }
+    }
+
+    public static boolean sendTelegramQuery(@Nullable ThingActions actions, @Nullable String message,
+            @Nullable String replyId, @Nullable String... buttons) {
+        if (actions instanceof TelegramActions) {
+            return ((TelegramActions) actions).sendTelegramQuery(message, replyId, buttons);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of TelegramActions");
+        }
+    }
+
+    public static boolean sendTelegramPhoto(@Nullable ThingActions actions, @Nullable String photoURL,
+            @Nullable String caption) {
+        if (actions instanceof TelegramActions) {
+            return ((TelegramActions) actions).sendTelegramPhoto(photoURL, caption, null, null);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of TelegramActions");
+        }
+    }
+
+    public static boolean sendTelegramPhoto(@Nullable ThingActions actions, @Nullable String photoURL,
+            @Nullable String caption, @Nullable String username, @Nullable String password) {
+        if (actions instanceof TelegramActions) {
+            return ((TelegramActions) actions).sendTelegramPhoto(photoURL, caption, username, password);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of TelegramActions");
+        }
+    }
+
+    public static boolean sendTelegramAnswer(@Nullable ThingActions actions, @Nullable String replyId,
+            @Nullable String message) {
+        if (actions instanceof TelegramActions) {
+            return ((TelegramActions) actions).sendTelegramAnswer(replyId, message);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of TelegramActions");
+        }
+    }
+
+    /* APIs with chatId parameter */
+
+    public static boolean sendTelegram(@Nullable ThingActions actions, @Nullable Long chatId, @Nullable String format,
+            @Nullable Object... args) {
+        if (actions instanceof TelegramActions) {
+            return ((TelegramActions) actions).sendTelegram(chatId, format, args);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of TelegramActions");
+        }
+    }
+
+    public static boolean sendTelegramQuery(@Nullable ThingActions actions, @Nullable Long chatId,
+            @Nullable String message, @Nullable String replyId, @Nullable String... buttons) {
+        if (actions instanceof TelegramActions) {
+            return ((TelegramActions) actions).sendTelegramQuery(chatId, message, replyId, buttons);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of TelegramActions");
+        }
+    }
+
+    public static boolean sendTelegramPhoto(@Nullable ThingActions actions, @Nullable Long chatId,
+            @Nullable String photoURL, @Nullable String caption) {
+        if (actions instanceof TelegramActions) {
+            return ((TelegramActions) actions).sendTelegramPhoto(chatId, photoURL, caption, null, null);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of TelegramActions");
+        }
+    }
+
+    public static boolean sendTelegramPhoto(@Nullable ThingActions actions, @Nullable Long chatId,
+            @Nullable String photoURL, @Nullable String caption, @Nullable String username, @Nullable String password) {
+        if (actions instanceof TelegramActions) {
+            return ((TelegramActions) actions).sendTelegramPhoto(chatId, photoURL, caption, username, password);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of TelegramActions");
+        }
+    }
+
+    public static boolean sendTelegramAnswer(@Nullable ThingActions actions, @Nullable Long chatId,
+            @Nullable String replyId, @Nullable String message) {
+        if (actions instanceof TelegramActions) {
+            return ((TelegramActions) actions).sendTelegramAnswer(chatId, replyId, message);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of TelegramActions");
+        }
+    }
+
+    public static boolean sendTelegramAnswer(@Nullable ThingActions actions, @Nullable String chatId,
+            @Nullable String replyId, @Nullable String message) {
+        if (actions instanceof TelegramActions) {
+            return ((TelegramActions) actions).sendTelegramAnswer(Long.valueOf(chatId), replyId, message);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of TelegramActions");
+        }
+    }
+
+    @Override
+    public void setThingHandler(@Nullable ThingHandler handler) {
+        this.handler = (TelegramHandler) handler;
+    }
+
+    @Override
+    public @Nullable ThingHandler getThingHandler() {
+        return handler;
+    }
+}
index d15fd3438c3ae82bd5f9221bb677e707c2e1bb0b..a00d92115674e53d2d8ab971228aafc3e4a7e5e5 100644 (file)
@@ -41,7 +41,7 @@ public class VolvoOnCallActions implements ThingActions {
     private @Nullable VehicleHandler handler;
 
     public VolvoOnCallActions() {
-        logger.info("Volvo On Call actions service instanciated");
+        logger.debug("Volvo On Call actions service instantiated");
     }
 
     @Override
@@ -53,7 +53,7 @@ public class VolvoOnCallActions implements ThingActions {
 
     @Override
     public @Nullable ThingHandler getThingHandler() {
-        return this.handler;
+        return handler;
     }
 
     @RuleAction(label = "close the car", description = "Closes the car")
diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/XMPPClientBindingConstants.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/XMPPClientBindingConstants.java
deleted file mode 100644 (file)
index 9ce9d51..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.xmppclient;
-
-import org.openhab.core.thing.ThingTypeUID;
-
-/**
- * The {@link XMPPClientBindingConstants} class defines common constants, which are
- * used across the whole binding.
- *
- * @author Pavel Gololobov - Initial contribution
- */
-public class XMPPClientBindingConstants {
-    private static final String BINDING_ID = "xmppclient";
-
-    // List of all Thing Type UIDs
-    public static final ThingTypeUID BRIDGE_TYPE_XMPP = new ThingTypeUID(BINDING_ID, "xmppBridge");
-    public static final String PUBLISH_TRIGGER_CHANNEL = "publishTrigger";
-}
diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/action/IXMPPActions.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/action/IXMPPActions.java
deleted file mode 100644 (file)
index 05e684d..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.xmppclient.action;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * This is the automation engine action handler service for the publishXMPP action.
- * <p>
- * <b>Note:</b>The static method <b>invokeMethodOf</b> handles the case where
- * the test <i>actions instanceof XMPPActions</i> fails. This test can fail
- * due to an issue in openHAB core v2.5.0 where the {@link IXMPPActions} class
- * can be loaded by a different classloader than the <i>actions</i> instance.
- *
- * @author Laurent Garnier - Initial contribution
- */
-@NonNullByDefault
-public interface IXMPPActions {
-
-    public void publishXMPP(@Nullable String to, @Nullable String text);
-}
diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/action/XMPPActions.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/action/XMPPActions.java
deleted file mode 100644 (file)
index 1d617fd..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.xmppclient.action;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.xmppclient.handler.XMPPClientHandler;
-import org.openhab.binding.xmppclient.internal.XMPPClient;
-import org.openhab.core.automation.annotation.ActionInput;
-import org.openhab.core.automation.annotation.RuleAction;
-import org.openhab.core.thing.binding.ThingActions;
-import org.openhab.core.thing.binding.ThingActionsScope;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * This is the automation engine action handler service for the publishXMPP action.
- * <p>
- * <b>Note:</b>The static method <b>invokeMethodOf</b> handles the case where
- * the test <i>actions instanceof XMPPActions</i> fails. This test can fail
- * due to an issue in openHAB core v2.5.0 where the {@link XMPPActions} class
- * can be loaded by a different classloader than the <i>actions</i> instance.
- *
- * @author Pavel Gololobov - Initial contribution
- */
-@ThingActionsScope(name = "xmpp")
-@NonNullByDefault
-public class XMPPActions implements ThingActions, IXMPPActions {
-    private static final Logger logger = LoggerFactory.getLogger(XMPPActions.class);
-    private @Nullable XMPPClientHandler handler;
-
-    @Override
-    public void setThingHandler(@Nullable ThingHandler handler) {
-        this.handler = (XMPPClientHandler) handler;
-    }
-
-    @Override
-    public @Nullable ThingHandler getThingHandler() {
-        return this.handler;
-    }
-
-    @Override
-    @RuleAction(label = "publishXMPP", description = "Publish to XMPP")
-    public void publishXMPP(@ActionInput(name = "to", label = "To", description = "Send to") @Nullable String to,
-            @ActionInput(name = "text", label = "Text", description = "Message text") @Nullable String text) {
-        XMPPClientHandler clientHandler = handler;
-        if (clientHandler == null) {
-            logger.warn("XMPP ThingHandler is null");
-            return;
-        }
-
-        XMPPClient connection = clientHandler.getXMPPClient();
-        if (connection == null) {
-            logger.warn("XMPP ThingHandler connection is null");
-            return;
-        }
-        if ((to == null) || (text == null)) {
-            logger.info("Skipping XMPP messaging to {} value {}", to, text);
-            return;
-        }
-        connection.sendMessage(to, text);
-    }
-
-    public static void publishXMPP(@Nullable ThingActions actions, @Nullable String to, @Nullable String text) {
-        invokeMethodOf(actions).publishXMPP(to, text);
-    }
-
-    private static IXMPPActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(XMPPActions.class.getName())) {
-            if (actions instanceof IXMPPActions) {
-                return (IXMPPActions) actions;
-            } else {
-                return (IXMPPActions) Proxy.newProxyInstance(IXMPPActions.class.getClassLoader(),
-                        new Class[] { IXMPPActions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
-        }
-        throw new IllegalArgumentException("Actions is not an instance of XMPPActions");
-    }
-}
diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/PublishTriggerChannel.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/PublishTriggerChannel.java
deleted file mode 100644 (file)
index 86334bc..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.xmppclient.handler;
-
-import org.openhab.binding.xmppclient.internal.XMPPClient;
-import org.openhab.core.thing.ChannelUID;
-
-/**
- * Subscribes to a chat and calls {@link AbstractBrokerHandler#triggerChannel(ChannelUID, String)} if a value has been
- * received.
- *
- * @author Pavel Gololobov - Initial contribution
- */
-public class PublishTriggerChannel implements XMPPClientMessageSubscriber {
-    private final XMPPClient connection;
-    private final PublishTriggerChannelConfig config;
-    private final ChannelUID uid;
-    private final XMPPClientHandler handler;
-
-    PublishTriggerChannel(PublishTriggerChannelConfig config, ChannelUID uid, XMPPClient connection,
-            XMPPClientHandler handler) {
-        this.config = config;
-        this.uid = uid;
-        this.connection = connection;
-        this.handler = handler;
-    }
-
-    public void start() {
-        connection.subscribe(this);
-    }
-
-    public void stop() {
-        connection.unsubscribe(this);
-    }
-
-    @Override
-    public void processMessage(String from, String payload) {
-        // Check condition if exists
-        String expectedPayload = config.payload;
-        if ((expectedPayload != null) && (!expectedPayload.isEmpty()) && !payload.equals(expectedPayload)) {
-            return;
-        }
-        String eventValue = "";
-        if (!config.separator.isEmpty()) {
-            eventValue = from + config.separator + payload;
-        } else {
-            eventValue = payload;
-        }
-        handler.triggerChannel(uid, eventValue);
-    }
-
-    @Override
-    public String getName() {
-        return uid.toString();
-    }
-}
diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/PublishTriggerChannelConfig.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/PublishTriggerChannelConfig.java
deleted file mode 100644 (file)
index b5c937a..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.xmppclient.handler;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * Holds the configuration of a {@link PublishTriggerChannel}.
- *
- * @author Pavel Gololobov - Initial contribution
- */
-@NonNullByDefault
-public class PublishTriggerChannelConfig {
-    public @Nullable String payload;
-    public String separator = "";
-}
diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/XMPPClientConfiguration.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/XMPPClientConfiguration.java
deleted file mode 100644 (file)
index 5e99bef..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.xmppclient.handler;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * The {@link XMPPClientConfiguration} class contains fields mapping thing configuration parameters.
- *
- * @author Pavel Gololobov - Initial contribution
- */
-@NonNullByDefault
-public class XMPPClientConfiguration {
-    public @Nullable String host;
-    public Integer port = 5222;
-    public String username = "";
-    public String password = "";
-    public String domain = "";
-}
diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/XMPPClientHandler.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/XMPPClientHandler.java
deleted file mode 100644 (file)
index ce74811..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.xmppclient.handler;
-
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-import org.jivesoftware.smack.*;
-import org.openhab.binding.xmppclient.action.XMPPActions;
-import org.openhab.binding.xmppclient.internal.XMPPClient;
-import org.openhab.core.thing.Bridge;
-import org.openhab.core.thing.Channel;
-import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.thing.ThingStatus;
-import org.openhab.core.thing.ThingStatusDetail;
-import org.openhab.core.thing.binding.BaseBridgeHandler;
-import org.openhab.core.thing.binding.ThingHandlerService;
-import org.openhab.core.types.Command;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link XMPPClientHandler} is responsible for handling commands, which are
- * sent to one of the channels.
- *
- * @author Pavel Gololobov - Initial contribution
- */
-
-public class XMPPClientHandler extends BaseBridgeHandler {
-    private final Logger logger = LoggerFactory.getLogger(XMPPClientHandler.class);
-    private XMPPClient xmppClient;
-    private XMPPClientConfiguration config;
-    private final Map<ChannelUID, PublishTriggerChannel> channelStateByChannelUID = new HashMap<>();
-
-    public XMPPClientHandler(Bridge thing) {
-        super(thing);
-    }
-
-    public XMPPClient getXMPPClient() {
-        return xmppClient;
-    }
-
-    @Override
-    public Collection<Class<? extends ThingHandlerService>> getServices() {
-        return Collections.singleton(XMPPActions.class);
-    }
-
-    @Override
-    protected void triggerChannel(ChannelUID channelUID, String event) {
-        super.triggerChannel(channelUID, event);
-    }
-
-    @Override
-    public void handleCommand(ChannelUID channelUID, Command command) {
-        // not supported
-    }
-
-    @Override
-    public void initialize() {
-        updateStatus(ThingStatus.UNKNOWN);
-        scheduler.schedule(this::doConnect, 0, TimeUnit.SECONDS);
-    }
-
-    @Override
-    public void dispose() {
-        channelStateByChannelUID.values().forEach(c -> c.stop());
-        channelStateByChannelUID.clear();
-        xmppClient.disconnect();
-        super.dispose();
-    }
-
-    private void doConnect() {
-        config = getConfigAs(XMPPClientConfiguration.class);
-        xmppClient = new XMPPClient();
-        try {
-            xmppClient.connect(config.host, config.port, config.username, config.domain, config.password);
-        } catch (SmackException | IOException | XMPPException e) {
-            logger.info("XMPP connection error", e);
-            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
-            return;
-        }
-
-        for (Channel channel : thing.getChannels()) {
-            final PublishTriggerChannelConfig channelConfig = channel.getConfiguration()
-                    .as(PublishTriggerChannelConfig.class);
-            PublishTriggerChannel c = new PublishTriggerChannel(channelConfig, channel.getUID(), xmppClient, this);
-            channelStateByChannelUID.put(channel.getUID(), c);
-            logger.info("XMPP added channel {} payload {}", channel.getUID().toString(), channelConfig.payload);
-        }
-        channelStateByChannelUID.values().forEach(c -> c.start());
-
-        updateStatus(ThingStatus.ONLINE);
-    }
-}
diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/XMPPClientMessageSubscriber.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/XMPPClientMessageSubscriber.java
deleted file mode 100644 (file)
index 8ffc1c7..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.xmppclient.handler;
-
-/**
- * Subscriber interface
- * 
- * @author Pavel Gololobov - Initial contribution
- */
-public interface XMPPClientMessageSubscriber {
-    public void processMessage(String from, String payload);
-
-    public String getName();
-}
index 93bac0845c54fe61347a40c8d6dac6da7745b69a..1ac261acc801cb3f8934ea9e58cc062f3c082eb7 100644 (file)
@@ -16,7 +16,12 @@ import java.io.IOException;
 import java.util.HashSet;
 import java.util.Set;
 
-import org.jivesoftware.smack.*;
+import org.jivesoftware.smack.AbstractXMPPConnection;
+import org.jivesoftware.smack.ConnectionListener;
+import org.jivesoftware.smack.ReconnectionManager;
+import org.jivesoftware.smack.SmackException;
+import org.jivesoftware.smack.XMPPConnection;
+import org.jivesoftware.smack.XMPPException;
 import org.jivesoftware.smack.chat2.Chat;
 import org.jivesoftware.smack.chat2.ChatManager;
 import org.jivesoftware.smack.chat2.IncomingChatMessageListener;
@@ -28,7 +33,7 @@ import org.jivesoftware.smackx.disco.packet.DiscoverInfo.Identity;
 import org.jxmpp.jid.EntityBareJid;
 import org.jxmpp.jid.impl.JidCreate;
 import org.jxmpp.stringprep.XmppStringprepException;
-import org.openhab.binding.xmppclient.handler.XMPPClientMessageSubscriber;
+import org.openhab.binding.xmppclient.internal.handler.XMPPClientMessageSubscriber;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClientBindingConstants.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClientBindingConstants.java
new file mode 100644 (file)
index 0000000..a07f609
--- /dev/null
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.xmppclient.internal;
+
+import org.openhab.core.thing.ThingTypeUID;
+
+/**
+ * The {@link XMPPClientBindingConstants} class defines common constants, which are
+ * used across the whole binding.
+ *
+ * @author Pavel Gololobov - Initial contribution
+ */
+public class XMPPClientBindingConstants {
+    private static final String BINDING_ID = "xmppclient";
+
+    // List of all Thing Type UIDs
+    public static final ThingTypeUID BRIDGE_TYPE_XMPP = new ThingTypeUID(BINDING_ID, "xmppBridge");
+    public static final String PUBLISH_TRIGGER_CHANNEL = "publishTrigger";
+}
index dea805fc233c27c18e3c5828cb90358acaf45dc9..129f8e5dd2636bc48f0a300f926afbb3e1715a35 100644 (file)
@@ -15,8 +15,7 @@ package org.openhab.binding.xmppclient.internal;
 import java.util.Collections;
 import java.util.Set;
 
-import org.openhab.binding.xmppclient.XMPPClientBindingConstants;
-import org.openhab.binding.xmppclient.handler.XMPPClientHandler;
+import org.openhab.binding.xmppclient.internal.handler.XMPPClientHandler;
 import org.openhab.core.thing.Bridge;
 import org.openhab.core.thing.Thing;
 import org.openhab.core.thing.ThingTypeUID;
diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/action/XMPPActions.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/action/XMPPActions.java
new file mode 100644 (file)
index 0000000..7edf1c6
--- /dev/null
@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.xmppclient.internal.action;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.xmppclient.internal.XMPPClient;
+import org.openhab.binding.xmppclient.internal.handler.XMPPClientHandler;
+import org.openhab.core.automation.annotation.ActionInput;
+import org.openhab.core.automation.annotation.RuleAction;
+import org.openhab.core.thing.binding.ThingActions;
+import org.openhab.core.thing.binding.ThingActionsScope;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This is the automation engine action handler service for the publishXMPP action.
+ *
+ * @author Pavel Gololobov - Initial contribution
+ */
+@ThingActionsScope(name = "xmpp")
+@NonNullByDefault
+public class XMPPActions implements ThingActions {
+    private static final Logger logger = LoggerFactory.getLogger(XMPPActions.class);
+    private @Nullable XMPPClientHandler handler;
+
+    @Override
+    public void setThingHandler(@Nullable ThingHandler handler) {
+        this.handler = (XMPPClientHandler) handler;
+    }
+
+    @Override
+    public @Nullable ThingHandler getThingHandler() {
+        return handler;
+    }
+
+    @RuleAction(label = "publish a message", description = "Publish a message using XMPP.")
+    public void publishXMPP(@ActionInput(name = "to", label = "To", description = "Send to") @Nullable String to,
+            @ActionInput(name = "text", label = "Text", description = "Message text") @Nullable String text) {
+        XMPPClientHandler clientHandler = handler;
+        if (clientHandler == null) {
+            logger.warn("XMPP ThingHandler is null");
+            return;
+        }
+
+        XMPPClient connection = clientHandler.getXMPPClient();
+        if (connection == null) {
+            logger.warn("XMPP ThingHandler connection is null");
+            return;
+        }
+        if ((to == null) || (text == null)) {
+            logger.info("Skipping XMPP messaging to {} value {}", to, text);
+            return;
+        }
+        connection.sendMessage(to, text);
+    }
+
+    public static void publishXMPP(@Nullable ThingActions actions, @Nullable String to, @Nullable String text) {
+        if (actions instanceof XMPPActions) {
+            ((XMPPActions) actions).publishXMPP(to, text);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of XMPPActions");
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/handler/PublishTriggerChannel.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/handler/PublishTriggerChannel.java
new file mode 100644 (file)
index 0000000..c5c703d
--- /dev/null
@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.xmppclient.internal.handler;
+
+import org.openhab.binding.xmppclient.internal.XMPPClient;
+import org.openhab.core.thing.ChannelUID;
+
+/**
+ * Subscribes to a chat and calls {@link AbstractBrokerHandler#triggerChannel(ChannelUID, String)} if a value has been
+ * received.
+ *
+ * @author Pavel Gololobov - Initial contribution
+ */
+public class PublishTriggerChannel implements XMPPClientMessageSubscriber {
+    private final XMPPClient connection;
+    private final PublishTriggerChannelConfig config;
+    private final ChannelUID uid;
+    private final XMPPClientHandler handler;
+
+    PublishTriggerChannel(PublishTriggerChannelConfig config, ChannelUID uid, XMPPClient connection,
+            XMPPClientHandler handler) {
+        this.config = config;
+        this.uid = uid;
+        this.connection = connection;
+        this.handler = handler;
+    }
+
+    public void start() {
+        connection.subscribe(this);
+    }
+
+    public void stop() {
+        connection.unsubscribe(this);
+    }
+
+    @Override
+    public void processMessage(String from, String payload) {
+        // Check condition if exists
+        String expectedPayload = config.payload;
+        if ((expectedPayload != null) && (!expectedPayload.isEmpty()) && !payload.equals(expectedPayload)) {
+            return;
+        }
+        String eventValue = "";
+        if (!config.separator.isEmpty()) {
+            eventValue = from + config.separator + payload;
+        } else {
+            eventValue = payload;
+        }
+        handler.triggerChannel(uid, eventValue);
+    }
+
+    @Override
+    public String getName() {
+        return uid.toString();
+    }
+}
diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/handler/PublishTriggerChannelConfig.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/handler/PublishTriggerChannelConfig.java
new file mode 100644 (file)
index 0000000..fc3055a
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.xmppclient.internal.handler;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Holds the configuration of a {@link PublishTriggerChannel}.
+ *
+ * @author Pavel Gololobov - Initial contribution
+ */
+@NonNullByDefault
+public class PublishTriggerChannelConfig {
+    public @Nullable String payload;
+    public String separator = "";
+}
diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/handler/XMPPClientConfiguration.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/handler/XMPPClientConfiguration.java
new file mode 100644 (file)
index 0000000..2344255
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.xmppclient.internal.handler;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * The {@link XMPPClientConfiguration} class contains fields mapping thing configuration parameters.
+ *
+ * @author Pavel Gololobov - Initial contribution
+ */
+@NonNullByDefault
+public class XMPPClientConfiguration {
+    public @Nullable String host;
+    public Integer port = 5222;
+    public String username = "";
+    public String password = "";
+    public String domain = "";
+}
diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/handler/XMPPClientHandler.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/handler/XMPPClientHandler.java
new file mode 100644 (file)
index 0000000..ded4452
--- /dev/null
@@ -0,0 +1,108 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.xmppclient.internal.handler;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.jivesoftware.smack.*;
+import org.openhab.binding.xmppclient.internal.XMPPClient;
+import org.openhab.binding.xmppclient.internal.action.XMPPActions;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.Channel;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.binding.BaseBridgeHandler;
+import org.openhab.core.thing.binding.ThingHandlerService;
+import org.openhab.core.types.Command;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link XMPPClientHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author Pavel Gololobov - Initial contribution
+ */
+
+public class XMPPClientHandler extends BaseBridgeHandler {
+    private final Logger logger = LoggerFactory.getLogger(XMPPClientHandler.class);
+    private XMPPClient xmppClient;
+    private XMPPClientConfiguration config;
+    private final Map<ChannelUID, PublishTriggerChannel> channelStateByChannelUID = new HashMap<>();
+
+    public XMPPClientHandler(Bridge thing) {
+        super(thing);
+    }
+
+    public XMPPClient getXMPPClient() {
+        return xmppClient;
+    }
+
+    @Override
+    public Collection<Class<? extends ThingHandlerService>> getServices() {
+        return Collections.singleton(XMPPActions.class);
+    }
+
+    @Override
+    protected void triggerChannel(ChannelUID channelUID, String event) {
+        super.triggerChannel(channelUID, event);
+    }
+
+    @Override
+    public void handleCommand(ChannelUID channelUID, Command command) {
+        // not supported
+    }
+
+    @Override
+    public void initialize() {
+        updateStatus(ThingStatus.UNKNOWN);
+        scheduler.schedule(this::doConnect, 0, TimeUnit.SECONDS);
+    }
+
+    @Override
+    public void dispose() {
+        channelStateByChannelUID.values().forEach(c -> c.stop());
+        channelStateByChannelUID.clear();
+        xmppClient.disconnect();
+        super.dispose();
+    }
+
+    private void doConnect() {
+        config = getConfigAs(XMPPClientConfiguration.class);
+        xmppClient = new XMPPClient();
+        try {
+            xmppClient.connect(config.host, config.port, config.username, config.domain, config.password);
+        } catch (SmackException | IOException | XMPPException e) {
+            logger.info("XMPP connection error", e);
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
+            return;
+        }
+
+        for (Channel channel : thing.getChannels()) {
+            final PublishTriggerChannelConfig channelConfig = channel.getConfiguration()
+                    .as(PublishTriggerChannelConfig.class);
+            PublishTriggerChannel c = new PublishTriggerChannel(channelConfig, channel.getUID(), xmppClient, this);
+            channelStateByChannelUID.put(channel.getUID(), c);
+            logger.info("XMPP added channel {} payload {}", channel.getUID().toString(), channelConfig.payload);
+        }
+        channelStateByChannelUID.values().forEach(c -> c.start());
+
+        updateStatus(ThingStatus.ONLINE);
+    }
+}
diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/handler/XMPPClientMessageSubscriber.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/handler/XMPPClientMessageSubscriber.java
new file mode 100644 (file)
index 0000000..6145ef7
--- /dev/null
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.xmppclient.internal.handler;
+
+/**
+ * Subscriber interface
+ * 
+ * @author Pavel Gololobov - Initial contribution
+ */
+public interface XMPPClientMessageSubscriber {
+    public void processMessage(String from, String payload);
+
+    public String getName();
+}
diff --git a/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/action/IZmActions.java b/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/action/IZmActions.java
deleted file mode 100644 (file)
index f0ed520..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.zoneminder.action;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * The {@link IZmActions} defines the interface for all thing actions supported by the binding.
- * These methods, parameters, and return types are explained in {@link ZmActions}.
- *
- * @author Mark Hilbush - Initial contribution
- */
-@NonNullByDefault
-public interface IZmActions {
-
-    public void triggerAlarm(@Nullable Number alarmDuration);
-
-    public void triggerAlarm();
-
-    public void cancelAlarm();
-}
diff --git a/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/action/ZmActions.java b/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/action/ZmActions.java
deleted file mode 100644 (file)
index 05752c5..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.zoneminder.action;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.zoneminder.internal.handler.ZmMonitorHandler;
-import org.openhab.core.automation.annotation.ActionInput;
-import org.openhab.core.automation.annotation.RuleAction;
-import org.openhab.core.thing.binding.ThingActions;
-import org.openhab.core.thing.binding.ThingActionsScope;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link ZmActions} defines the thing actions provided by this binding.
- *
- * <b>Note:</b>The static method <b>invokeMethodOf</b> handles the case where
- * the test <i>actions instanceof ZmActions</i> fails. This test can fail
- * due to an issue in openHAB core v2.5.0 where the {@link ZmActions} class
- * can be loaded by a different classloader than the <i>actions</i> instance.
- *
- * @author Mark Hilbush - Initial contribution
- */
-@ThingActionsScope(name = "zm")
-@NonNullByDefault
-public class ZmActions implements ThingActions, IZmActions {
-    private final Logger logger = LoggerFactory.getLogger(ZmActions.class);
-
-    private @Nullable ZmMonitorHandler handler;
-
-    @Override
-    public @Nullable ThingHandler getThingHandler() {
-        return this.handler;
-    }
-
-    @Override
-    public void setThingHandler(@Nullable ThingHandler handler) {
-        if (handler instanceof ZmMonitorHandler) {
-            this.handler = (ZmMonitorHandler) handler;
-        }
-    }
-
-    private static IZmActions invokeMethodOf(@Nullable ThingActions actions) {
-        if (actions == null) {
-            throw new IllegalArgumentException("actions cannot be null");
-        }
-        if (actions.getClass().getName().equals(ZmActions.class.getName())) {
-            if (actions instanceof IZmActions) {
-                return (IZmActions) actions;
-            } else {
-                return (IZmActions) Proxy.newProxyInstance(IZmActions.class.getClassLoader(),
-                        new Class[] { IZmActions.class }, (Object proxy, Method method, Object[] args) -> {
-                            Method m = actions.getClass().getDeclaredMethod(method.getName(),
-                                    method.getParameterTypes());
-                            return m.invoke(actions, args);
-                        });
-            }
-        }
-        throw new IllegalArgumentException("Actions is not an instance of ZmActions");
-    }
-
-    /**
-     * The Trigger Alarm function triggers an alarm that will run for the number of seconds
-     * specified by the supplied parameter duration.
-     */
-    @Override
-    @RuleAction(label = "TriggerAlarm", description = "Trigger an alarm on the monitor.")
-    public void triggerAlarm(
-            @ActionInput(name = "duration", description = "The duration of the alarm in seconds.") @Nullable Number duration) {
-        logger.debug("ZmActions: Action 'TriggerAlarm' called");
-        ZmMonitorHandler localHandler = handler;
-        if (localHandler == null) {
-            logger.warn("ZmActions: Action service ThingHandler is null!");
-            return;
-        }
-        localHandler.actionTriggerAlarm(duration);
-    }
-
-    public static void triggerAlarm(@Nullable ThingActions actions, @Nullable Number alarmDuration) {
-        invokeMethodOf(actions).triggerAlarm(alarmDuration);
-    }
-
-    /**
-     * The Trigger Alarm function triggers an alarm that will run for the number of seconds
-     * specified in the thing configuration.
-     */
-    @Override
-    @RuleAction(label = "TriggerAlarm", description = "Trigger an alarm on the monitor.")
-    public void triggerAlarm() {
-        logger.debug("ZmActions: Action 'TriggerAlarm' called");
-        ZmMonitorHandler localHandler = handler;
-        if (localHandler == null) {
-            logger.warn("ZmActions: Action service ThingHandler is null!");
-            return;
-        }
-        localHandler.actionTriggerAlarm();
-    }
-
-    public static void triggerAlarm(@Nullable ThingActions actions) {
-        invokeMethodOf(actions).triggerAlarm();
-    }
-
-    /**
-     * The Cancel Alarm function cancels a running alarm.
-     */
-    @Override
-    @RuleAction(label = "CancelAlarm", description = "Cancel a running alarm.")
-    public void cancelAlarm() {
-        logger.debug("ZmActions: Action 'CancelAlarm' called");
-        ZmMonitorHandler localHandler = handler;
-        if (localHandler == null) {
-            logger.warn("ZmActions: Action service ThingHandler is null!");
-            return;
-        }
-        localHandler.actionCancelAlarm();
-    }
-
-    public static void cancelAlarm(@Nullable ThingActions actions) {
-        invokeMethodOf(actions).cancelAlarm();
-    }
-}
diff --git a/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/action/ZmActions.java b/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/action/ZmActions.java
new file mode 100644 (file)
index 0000000..8ca8809
--- /dev/null
@@ -0,0 +1,118 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.zoneminder.internal.action;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.zoneminder.internal.handler.ZmMonitorHandler;
+import org.openhab.core.automation.annotation.ActionInput;
+import org.openhab.core.automation.annotation.RuleAction;
+import org.openhab.core.thing.binding.ThingActions;
+import org.openhab.core.thing.binding.ThingActionsScope;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link ZmActions} defines the thing actions provided by this binding.
+ *
+ * @author Mark Hilbush - Initial contribution
+ */
+@ThingActionsScope(name = "zm")
+@NonNullByDefault
+public class ZmActions implements ThingActions {
+    private final Logger logger = LoggerFactory.getLogger(ZmActions.class);
+
+    private @Nullable ZmMonitorHandler handler;
+
+    @Override
+    public @Nullable ThingHandler getThingHandler() {
+        return handler;
+    }
+
+    @Override
+    public void setThingHandler(@Nullable ThingHandler handler) {
+        if (handler instanceof ZmMonitorHandler) {
+            this.handler = (ZmMonitorHandler) handler;
+        }
+    }
+
+    /**
+     * The Trigger Alarm function triggers an alarm that will run for the number of seconds
+     * specified by the supplied parameter duration.
+     */
+    @RuleAction(label = "trigger an alarm", description = "Trigger an alarm on the monitor.")
+    public void triggerAlarm(
+            @ActionInput(name = "duration", description = "The duration of the alarm in seconds.") @Nullable Number duration) {
+        logger.debug("ZmActions: Action 'TriggerAlarm' called");
+        ZmMonitorHandler localHandler = handler;
+        if (localHandler == null) {
+            logger.warn("ZmActions: Action service ThingHandler is null!");
+            return;
+        }
+        localHandler.actionTriggerAlarm(duration);
+    }
+
+    public static void triggerAlarm(@Nullable ThingActions actions, @Nullable Number alarmDuration) {
+        if (actions instanceof ZmActions) {
+            ((ZmActions) actions).triggerAlarm(alarmDuration);
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of ZmActions");
+        }
+    }
+
+    /**
+     * The Trigger Alarm function triggers an alarm that will run for the number of seconds
+     * specified in the thing configuration.
+     */
+    @RuleAction(label = "trigger an alarm", description = "Trigger an alarm on the monitor.")
+    public void triggerAlarm() {
+        logger.debug("ZmActions: Action 'TriggerAlarm' called");
+        ZmMonitorHandler localHandler = handler;
+        if (localHandler == null) {
+            logger.warn("ZmActions: Action service ThingHandler is null!");
+            return;
+        }
+        localHandler.actionTriggerAlarm();
+    }
+
+    public static void triggerAlarm(@Nullable ThingActions actions) {
+        if (actions instanceof ZmActions) {
+            ((ZmActions) actions).triggerAlarm();
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of ZmActions");
+        }
+    }
+
+    /**
+     * The Cancel Alarm function cancels a running alarm.
+     */
+    @RuleAction(label = "cancel a running alarm", description = "Cancel a running alarm.")
+    public void cancelAlarm() {
+        logger.debug("ZmActions: Action 'CancelAlarm' called");
+        ZmMonitorHandler localHandler = handler;
+        if (localHandler == null) {
+            logger.warn("ZmActions: Action service ThingHandler is null!");
+            return;
+        }
+        localHandler.actionCancelAlarm();
+    }
+
+    public static void cancelAlarm(@Nullable ThingActions actions) {
+        if (actions instanceof ZmActions) {
+            ((ZmActions) actions).cancelAlarm();
+        } else {
+            throw new IllegalArgumentException("Actions is not an instance of ZmActions");
+        }
+    }
+}
index a27df713037d22ab75fddf934fc0e5e07de899af..3265a5da0000bda3e526ae2e33afcc52c340e6d9 100644 (file)
@@ -26,7 +26,7 @@ import javax.measure.quantity.Time;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.zoneminder.action.ZmActions;
+import org.openhab.binding.zoneminder.internal.action.ZmActions;
 import org.openhab.binding.zoneminder.internal.config.ZmMonitorConfig;
 import org.openhab.core.i18n.TimeZoneProvider;
 import org.openhab.core.library.types.DateTimeType;