From: Wouter Born Date: Wed, 14 Oct 2020 17:01:12 +0000 (+0200) Subject: Rework ThingActions (#8732) X-Git-Url: https://git.basschouten.com/?a=commitdiff_plain;h=bef1046258e666ce180d5b5c73a0f60a100eb4e7;p=openhab-addons.git Rework ThingActions (#8732) * 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 --- diff --git a/bundles/org.openhab.binding.alarmdecoder/src/main/java/org/openhab/binding/alarmdecoder/internal/actions/BridgeActions.java b/bundles/org.openhab.binding.alarmdecoder/src/main/java/org/openhab/binding/alarmdecoder/internal/actions/BridgeActions.java index bbcd104017..658f978179 100644 --- a/bundles/org.openhab.binding.alarmdecoder/src/main/java/org/openhab/binding/alarmdecoder/internal/actions/BridgeActions.java +++ b/bundles/org.openhab.binding.alarmdecoder/src/main/java/org/openhab/binding/alarmdecoder/internal/actions/BridgeActions.java @@ -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 index 849ca7c118..0000000000 --- a/bundles/org.openhab.binding.alarmdecoder/src/main/java/org/openhab/binding/alarmdecoder/internal/actions/IBridgeActions.java +++ /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(); -} diff --git a/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/action/AstroActions.java b/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/action/AstroActions.java index 202135e92e..f8415c5e67 100644 --- a/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/action/AstroActions.java +++ b/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/action/AstroActions.java @@ -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. - *

- * Note:The static method invokeMethodOf handles the case where - * the test actions instanceof AstroActions 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 actions 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") QuantityType 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") QuantityType 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 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 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 index c0feeffa1b..0000000000 --- a/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/action/IAstroActions.java +++ /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 getAzimuth(@Nullable ZonedDateTime date); - - public @Nullable QuantityType getElevation(@Nullable ZonedDateTime date); -} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java index e63f453ce0..1086a0bfca 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java @@ -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 index 553401b13e..0000000000 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/IAutomowerActions.java +++ /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 index 662beefcdc..0000000000 --- a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/actions/AVMFritzHeatingActions.java +++ /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 index 0000000000..ef5530b48f --- /dev/null +++ b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/actions/AVMFritzHeatingActions.java @@ -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 index 0c30df92ac..0000000000 --- a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/actions/IAVMFritzHeatingActions.java +++ /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); -} diff --git a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/handler/AVMFritzHeatingActionsHandler.java b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/handler/AVMFritzHeatingActionsHandler.java index 8be206f50c..7f1cf572e1 100644 --- a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/handler/AVMFritzHeatingActionsHandler.java +++ b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/handler/AVMFritzHeatingActionsHandler.java @@ -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 index a18edccc47..0000000000 --- a/bundles/org.openhab.binding.avmfritz/src/test/java/org/openhab/binding/avmfritz/actions/AVMFritzHeatingActionsTest.java +++ /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 index 0000000000..f0f5865738 --- /dev/null +++ b/bundles/org.openhab.binding.avmfritz/src/test/java/org/openhab/binding/avmfritz/internal/actions/AVMFritzHeatingActionsTest.java @@ -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 index cbbf729f21..0000000000 --- a/bundles/org.openhab.binding.dmx/src/main/java/org/openhab/binding/dmx/action/DmxActions.java +++ /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 - *

