[epsonprojector] Add ESC/VP.net handshake for projectors with built-in ethernet (#9375)
Signed-off-by: Michael Lobstein <michael.lobstein@gmail.com>
This commit is contained in:
parent
750ea5fd2d
commit
6c86f8d366
|
@ -1,7 +1,7 @@
|
|||
# Epson Projector Binding
|
||||
|
||||
This binding is compatible with Epson projectors that support the ESC/VP21 protocol over a serial port or USB to serial adapter.
|
||||
Alternatively, you can connect to your projector via a TCP connection using a serial over IP device or by using`ser2net`.
|
||||
This binding is compatible with Epson projectors that support the ESC/VP21 protocol over the built-in ethernet port, serial port or USB to serial adapter.
|
||||
If your projector does not have a built-in ethernet port, you can connect to your projector's serial port via a TCP connection using a serial over IP device or by using`ser2net`.
|
||||
|
||||
## Supported Things
|
||||
|
||||
|
@ -9,7 +9,8 @@ This binding supports two thing types based on the connection used: `projector-s
|
|||
|
||||
## Discovery
|
||||
|
||||
The projector thing cannot be auto-discovered, it has to be configured manually.
|
||||
If the projector has a built-in ethernet port connected to the same network as the openHAB server and the 'AMX Device Discovery' option is present and enabled in the projector's network menu, the thing will be discovered automatically.
|
||||
Serial port or serial over IP connections must be configured manually.
|
||||
|
||||
## Binding Configuration
|
||||
|
||||
|
@ -25,8 +26,8 @@ The `projector-serial` thing has the following configuration parameters:
|
|||
|
||||
The `projector-tcp` thing has the following configuration parameters:
|
||||
|
||||
- _host_: IP address for the serial over IP device
|
||||
- _port_: Port for the serial over IP device
|
||||
- _host_: IP address for the projector or serial over IP device
|
||||
- _port_: Port for the projector or serial over IP device; default 3629 for projectors with built-in ethernet connector
|
||||
- _pollingInterval_: Polling interval in seconds to update channel states | 5-60 seconds; default 10 seconds
|
||||
|
||||
Some notes:
|
||||
|
@ -38,6 +39,7 @@ Some notes:
|
|||
* The following channels _aspectratio_, _colormode_, _luminance_, _gamma_ and _background_ are pre-populated with a full set of options and not every option will be useable on all projectors.
|
||||
* If your projector has an option in one of the above mentioned channels that is not recognized by the binding, the channel will display 'UNKNOWN' if that un-recognized option is selected by the remote control.
|
||||
* If the projector power is switched to off in the middle of a polling operation, some of the channel values may become undefined until the projector is switched on again.
|
||||
* If the binding fails to connect to the projector using the direct IP connection, ensure that no password is configured on the projctor.
|
||||
|
||||
* On Linux, you may get an error stating the serial port cannot be opened when the epsonprojector binding tries to load.
|
||||
* You can get around this by adding the `openhab` user to the `dialout` group like this: `usermod -a -G dialout openhab`.
|
||||
|
@ -86,11 +88,11 @@ Some notes:
|
|||
things/epson.things:
|
||||
|
||||
```java
|
||||
//serial port connection
|
||||
// serial port connection
|
||||
epsonprojector:projector-serial:hometheater "Projector" [ serialPort="COM5", pollingInterval=10 ]
|
||||
|
||||
// serial over IP connection
|
||||
epsonprojector:projector-tcp:hometheater "Projector" [ host="192.168.0.10", port=4444, pollingInterval=10 ]
|
||||
// direct IP or serial over IP connection
|
||||
epsonprojector:projector-tcp:hometheater "Projector" [ host="192.168.0.10", port=3629, pollingInterval=10 ]
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
*/
|
||||
package org.openhab.binding.epsonprojector.internal;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
|
@ -20,18 +22,28 @@ import org.openhab.core.thing.ThingTypeUID;
|
|||
* used across the whole binding.
|
||||
*
|
||||
* @author Yannick Schaus - Initial contribution
|
||||
* @author Michael Lobstein - Updated for OH3
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class EpsonProjectorBindingConstants {
|
||||
|
||||
private static final String BINDING_ID = "epsonprojector";
|
||||
public static final String BINDING_ID = "epsonprojector";
|
||||
public static final int DEFAULT_PORT = 3629;
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_PROJECTOR_SERIAL = new ThingTypeUID(BINDING_ID, "projector-serial");
|
||||
public static final ThingTypeUID THING_TYPE_PROJECTOR_TCP = new ThingTypeUID(BINDING_ID, "projector-tcp");
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_PROJECTOR_SERIAL,
|
||||
THING_TYPE_PROJECTOR_TCP);
|
||||
|
||||
// Some Channel types
|
||||
public static final String CHANNEL_TYPE_POWER = "power";
|
||||
public static final String CHANNEL_TYPE_POWERSTATE = "powerstate";
|
||||
public static final String CHANNEL_TYPE_LAMPTIME = "lamptime";
|
||||
|
||||
// Config properties
|
||||
public static final String THING_PROPERTY_HOST = "host";
|
||||
public static final String THING_PROPERTY_PORT = "port";
|
||||
public static final String THING_PROPERTY_MAC = "macAddress";
|
||||
}
|
||||
|
|
|
@ -137,12 +137,13 @@ public class EpsonProjectorDevice {
|
|||
return response;
|
||||
}
|
||||
|
||||
private String splitResponse(@Nullable String response) throws EpsonProjectorException {
|
||||
private String splitResponse(@Nullable String response)
|
||||
throws EpsonProjectorCommandException, EpsonProjectorException {
|
||||
if (response != null && !"".equals(response)) {
|
||||
String[] pieces = response.split("=");
|
||||
|
||||
if (pieces.length < 2) {
|
||||
throw new EpsonProjectorException("Invalid response from projector: " + response);
|
||||
throw new EpsonProjectorCommandException("Invalid response from projector: " + response);
|
||||
}
|
||||
|
||||
return pieces[1].trim();
|
||||
|
|
|
@ -14,11 +14,6 @@ package org.openhab.binding.epsonprojector.internal;
|
|||
|
||||
import static org.openhab.binding.epsonprojector.internal.EpsonProjectorBindingConstants.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
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.epsonprojector.internal.handler.EpsonProjectorHandler;
|
||||
|
@ -42,9 +37,6 @@ import org.osgi.service.component.annotations.Reference;
|
|||
@NonNullByDefault
|
||||
@Component(configurationPid = "binding.epsonprojector", service = ThingHandlerFactory.class)
|
||||
public class EpsonProjectorHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet(
|
||||
Stream.of(THING_TYPE_PROJECTOR_SERIAL, THING_TYPE_PROJECTOR_TCP).collect(Collectors.toSet()));
|
||||
private final SerialPortManager serialPortManager;
|
||||
|
||||
@Override
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
*/
|
||||
package org.openhab.binding.epsonprojector.internal.connector;
|
||||
|
||||
import static org.openhab.binding.epsonprojector.internal.EpsonProjectorBindingConstants.DEFAULT_PORT;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
@ -32,6 +34,7 @@ import org.slf4j.LoggerFactory;
|
|||
*/
|
||||
@NonNullByDefault
|
||||
public class EpsonProjectorTcpConnector implements EpsonProjectorConnector {
|
||||
private static final String ESC_VP_HANDSHAKE = "ESC/VP.net\u0010\u0003\u0000\u0000\u0000\u0000";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(EpsonProjectorTcpConnector.class);
|
||||
private final String ip;
|
||||
|
@ -58,6 +61,16 @@ public class EpsonProjectorTcpConnector implements EpsonProjectorConnector {
|
|||
} catch (IOException e) {
|
||||
throw new EpsonProjectorException(e);
|
||||
}
|
||||
|
||||
// Projectors with built in Ethernet listen on 3629, we must send the handshake to initialize the connection
|
||||
if (port == DEFAULT_PORT) {
|
||||
try {
|
||||
String response = sendMessage(ESC_VP_HANDSHAKE, 5000);
|
||||
logger.debug("Response to initialisation of ESC/VP.net is: {}", response);
|
||||
} catch (EpsonProjectorException e) {
|
||||
logger.debug("Error within initialisation of ESC/VP.net: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
/**
|
||||
* 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.epsonprojector.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.epsonprojector.internal.EpsonProjectorBindingConstants.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.net.NetworkAddressService;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link EpsonProjectoreDiscoveryService} class implements a service
|
||||
* for discovering Epson projectors using the AMX Device Discovery protocol.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
* @author Michael Lobstein - Adapted for the Epson Projector binding
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = DiscoveryService.class, configurationPid = "discovery.epsonprojector")
|
||||
public class EpsonProjectorDiscoveryService extends AbstractDiscoveryService {
|
||||
private final Logger logger = LoggerFactory.getLogger(EpsonProjectorDiscoveryService.class);
|
||||
private @Nullable ScheduledFuture<?> epsonDiscoveryJob;
|
||||
|
||||
// Discovery parameters
|
||||
public static final boolean BACKGROUND_DISCOVERY_ENABLED = true;
|
||||
public static final int BACKGROUND_DISCOVERY_DELAY_TIMEOUT_SEC = 10;
|
||||
|
||||
private NetworkAddressService networkAddressService;
|
||||
|
||||
private boolean terminate = false;
|
||||
|
||||
@Activate
|
||||
public EpsonProjectorDiscoveryService(@Reference NetworkAddressService networkAddressService) {
|
||||
super(SUPPORTED_THING_TYPES_UIDS, 0, BACKGROUND_DISCOVERY_ENABLED);
|
||||
this.networkAddressService = networkAddressService;
|
||||
epsonDiscoveryJob = null;
|
||||
terminate = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ThingTypeUID> getSupportedThingTypes() {
|
||||
return SUPPORTED_THING_TYPES_UIDS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startBackgroundDiscovery() {
|
||||
if (epsonDiscoveryJob == null) {
|
||||
terminate = false;
|
||||
logger.debug("Starting background discovery job in {} seconds", BACKGROUND_DISCOVERY_DELAY_TIMEOUT_SEC);
|
||||
epsonDiscoveryJob = scheduler.schedule(this::discover, BACKGROUND_DISCOVERY_DELAY_TIMEOUT_SEC,
|
||||
TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopBackgroundDiscovery() {
|
||||
ScheduledFuture<?> epsonDiscoveryJob = this.epsonDiscoveryJob;
|
||||
if (epsonDiscoveryJob != null) {
|
||||
terminate = true;
|
||||
epsonDiscoveryJob.cancel(false);
|
||||
this.epsonDiscoveryJob = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startScan() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopScan() {
|
||||
}
|
||||
|
||||
private synchronized void discover() {
|
||||
logger.debug("Discovery job is running");
|
||||
MulticastListener epsonMulticastListener;
|
||||
String local = "127.0.0.1";
|
||||
|
||||
try {
|
||||
String ip = networkAddressService.getPrimaryIpv4HostAddress();
|
||||
epsonMulticastListener = new MulticastListener((ip != null ? ip : local));
|
||||
} catch (SocketException se) {
|
||||
logger.debug("Discovery job got Socket exception creating multicast socket: {}", se.getMessage());
|
||||
return;
|
||||
} catch (IOException ioe) {
|
||||
logger.debug("Discovery job got IO exception creating multicast socket: {}", ioe.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
while (!terminate) {
|
||||
boolean beaconReceived;
|
||||
try {
|
||||
// Wait for a discovery beacon.
|
||||
beaconReceived = epsonMulticastListener.waitForBeacon();
|
||||
} catch (IOException ioe) {
|
||||
logger.debug("Discovery job got exception waiting for beacon: {}", ioe.getMessage());
|
||||
beaconReceived = false;
|
||||
}
|
||||
|
||||
if (beaconReceived) {
|
||||
// We got a discovery beacon. Process it as a potential new thing
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
String uid = epsonMulticastListener.getUID();
|
||||
|
||||
properties.put(THING_PROPERTY_HOST, epsonMulticastListener.getIPAddress());
|
||||
properties.put(THING_PROPERTY_PORT, DEFAULT_PORT);
|
||||
|
||||
logger.trace("Projector with UID {} discovered at IP: {}", uid, epsonMulticastListener.getIPAddress());
|
||||
|
||||
ThingUID thingUid = new ThingUID(THING_TYPE_PROJECTOR_TCP, uid);
|
||||
logger.trace("Creating epson projector discovery result for: {}, IP={}", uid,
|
||||
epsonMulticastListener.getIPAddress());
|
||||
thingDiscovered(DiscoveryResultBuilder.create(thingUid).withProperties(properties)
|
||||
.withLabel("Epson Projector " + uid).withProperty(THING_PROPERTY_MAC, uid)
|
||||
.withRepresentationProperty(THING_PROPERTY_MAC).build());
|
||||
}
|
||||
}
|
||||
epsonMulticastListener.shutdown();
|
||||
logger.debug("Discovery job is exiting");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
/**
|
||||
* 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.epsonprojector.internal.discovery;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.MulticastSocket;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link MulticastListener} class is responsible for listening for the Epson projector device announcement
|
||||
* beacons on the multicast address, and then extracting the data fields out of the received datagram.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
* @author Michael Lobstein - Adapted for the Epson Projector binding
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class MulticastListener {
|
||||
private final Logger logger = LoggerFactory.getLogger(MulticastListener.class);
|
||||
|
||||
private MulticastSocket socket;
|
||||
|
||||
// Epson-specific properties defined in this binding
|
||||
private String uid = "";
|
||||
private String ipAddress = "";
|
||||
|
||||
// Epson projector devices announce themselves on a multicast port
|
||||
private static final String EPSON_MULTICAST_GROUP = "239.255.250.250";
|
||||
private static final int EPSON_MULTICAST_PORT = 9131;
|
||||
|
||||
// How long to wait in milliseconds for a discovery beacon
|
||||
public static final int DEFAULT_SOCKET_TIMEOUT_SEC = 3000;
|
||||
|
||||
/*
|
||||
* Constructor joins the multicast group, throws IOException on failure.
|
||||
*/
|
||||
public MulticastListener(String ipv4Address) throws IOException, SocketException {
|
||||
InetAddress ifAddress = InetAddress.getByName(ipv4Address);
|
||||
logger.debug("Discovery job using address {} on network interface {}", ifAddress.getHostAddress(),
|
||||
NetworkInterface.getByInetAddress(ifAddress).getName());
|
||||
socket = new MulticastSocket(EPSON_MULTICAST_PORT);
|
||||
socket.setInterface(ifAddress);
|
||||
socket.setSoTimeout(DEFAULT_SOCKET_TIMEOUT_SEC);
|
||||
InetAddress mcastAddress = InetAddress.getByName(EPSON_MULTICAST_GROUP);
|
||||
socket.joinGroup(mcastAddress);
|
||||
logger.debug("Multicast listener joined multicast group {}:{}", EPSON_MULTICAST_GROUP, EPSON_MULTICAST_PORT);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
logger.debug("Multicast listener closing down multicast socket");
|
||||
socket.close();
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait on the multicast socket for an announcement beacon. Return false on socket timeout or error.
|
||||
* Otherwise, parse the beacon for information about the device.
|
||||
*/
|
||||
public boolean waitForBeacon() throws IOException {
|
||||
byte[] bytes = new byte[600];
|
||||
boolean beaconFound;
|
||||
|
||||
// Wait for a device to announce itself
|
||||
logger.trace("Multicast listener waiting for datagram on multicast port");
|
||||
DatagramPacket msgPacket = new DatagramPacket(bytes, bytes.length);
|
||||
try {
|
||||
socket.receive(msgPacket);
|
||||
beaconFound = true;
|
||||
logger.trace("Multicast listener got datagram of length {} from multicast port: {}", msgPacket.getLength(),
|
||||
msgPacket.toString());
|
||||
} catch (SocketTimeoutException e) {
|
||||
beaconFound = false;
|
||||
}
|
||||
|
||||
if (beaconFound) {
|
||||
// Get the device properties from the announcement beacon
|
||||
parseAnnouncementBeacon(msgPacket);
|
||||
}
|
||||
|
||||
return beaconFound;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the announcement beacon into the elements needed to create the thing.
|
||||
*
|
||||
* Example Epson beacon:
|
||||
* AMXB<-UUID=000048746B33><-SDKClass=VideoProjector><-GUID=EPSON_EMP001><-Revision=1.0.0>
|
||||
*/
|
||||
private void parseAnnouncementBeacon(DatagramPacket packet) {
|
||||
String beacon = (new String(packet.getData(), StandardCharsets.UTF_8)).trim();
|
||||
|
||||
logger.trace("Multicast listener parsing announcement packet: {}", beacon);
|
||||
|
||||
clearProperties();
|
||||
|
||||
if (beacon.toUpperCase().contains("EPSON") && beacon.toUpperCase().contains("VIDEOPROJECTOR")) {
|
||||
ipAddress = packet.getAddress().getHostAddress();
|
||||
parseEpsonAnnouncementBeacon(beacon);
|
||||
} else {
|
||||
logger.debug("Multicast listener doesn't know how to parse beacon: {}", beacon);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseEpsonAnnouncementBeacon(String beacon) {
|
||||
String[] parameterList = beacon.split("<-");
|
||||
|
||||
for (String parameter : parameterList) {
|
||||
String[] keyValue = parameter.split("=");
|
||||
|
||||
if (keyValue.length != 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (keyValue[0].contains("UUID")) {
|
||||
uid = keyValue[1].substring(0, keyValue[1].length() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void clearProperties() {
|
||||
uid = "";
|
||||
ipAddress = "";
|
||||
}
|
||||
|
||||
public String getUID() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
public String getIPAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
}
|
|
@ -56,7 +56,8 @@
|
|||
|
||||
<thing-type id="projector-tcp">
|
||||
<label>Epson Projector - TCP/IP</label>
|
||||
<description>An Epson projector which supports the ESC/VP21 protocol via a serial over IP connection</description>
|
||||
<description>An Epson projector which supports the ESC/VP21 protocol via the built-in ethernet port or a serial over
|
||||
IP connection</description>
|
||||
|
||||
<channels>
|
||||
<channel id="power" typeId="power"/>
|
||||
|
@ -89,15 +90,18 @@
|
|||
<channel id="errmessage" typeId="errmessage"/>
|
||||
</channels>
|
||||
|
||||
<representation-property>macAddress</representation-property>
|
||||
|
||||
<config-description>
|
||||
<parameter name="host" type="text" required="true">
|
||||
<label>Host</label>
|
||||
<context>network-address</context>
|
||||
<description>IP address for the serial over IP device</description>
|
||||
<description>IP address for the projector or serial over IP device</description>
|
||||
</parameter>
|
||||
<parameter name="port" type="integer" min="1" max="65535" required="true">
|
||||
<label>Port</label>
|
||||
<description>Port for the serial over IP device</description>
|
||||
<description>Port for the projector or serial over IP device</description>
|
||||
<default>3629</default>
|
||||
</parameter>
|
||||
<parameter name="pollingInterval" type="integer" min="5" max="60" unit="s" required="false">
|
||||
<label>Polling interval</label>
|
||||
|
|
Loading…
Reference in New Issue