]> git.basschouten.com Git - openhab-addons.git/commitdiff
[unifi] guest voucher actions (#14303)
authorMark Herwege <mherwege@users.noreply.github.com>
Sun, 5 Feb 2023 15:59:51 +0000 (16:59 +0100)
committerGitHub <noreply@github.com>
Sun, 5 Feb 2023 15:59:51 +0000 (16:59 +0100)
* guest voucher actions

Signed-off-by: Mark Herwege <mark.herwege@telenet.be>
bundles/org.openhab.binding.unifi/README.md
bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/UniFiThingHandlerFactory.java
bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/action/UniFiSiteActions.java [new file with mode: 0644]
bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/UniFiController.java
bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiVoucher.java
bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/handler/UniFiBaseThingHandler.java
bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/handler/UniFiSiteThingHandler.java
bundles/org.openhab.binding.unifi/src/main/resources/OH-INF/i18n/unifi.properties

index b2c70edf7e1005cecbe099ab3cbf30bd383dbd27..6ac989449573a74feebc2c1b88d3bff4559d50bf 100644 (file)
@@ -230,21 +230,89 @@ The `poePort` information that is retrieved is available as these channels:
 The `enable` switch channel has a configuration parameter `mode` which is the value used to switch PoE on when the channel is switched to ON.
 The default mode value is `auto`.
 
+## Rule Actions
+
+As an alternative to using the `guestVoucher` and `guestVouchersGenerate` channels on the `site` thing, it is possible to use rule actions on the thing to generate, revoke and list guest vouchers.
+The following actions are available:
+
+- `boolean success = generateVoucher(Integer expire, Integer users, Integer upLimit, Integer downLimit, Integer dataQuota)`
+- `boolean success = generateVouchers(Integer count, Integer expire, Integer users, Integer upLimit, Integer downLimit, Integer dataQuota)`
+- `boolean success = revokeVoucher(String voucherCode)`
+- `boolean success = revokeVouchers(List<String> voucherCodes)`
+- `boolean success = revokeAllVouchers()`
+- `String vouchers = listVouchers()`
+
+Since there is a separate rule action instance for each `site` thing, this needs to be retrieved through `getActions(scope, thingUID)`.
+The first parameter always has to be `unifi` and the second is the full Thing UID of the site that should be used.
+Once this action instance is retrieved, you can invoke the action method on it.
+
+Boolean return values for the actions indicate success or failure.
+
+The `generateVoucher(s)` actions parameters match the configuration parameters for the `guestVouchersGenerate` channel.
+With the actions, these parameters can be controlled in a rule or script.
+`null` values for the parameters are allowed, and will set the parameter to the default value.
+
+| Parameter     | Description                                                                 | Default |
+| ------------- | --------------------------------------------------------------------------- | ------- |
+| count         | Number of vouchers to create                                                |    1    |
+| expire        | Minutes a voucher is valid after activation (default is 1 day)              |  1440   |
+| users         | Number of users for voucher, 0 for no limit                                 |    1    |
+| upLimit       | Upload speed limit in kbps, no limit if not set                             |         |
+| downLimit     | Download speed limit in kbps, no limit if not set                           |         |
+| dataQuota     | Data transfer quota in MB per user, no limit if not set                     |         |
+
+The `revoke...` actions allow you to revoke previously created vouchers.
+The parameter is the voucher code or a list of voucher codes to be revoked.
+
+The `listVouchers` action will return a json string representing the currently available vouchers for the site.
+The json contains all parameters for the voucher, therefore it is possible to filter on these in a rule or script.
+For example, one could retrieve all vouchers created before a certain time and use the `revokeVouchers` action to delete these.
+The structure of the returned json is (depending on content, some fields may be missing):
+
+```json
+[
+    {
+        "code": "3867791284",
+        "createTime": "2023-01-31T14:40:47Z",
+        "duration": 1440,
+        "quota": 2,
+        "used": 0,
+        "qosUsageQuota": 300,
+        "qosRateMaxUp": 200,
+        "qosRateMaxDown": 100,
+        "qosOverwrite": true,
+        "note": "I added a note when creating vouchers in the UniFi hotspot UI",
+        "status": "VALID_MULTI"
+    },
+    {
+        "code": "0021952641",
+        "createTime": "2023-01-31T14:38:47Z",
+        "duration": 1440,
+        "quota": 1,
+        "used": 0,
+        "qosOverwrite": false,
+        "status": "VALID_ONE"
+    },
+    { ... }
+]
+```
+
 ## Full Example
 
-things/unifi.things
+### `things/unifi.things`
 
 ```
 Bridge unifi:controller:home "UniFi Controller" [ host="unifi", port=8443, unifios=false, username="$username", password="$password", refresh=10 ] {
        Thing wirelessClient matthewsPhone "Matthew's iPhone" [ cid="$cid", site="default", considerHome=180 ]
+       Thing site mysite "My Site" [ sid="$sid" ]
 }
 ```
 
 _Note: Usually on Unifi OS, the default port is 443_
 
-Replace `$user`, `$password` and `$cid` accordingly.
+Replace `$user`, `$password`, `$cid` and `$sid` accordingly.
 
-items/unifi.items
+### `items/unifi.items`
 
 ```
 Switch       MatthewsPhone           "Matthew's iPhone [MAP(unifi.map):%s]"             { channel="unifi:wirelessClient:home:matthewsPhone:online" }
@@ -260,14 +328,14 @@ Switch       MatthewsPhoneBlocked    "Matthew's iPhone: Blocked"
 Switch       MatthewsPhoneReconnect  "Matthew's iPhone: Reconnect"                      { channel="unifi:wirelessClient:home:matthewsPhone:reconnect" }
 ```
 
-transform/unifi.map
+### `transform/unifi.map`
 
 ```
 ON=Home
 OFF=Away
 ```
 
-sitemaps/unifi.sitemap
+### `sitemaps/unifi.sitemap`
 
 ```
 sitemap unifi label="UniFi Binding"
@@ -287,3 +355,23 @@ sitemap unifi label="UniFi Binding"
        }
 }
 ```
+
+### `rule actions` for `site` thing
+
+```java
+val uniFiActions = getActions("unifi","unifi:site:home:mysite")
+val success = uniFiActions.generateVoucher(null, null, null, 100, 500, 250)
+```
+
+```java
+val uniFiActions = getActions("unifi","unifi:site:home:mysite")
+val vouchersJson = uniFiActions.listVouchers()
+```
+
+```java
+import java.util.List
+
+val List<String> voucherList = newArrayList("38677-91284", "46415-36104")
+val uniFiActions = getActions("unifi","unifi:site:home:mysite")
+val success = uniFiActions.revokeVouchers(voucherList)
+```
index ddca4f06dc9b4e0a2c6e33438781b6cc721b12da..032189c58877dfc882ee32d8eb9a01f90cc476a5 100644 (file)
  */
 package org.openhab.binding.unifi.internal;
 
-import static org.openhab.binding.unifi.internal.UniFiBindingConstants.ALL_THING_TYPE_SUPPORTED;
-import static org.openhab.binding.unifi.internal.UniFiBindingConstants.THING_TYPE_CONTROLLER;
-import static org.openhab.binding.unifi.internal.UniFiBindingConstants.THING_TYPE_POE_PORT;
-import static org.openhab.binding.unifi.internal.UniFiBindingConstants.THING_TYPE_SITE;
-import static org.openhab.binding.unifi.internal.UniFiBindingConstants.THING_TYPE_WIRED_CLIENT;
-import static org.openhab.binding.unifi.internal.UniFiBindingConstants.THING_TYPE_WIRELESS_CLIENT;
-import static org.openhab.binding.unifi.internal.UniFiBindingConstants.THING_TYPE_WLAN;
+import static org.openhab.binding.unifi.internal.UniFiBindingConstants.*;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
diff --git a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/action/UniFiSiteActions.java b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/action/UniFiSiteActions.java
new file mode 100644 (file)
index 0000000..59b7cfb
--- /dev/null
@@ -0,0 +1,271 @@
+/**
+ * Copyright (c) 2010-2023 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.unifi.internal.action;
+
+import java.util.List;
+import java.util.regex.Pattern;
+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.unifi.internal.api.UniFiController;
+import org.openhab.binding.unifi.internal.api.UniFiException;
+import org.openhab.binding.unifi.internal.api.dto.UniFiSite;
+import org.openhab.binding.unifi.internal.api.dto.UniFiVoucher;
+import org.openhab.binding.unifi.internal.handler.UniFiSiteThingHandler;
+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;
+
+import com.google.gson.Gson;
+
+/**
+ * The {@link UniFiSiteActions} class defines rule actions for creating guest hotspot vouchers
+ *
+ * @author Mark Herwege - Initial contribution
+ */
+@ThingActionsScope(name = "unifi")
+@NonNullByDefault
+public class UniFiSiteActions implements ThingActions {
+
+    private static final int DEFAULT_COUNT = 1;
+    private static final int DEFAULT_EXPIRE_MIN = 1440;
+    private static final int DEFAULT_USERS = 1;
+
+    private static final Pattern NON_DIGITS_PATTERN = Pattern.compile("\\D+");
+
+    private final Logger logger = LoggerFactory.getLogger(UniFiSiteActions.class);
+
+    private @Nullable UniFiSiteThingHandler handler;
+    private final Gson gson = new Gson();
+
+    @RuleAction(label = "@text/action.unifi.generateVouchers.label", description = "@text/action.unifi.generateVouchers.description")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean generateVoucher(
+    /* @formatter:off */
+            @ActionInput(name = "expire",
+                label = "@text/channel-type.config.unifi.guestVouchersGenerate.voucherExpiration.label",
+                description = "@text/channel-type.config.unifi.guestVouchersGenerate.voucherExpiration.description") @Nullable Integer expire,
+            @ActionInput(name = "users",
+                label = "@text/channel-type.config.unifi.guestVouchersGenerate.voucherUsers.label",
+                description = "@text/channel-type.config.unifi.guestVouchersGenerate.voucherUsers.description") @Nullable Integer users,
+            @ActionInput(name = "upLimit",
+                label = "@text/channel-type.config.unifi.guestVouchersGenerate.voucherUpLimit.label",
+                description = "@text/channel-type.config.unifi.guestVouchersGenerate.voucherUpLimit.description") @Nullable Integer upLimit,
+            @ActionInput(name = "downLimit",
+                label = "@text/channel-type.config.unifi.guestVouchersGenerate.voucherDownLimit.label",
+                description = "@text/channel-type.config.unifi.guestVouchersGenerate.voucherDownLimit.description") @Nullable Integer downLimit,
+            @ActionInput(name = "dataQuota",
+                label = "@text/channel-type.config.unifi.guestVouchersGenerate.voucherDataQuota.label",
+                description = "@text/channel-type.config.unifi.guestVouchersGenerate.voucherDataQuota.description") @Nullable Integer dataQuota) {
+    /* @formatter:on */
+        return generateVouchers(1, expire, users, upLimit, downLimit, dataQuota);
+    }
+
+    @RuleAction(label = "@text/action.unifi.generateVouchers.label", description = "@text/action.unifi.generateVouchers.description")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean generateVouchers(
+    /* @formatter:off */
+            @ActionInput(name = "count",
+                label = "@text/channel-type.config.unifi.guestVouchersGenerate.voucherCount.label",
+                description = "@text/channel-type.config.unifi.guestVouchersGenerate.voucherCount.description") @Nullable Integer count,
+            @ActionInput(name = "expire",
+                label = "@text/channel-type.config.unifi.guestVouchersGenerate.voucherExpiration.label",
+                description = "@text/channel-type.config.unifi.guestVouchersGenerate.voucherExpiration.description") @Nullable Integer expire,
+            @ActionInput(name = "users",
+                label = "@text/channel-type.config.unifi.guestVouchersGenerate.voucherUsers.label",
+                description = "@text/channel-type.config.unifi.guestVouchersGenerate.voucherUsers.description") @Nullable Integer users,
+            @ActionInput(name = "upLimit",
+                label = "@text/channel-type.config.unifi.guestVouchersGenerate.voucherUpLimit.label",
+                description = "@text/channel-type.config.unifi.guestVouchersGenerate.voucherUpLimit.description") @Nullable Integer upLimit,
+            @ActionInput(name = "downLimit",
+                label = "@text/channel-type.config.unifi.guestVouchersGenerate.voucherDownLimit.label",
+                description = "@text/channel-type.config.unifi.guestVouchersGenerate.voucherDownLimit.description") @Nullable Integer downLimit,
+            @ActionInput(name = "dataQuota",
+                label = "@text/channel-type.config.unifi.guestVouchersGenerate.voucherDataQuota.label",
+                description = "@text/channel-type.config.unifi.guestVouchersGenerate.voucherDataQuota.description") @Nullable Integer dataQuota) {
+    /* @formatter:on */
+        UniFiSiteThingHandler handler = this.handler;
+        if (handler == null) {
+            logger.debug("Could not create guest vouchers, site thing handler not set");
+            return false;
+        }
+        final @Nullable UniFiSite entity = handler.getEntity();
+        final UniFiController controller = handler.getController();
+        if (entity == null || controller == null) {
+            logger.debug("Could not create guest vouchers, site thing error");
+            return false;
+        }
+        try {
+            controller.generateVouchers(entity, ((count != null) && (count != 0)) ? count : DEFAULT_COUNT,
+                    (expire != null) ? expire : DEFAULT_EXPIRE_MIN, (users != null) ? users : DEFAULT_USERS, upLimit,
+                    downLimit, dataQuota);
+        } catch (UniFiException e) {
+            logger.debug("Could not create guest vouchers, uniFi exception", e);
+            return false;
+        }
+        return true;
+    }
+
+    public static boolean generateVoucher(ThingActions actions) {
+        return UniFiSiteActions.generateVoucher(actions, DEFAULT_EXPIRE_MIN);
+    }
+
+    public static boolean generateVoucher(ThingActions actions, @Nullable Integer expire) {
+        return UniFiSiteActions.generateVoucher(actions, expire, DEFAULT_USERS);
+    }
+
+    public static boolean generateVoucher(ThingActions actions, @Nullable Integer expire, @Nullable Integer users) {
+        return UniFiSiteActions.generateVoucher(actions, expire, users, null, null, null);
+    }
+
+    public static boolean generateVoucher(ThingActions actions, @Nullable Integer expire, @Nullable Integer users,
+            @Nullable Integer upLimit, @Nullable Integer downLimit, @Nullable Integer dataQuota) {
+        return ((UniFiSiteActions) actions).generateVoucher(expire, users, upLimit, downLimit, dataQuota);
+    }
+
+    public static boolean generateVouchers(ThingActions actions) {
+        return UniFiSiteActions.generateVouchers(actions, DEFAULT_COUNT);
+    }
+
+    public static boolean generateVouchers(ThingActions actions, @Nullable Integer count) {
+        return UniFiSiteActions.generateVouchers(actions, count, DEFAULT_EXPIRE_MIN);
+    }
+
+    public static boolean generateVouchers(ThingActions actions, @Nullable Integer count, @Nullable Integer expire) {
+        return UniFiSiteActions.generateVouchers(actions, count, expire, DEFAULT_USERS);
+    }
+
+    public static boolean generateVouchers(ThingActions actions, @Nullable Integer count, @Nullable Integer expire,
+            @Nullable Integer users) {
+        return UniFiSiteActions.generateVouchers(actions, count, expire, users, null, null, null);
+    }
+
+    public static boolean generateVouchers(ThingActions actions, @Nullable Integer count, @Nullable Integer expire,
+            @Nullable Integer users, @Nullable Integer upLimit, @Nullable Integer downLimit,
+            @Nullable Integer dataQuota) {
+        return ((UniFiSiteActions) actions).generateVouchers(count, expire, users, upLimit, downLimit, dataQuota);
+    }
+
+    @RuleAction(label = "@text/action.unifi.revokeVouchers.label", description = "@text/action.unifi.revokeVouchers.description")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean revokeVoucher(
+    /* @formatter:off */
+            @ActionInput(name = "voucherCodes", label = "@text/action.unifi.vouchersInputVoucherCodes.label",
+                description = "@text/action.unifi.vouchersInputVoucherCodes.description") String voucherCode) {
+    /* @formatter:on */
+        return revokeVouchers(List.of(voucherCode));
+    }
+
+    @RuleAction(label = "@text/action.unifi.revokeVouchers.label", description = "@text/action.unifi.revokeVouchers.description")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean revokeVouchers(
+    /* @formatter:off */
+            @ActionInput(name = "voucherCodes", label = "@text/action.unifi.vouchersInputVoucherCodes.label",
+                description = "@text/action.unifi.vouchersInputVoucherCodes.description") List<String> voucherCodes) {
+    /* @formatter:on */
+        UniFiSiteThingHandler handler = this.handler;
+        if (handler == null) {
+            logger.debug("Could not revoke guest vouchers, site thing handler not set");
+            return false;
+        }
+        final @Nullable UniFiSite entity = handler.getEntity();
+        final UniFiController controller = handler.getController();
+        if (entity == null || controller == null) {
+            logger.debug("Could not revoke guest vouchers, site thing error");
+            return false;
+        }
+
+        // Only keep digits in provided codes, so matching is done correctly. This makes blanks and dashes in the input
+        // possible, as shown in the UniFi voucher UI.
+        List<String> cleanCodes = voucherCodes.stream().map(c -> NON_DIGITS_PATTERN.matcher(c).replaceAll(""))
+                .filter(c -> !c.isEmpty()).toList();
+        Stream<UniFiVoucher> voucherStream = entity.getCache().getVoucherStreamForSite(entity);
+        // If no codes provided, revoke all codes
+        List<UniFiVoucher> vouchers = (voucherCodes.isEmpty() ? voucherStream
+                : voucherStream.filter(v -> cleanCodes.contains(v.getCode()))).toList();
+        try {
+            controller.revokeVouchers(entity, vouchers);
+        } catch (UniFiException e) {
+            logger.debug("Could not revoke guest vouchers, uniFi exception", e);
+            return false;
+        }
+        return true;
+    }
+
+    @RuleAction(label = "@text/action.unifi.revokeAllVouchers.label", description = "@text/action.unifi.revokeAllVouchers.description")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean revokeAllVouchers() {
+        return revokeVouchers(List.of());
+    }
+
+    public static boolean revokeVoucher(ThingActions actions, String voucherCode) {
+        return revokeVouchers(actions, List.of(voucherCode));
+    }
+
+    public static boolean revokeVouchers(ThingActions actions, List<String> voucherCodes) {
+        return ((UniFiSiteActions) actions).revokeVouchers(voucherCodes);
+    }
+
+    public static boolean revokeAllVouchers(ThingActions actions) {
+        return revokeVouchers(actions);
+    }
+
+    public static boolean revokeVouchers(ThingActions actions) {
+        return revokeVouchers(actions, List.of());
+    }
+
+    @RuleAction(label = "@text/action.unifi.listVouchers.label", description = "@text/action.unifi.listVouchers.description")
+    public @ActionOutput(name = "vouchers", type = "java.lang.String") String listVouchers() {
+        UniFiSiteThingHandler handler = this.handler;
+        if (handler == null) {
+            logger.debug("Could not list guest vouchers, site thing handler not set");
+            return "";
+        }
+        final @Nullable UniFiSite entity = handler.getEntity();
+        if (entity == null) {
+            logger.debug("Could not list guest vouchers, site thing error");
+            return "";
+        }
+
+        record Voucher(String code, String createTime, Integer duration, Integer quota, Integer used,
+                Integer qosUsageQuota, Integer qosRateMaxUp, Integer qosRateMaxDown, Boolean qosOverwrite, String note,
+                String status) {
+        }
+
+        return gson
+                .toJson(entity.getCache().getVoucherStreamForSite(entity)
+                        .collect(Collectors.mapping(
+                                v -> new Voucher(v.getCode(), v.getCreateTime().toString(), v.getDuration(),
+                                        v.getQuota(), v.getUsed(), v.getQosUsageQuota(), v.getQosRateMaxUp(),
+                                        v.getQosRateMaxDown(), v.isQosOverwrite(), v.getNote(), v.getStatus()),
+                                Collectors.toList())));
+    }
+
+    public static String listVouchers(ThingActions actions) {
+        return ((UniFiSiteActions) actions).listVouchers();
+    }
+
+    @Override
+    public void setThingHandler(ThingHandler handler) {
+        if (handler instanceof UniFiSiteThingHandler) {
+            this.handler = (UniFiSiteThingHandler) handler;
+        }
+    }
+
+    @Override
+    public @Nullable ThingHandler getThingHandler() {
+        return handler;
+    }
+}
index 18467f9e6ed2f361eeaf63600f592a2cd0320870..01995f48727d5e2f6b59b800e1e5bf9bfe8c5063 100644 (file)
@@ -215,7 +215,7 @@ public class UniFiController {
         refresh();
     }
 
-    public void generateGuestVouchers(final UniFiSite site, final int count, final int expiration, final int users,
+    public void generateVouchers(final UniFiSite site, final int count, final int expiration, final int users,
             @Nullable Integer upLimit, @Nullable Integer downLimit, @Nullable Integer dataQuota) throws UniFiException {
         final UniFiControllerRequest<Void> req = newRequest(Void.class, HttpMethod.POST, gson);
         req.setAPIPath(String.format("/api/s/%s/cmd/hotspot", site.getName()));
@@ -236,6 +236,17 @@ public class UniFiController {
         refresh();
     }
 
+    public void revokeVouchers(final UniFiSite site, final List<UniFiVoucher> vouchers) throws UniFiException {
+        for (UniFiVoucher voucher : vouchers) {
+            final UniFiControllerRequest<Void> req = newRequest(Void.class, HttpMethod.POST, gson);
+            req.setAPIPath(String.format("/api/s/%s/cmd/hotspot", site.getName()));
+            req.setBodyParameter("cmd", "delete-voucher");
+            req.setBodyParameter("_id", voucher.getId());
+            executeRequest(req);
+        }
+        refresh();
+    }
+
     // Internal API
 
     private <T> UniFiControllerRequest<T> newRequest(final Class<T> responseType, final HttpMethod method,
index 5a62f6e2b75649467662787759e10b4cfd0b50a9..55d581fd3134bf8997c9e60b30d18a442b0fa23f 100644 (file)
@@ -40,8 +40,11 @@ public class UniFiVoucher implements HasId {
     private Integer duration;
     private Integer quota;
     private Integer used;
-    private boolean qosOverwrite;
     private Integer qosUsageQuota;
+    private Integer qosRateMaxUp;
+    private Integer qosRateMaxDown;
+    private boolean qosOverwrite;
+    private String note;
     private String status;
 
     public UniFiVoucher(final UniFiControllerCache cache) {
@@ -73,12 +76,24 @@ public class UniFiVoucher implements HasId {
         return used;
     }
 
+    public Integer getQosUsageQuota() {
+        return qosUsageQuota;
+    }
+
+    public Integer getQosRateMaxUp() {
+        return qosRateMaxUp;
+    }
+
+    public Integer getQosRateMaxDown() {
+        return qosRateMaxDown;
+    }
+
     public boolean isQosOverwrite() {
         return qosOverwrite;
     }
 
-    public Integer getQosUsageQuota() {
-        return qosUsageQuota;
+    public String getNote() {
+        return note;
     }
 
     public String getStatus() {
@@ -92,7 +107,9 @@ public class UniFiVoucher implements HasId {
     @Override
     public String toString() {
         return String.format(
-                "UniFiVoucher{id: '%s', code: '%s', created: '%s', duration: '%s', quota: '%s', used: '%s', qosUsageQuota: '%s', status: '%s', site: %s}",
-                id, code, createTime, duration, quota, used, qosUsageQuota, status, getSite());
+                "UniFiVoucher{id: '%s', code: '%s', created: '%s', duration: '%s', quota: '%s', used: '%s', qosUsageQuota: '%s', "
+                        + "qosRateMaxUp: '%s', qosRateMaxDown: '%s', qosOverwrite: '%s', note: '%s', status: '%s', site: %s}",
+                id, code, createTime, duration, quota, used, qosUsageQuota, qosRateMaxUp, qosRateMaxDown, qosOverwrite,
+                note, status, getSite());
     }
 }
index 6d089732c9e4b41484b03fdc8ada6bd3479a7f31..ba63f3e5f67a19d204b8109c36fcb6f8461b4418 100644 (file)
@@ -12,8 +12,7 @@
  */
 package org.openhab.binding.unifi.internal.handler;
 
-import static org.openhab.core.thing.ThingStatus.OFFLINE;
-import static org.openhab.core.thing.ThingStatus.ONLINE;
+import static org.openhab.core.thing.ThingStatus.*;
 import static org.openhab.core.types.RefreshType.REFRESH;
 
 import java.lang.reflect.ParameterizedType;
@@ -81,7 +80,7 @@ public abstract class UniFiBaseThingHandler<E, C> extends BaseThingHandler {
      * @return
      */
     @SuppressWarnings("null")
-    private final @Nullable UniFiController getController() {
+    public final @Nullable UniFiController getController() {
         final Bridge bridge = getBridge();
         if (bridge != null && bridge.getHandler() != null
                 && (bridge.getHandler() instanceof UniFiControllerThingHandler)) {
@@ -90,7 +89,8 @@ public abstract class UniFiBaseThingHandler<E, C> extends BaseThingHandler {
         return null;
     }
 
-    private @Nullable E getEntity() {
+    @Nullable
+    public E getEntity() {
         final UniFiController controller = getController();
         return controller == null ? null : getEntity(controller.getCache());
     }
index 5234fc49134d5ce6a178a33285964abf56f1f758..7ec1c0d1fa2b10545e6b183865a3c415dc91b867 100644 (file)
  */
 package org.openhab.binding.unifi.internal.handler;
 
-import static org.openhab.binding.unifi.internal.UniFiBindingConstants.CHANNEL_GUEST_CLIENTS;
-import static org.openhab.binding.unifi.internal.UniFiBindingConstants.CHANNEL_GUEST_VOUCHER;
-import static org.openhab.binding.unifi.internal.UniFiBindingConstants.CHANNEL_GUEST_VOUCHERS_GENERATE;
-import static org.openhab.binding.unifi.internal.UniFiBindingConstants.CHANNEL_TOTAL_CLIENTS;
-import static org.openhab.binding.unifi.internal.UniFiBindingConstants.CHANNEL_WIRED_CLIENTS;
-import static org.openhab.binding.unifi.internal.UniFiBindingConstants.CHANNEL_WIRELESS_CLIENTS;
+import static org.openhab.binding.unifi.internal.UniFiBindingConstants.*;
 
+import java.util.Collection;
+import java.util.Collections;
 import java.util.function.Predicate;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.unifi.internal.UniFiSiteThingConfig;
 import org.openhab.binding.unifi.internal.UniFiVoucherChannelConfig;
+import org.openhab.binding.unifi.internal.action.UniFiSiteActions;
 import org.openhab.binding.unifi.internal.api.UniFiController;
 import org.openhab.binding.unifi.internal.api.UniFiException;
 import org.openhab.binding.unifi.internal.api.cache.UniFiControllerCache;
@@ -38,6 +36,7 @@ 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.ThingHandlerService;
 import org.openhab.core.types.Command;
 import org.openhab.core.types.State;
 import org.openhab.core.types.UnDefType;
@@ -127,9 +126,14 @@ public class UniFiSiteThingHandler extends UniFiBaseThingHandler<UniFiSite, UniF
             final Integer upLimit = config.getUpLimit();
             final Integer downLimit = config.getDownLimit();
             final Integer dataQuota = config.getDataQuota();
-            controller.generateGuestVouchers(entity, count, expire, users, upLimit, downLimit, dataQuota);
+            controller.generateVouchers(entity, count, expire, users, upLimit, downLimit, dataQuota);
             return true;
         }
         return false;
     }
+
+    @Override
+    public Collection<Class<? extends ThingHandlerService>> getServices() {
+        return Collections.singleton(UniFiSiteActions.class);
+    }
 }
index ee1b74edaa9fb424d3832deddb13fc6e0734e967..d4829cc3a53143402192322a3275d7c55488e487 100644 (file)
@@ -150,6 +150,19 @@ channel-type.config.unifi.poeEnable.mode.description = The value to set when set
 channel-type.config.unifi.poeEnable.mode.option.auto = Auto
 channel-type.config.unifi.poeEnable.mode.option.pasv24 = 24V
 channel-type.config.unifi.poeEnable.mode.option.passthrough = Passthrough
+channel-type.config.unifi.guestVouchersGenerate.voucherCount.label = Number
+channel-type.config.unifi.guestVouchersGenerate.voucherCount.description = Number of vouchers to create
+channel-type.config.unifi.guestVouchersGenerate.voucherExpiration.label = Expiration Time
+channel-type.config.unifi.guestVouchersGenerate.voucherExpiration.description = Minutes a voucher is valid after activation
+channel-type.config.unifi.guestVouchersGenerate.voucherUsers.label = Users
+channel-type.config.unifi.guestVouchersGenerate.voucherUsers.description = Number of users for voucher, 0 if no limit
+channel-type.config.unifi.guestVouchersGenerate.voucherUpLimit.label = Upload Speed Limit
+channel-type.config.unifi.guestVouchersGenerate.voucherUpLimit.description = Upload speed limit in kbps, no limit if not set
+channel-type.config.unifi.guestVouchersGenerate.voucherDownLimit.label = Download Speed Limit
+channel-type.config.unifi.guestVouchersGenerate.voucherDownLimit.description = Download speed limit in kbps, no limit if not set
+channel-type.config.unifi.guestVouchersGenerate.voucherDataQuota.label = Data Transfer Quota
+channel-type.config.unifi.guestVouchersGenerate.voucherDataQuota.description = Data transfer quota in MB per user, no limit if not set
+channel-type.config.unifi.guestVouchersGenerate.option.GENERATE = Generate
 
 # status messages
 
@@ -164,3 +177,16 @@ error.thing.offline.configuration_error = You must choose a UniFi Controller for
 error.thing.poe.offline.configuration_error = The configuration parameter macAddress must be set and not be empty.
 error.thing.poe.offline.nodata_error = No data for the PoE port could be found in the UniFi API data. See TRACE log for actual API data.
 error.thing.site.offline.configuration_error = The configuration parameter sid must be set and not be empty.
+
+# actions
+
+action.unifi.generateVouchers.label = Generate
+action.unifi.generateVouchers.description = Generate guest voucher(s)
+action.unifi.revokeAllVouchers.label = Revoke All
+action.unifi.revokeAllVouchers.description = Revoke all guest vouchers
+action.unifi.revokeVouchers.label = Revoke
+action.unifi.revokeVouchers.description = Revoke guest voucher(s)
+action.unifi.vouchersInputVoucherCodes.label = Voucher Code(s)
+action.unifi.vouchersInputVoucherCodes.description = Code(s) of voucher(s) to revoke
+action.unifi.listVouchers.label = List Vouchers
+action.unifi.listVouchers.description = List available vouchers with their parameters and status