- * Note:The static method invokeMethodOf handles the case where - * the test actions instanceof DmxActions 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 actions 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 index 9444044263..0000000000 --- a/bundles/org.openhab.binding.dmx/src/main/java/org/openhab/binding/dmx/action/IDmxActions.java +++ /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); -} diff --git a/bundles/org.openhab.binding.dmx/src/main/java/org/openhab/binding/dmx/internal/DmxBridgeHandler.java b/bundles/org.openhab.binding.dmx/src/main/java/org/openhab/binding/dmx/internal/DmxBridgeHandler.java index 59a74d60ab..bf06dd2ac5 100644 --- a/bundles/org.openhab.binding.dmx/src/main/java/org/openhab/binding/dmx/internal/DmxBridgeHandler.java +++ b/bundles/org.openhab.binding.dmx/src/main/java/org/openhab/binding/dmx/internal/DmxBridgeHandler.java @@ -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 index 0000000000..0e92e881a5 --- /dev/null +++ b/bundles/org.openhab.binding.dmx/src/main/java/org/openhab/binding/dmx/internal/action/DmxActions.java @@ -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 index d6095a8d40..0000000000 --- a/bundles/org.openhab.binding.doorbird/src/main/java/org/openhab/binding/doorbird/action/DoorbirdActions.java +++ /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 index 9286046543..0000000000 --- a/bundles/org.openhab.binding.doorbird/src/main/java/org/openhab/binding/doorbird/action/IDoorbirdActions.java +++ /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 index 0000000000..389a7bd58d --- /dev/null +++ b/bundles/org.openhab.binding.doorbird/src/main/java/org/openhab/binding/doorbird/internal/action/DoorbirdActions.java @@ -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"); + } + } +} diff --git a/bundles/org.openhab.binding.doorbird/src/main/java/org/openhab/binding/doorbird/internal/handler/DoorbellHandler.java b/bundles/org.openhab.binding.doorbird/src/main/java/org/openhab/binding/doorbird/internal/handler/DoorbellHandler.java index 1db3f49245..f5017578d1 100644 --- a/bundles/org.openhab.binding.doorbird/src/main/java/org/openhab/binding/doorbird/internal/handler/DoorbellHandler.java +++ b/bundles/org.openhab.binding.doorbird/src/main/java/org/openhab/binding/doorbird/internal/handler/DoorbellHandler.java @@ -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 index eea101b754..0000000000 --- a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/action/EcobeeActions.java +++ /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. - *

- * Note:The static method invokeMethod handles the case where - * the test actions instanceof EcobeeActions 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 actions 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 Acknowledge - * - */ - @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 Control - * Plug - */ - @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 Create - * Vacation - */ - @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 coolHoldTemp, - @ActionInput(name = "heatHoldTemp", description = "The temperature at which to set the heat vacation hold.") @Nullable QuantityType 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 coolHoldTemp, @Nullable QuantityType 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 Delete - * Vacation - */ - @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 Reset - * Preferences - */ - @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 Resume - * Program - */ - @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 Send - * Message - */ - @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 Set Hold - */ - @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 coolHoldTemp, - @ActionInput(name = "heatHoldTemp", description = "The temperature at which to set the heat hold.") @Nullable QuantityType heatHoldTemp) { - if (coolHoldTemp == null || heatHoldTemp == null) { - throw new IllegalArgumentException("hold temperatures cannot be null"); - } - Map params = new HashMap(); - params.put("coolHoldTemp", coolHoldTemp); - params.put("heatHoldTemp", heatHoldTemp); - return setHold(params, null, null, null, null); - } - - public static boolean setHold(@Nullable ThingActions actions, @Nullable QuantityType coolHoldTemp, - @Nullable QuantityType 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 coolHoldTemp, - @ActionInput(name = "heatHoldTemp", description = "The temperature at which to set the heat hold.") @Nullable QuantityType 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 params = new HashMap(); - 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 coolHoldTemp, - @Nullable QuantityType 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 params = new HashMap(); - 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 params = new HashMap(); - 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 coolHoldTemp, - @ActionInput(name = "heatHoldTemp", description = "(opt) The temperature at which to set the heat hold.") @Nullable QuantityType 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 params = new HashMap(); - 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 coolHoldTemp, - @Nullable QuantityType 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 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 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 Set - * Occupied - */ - @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 Update - * Sensor - */ - @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 index 3ed103c272..0000000000 --- a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/action/IEcobeeActions.java +++ /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 coolHoldTemp, - @Nullable QuantityType 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 coolHoldTemp, - @Nullable QuantityType heatHoldTemp); - - public Boolean setHold(@Nullable QuantityType coolHoldTemp, - @Nullable QuantityType heatHoldTemp, @Nullable Number holdHours); - - public Boolean setHold(@Nullable String holdClimateRef); - - public Boolean setHold(@Nullable String holdClimateRef, @Nullable Number holdHours); - - public Boolean setHold(@Nullable QuantityType coolHoldTemp, - @Nullable QuantityType heatHoldTemp, @Nullable String holdClimateRef, - @Nullable Date startDateTime, @Nullable Date endDateTime, @Nullable String holdType, - @Nullable Number holdHours); - - public Boolean setHold(@Nullable Map 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 index 0000000000..fbf788f119 --- /dev/null +++ b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/action/EcobeeActions.java @@ -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 Acknowledge + * + */ + @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 Control + * Plug + */ + @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 Create + * Vacation + */ + @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 coolHoldTemp, + @ActionInput(name = "heatHoldTemp", description = "The temperature at which to set the heat vacation hold.") @Nullable QuantityType 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 coolHoldTemp, @Nullable QuantityType 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 Delete + * Vacation + */ + @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 Reset + * Preferences + */ + @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 Resume + * Program + */ + @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 Send + * Message + */ + @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 Set Hold + */ + @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 coolHoldTemp, + @ActionInput(name = "heatHoldTemp", description = "The temperature at which to set the heat hold.") @Nullable QuantityType heatHoldTemp) { + if (coolHoldTemp == null || heatHoldTemp == null) { + throw new IllegalArgumentException("hold temperatures cannot be null"); + } + Map params = new HashMap(); + params.put("coolHoldTemp", coolHoldTemp); + params.put("heatHoldTemp", heatHoldTemp); + return setHold(params, null, null, null, null); + } + + public static boolean setHold(@Nullable ThingActions actions, @Nullable QuantityType coolHoldTemp, + @Nullable QuantityType 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 coolHoldTemp, + @ActionInput(name = "heatHoldTemp", description = "The temperature at which to set the heat hold.") @Nullable QuantityType 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 params = new HashMap(); + 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 coolHoldTemp, + @Nullable QuantityType 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 params = new HashMap(); + 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 params = new HashMap(); + 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 coolHoldTemp, + @ActionInput(name = "heatHoldTemp", description = "(opt) The temperature at which to set the heat hold.") @Nullable QuantityType 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 params = new HashMap(); + 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 coolHoldTemp, + @Nullable QuantityType 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 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 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 Set + * Occupied + */ + @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 Update + * Sensor + */ + @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"); + } + } +} diff --git a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/handler/EcobeeThermostatBridgeHandler.java b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/handler/EcobeeThermostatBridgeHandler.java index 1e2b2d17f0..a6df47edbc 100644 --- a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/handler/EcobeeThermostatBridgeHandler.java +++ b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/handler/EcobeeThermostatBridgeHandler.java @@ -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 index e2806fa66d..0000000000 --- a/bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/actions/Enigma2Actions.java +++ /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 index 591d2565cf..0000000000 --- a/bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/actions/IEnigma2Actions.java +++ /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 index 8bf429440c..0000000000 --- a/bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/handler/Enigma2Handler.java +++ /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 = 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> getServices() { - return Collections.singleton(Enigma2Actions.class); - } - - /** - * Getter for Test-Injection - * - * @return Enigma2Client. - */ - Optional getEnigma2Client() { - return enigma2Client; - } -} diff --git a/bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/internal/Enigma2HandlerFactory.java b/bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/internal/Enigma2HandlerFactory.java index 002faee91a..1e8a6a7b0d 100644 --- a/bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/internal/Enigma2HandlerFactory.java +++ b/bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/internal/Enigma2HandlerFactory.java @@ -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 index 0000000000..258fd239d5 --- /dev/null +++ b/bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/internal/actions/Enigma2Actions.java @@ -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 index 0000000000..1b7171a600 --- /dev/null +++ b/bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/internal/handler/Enigma2Handler.java @@ -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 = 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> getServices() { + return Collections.singleton(Enigma2Actions.class); + } + + /** + * Getter for Test-Injection + * + * @return Enigma2Client. + */ + Optional 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 index 827c76af90..0000000000 --- a/bundles/org.openhab.binding.enigma2/src/test/java/org/openhab/binding/enigma2/actions/Enigma2ActionsTest.java +++ /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 index f5b2e63ef6..0000000000 --- a/bundles/org.openhab.binding.enigma2/src/test/java/org/openhab/binding/enigma2/handler/Enigma2HandlerTest.java +++ /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 index 0000000000..2a9a7cf902 --- /dev/null +++ b/bundles/org.openhab.binding.enigma2/src/test/java/org/openhab/binding/enigma2/internal/actions/Enigma2ActionsTest.java @@ -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 index 0000000000..f0c52bb380 --- /dev/null +++ b/bundles/org.openhab.binding.enigma2/src/test/java/org/openhab/binding/enigma2/internal/handler/Enigma2HandlerTest.java @@ -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 index 03cd5d15ed..0000000000 --- a/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/action/IIpx800Actions.java +++ /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); -} diff --git a/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/action/Ipx800Actions.java b/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/action/Ipx800Actions.java index acdb937a1b..d27f8494d7 100644 --- a/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/action/Ipx800Actions.java +++ b/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/action/Ipx800Actions.java @@ -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. - *

- * Note:The static method invokeMethodOf handles the case where - * the test actions instanceof Ipx800Actions 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 actions 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"); } } diff --git a/bundles/org.openhab.binding.heos/src/main/java/org/openhab/binding/heos/internal/action/HeosActions.java b/bundles/org.openhab.binding.heos/src/main/java/org/openhab/binding/heos/internal/action/HeosActions.java index e4e6e9c74c..72dbe8e7b2 100644 --- a/bundles/org.openhab.binding.heos/src/main/java/org/openhab/binding/heos/internal/action/HeosActions.java +++ b/bundles/org.openhab.binding.heos/src/main/java/org/openhab/binding/heos/internal/action/HeosActions.java @@ -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 - *

- * Note:The static method invokeMethodOf handles the case where - * the test actions instanceof HeosActions 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 actions 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 index 3739292f97..0000000000 --- a/bundles/org.openhab.binding.heos/src/main/java/org/openhab/binding/heos/internal/action/IHeosActions.java +++ /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 index 912791bb63..0000000000 --- a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/action/ILightActions.java +++ /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); -} diff --git a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/action/LightActions.java b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/action/LightActions.java index 6d4942e822..2e994368e7 100644 --- a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/action/LightActions.java +++ b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/action/LightActions.java @@ -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. - *

- * Note:The static method invokeMethodOf handles the case where - * the test actions instanceof LightActions 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 actions 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 index 3643273d66..0000000000 --- a/bundles/org.openhab.binding.kaleidescape/src/main/java/org/openhab/binding/kaleidescape/internal/IKaleidescapeThingActions.java +++ /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); -} diff --git a/bundles/org.openhab.binding.kaleidescape/src/main/java/org/openhab/binding/kaleidescape/internal/KaleidescapeThingActions.java b/bundles/org.openhab.binding.kaleidescape/src/main/java/org/openhab/binding/kaleidescape/internal/KaleidescapeThingActions.java index 577422439c..6a5fad50bb 100644 --- a/bundles/org.openhab.binding.kaleidescape/src/main/java/org/openhab/binding/kaleidescape/internal/KaleidescapeThingActions.java +++ b/bundles/org.openhab.binding.kaleidescape/src/main/java/org/openhab/binding/kaleidescape/internal/KaleidescapeThingActions.java @@ -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 index e35924a093..0000000000 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/ILcnModuleActions.java +++ /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); -} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleActions.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleActions.java index d023fcc48c..cad59e5574 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleActions.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleActions.java @@ -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 index 5eb004b054..0000000000 --- a/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/action/ILGWebOSActions.java +++ /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 index 6cf60a38a2..0000000000 --- a/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/action/LGWebOSActions.java +++ /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. - *

- * Note:The static method invokeMethodOf handles the case where - * the test actions instanceof LGWebOSActions 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 actions 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 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 getAppInfos() { - LGWebOSHandler lgWebOSHandler = getLGWebOSHandler(); - - if (!this.getConnectedSocket().isPresent()) { - return Collections.emptyList(); - } - - List 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 = 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 = 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 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 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 createTextInputStatusListener() { - return new ResponseListener() { - - @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 ResponseListener createResponseListener() { - return new ResponseListener() { - - @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 index 0000000000..954a09715c --- /dev/null +++ b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/action/LGWebOSActions.java @@ -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 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 getAppInfos() { + LGWebOSHandler lgWebOSHandler = getLGWebOSHandler(); + + if (!this.getConnectedSocket().isPresent()) { + return Collections.emptyList(); + } + + List 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 = 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 = 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 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 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 createTextInputStatusListener() { + return new ResponseListener() { + + @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 ResponseListener createResponseListener() { + return new ResponseListener() { + + @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"); + } + } +} diff --git a/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/handler/LGWebOSHandler.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/handler/LGWebOSHandler.java index 26a1d4bd4c..cf40ac53d3 100644 --- a/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/handler/LGWebOSHandler.java +++ b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/handler/LGWebOSHandler.java @@ -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 index b7d4f40e3e..0000000000 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/action/DimmerActions.java +++ /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 index 551e8a6598..0000000000 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/action/IDimmerActions.java +++ /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 index 0000000000..48fb70eafc --- /dev/null +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/action/DimmerActions.java @@ -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"); + } + } +} diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/handler/DimmerHandler.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/handler/DimmerHandler.java index 35c01d2b15..84f002cbca 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/handler/DimmerHandler.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/handler/DimmerHandler.java @@ -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 index 1a0b3a3dfa..0000000000 --- a/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/action/ISendMailActions.java +++ /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 urlStringList); - - Boolean sendHtmlMail(@Nullable String recipient, @Nullable String subject, @Nullable String html, - @Nullable List 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 index 1513025f79..0000000000 --- a/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/action/SendMailActions.java +++ /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 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 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 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 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 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 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 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 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"); - } -} diff --git a/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/SMTPHandler.java b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/SMTPHandler.java index 69b36f6af0..476cdceabc 100644 --- a/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/SMTPHandler.java +++ b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/SMTPHandler.java @@ -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 index 0000000000..2c86e18cef --- /dev/null +++ b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/action/SendMailActions.java @@ -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 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 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 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 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 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 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 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 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 index 1f709b4c11..0000000000 --- a/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/actions/MaxCubeActions.java +++ /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 index d9291fa754..0000000000 --- a/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/actions/MaxDevicesActions.java +++ /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 index 69d57b5297..0000000000 --- a/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/actions/IMaxCubeActions.java +++ /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 index 0fcb61e207..0000000000 --- a/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/actions/IMaxDevicesActions.java +++ /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 index 0000000000..0814fe1e77 --- /dev/null +++ b/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/actions/MaxCubeActions.java @@ -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 index 0000000000..f14fdbda9f --- /dev/null +++ b/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/actions/MaxDevicesActions.java @@ -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"); + } + } +} diff --git a/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/handler/MaxCubeBridgeHandler.java b/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/handler/MaxCubeBridgeHandler.java index e2deb6025d..ca7aa63c99 100644 --- a/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/handler/MaxCubeBridgeHandler.java +++ b/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/handler/MaxCubeBridgeHandler.java @@ -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; diff --git a/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/handler/MaxDevicesHandler.java b/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/handler/MaxDevicesHandler.java index 860a641a23..753427ad80 100644 --- a/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/handler/MaxDevicesHandler.java +++ b/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/handler/MaxDevicesHandler.java @@ -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 index 6accb2c11b..0000000000 --- a/bundles/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/internal/action/IMPDActions.java +++ /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); -} diff --git a/bundles/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/internal/action/MPDActions.java b/bundles/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/internal/action/MPDActions.java index 8545085379..060ea6d37b 100644 --- a/bundles/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/internal/action/MPDActions.java +++ b/bundles/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/internal/action/MPDActions.java @@ -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 index 9cc1463a52..0000000000 --- a/bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/action/IMQTTActions.java +++ /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 index 339236a729..0000000000 --- a/bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/action/MQTTActions.java +++ /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. - *

- * Note:The static method invokeMethodOf handles the case where - * the test actions instanceof MQTTActions 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 actions 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"); - } -} diff --git a/bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/handler/AbstractBrokerHandler.java b/bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/handler/AbstractBrokerHandler.java index 9b58a7cab8..6468e34374 100644 --- a/bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/handler/AbstractBrokerHandler.java +++ b/bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/handler/AbstractBrokerHandler.java @@ -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 index 0000000000..75b748c66b --- /dev/null +++ b/bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/internal/action/MQTTActions.java @@ -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 index cc068ae2de..0000000000 --- a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/action/INetworkActions.java +++ /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(); -} diff --git a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/action/NetworkActions.java b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/action/NetworkActions.java index ab2d6801f1..a28837f023 100644 --- a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/action/NetworkActions.java +++ b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/action/NetworkActions.java @@ -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}. - *

- * Note:The static method invokeMethodOf handles the case where - * the test actions instanceof NetworkActions 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 actions 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 index 8bec3d20d3..0000000000 --- a/bundles/org.openhab.binding.nuvo/src/main/java/org/openhab/binding/nuvo/internal/INuvoThingActions.java +++ /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); -} diff --git a/bundles/org.openhab.binding.nuvo/src/main/java/org/openhab/binding/nuvo/internal/NuvoThingActions.java b/bundles/org.openhab.binding.nuvo/src/main/java/org/openhab/binding/nuvo/internal/NuvoThingActions.java index cf37669eb9..59716555bb 100644 --- a/bundles/org.openhab.binding.nuvo/src/main/java/org/openhab/binding/nuvo/internal/NuvoThingActions.java +++ b/bundles/org.openhab.binding.nuvo/src/main/java/org/openhab/binding/nuvo/internal/NuvoThingActions.java @@ -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; } } diff --git a/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/automation/modules/OnkyoThingActions.java b/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/automation/modules/OnkyoThingActions.java index 6386f7fe36..9c84aa7e6d 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/automation/modules/OnkyoThingActions.java +++ b/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/automation/modules/OnkyoThingActions.java @@ -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 index 3b2a0d86c8..0000000000 --- a/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/automation/modules/OnkyoThingActionsService.java +++ /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} - *

- * Note:The static method invokeMethodOf handles the case where - * the test actions instanceof OnkyoThingActionsService 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 actions 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; - } -} diff --git a/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/handler/OnkyoHandler.java b/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/handler/OnkyoHandler.java index 0e54d42927..717873b3db 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/handler/OnkyoHandler.java +++ b/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/handler/OnkyoHandler.java @@ -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> 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 index 46cbbc462a..0000000000 --- a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/action/IPushbulletActions.java +++ /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); -} diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/action/PushbulletActions.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/action/PushbulletActions.java index f1ae9e55f1..103828e378 100644 --- a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/action/PushbulletActions.java +++ b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/action/PushbulletActions.java @@ -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 - *

- * Note:The static method invokeMethodOf handles the case where - * the test actions instanceof PushbulletActions 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 actions 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 index 101cef8f6b..0000000000 --- a/bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/IRadioThermostatThingActions.java +++ /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); -} diff --git a/bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/RadioThermostatThingActions.java b/bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/RadioThermostatThingActions.java index 730dc4971a..997a71b27e 100644 --- a/bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/RadioThermostatThingActions.java +++ b/bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/RadioThermostatThingActions.java @@ -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 index 53a8786bec..0000000000 --- a/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/action/ISatelEventLogActions.java +++ /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 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 index 95d2a4732f..0000000000 --- a/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/action/SatelEventLogActions.java +++ /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 readEvent( - @ActionInput(name = "index", label = "@text/actionInputIndexLabel", description = "@text/actionInputIndexDesc") @Nullable Number index) { - logger.debug("satel.readEvent called with input: index={}", index); - - Map 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 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 index 0000000000..a2f49e1a0f --- /dev/null +++ b/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/action/SatelEventLogActions.java @@ -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 readEvent( + @ActionInput(name = "index", label = "@text/actionInputIndexLabel", description = "@text/actionInputIndexDesc") @Nullable Number index) { + logger.debug("satel.readEvent called with input: index={}", index); + + Map 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 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"); + } + } +} diff --git a/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/handler/SatelEventLogHandler.java b/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/handler/SatelEventLogHandler.java index cca874f657..339bcedcf2 100644 --- a/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/handler/SatelEventLogHandler.java +++ b/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/handler/SatelEventLogHandler.java @@ -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 index 9b6787aa42..0000000000 --- a/bundles/org.openhab.binding.telegram/src/main/java/org/openhab/binding/telegram/bot/ITelegramActions.java +++ /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 index 9171dc53d7..0000000000 --- a/bundles/org.openhab.binding.telegram/src/main/java/org/openhab/binding/telegram/bot/TelegramActions.java +++ /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. - *

- * Note:The static method invokeMethodOf handles the case where - * the test actions instanceof TelegramActions 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 actions 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; - } -} diff --git a/bundles/org.openhab.binding.telegram/src/main/java/org/openhab/binding/telegram/internal/TelegramHandler.java b/bundles/org.openhab.binding.telegram/src/main/java/org/openhab/binding/telegram/internal/TelegramHandler.java index c4c8c94d8a..e28432b725 100644 --- a/bundles/org.openhab.binding.telegram/src/main/java/org/openhab/binding/telegram/internal/TelegramHandler.java +++ b/bundles/org.openhab.binding.telegram/src/main/java/org/openhab/binding/telegram/internal/TelegramHandler.java @@ -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 index 0000000000..42ca057f1b --- /dev/null +++ b/bundles/org.openhab.binding.telegram/src/main/java/org/openhab/binding/telegram/internal/action/TelegramActions.java @@ -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; + } +} diff --git a/bundles/org.openhab.binding.volvooncall/src/main/java/org/openhab/binding/volvooncall/internal/action/VolvoOnCallActions.java b/bundles/org.openhab.binding.volvooncall/src/main/java/org/openhab/binding/volvooncall/internal/action/VolvoOnCallActions.java index d15fd3438c..a00d921156 100644 --- a/bundles/org.openhab.binding.volvooncall/src/main/java/org/openhab/binding/volvooncall/internal/action/VolvoOnCallActions.java +++ b/bundles/org.openhab.binding.volvooncall/src/main/java/org/openhab/binding/volvooncall/internal/action/VolvoOnCallActions.java @@ -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 index 9ce9d51f1d..0000000000 --- a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/XMPPClientBindingConstants.java +++ /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 index 05e684d9a8..0000000000 --- a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/action/IXMPPActions.java +++ /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. - *

- * Note:The static method invokeMethodOf handles the case where - * the test actions instanceof XMPPActions 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 actions 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 index 1d617fd7db..0000000000 --- a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/action/XMPPActions.java +++ /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. - *

- * Note:The static method invokeMethodOf handles the case where - * the test actions instanceof XMPPActions 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 actions 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 index 86334bc141..0000000000 --- a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/PublishTriggerChannel.java +++ /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 index b5c937a49e..0000000000 --- a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/PublishTriggerChannelConfig.java +++ /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 index 5e99bef6ab..0000000000 --- a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/XMPPClientConfiguration.java +++ /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 index ce748112c5..0000000000 --- a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/XMPPClientHandler.java +++ /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 channelStateByChannelUID = new HashMap<>(); - - public XMPPClientHandler(Bridge thing) { - super(thing); - } - - public XMPPClient getXMPPClient() { - return xmppClient; - } - - @Override - public Collection> 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 index 8ffc1c78dd..0000000000 --- a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/XMPPClientMessageSubscriber.java +++ /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(); -} diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClient.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClient.java index 93bac0845c..1ac261acc8 100644 --- a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClient.java +++ b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClient.java @@ -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 index 0000000000..a07f609a62 --- /dev/null +++ b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClientBindingConstants.java @@ -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"; +} diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClientHandlerFactory.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClientHandlerFactory.java index dea805fc23..129f8e5dd2 100644 --- a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClientHandlerFactory.java +++ b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClientHandlerFactory.java @@ -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 index 0000000000..7edf1c6e4d --- /dev/null +++ b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/action/XMPPActions.java @@ -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 index 0000000000..c5c703d508 --- /dev/null +++ b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/handler/PublishTriggerChannel.java @@ -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 index 0000000000..fc3055afdd --- /dev/null +++ b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/handler/PublishTriggerChannelConfig.java @@ -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 index 0000000000..2344255aaa --- /dev/null +++ b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/handler/XMPPClientConfiguration.java @@ -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 index 0000000000..ded44527a5 --- /dev/null +++ b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/handler/XMPPClientHandler.java @@ -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 channelStateByChannelUID = new HashMap<>(); + + public XMPPClientHandler(Bridge thing) { + super(thing); + } + + public XMPPClient getXMPPClient() { + return xmppClient; + } + + @Override + public Collection> 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 index 0000000000..6145ef74b2 --- /dev/null +++ b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/handler/XMPPClientMessageSubscriber.java @@ -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 index f0ed5208a0..0000000000 --- a/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/action/IZmActions.java +++ /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 index 05752c5a07..0000000000 --- a/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/action/ZmActions.java +++ /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. - * - * Note:The static method invokeMethodOf handles the case where - * the test actions instanceof ZmActions 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 actions 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 index 0000000000..8ca8809b02 --- /dev/null +++ b/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/action/ZmActions.java @@ -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"); + } + } +} diff --git a/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZmMonitorHandler.java b/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZmMonitorHandler.java index a27df71303..3265a5da00 100644 --- a/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZmMonitorHandler.java +++ b/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZmMonitorHandler.java @@ -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;