mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[GPIO] Update GPIO binding to fix issues and provide new functionality (#13643)
* [GPIO] Update the GPIO binding to fix issues and provide new functionality. * Add the ability to recover from network disconnects to pigpiod. * Provide actions to what the binding does on pigpiod connect/disconnect/reconnect. * Provide the ability to pulse outputs in a one-shot/momentary fashion. * Provide edge level configuration for gpio outputs. * Fix automated style checks. * Attempt to squash jenkins build warnings/errors. * Correct Null annotations and adjust log levels in certain areas. * Fix bracing per checkstyle review. * Normalize gpiod/pigpiod to pigpiod. openhab/openHAB normalized to OpenHAB. * Fix a copy/paste error. Output channels should not be referred as input. Attempt to clarify plurals. * Convert strings to defined constants. * Convert pulse command strings to binding constants. * Java17 instanceof pattern matching. * Nit, fix missed pulse command string to binding constant. * Add missing quotes in demo.things file definition. Workaround #11039 Fixes #11038 Fixes #13376 Signed-off-by: Jeremy Rumpf <rumpf.99@gmail.com>
This commit is contained in:
parent
597f01efe1
commit
b24f3a2feb
@ -1,19 +1,19 @@
|
||||
# GPIO Binding
|
||||
|
||||
This binding adds GPIO support via the pigpio daemon to openhab.
|
||||
It requires the pigpio (<http://abyz.me.uk/rpi/pigpio/>) to be running on the pi that should be controlled.
|
||||
This binding adds GPIO support via the pigpiod daemon to openHAB.
|
||||
It requires the pigpiod daemon (<http://abyz.me.uk/rpi/pigpio/>) to be installed on the pi that should be controlled.
|
||||
|
||||
## Supported Things
|
||||
|
||||
### pigpio-remote
|
||||
|
||||
This thing represents a remote pigpio instance running as daemon on a raspberry pi.
|
||||
This thing represents a remote pigpiod instance running as daemon on a raspberry pi.
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
### Pigpio Remote (`pigpio-remote`)
|
||||
|
||||
On a raspberry (or a compatible device) you have to install pigpio:
|
||||
On a raspberry (or a compatible device) you have to install pigpiod.
|
||||
|
||||
```shell
|
||||
sudo apt-get install pigpiod
|
||||
@ -39,71 +39,204 @@ ExecStart=/usr/bin/pigpiod
|
||||
sudo systemctl daemon-reload
|
||||
```
|
||||
|
||||
Now that Remote GPIO is enabled, get the daemon going (even if installed with apt-get):
|
||||
Now that Remote GPIO is enabled, get the pigpiod daemon going (even if installed with apt-get):
|
||||
|
||||
```shell
|
||||
sudo systemctl enable pigpiod
|
||||
sudo systemctl start pigpiod
|
||||
```
|
||||
|
||||
In openHAB, set `host` to the address of the pi and the `port` to the port of pigpio (default: 8888).
|
||||
## General Configuration
|
||||
|
||||
Note: If you are running Pigpio on same host as openHAB, then set host to **::1**.
|
||||
Binding general configuration options. If you do not see all options, ensure `Show Advanced` is selected.
|
||||
|
||||
### Host
|
||||
|
||||
Set `Host` to the address of the Pi that pigpiod is running on. Default is 127.0.0.1 (IPV4).
|
||||
Note: If you are running pigpiod on same host as openHAB, set the host to 127.0.0.1 (IPV4) or ::1 (IPV6).
|
||||
|
||||
### Port
|
||||
|
||||
Set `Port` to the network port that pigpiod is listening on. Default is 8888.
|
||||
|
||||
### Heart Beat Interval
|
||||
|
||||
The binding will poll pigpiod running on the Pi to determine if a network disconnect has occurred.
|
||||
This is the interval in milliseconds that the heart beat poll occurs. Defaults to 30000 (30 seconds).
|
||||
|
||||
## Input Channel Connect Action
|
||||
|
||||
Input Channel Connect Action determines what happens when the binding initially connects to pigpiod.
|
||||
This action only occurs once after binding startup.
|
||||
|
||||
- **Do Nothing:** The default, do nothing. Input channels will retain their default value (UNDEF).
|
||||
- **Refresh Channel:** Issues a refresh command on the input channels. This will refresh the channels from pigpiod causing the gpio pin state to reflect on the channel state.
|
||||
|
||||
Input Channel Disconnect Connect Action:
|
||||
|
||||
### Input Channel Disconnect Connect Action
|
||||
|
||||
Input Channel Disconnect Connect Action determines what happens when the binding disconnects from pigpiod.
|
||||
|
||||
- **Do Nothing:** The default, do nothing. Input channels will retain their current value.
|
||||
- **Set Undef:** Sets the input channel states to UNDEF to indicate that pigpiod has disconnected.
|
||||
|
||||
### Input Channel Reconnect Connect Action
|
||||
|
||||
Input Channel Reconnect Action determines what happens when the binding reconnects to pigpiod
|
||||
after a disconnect. This action does not occur on the initial binding connect to pigpiod.
|
||||
startup.
|
||||
|
||||
- **Do Nothing:** The default, do nothing. Input channels will retain their current value.
|
||||
- **Refresh Channel:** Issues a refresh command on the input channels. This will refresh the channels from
|
||||
pigpiod causing the gpio pin state to reflect on the channel state.
|
||||
|
||||
### Output Channel Connect Action
|
||||
|
||||
Output Channel Connect Action determines what happens when the binding initially connects to pigpiod.
|
||||
This action only occurs once after binding startup.
|
||||
|
||||
- **Do Nothing:** The default, do nothing. Output channels will retain their default value (UNDEF).
|
||||
- **All On:** Issues a ON command to all configured output channels.
|
||||
- **All Off:** Issues a OFF command to all configured output channels.
|
||||
- **Refresh Channel:** Issues a refresh command on the output channels. This will refresh the channels from
|
||||
pigpiod causing the gpio pin state to reflect on the channel state. NOTE: This does
|
||||
not update the gpio pin state on the Pi itself. It only updates the channel state
|
||||
within openHAB.
|
||||
|
||||
### Output Channel Disconnect Connect Action
|
||||
|
||||
Output Channel Disconnect Connect Action determines what happens when the binding disconnects from pigpiod.
|
||||
|
||||
- **Do Nothing:** he default, do nothing. Input channels will retain their current value.
|
||||
- **Set Undef:** Sets the output channel states to UNDEF to indicate that pigpiod has disconnected.
|
||||
|
||||
### Output Channel Reconnect Connect Action
|
||||
|
||||
Output Channel Reconnect Action determines what happens when the binding reconnects to pigpiod
|
||||
after a disconnect. This action does not occur on the initial binding connect to pigpiod.
|
||||
|
||||
- **Do Nothing:** The default, do nothing. Output channels will retain their current value.
|
||||
- **Refresh Channel:** Issues a refresh command on the output channels. This will refresh the channels from
|
||||
pigpiod causing the gpio pin state to reflect on the channel state. NOTE: This does
|
||||
not update the gpio pin state on the Pi itself. It only updates the channel state
|
||||
within openHAB.
|
||||
|
||||
## Channels
|
||||
|
||||
### Pigpio Remote
|
||||
The binding has two channel types.
|
||||
One for gpio input pins, and another for gpio output pins.
|
||||
|
||||
| channel | type | description |
|
||||
|-----------------------|--------|---------------------------------|
|
||||
| pigpio-digital-input | Switch | Read-only value of the gpio pin |
|
||||
| pigpio-digital-output | Switch | Controls the gpio pin |
|
||||
|
||||
### GPIO digital input channel
|
||||
### GPIO pigpio-digital-input channel configuration
|
||||
|
||||
Set the number of the pin in `gpioId`.
|
||||
If you want to invert the value, set `invert` to true.
|
||||
To prevent incorrect change events, you can adjust the `debouncingTime`.
|
||||
Using `pullupdown` you can enable pull up or pull down resistor (OFF = Off, DOWN = Pull Down, UP = Pull Up).
|
||||
Input channels provide a read-only value of the gpio pin state using the `OnOffType` datatype.
|
||||
|
||||
### GPIO digital output channel
|
||||
GPIO Pin:
|
||||
|
||||
Set the number of the pin in `gpioId`.
|
||||
If you want to invert the value, set `invert` to true.
|
||||
The gpio pin number on the Pi that the channel will monitor.
|
||||
|
||||
## Full Example
|
||||
Invert:
|
||||
|
||||
Inverts the value of the gpio pin before reflecting the value on the channel.
|
||||
Useful for active low gpio pins where you want the channel state to reflect an ON value when the pin is low (OFF).
|
||||
|
||||
Delay Time:
|
||||
|
||||
Sets a delay value in milliseconds that the gpio pin must remain at prior to updating the channel state.
|
||||
This is the same as switch debouncing or hysteresis.
|
||||
Default value is 10 milliseconds.
|
||||
Increase this value for noisy inputs.
|
||||
|
||||
Pull Up/Down Resistor:
|
||||
|
||||
Sets the mode of operation for the internal pull up/ down resistor on the gpio pin.
|
||||
Set this to OFF if you use external pull up/down resistors.
|
||||
|
||||
Edge Detection Mode:
|
||||
|
||||
Sets the mode of operation for the pin edge detection mode.
|
||||
If you are not sure what the use case is for this, leave at the default value of `Either Edge`.
|
||||
This is the most common mode that gpio inputs are used.
|
||||
|
||||
### GPIO pigpio-digital-output channel configuration
|
||||
|
||||
Output channels provide a means of controlling the output value of the gpio pin using the `OnOffType` datatype.
|
||||
|
||||
GPIO Pin:
|
||||
|
||||
The gpio pin number on the Pi that the channel will control.
|
||||
|
||||
Invert:
|
||||
|
||||
Inverts the value of the channel state before commanding the gpio pin.
|
||||
Useful to simulate active low gpio pins.
|
||||
|
||||
Pulse:
|
||||
|
||||
Time in milliseconds that must elapse before the Pulse Command is sent to the channel.
|
||||
Default value is 0, which disables the Pulse feature.
|
||||
|
||||
Pulse Command:
|
||||
|
||||
Together with the Pulse configuration, can be used to create a one shot or momentary output.
|
||||
This is useful to simulate momentary button presses or to drive motors for a predefined amount
|
||||
of time.
|
||||
|
||||
- **Off:** When the ON command is issued to the channel. The Pulse feature will send an OFF command
|
||||
after the Pulse duration.
|
||||
- **On:** When the OFF command is issued to the channel. The Pulse feature will send an
|
||||
ON command after the Pulse duration.
|
||||
- **Blink:** Cycles the channel ON, OFF, ON indefinitely with a 50% duty cycle. The Blink
|
||||
operation continues regardless of the commanded channel state. This was originaly
|
||||
developed as a way to flash a status LED to visually confirm that a remote pigpiod
|
||||
instance has connectivity to openHAB.
|
||||
|
||||
## Config file example
|
||||
|
||||
Example for users who still prefer configuration files.
|
||||
|
||||
demo.things:
|
||||
|
||||
```java
|
||||
Thing gpio:pigpio-remote:sample-pi-1 "Sample-Pi 1" [host="192.168.2.36", port=8888] {
|
||||
Thing gpio:pigpio-remote:mypi "MyPi GPIO" [ host="192.168.1.5", port=8888,
|
||||
heartBeatInterval=10000,
|
||||
inputConnectAction="REFRESH", # REFRESH,NOTHING
|
||||
inputDisconnectAction="NOTHING", # SETUNDEF,NOTHING
|
||||
inputReconnectAction="REFRESH", # REFRESH,NOTHING
|
||||
outputConnectAction="REFRESH", # ALLOFF,ALLON,REFRESH,NOTHING
|
||||
outputDisconnectAction="SETUNDEF", # SETUNDEF,NOTHING
|
||||
outputReconnectAction="REFRESH" ] # REFRESH,NOTHING
|
||||
{
|
||||
Channels:
|
||||
Type pigpio-digital-input : sample-input-1 [ gpioId=10]
|
||||
Type pigpio-digital-input : sample-input-2 [ gpioId=14, invert=true]
|
||||
Type pigpio-digital-output : sample-output-1 [ gpioId=3]
|
||||
}
|
||||
Type pigpio-digital-output : BCM18 [ gpioId=18,invert=false,pulse=3000,pulseCommand="BLINK" ] # OFF,ON,BLINK
|
||||
|
||||
Thing gpio:pigpio-remote:sample-pi-2 "Sample-Pi 2" [host="192.168.2.37", port=8888] {
|
||||
Channels:
|
||||
Type pigpio-digital-input : sample-input-3 [ gpioId=16, debouncingTime=20]
|
||||
Type pigpio-digital-input : sample-input-4 [ gpioId=17, invert=true, debouncingTime=5, pullupdown="UP"]
|
||||
Type pigpio-digital-output : sample-output-2 [ gpioId=4, invert=true]
|
||||
}
|
||||
Type pigpio-digital-output : GPO4 [ gpioId=4, invert=true,pulse=5000,pulseCommand="OFF" ]
|
||||
Type pigpio-digital-output : GPO17 [ gpioId=17,invert=false,pulse=500,pulseCommand="ON" ]
|
||||
Type pigpio-digital-output : GPO27 [ gpioId=27,invert=false ]
|
||||
Type pigpio-digital-output : GPO22 [ gpioId=22,invert=true ]
|
||||
|
||||
Type pigpio-digital-input : GPI23 [ gpioId=23,debouncingTime=50,pullupdown="UP",invert=true ] # OFF,DOWN,UP
|
||||
Type pigpio-digital-input : GPI24 [ gpioId=24,debouncingTime=50,pullupdown="UP",invert=true ]
|
||||
Type pigpio-digital-input : GPI25 [ gpioId=25,debouncingTime=50,pullupdown="UP",invert=true ]
|
||||
Type pigpio-digital-input : GPI12 [ gpioId=12,debouncingTime=50,pullupdown="UP",invert=true ]
|
||||
Type pigpio-digital-input : GPI16 [ gpioId=16,debouncingTime=50,pullupdown="UP",invert=true ]
|
||||
Type pigpio-digital-input : GPI20 [ gpioId=20,debouncingTime=50,pullupdown="UP",invert=true,edgeMode="EDGE_EITHER" ] # EITHER,RISING,FALLING
|
||||
Type pigpio-digital-input : GPI21 [ gpioId=21,debouncingTime=50,pullupdown="UP",invert=true,edgeMode="EDGE_RISING" ]
|
||||
Type pigpio-digital-input : GPI5 [ gpioId=5, debouncingTime=50,pullupdown="UP",invert=true,edgeMode="EDGE_FALLING" ]
|
||||
Type pigpio-digital-input : GPI6 [ gpioId=6, debouncingTime=50,pullupdown="UP",invert=true ]
|
||||
Type pigpio-digital-input : GPI13 [ gpioId=13,debouncingTime=50,pullupdown="DOWN",invert=false ]
|
||||
Type pigpio-digital-input : GPI26 [ gpioId=26,debouncingTime=50,pullupdown="OFF",invert=false ]
|
||||
}
|
||||
```
|
||||
|
||||
demo.items:
|
||||
|
||||
```java
|
||||
Switch SampleInput1 {channel="gpio:pigpio-remote:sample-pi-1:sample-input-1"}
|
||||
Switch SampleOutput1 {channel="gpio:pigpio-remote:sample-pi-1:sample-output-1"}
|
||||
```
|
||||
|
||||
demo.sitemap:
|
||||
|
||||
```perl
|
||||
sitemap demo label="Main Menu"
|
||||
{
|
||||
Switch item=SampleInput1
|
||||
Switch item=SampleOutput1
|
||||
}
|
||||
Switch SampleInput1 {channel="gpio:pigpio-remote:mypi:GPI23"}
|
||||
Switch SampleOutput1 {channel="gpio:pigpio-remote:mypi:GPO4"}
|
||||
```
|
||||
|
@ -15,11 +15,15 @@ package org.openhab.binding.gpio.internal;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Is thrown when invalid GPIO pin Pull Up/Down resistor configuration is set
|
||||
* Is thrown when a channel configuration is invalid
|
||||
*
|
||||
* @author Martin Dagarin - Initial contribution
|
||||
* @author Jeremy Rumpf - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class InvalidPullUpDownException extends Exception {
|
||||
public class ChannelConfigurationException extends Exception {
|
||||
private static final long serialVersionUID = -1281107134439928767L;
|
||||
|
||||
public ChannelConfigurationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
*
|
||||
* @author Nils Bauer - Initial contribution
|
||||
* @author Martin Dagarin - Pull Up/Down GPIO pin
|
||||
* @author Jeremy Rumpf - Added Action/Edge constants
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class GPIOBindingConstants {
|
||||
@ -43,12 +44,27 @@ public class GPIOBindingConstants {
|
||||
public static final String DEBOUNCING_TIME = "debouncing_time";
|
||||
public static final String STRICT_DEBOUNCING = "debouncing_strict";
|
||||
public static final String PULLUPDOWN_RESISTOR = "pullupdown";
|
||||
public static final String ACTION_SET_UNDEF = "SETUNDEF";
|
||||
public static final String ACTION_NOTHING = "NOTHING";
|
||||
public static final String ACTION_REFRESH = "REFRESH";
|
||||
public static final String ACTION_ALL_ON = "ALLON";
|
||||
public static final String ACTION_ALL_OFF = "ALLOFF";
|
||||
|
||||
// Pull Up/Down modes
|
||||
public static final String PUD_OFF = "OFF";
|
||||
public static final String PUD_DOWN = "DOWN";
|
||||
public static final String PUD_UP = "UP";
|
||||
|
||||
// Pulse
|
||||
public static final String PULSE_OFF = "OFF";
|
||||
public static final String PULSE_ON = "ON";
|
||||
public static final String PULSE_BLINK = "BLINK";
|
||||
|
||||
// Edge modes
|
||||
public static final String EDGE_EITHER = "EDGE_EITHER";
|
||||
public static final String EDGE_RISING = "EDGE_RISING";
|
||||
public static final String EDGE_FALLING = "EDGE_FALLING";
|
||||
|
||||
// GPIO config properties
|
||||
public static final String GPIO_ID = "gpioId";
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
/**
|
||||
* 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.gpio.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Is thrown when no gpio id is provided
|
||||
*
|
||||
* @author Nils Bauer - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NoGpioIdException extends Exception {
|
||||
private static final long serialVersionUID = -1281107134439928767L;
|
||||
}
|
@ -13,7 +13,6 @@
|
||||
package org.openhab.binding.gpio.internal.configuration;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link GPIOConfiguration} class contains fields mapping thing configuration parameters.
|
||||
@ -26,7 +25,7 @@ public class GPIOConfiguration {
|
||||
/**
|
||||
* The id of the gpio pin.
|
||||
*/
|
||||
public @Nullable Integer gpioId;
|
||||
public Integer gpioId = 0;
|
||||
|
||||
/**
|
||||
* Should the input/output be inverted?
|
||||
|
@ -12,6 +12,8 @@
|
||||
*/
|
||||
package org.openhab.binding.gpio.internal.configuration;
|
||||
|
||||
import static org.openhab.binding.gpio.internal.GPIOBindingConstants.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
@ -31,5 +33,12 @@ public class GPIOInputConfiguration extends GPIOConfiguration {
|
||||
* Setup a pullup resistor on the GPIO pin
|
||||
* OFF = PI_PUD_OFF, DOWN = PI_PUD_DOWN, UP = PI_PUD_UP
|
||||
*/
|
||||
public String pullupdown = "OFF";
|
||||
public String pullupdown = PUD_OFF;
|
||||
|
||||
/**
|
||||
* Sets the input detection type.
|
||||
* EDGE_EITHER = PI_EITHER_EDGE, EDGE_FALLING = PI_FALLING_EDGE,
|
||||
* EDGE_RISING = PI_RISING_EDGE
|
||||
*/
|
||||
public String edgeMode = EDGE_EITHER;
|
||||
}
|
||||
|
@ -12,6 +12,8 @@
|
||||
*/
|
||||
package org.openhab.binding.gpio.internal.configuration;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
@ -21,5 +23,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class GPIOOutputConfiguration extends GPIOConfiguration {
|
||||
|
||||
public BigDecimal pulse = new BigDecimal(0);
|
||||
public String pulseCommand = "OFF";
|
||||
}
|
||||
|
@ -32,4 +32,41 @@ public class PigpioConfiguration {
|
||||
* Port of pigpio on the remote raspberry pi
|
||||
*/
|
||||
public int port = 8888;
|
||||
|
||||
/**
|
||||
* Interval to send heartbeat checks
|
||||
*/
|
||||
public int heartBeatInterval = 60000;
|
||||
|
||||
/**
|
||||
* Input channel action on connect
|
||||
* (First connect after INITIALIATION)
|
||||
*/
|
||||
public @Nullable String inputConnectAction;
|
||||
|
||||
/**
|
||||
* Input channel action on reconnect
|
||||
*/
|
||||
public @Nullable String inputReconnectAction;
|
||||
|
||||
/**
|
||||
* Input channel action on disconnect
|
||||
*/
|
||||
public @Nullable String inputDisconnectAction;
|
||||
|
||||
/**
|
||||
* Output channel action on connect
|
||||
* (First connect after INITIALIATION)
|
||||
*/
|
||||
public @Nullable String outputConnectAction;
|
||||
|
||||
/**
|
||||
* Output channel action on reconnect
|
||||
*/
|
||||
public @Nullable String outputReconnectAction;
|
||||
|
||||
/**
|
||||
* Output channel action on disconnect
|
||||
*/
|
||||
public @Nullable String outputDisconnectAction;
|
||||
}
|
||||
|
@ -13,8 +13,12 @@
|
||||
package org.openhab.binding.gpio.internal.handler;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.types.Command;
|
||||
|
||||
import eu.xeli.jpigpio.JPigpio;
|
||||
import eu.xeli.jpigpio.PigpioException;
|
||||
|
||||
/**
|
||||
* The {@link ChannelHandler} provides an interface for different pin
|
||||
* configuration handlers
|
||||
@ -24,5 +28,20 @@ import org.openhab.core.types.Command;
|
||||
@NonNullByDefault
|
||||
public interface ChannelHandler {
|
||||
|
||||
void handleCommand(Command command);
|
||||
/**
|
||||
* Handles a Command being sent from the
|
||||
* Openhab framework.
|
||||
*/
|
||||
void handleCommand(Command command) throws PigpioException;
|
||||
|
||||
/**
|
||||
* (Re)Establishes the JPigpio listeners.
|
||||
*/
|
||||
void listen(@Nullable JPigpio jPigpio) throws PigpioException;
|
||||
|
||||
/**
|
||||
* Terminates sending Channels status updates and
|
||||
* shuts down any JPigpio listeners.
|
||||
*/
|
||||
void dispose();
|
||||
}
|
||||
|
@ -18,9 +18,9 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.gpio.internal.ChannelConfigurationException;
|
||||
import org.openhab.binding.gpio.internal.GPIOBindingConstants;
|
||||
import org.openhab.binding.gpio.internal.InvalidPullUpDownException;
|
||||
import org.openhab.binding.gpio.internal.NoGpioIdException;
|
||||
import org.openhab.binding.gpio.internal.configuration.GPIOInputConfiguration;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.types.Command;
|
||||
@ -30,6 +30,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import eu.xeli.jpigpio.GPIO;
|
||||
import eu.xeli.jpigpio.GPIOListener;
|
||||
import eu.xeli.jpigpio.JPigpio;
|
||||
import eu.xeli.jpigpio.PigpioException;
|
||||
|
||||
@ -39,64 +40,174 @@ import eu.xeli.jpigpio.PigpioException;
|
||||
* @author Nils Bauer - Initial contribution
|
||||
* @author Jan N. Klug - Channel redesign
|
||||
* @author Martin Dagarin - Pull Up/Down GPIO pin
|
||||
* @author Jeremy Rumpf - Refactored for network disruptions
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PigpioDigitalInputHandler implements ChannelHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(PigpioDigitalInputHandler.class);
|
||||
private Date lastChanged = new Date();
|
||||
|
||||
private final GPIOInputConfiguration configuration;
|
||||
private final GPIO gpio;
|
||||
private final Consumer<State> updateStatus;
|
||||
private final ScheduledExecutorService scheduler;
|
||||
private final Integer gpioId;
|
||||
private @Nullable GPIO gpio;
|
||||
private @Nullable Consumer<State> updateStatus;
|
||||
private Integer pullupdown = JPigpio.PI_PUD_OFF;
|
||||
private final GPIOListener listener;
|
||||
private int edgeMode = JPigpio.PI_EITHER_EDGE;
|
||||
|
||||
public PigpioDigitalInputHandler(GPIOInputConfiguration configuration, JPigpio jPigpio,
|
||||
ScheduledExecutorService scheduler, Consumer<State> updateStatus)
|
||||
throws PigpioException, InvalidPullUpDownException, NoGpioIdException {
|
||||
/**
|
||||
* Constructor for PigpioDigitalOutputHandler
|
||||
*
|
||||
* @param configuration The channel configuration
|
||||
* @param jPigpio The jPigpio instance
|
||||
* @param updateStatus Is called when the state should be changed
|
||||
*
|
||||
* @throws PigpioException Can be thrown by Pigpio
|
||||
* @throws ChannelConfigurationException Thrown on configuration error
|
||||
*/
|
||||
public PigpioDigitalInputHandler(GPIOInputConfiguration configuration, ScheduledExecutorService scheduler,
|
||||
Consumer<State> updateStatus) throws PigpioException, ChannelConfigurationException {
|
||||
this.configuration = configuration;
|
||||
this.scheduler = scheduler;
|
||||
this.updateStatus = updateStatus;
|
||||
Integer gpioId = configuration.gpioId;
|
||||
if (gpioId == null) {
|
||||
throw new NoGpioIdException();
|
||||
this.gpioId = configuration.gpioId;
|
||||
|
||||
if (this.gpioId <= 0) {
|
||||
throw new ChannelConfigurationException("Invalid gpioId value: " + this.gpioId);
|
||||
}
|
||||
Integer pullupdown = JPigpio.PI_PUD_OFF;
|
||||
|
||||
String pullupdownStr = configuration.pullupdown.toUpperCase();
|
||||
if (pullupdownStr.equals(GPIOBindingConstants.PUD_DOWN)) {
|
||||
pullupdown = JPigpio.PI_PUD_DOWN;
|
||||
this.pullupdown = JPigpio.PI_PUD_DOWN;
|
||||
} else if (pullupdownStr.equals(GPIOBindingConstants.PUD_UP)) {
|
||||
pullupdown = JPigpio.PI_PUD_UP;
|
||||
this.pullupdown = JPigpio.PI_PUD_UP;
|
||||
} else if (pullupdownStr.equals(GPIOBindingConstants.PUD_OFF)) {
|
||||
this.pullupdown = JPigpio.PI_PUD_OFF;
|
||||
} else {
|
||||
if (!pullupdownStr.equals(GPIOBindingConstants.PUD_OFF)) {
|
||||
throw new InvalidPullUpDownException();
|
||||
}
|
||||
}
|
||||
gpio = new GPIO(jPigpio, gpioId, JPigpio.PI_INPUT);
|
||||
jPigpio.gpioSetAlertFunc(gpio.getPin(), (gpio, level, tick) -> {
|
||||
lastChanged = new Date();
|
||||
Date thisChange = new Date();
|
||||
scheduler.schedule(() -> afterDebounce(thisChange), configuration.debouncingTime, TimeUnit.MILLISECONDS);
|
||||
});
|
||||
jPigpio.gpioSetPullUpDown(gpio.getPin(), pullupdown);
|
||||
throw new ChannelConfigurationException("Invalid pull up/down value.");
|
||||
}
|
||||
|
||||
String edgeModeStr = configuration.edgeMode;
|
||||
if (edgeModeStr.equals(GPIOBindingConstants.EDGE_RISING)) {
|
||||
this.edgeMode = JPigpio.PI_RISING_EDGE;
|
||||
} else if (edgeModeStr.equals(GPIOBindingConstants.EDGE_FALLING)) {
|
||||
this.edgeMode = JPigpio.PI_FALLING_EDGE;
|
||||
} else if (edgeModeStr.equals(GPIOBindingConstants.EDGE_EITHER)) {
|
||||
this.edgeMode = JPigpio.PI_EITHER_EDGE;
|
||||
} else {
|
||||
throw new ChannelConfigurationException("Invalid edgeMode value.");
|
||||
}
|
||||
|
||||
this.listener = new GPIOListener(this.gpioId, this.edgeMode) {
|
||||
@Override
|
||||
public void alert(int gpio, int level, long tick) {
|
||||
alertFunc(gpio, level, tick);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void alertFunc(int gpio, int level, long tick) {
|
||||
this.lastChanged = new Date();
|
||||
Date thisChange = new Date();
|
||||
if (configuration.debouncingTime > 0) {
|
||||
scheduler.schedule(() -> afterDebounce(thisChange), configuration.debouncingTime, TimeUnit.MILLISECONDS);
|
||||
} else {
|
||||
afterDebounce(thisChange);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Syncronize debouncing callbacks to
|
||||
* ensure they are not out of order.
|
||||
*/
|
||||
private Object debounceLock = new Object();
|
||||
|
||||
private void afterDebounce(Date thisChange) {
|
||||
synchronized (debounceLock) {
|
||||
GPIO lgpio = this.gpio;
|
||||
Consumer<State> lupdateStatus = this.updateStatus;
|
||||
|
||||
if (lgpio == null || lupdateStatus == null) {
|
||||
// We raced and went offline in the meantime.
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Check if value changed over time
|
||||
if (!thisChange.before(lastChanged)) {
|
||||
updateStatus.accept(OnOffType.from(configuration.invert != gpio.getValue()));
|
||||
lupdateStatus.accept(OnOffType.from(configuration.invert != lgpio.getValue()));
|
||||
}
|
||||
} catch (PigpioException e) {
|
||||
logger.warn("Unknown pigpio exception", e);
|
||||
// -99999999 is communication related, we will let the Thing connect poll refresh it.
|
||||
if (e.getErrorCode() != -99999999) {
|
||||
logger.debug("Debounce exception :", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes or re-establishes a listener on the JPigpio
|
||||
* instance for the configured gpio pin.
|
||||
*/
|
||||
public void listen(@Nullable JPigpio jPigpio) throws PigpioException {
|
||||
if (jPigpio == null) {
|
||||
this.gpio = null;
|
||||
return;
|
||||
}
|
||||
|
||||
GPIO lgpio = new GPIO(jPigpio, this.gpioId, JPigpio.PI_INPUT);
|
||||
this.gpio = lgpio;
|
||||
|
||||
try {
|
||||
lgpio.setDirection(JPigpio.PI_INPUT);
|
||||
jPigpio.gpioSetPullUpDown(lgpio.getPin(), this.pullupdown);
|
||||
jPigpio.removeCallback(this.listener);
|
||||
} catch (PigpioException e) {
|
||||
// If there is a communication error, the set alert below will throw.
|
||||
if (e.getErrorCode() != -99999999) {
|
||||
logger.debug("Listen exception :", e);
|
||||
}
|
||||
}
|
||||
|
||||
jPigpio.gpioSetAlertFunc(lgpio.getPin(), this.listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(Command command) throws PigpioException {
|
||||
GPIO lgpio = this.gpio;
|
||||
Consumer<State> lupdateStatus = this.updateStatus;
|
||||
|
||||
if (lgpio == null || lupdateStatus == null) {
|
||||
logger.warn("An attempt to submit a command was made when pigpiod was offline: {}", command.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (command instanceof RefreshType) {
|
||||
lupdateStatus.accept(OnOffType.from(configuration.invert != lgpio.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(Command command) {
|
||||
if (command instanceof RefreshType) {
|
||||
public void dispose() {
|
||||
synchronized (debounceLock) {
|
||||
GPIO lgpio = this.gpio;
|
||||
|
||||
updateStatus = null;
|
||||
if (lgpio != null) {
|
||||
JPigpio ljPigpio = lgpio.getPigpio();
|
||||
if (ljPigpio != null) {
|
||||
try {
|
||||
updateStatus.accept(OnOffType.from(configuration.invert != gpio.getValue()));
|
||||
ljPigpio.removeCallback(listener);
|
||||
} catch (PigpioException e) {
|
||||
logger.warn("Unknown pigpio exception while handling Refresh", e);
|
||||
// Best effort to remove listener,
|
||||
// the command socket could already be dead.
|
||||
if (e.getErrorCode() != -99999999) {
|
||||
logger.debug("Dispose exception :", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,10 +12,17 @@
|
||||
*/
|
||||
package org.openhab.binding.gpio.internal.handler;
|
||||
|
||||
import static org.openhab.binding.gpio.internal.GPIOBindingConstants.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.gpio.internal.NoGpioIdException;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.gpio.internal.ChannelConfigurationException;
|
||||
import org.openhab.binding.gpio.internal.configuration.GPIOOutputConfiguration;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.types.Command;
|
||||
@ -33,16 +40,19 @@ import eu.xeli.jpigpio.PigpioException;
|
||||
*
|
||||
* @author Nils Bauer - Initial contribution
|
||||
* @author Jan N. Klug - Channel redesign
|
||||
* @author Jeremy Rumpf - Refactored for network disruptions
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PigpioDigitalOutputHandler implements ChannelHandler {
|
||||
|
||||
/** The logger. */
|
||||
private final Logger logger = LoggerFactory.getLogger(PigpioDigitalOutputHandler.class);
|
||||
|
||||
private final GPIOOutputConfiguration configuration;
|
||||
private final GPIO gpio;
|
||||
private final Consumer<State> updateStatus;
|
||||
private final ScheduledExecutorService scheduler;
|
||||
private final Integer gpioId;
|
||||
private Integer pulseTimeout = -1;
|
||||
private @Nullable String pulseCommand = "";
|
||||
private @Nullable GPIO gpio;
|
||||
private @Nullable Consumer<State> updateStatus;
|
||||
|
||||
/**
|
||||
* Constructor for PigpioDigitalOutputHandler
|
||||
@ -52,34 +62,183 @@ public class PigpioDigitalOutputHandler implements ChannelHandler {
|
||||
* @param updateStatus Is called when the state should be changed
|
||||
*
|
||||
* @throws PigpioException Can be thrown by Pigpio
|
||||
* @throws NoGpioIdException Is thrown when no gpioId is defined
|
||||
* @throws ChannelConfigurationException Thrown on configuration error
|
||||
*/
|
||||
public PigpioDigitalOutputHandler(GPIOOutputConfiguration configuration, JPigpio jPigpio,
|
||||
Consumer<State> updateStatus) throws PigpioException, NoGpioIdException {
|
||||
public PigpioDigitalOutputHandler(GPIOOutputConfiguration configuration, ScheduledExecutorService scheduler,
|
||||
Consumer<State> updateStatus) throws PigpioException, ChannelConfigurationException {
|
||||
this.configuration = configuration;
|
||||
this.gpioId = configuration.gpioId;
|
||||
this.scheduler = scheduler;
|
||||
this.updateStatus = updateStatus;
|
||||
Integer gpioId = configuration.gpioId;
|
||||
if (gpioId == null) {
|
||||
throw new NoGpioIdException();
|
||||
|
||||
if (this.gpioId <= 0) {
|
||||
throw new ChannelConfigurationException("Invalid gpioId value.");
|
||||
}
|
||||
|
||||
if (configuration.pulse.compareTo(BigDecimal.ZERO) > 0) {
|
||||
try {
|
||||
this.pulseTimeout = configuration.pulse.intValue();
|
||||
} catch (Exception e) {
|
||||
throw new ChannelConfigurationException("Invalid expire value.");
|
||||
}
|
||||
}
|
||||
|
||||
if (configuration.pulseCommand.length() > 0) {
|
||||
this.pulseCommand = configuration.pulseCommand.toUpperCase();
|
||||
if (!PULSE_ON.equals(pulseCommand) && !PULSE_OFF.equals(pulseCommand)
|
||||
&& !PULSE_BLINK.equals(pulseCommand)) {
|
||||
throw new ChannelConfigurationException("Invalid pulseCommand value.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Future to track pulse commands.
|
||||
*/
|
||||
private @Nullable Future<?> pulseJob = null;
|
||||
private @Nullable OnOffType lastPulseCommand;
|
||||
|
||||
/**
|
||||
* Used to only keep a single gpio command handle in flight
|
||||
* at a time.
|
||||
*/
|
||||
private Object handleLock = new Object();
|
||||
|
||||
@Override
|
||||
public void handleCommand(Command command) throws PigpioException {
|
||||
synchronized (handleLock) {
|
||||
GPIO lgpio = this.gpio;
|
||||
Consumer<State> lupdateStatus = this.updateStatus;
|
||||
Future<?> job = this.pulseJob;
|
||||
|
||||
if (lgpio == null || lupdateStatus == null) {
|
||||
logger.warn("An attempt to submit a command was made when the pigpiod was offline: {}",
|
||||
command.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (command instanceof RefreshType) {
|
||||
lupdateStatus.accept(OnOffType.from(configuration.invert != lgpio.getValue()));
|
||||
} else if (command instanceof OnOffType) {
|
||||
lgpio.setValue(configuration.invert != (OnOffType.ON.equals(command)));
|
||||
lupdateStatus.accept((State) command);
|
||||
|
||||
if (this.pulseTimeout > 0 && this.pulseCommand != null) {
|
||||
if (job != null) {
|
||||
job.cancel(false);
|
||||
}
|
||||
|
||||
this.pulseJob = scheduler.schedule(() -> handlePulseCommand(command), this.pulseTimeout,
|
||||
TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void handlePulseCommand(@Nullable Command command) {
|
||||
OnOffType eCommand = OnOffType.OFF;
|
||||
|
||||
try {
|
||||
synchronized (handleLock) {
|
||||
GPIO lgpio = this.gpio;
|
||||
Consumer<State> lupdateStatus = this.updateStatus;
|
||||
Future<?> job = this.pulseJob;
|
||||
|
||||
if (lgpio == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (command instanceof OnOffType) {
|
||||
if (this.pulseCommand != null) {
|
||||
if (PULSE_ON.equals(this.pulseCommand)) {
|
||||
eCommand = OnOffType.ON;
|
||||
} else if (PULSE_OFF.equals(this.pulseCommand)) {
|
||||
eCommand = OnOffType.OFF;
|
||||
} else if (PULSE_BLINK.equals(this.pulseCommand)) {
|
||||
if (OnOffType.ON.equals(command)) {
|
||||
eCommand = OnOffType.OFF;
|
||||
} else if (OnOffType.OFF.equals(command)) {
|
||||
eCommand = OnOffType.ON;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (OnOffType.ON.equals(command)) {
|
||||
eCommand = OnOffType.OFF;
|
||||
} else if (OnOffType.OFF.equals(command)) {
|
||||
eCommand = OnOffType.ON;
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("gpio pulse command : {} {}", this.gpioId, eCommand.toString());
|
||||
|
||||
lgpio.setValue(configuration.invert != (OnOffType.ON.equals(eCommand)));
|
||||
if (lupdateStatus != null) {
|
||||
lupdateStatus.accept((State) eCommand);
|
||||
}
|
||||
|
||||
lastPulseCommand = eCommand;
|
||||
|
||||
if (PULSE_BLINK.equals(this.pulseCommand) && this.pulseTimeout > 0) {
|
||||
final OnOffType feCommand = eCommand;
|
||||
if (job != null) {
|
||||
job.cancel(false);
|
||||
}
|
||||
this.pulseJob = scheduler.schedule(() -> handlePulseCommand(feCommand), this.pulseTimeout,
|
||||
TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn(
|
||||
"Pulse command exception, {} command may not have been received by pigpiod resulting in an unknown state:",
|
||||
eCommand.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the GPIO pin for OUTPUT.
|
||||
*/
|
||||
public void listen(@Nullable JPigpio jPigpio) throws PigpioException {
|
||||
if (jPigpio == null) {
|
||||
this.gpio = null;
|
||||
return;
|
||||
}
|
||||
|
||||
GPIO lgpio = new GPIO(jPigpio, gpioId, JPigpio.PI_OUTPUT);
|
||||
this.gpio = lgpio;
|
||||
lgpio.setDirection(JPigpio.PI_OUTPUT);
|
||||
scheduleBlink();
|
||||
}
|
||||
|
||||
private void scheduleBlink() {
|
||||
synchronized (handleLock) {
|
||||
Future<?> job = this.pulseJob;
|
||||
|
||||
if (this.pulseTimeout > 0 && PULSE_BLINK.equals(configuration.pulseCommand)) {
|
||||
if (job != null) {
|
||||
job.cancel(false);
|
||||
}
|
||||
if (this.lastPulseCommand != null) {
|
||||
scheduler.schedule(() -> handlePulseCommand(this.lastPulseCommand), this.pulseTimeout,
|
||||
TimeUnit.MILLISECONDS);
|
||||
} else {
|
||||
this.pulseJob = scheduler.schedule(() -> handlePulseCommand(OnOffType.OFF), this.pulseTimeout,
|
||||
TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.gpio = new GPIO(jPigpio, gpioId, JPigpio.PI_OUTPUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(Command command) {
|
||||
if (command instanceof RefreshType) {
|
||||
try {
|
||||
updateStatus.accept(OnOffType.from(configuration.invert != gpio.getValue()));
|
||||
} catch (PigpioException e) {
|
||||
logger.warn("Unknown pigpio exception while handling Refresh", e);
|
||||
}
|
||||
}
|
||||
if (command instanceof OnOffType) {
|
||||
try {
|
||||
gpio.setValue(configuration.invert != (OnOffType.ON.equals(command)));
|
||||
} catch (PigpioException e) {
|
||||
logger.warn("An error occured while changing the gpio value: {}", e.getMessage());
|
||||
public void dispose() {
|
||||
synchronized (handleLock) {
|
||||
Future<?> job = this.pulseJob;
|
||||
|
||||
if (job != null) {
|
||||
job.cancel(true);
|
||||
}
|
||||
this.updateStatus = null;
|
||||
this.gpio = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,13 +16,16 @@ import static org.openhab.binding.gpio.internal.GPIOBindingConstants.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.gpio.internal.InvalidPullUpDownException;
|
||||
import org.openhab.binding.gpio.internal.NoGpioIdException;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.gpio.internal.ChannelConfigurationException;
|
||||
import org.openhab.binding.gpio.internal.configuration.GPIOInputConfiguration;
|
||||
import org.openhab.binding.gpio.internal.configuration.GPIOOutputConfiguration;
|
||||
import org.openhab.binding.gpio.internal.configuration.PigpioConfiguration;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
@ -30,6 +33,8 @@ import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -44,6 +49,7 @@ import eu.xeli.jpigpio.PigpioSocket;
|
||||
*
|
||||
* @author Nils Bauer - Initial contribution
|
||||
* @author Jan N. Klug - Channel redesign
|
||||
* @author Jeremy Rumpf - Improve JPigpio connection handling
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PigpioRemoteHandler extends BaseThingHandler {
|
||||
@ -61,56 +67,355 @@ public class PigpioRemoteHandler extends BaseThingHandler {
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
try {
|
||||
synchronized (this.connectionLock) {
|
||||
ChannelHandler channelHandler = channelHandlers.get(channelUID);
|
||||
if (channelHandler != null) {
|
||||
channelHandler.handleCommand(command);
|
||||
|
||||
if (channelHandler == null || !(ThingStatus.ONLINE.equals(thing.getStatus()))) {
|
||||
// We raced with connectPollWorker and lost
|
||||
return;
|
||||
}
|
||||
|
||||
if (channelHandler instanceof PigpioDigitalInputHandler inputHandler) {
|
||||
try {
|
||||
inputHandler.handleCommand(command);
|
||||
} catch (PigpioException pe) {
|
||||
logger.warn("Input command exception on channel {} {}", channelUID, pe.toString());
|
||||
if (pe.getErrorCode() == -99999999) {
|
||||
runDisconnectActions();
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
|
||||
pe.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
} else if (channelHandler instanceof PigpioDigitalOutputHandler outputHandler) {
|
||||
try {
|
||||
outputHandler.handleCommand(command);
|
||||
} catch (PigpioException pe) {
|
||||
logger.warn("Output command exception on channel {} {}", channelUID, pe.toString());
|
||||
if (pe.getErrorCode() == -99999999) {
|
||||
runDisconnectActions();
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
|
||||
pe.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.warn("Command received for an unknown channel: {}", channelUID);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("Command exception on channel {} {}", channelUID, e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
protected PigpioConfiguration config = new PigpioConfiguration();
|
||||
protected @Nullable JPigpio jPigpio = null;
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
PigpioConfiguration config = getConfigAs(PigpioConfiguration.class);
|
||||
String host = config.host;
|
||||
int port = config.port;
|
||||
JPigpio jPigpio;
|
||||
if (host == null) {
|
||||
PigpioConfiguration lconfig = getConfigAs(PigpioConfiguration.class);
|
||||
this.config = lconfig;
|
||||
|
||||
if (lconfig.host == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
|
||||
"Cannot connect to PiGPIO Service on remote raspberry. IP address not set.");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
jPigpio = new PigpioSocket(host, port);
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
} catch (PigpioException e) {
|
||||
if (e.getErrorCode() == PigpioException.PI_BAD_SOCKET_PORT) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, "Port out of range");
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
|
||||
e.getLocalizedMessage());
|
||||
}
|
||||
if (lconfig.port < 1 && lconfig.port > 65535) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
|
||||
"Cannot connect to PiGPIO Service on remote raspberry. Invalid Port.");
|
||||
return;
|
||||
}
|
||||
thing.getChannels().forEach(channel -> {
|
||||
|
||||
createChannelHandlers();
|
||||
|
||||
logger.debug("gpio binding initialized");
|
||||
|
||||
connectionJob = scheduler.submit(() -> {
|
||||
connectionPollWorker();
|
||||
});
|
||||
}
|
||||
|
||||
protected void clearChannelHandlers() {
|
||||
for (ChannelHandler handler : channelHandlers.values()) {
|
||||
handler.dispose();
|
||||
}
|
||||
channelHandlers.clear();
|
||||
}
|
||||
|
||||
protected void createChannelHandlers() {
|
||||
clearChannelHandlers();
|
||||
this.getThing().getChannels().forEach(channel -> {
|
||||
ChannelUID channelUID = channel.getUID();
|
||||
ChannelTypeUID type = channel.getChannelTypeUID();
|
||||
|
||||
try {
|
||||
if (CHANNEL_TYPE_DIGITAL_INPUT.equals(type)) {
|
||||
GPIOInputConfiguration configuration = channel.getConfiguration().as(GPIOInputConfiguration.class);
|
||||
channelHandlers.put(channelUID, new PigpioDigitalInputHandler(configuration, jPigpio, scheduler,
|
||||
this.channelHandlers.put(channelUID, new PigpioDigitalInputHandler(configuration, scheduler,
|
||||
state -> updateState(channelUID.getId(), state)));
|
||||
} else if (CHANNEL_TYPE_DIGITAL_OUTPUT.equals(type)) {
|
||||
GPIOOutputConfiguration configuration = channel.getConfiguration()
|
||||
.as(GPIOOutputConfiguration.class);
|
||||
channelHandlers.put(channelUID, new PigpioDigitalOutputHandler(configuration, jPigpio,
|
||||
state -> updateState(channelUID.getId(), state)));
|
||||
PigpioDigitalOutputHandler handler = new PigpioDigitalOutputHandler(configuration, scheduler,
|
||||
state -> updateState(channelUID.getId(), state));
|
||||
this.channelHandlers.put(channelUID, handler);
|
||||
}
|
||||
} catch (PigpioException e) {
|
||||
logger.warn("Failed to initialize {}: {}", channelUID, e.getMessage());
|
||||
} catch (InvalidPullUpDownException e) {
|
||||
logger.warn("Failed to initialize {}: Invalid Pull Up/Down resistor configuration", channelUID);
|
||||
} catch (NoGpioIdException e) {
|
||||
logger.warn("Failed to initialize {}: GpioId is not set", channelUID);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
|
||||
String.format("Failed to initialize channel {} {}", channelUID, e.getLocalizedMessage()));
|
||||
} catch (ChannelConfigurationException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
|
||||
String.format("Invalid configuration for channel {} {}", channelUID, e.getLocalizedMessage()));
|
||||
}
|
||||
});
|
||||
|
||||
logger.debug("gpio channels initialized");
|
||||
}
|
||||
|
||||
protected void setChannelJPigpio(@Nullable JPigpio jPigpio) throws PigpioException {
|
||||
if (this.channelHandlers.isEmpty()) {
|
||||
createChannelHandlers();
|
||||
}
|
||||
|
||||
for (ChannelHandler handler : this.channelHandlers.values()) {
|
||||
handler.listen(jPigpio);
|
||||
}
|
||||
|
||||
logger.debug("gpio jPigpio listening");
|
||||
}
|
||||
|
||||
private @Nullable Future<?> connectionJob = null;
|
||||
/**
|
||||
* Syncronizes all socket related code
|
||||
* to avoid racing.
|
||||
*/
|
||||
private Object connectionLock = new Object();
|
||||
|
||||
protected void killConnectionPoll() {
|
||||
if (this.connectionJob != null) {
|
||||
synchronized (this.connectionLock) {
|
||||
if (this.connectionJob != null) {
|
||||
Future<?> job = this.connectionJob;
|
||||
this.connectionJob = null;
|
||||
if (job != null) {
|
||||
logger.debug("gpio connection poll : killing");
|
||||
job.cancel(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void connectionPollWorker() {
|
||||
Thing thing = this.getThing();
|
||||
|
||||
synchronized (connectionLock) {
|
||||
ThingStatus currentStatus = thing.getStatus();
|
||||
JPigpio ljPigpio = this.jPigpio;
|
||||
|
||||
if (ThingStatus.ONLINE.equals(currentStatus) && ljPigpio != null) {
|
||||
// We are ONLINE and jPigpio is instantiated, this is the normal path
|
||||
try {
|
||||
logger.debug("gpio connection poll : CMD_TICK");
|
||||
ljPigpio.getCurrentTick();
|
||||
} catch (PigpioException e) {
|
||||
logger.debug("gpio connection poll : disconnect");
|
||||
runDisconnectActions();
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
|
||||
e.getLocalizedMessage());
|
||||
|
||||
// We disconnected, reschedule ourselves to try a reconnect.
|
||||
// First, try a quick reconnect if the user specified a long(ish) interval
|
||||
int interval = this.config.heartBeatInterval;
|
||||
if (interval > 1000) {
|
||||
interval = 1000;
|
||||
}
|
||||
|
||||
this.connectionJob = scheduler.schedule(() -> {
|
||||
connectionPollWorker();
|
||||
}, interval, TimeUnit.MILLISECONDS);
|
||||
|
||||
logger.warn("Pigpiod disconnected : {}", this.config.host);
|
||||
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// We are OFFLINE and jPigpio may or may not be instantiated
|
||||
try {
|
||||
if (ljPigpio == null) {
|
||||
// First initialization or re-initialization after dispose()
|
||||
// jPigpio is not up and running yet.
|
||||
logger.debug("gpio connection poll : connecting");
|
||||
ljPigpio = new PigpioSocket(this.config.host, this.config.port);
|
||||
this.jPigpio = ljPigpio;
|
||||
setChannelJPigpio(ljPigpio);
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
runConnectActions();
|
||||
} else {
|
||||
// jPigpio is instantiated, but not connected.
|
||||
// Use it's internal reconnect logic.
|
||||
logger.debug("gpio connection poll : reconnecting");
|
||||
ljPigpio.reconnect();
|
||||
// jPigpio listeners are not re-established after reconnect.
|
||||
// We need to reinject them into the channel handlers.
|
||||
setChannelJPigpio(ljPigpio);
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
runReconnectActions();
|
||||
}
|
||||
|
||||
logger.debug("Pigpiod connected : {}", this.config.host);
|
||||
} catch (PigpioException e) {
|
||||
logger.debug("gpio connection poll : failed, {}", e.getErrorCode());
|
||||
if (currentStatus.equals(ThingStatus.ONLINE) || currentStatus.equals(ThingStatus.INITIALIZING)) {
|
||||
runDisconnectActions();
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
|
||||
e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.config.heartBeatInterval > 0) {
|
||||
this.connectionJob = scheduler.schedule(() -> {
|
||||
connectionPollWorker();
|
||||
}, this.config.heartBeatInterval, TimeUnit.MILLISECONDS);
|
||||
} else {
|
||||
// User disabled periodic connections, one shot?
|
||||
logger.debug("gpio connection poll : disabled");
|
||||
this.connectionJob = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void runConnectActions() throws PigpioException {
|
||||
if (this.config.inputConnectAction != null) {
|
||||
if (ACTION_REFRESH.equals(this.config.inputConnectAction)) {
|
||||
refreshInputChannels();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.config.outputConnectAction != null) {
|
||||
if (ACTION_ALL_ON.equals(this.config.outputConnectAction)) {
|
||||
setOutputChannels(OnOffType.ON);
|
||||
} else if (ACTION_ALL_OFF.equals(this.config.outputConnectAction)) {
|
||||
setOutputChannels(OnOffType.OFF);
|
||||
} else if (ACTION_REFRESH.equals(this.config.outputConnectAction)) {
|
||||
refreshOutputChannels();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void runReconnectActions() throws PigpioException {
|
||||
if (this.config.inputConnectAction != null) {
|
||||
if (ACTION_REFRESH.equals(this.config.inputConnectAction)) {
|
||||
refreshInputChannels();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.config.outputConnectAction != null) {
|
||||
if (ACTION_REFRESH.equals(this.config.outputConnectAction)) {
|
||||
refreshOutputChannels();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void runDisconnectActions() {
|
||||
if (this.config.inputDisconnectAction != null) {
|
||||
if (ACTION_SET_UNDEF.equals(this.config.inputDisconnectAction)) {
|
||||
undefInputChannels();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.config.outputDisconnectAction != null) {
|
||||
if (ACTION_SET_UNDEF.equals(this.config.outputDisconnectAction)) {
|
||||
undefOutputChannels();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void refreshInputChannels() throws PigpioException {
|
||||
logger.debug("gpio refresh input channels");
|
||||
for (ChannelUID channelUID : channelHandlers.keySet()) {
|
||||
ChannelHandler handler = channelHandlers.get(channelUID);
|
||||
if (handler instanceof PigpioDigitalInputHandler) {
|
||||
handler.handleCommand(RefreshType.REFRESH);
|
||||
postCommand(channelUID, RefreshType.REFRESH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void refreshOutputChannels() throws PigpioException {
|
||||
logger.debug("gpio refresh output channels");
|
||||
for (ChannelUID channelUID : this.channelHandlers.keySet()) {
|
||||
ChannelHandler handler = this.channelHandlers.get(channelUID);
|
||||
if (handler instanceof PigpioDigitalOutputHandler) {
|
||||
handler.handleCommand(RefreshType.REFRESH);
|
||||
postCommand(channelUID, RefreshType.REFRESH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void undefInputChannels() {
|
||||
logger.debug("gpio undef input channels");
|
||||
for (ChannelUID channelUID : this.channelHandlers.keySet()) {
|
||||
ChannelHandler handler = this.channelHandlers.get(channelUID);
|
||||
if (handler instanceof PigpioDigitalInputHandler) {
|
||||
updateState(channelUID, UnDefType.UNDEF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void undefOutputChannels() {
|
||||
logger.debug("gpio undef output channels");
|
||||
for (ChannelUID channelUID : channelHandlers.keySet()) {
|
||||
ChannelHandler handler = channelHandlers.get(channelUID);
|
||||
if (handler instanceof PigpioDigitalOutputHandler) {
|
||||
updateState(channelUID, UnDefType.UNDEF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void setOutputChannels(OnOffType command) throws PigpioException {
|
||||
logger.debug("gpio setting output channels: {}", command.toString());
|
||||
for (ChannelUID channelUID : this.channelHandlers.keySet()) {
|
||||
ChannelHandler handler = this.channelHandlers.get(channelUID);
|
||||
if (handler instanceof PigpioDigitalOutputHandler) {
|
||||
handler.handleCommand(command);
|
||||
postCommand(channelUID, command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
try {
|
||||
synchronized (this.connectionLock) {
|
||||
JPigpio ljPigpio = this.jPigpio;
|
||||
|
||||
killConnectionPoll();
|
||||
|
||||
if (ACTION_SET_UNDEF.equals(this.config.inputDisconnectAction)) {
|
||||
undefInputChannels();
|
||||
}
|
||||
if (ACTION_SET_UNDEF.equals(this.config.outputDisconnectAction)) {
|
||||
undefOutputChannels();
|
||||
}
|
||||
|
||||
clearChannelHandlers();
|
||||
|
||||
if (ljPigpio != null) {
|
||||
try {
|
||||
ljPigpio.gpioTerminate();
|
||||
this.jPigpio = null;
|
||||
} catch (PigpioException e) {
|
||||
// Best effort at a socket shutdown
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.debug("gpio disposed");
|
||||
} catch (Exception e) {
|
||||
logger.debug("Dispose exception :", e);
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
@ -10,8 +10,36 @@ thing-type.gpio.pigpio-remote.description = The remote pigpio thing represents a
|
||||
|
||||
# thing types config
|
||||
|
||||
thing-type.config.gpio.pigpio-remote.heartBeatInterval.label = Heart Beat Interval
|
||||
thing-type.config.gpio.pigpio-remote.heartBeatInterval.description = Time in ms to send CMD_TICK calls on the communication socket. Used to detect and recover from pigpiod disconnects.
|
||||
thing-type.config.gpio.pigpio-remote.host.label = Network Address
|
||||
thing-type.config.gpio.pigpio-remote.host.description = Network address of the Raspberry Pi.
|
||||
thing-type.config.gpio.pigpio-remote.inputConnectAction.label = Input Channel Connect Action
|
||||
thing-type.config.gpio.pigpio-remote.inputConnectAction.description = When a pigpiod connection is first established after binding INITIALIZATION. The desired action to perform on input channels. REFRESH: Send a REFRESH command to the channel. NOTHING: Leave all channels at their current state.
|
||||
thing-type.config.gpio.pigpio-remote.inputConnectAction.option.REFRESH = Refresh Channel
|
||||
thing-type.config.gpio.pigpio-remote.inputConnectAction.option.NOTHING = Do Nothing
|
||||
thing-type.config.gpio.pigpio-remote.inputDisconnectAction.label = Input Channel Disconnect Action
|
||||
thing-type.config.gpio.pigpio-remote.inputDisconnectAction.description = When a pigpiod disconnect is encountered. The desired action to perform on input channel. SETUNDEF: Set all configured channels to UNDEF. NOTHING: Leave all channels at their current state.
|
||||
thing-type.config.gpio.pigpio-remote.inputDisconnectAction.option.SETUNDEF = Set Undef
|
||||
thing-type.config.gpio.pigpio-remote.inputDisconnectAction.option.NOTHING = Do Nothing
|
||||
thing-type.config.gpio.pigpio-remote.inputReconnectAction.label = Input Channel Reconnect Action
|
||||
thing-type.config.gpio.pigpio-remote.inputReconnectAction.description = When a pigpiod connection is re-established after being disconnected. The desired action to perform on input channels. REFRESH: Send a REFRESH command to the channel. NOTHING: Leave all channels at their current state.
|
||||
thing-type.config.gpio.pigpio-remote.inputReconnectAction.option.REFRESH = Refresh Channel
|
||||
thing-type.config.gpio.pigpio-remote.inputReconnectAction.option.NOTHING = Do Nothing
|
||||
thing-type.config.gpio.pigpio-remote.outputConnectAction.label = Output Channel Connect Action
|
||||
thing-type.config.gpio.pigpio-remote.outputConnectAction.description = When a pigpiod connection is first established after binding INITIALIZATION. The desired action to perform on outputs. ALLOFF: Update the GPIO pin to OFF. ALLON: Update the GPIO pin to ON. REFRESH: Send a REFRESH command to the channel. NOTHING: Leave all channels at their current state.
|
||||
thing-type.config.gpio.pigpio-remote.outputConnectAction.option.ALLOFF = All OFF
|
||||
thing-type.config.gpio.pigpio-remote.outputConnectAction.option.ALLON = All ON
|
||||
thing-type.config.gpio.pigpio-remote.outputConnectAction.option.REFRESH = Refresh Channel
|
||||
thing-type.config.gpio.pigpio-remote.outputConnectAction.option.NOTHING = Do Nothing
|
||||
thing-type.config.gpio.pigpio-remote.outputDisconnectAction.label = Output Channel Disconnect Action
|
||||
thing-type.config.gpio.pigpio-remote.outputDisconnectAction.description = When a pigpiod disconnect is encountered. The desired action to perform on outputs. SETUNDEF: Set all configured channels to UNDEF. NOTHING: Leave all channels at their current state.
|
||||
thing-type.config.gpio.pigpio-remote.outputDisconnectAction.option.SETUNDEF = Set Undef
|
||||
thing-type.config.gpio.pigpio-remote.outputDisconnectAction.option.NOTHING = Do Nothing
|
||||
thing-type.config.gpio.pigpio-remote.outputReconnectAction.label = Output Channel Reconnect Action
|
||||
thing-type.config.gpio.pigpio-remote.outputReconnectAction.description = When a pigpiod connection is re-established after being disconnected. The desired action to perform on outputs. REFRESH: Send a REFRESH command to the channel. NOTHING: Leave all channels at their current state.
|
||||
thing-type.config.gpio.pigpio-remote.outputReconnectAction.option.REFRESH = Refresh Channel
|
||||
thing-type.config.gpio.pigpio-remote.outputReconnectAction.option.NOTHING = Do Nothing
|
||||
thing-type.config.gpio.pigpio-remote.port.label = Port
|
||||
thing-type.config.gpio.pigpio-remote.port.description = Port of pigpio on the remote Raspberry Pi.
|
||||
|
||||
@ -25,10 +53,16 @@ channel-type.gpio.pigpio-digital-output.description = Set digital state of a GPI
|
||||
# channel types config
|
||||
|
||||
channel-type.config.gpio.pigpio-digital-input.debouncingTime.label = Delay Time
|
||||
channel-type.config.gpio.pigpio-digital-input.debouncingTime.description = Time in ms to double check if value hasn't changed
|
||||
channel-type.config.gpio.pigpio-digital-input.debouncingTime.description = Time in ms to double check if value hasn't changed. Be sure that the maximum latency of your network is not greater than two times this value.
|
||||
channel-type.config.gpio.pigpio-digital-input.edgeMode.label = Edge Detection Mode
|
||||
channel-type.config.gpio.pigpio-digital-input.edgeMode.description = Edge detection mode of the GPIO pin
|
||||
channel-type.config.gpio.pigpio-digital-input.edgeMode.option.EDGE_EITHER = Either Edge
|
||||
channel-type.config.gpio.pigpio-digital-input.edgeMode.option.EDGE_FALLING = Falling Edge
|
||||
channel-type.config.gpio.pigpio-digital-input.edgeMode.option.EDGE_RISING = Rising Edge
|
||||
channel-type.config.gpio.pigpio-digital-input.gpioId.label = GPIO Pin
|
||||
channel-type.config.gpio.pigpio-digital-input.gpioId.description = GPIO pin to use as input
|
||||
channel-type.config.gpio.pigpio-digital-input.invert.label = Invert
|
||||
channel-type.config.gpio.pigpio-digital-input.invert.description = Inverts the GPIO pin state from the channel state. Setting this to true can simulate an active low GPIO pin.
|
||||
channel-type.config.gpio.pigpio-digital-input.pullupdown.label = Pull Up/Down Resistor
|
||||
channel-type.config.gpio.pigpio-digital-input.pullupdown.description = Configure Pull Up/Down Resistor of GPIO pin
|
||||
channel-type.config.gpio.pigpio-digital-input.pullupdown.option.OFF = Off
|
||||
@ -37,3 +71,11 @@ channel-type.config.gpio.pigpio-digital-input.pullupdown.option.UP = Pull Up
|
||||
channel-type.config.gpio.pigpio-digital-output.gpioId.label = GPIO Pin
|
||||
channel-type.config.gpio.pigpio-digital-output.gpioId.description = GPIO pin to use as output
|
||||
channel-type.config.gpio.pigpio-digital-output.invert.label = Invert
|
||||
channel-type.config.gpio.pigpio-digital-output.invert.description = Inverts the GPIO pin state from the channel state. Setting this to true can simulate an active low GPIO pin.
|
||||
channel-type.config.gpio.pigpio-digital-output.pulse.label = Pulse
|
||||
channel-type.config.gpio.pigpio-digital-output.pulse.description = Issues the pulse command after the given number of milliseconds. Used to pulse outputs.
|
||||
channel-type.config.gpio.pigpio-digital-output.pulseCommand.label = Pulse Command
|
||||
channel-type.config.gpio.pigpio-digital-output.pulseCommand.description = The command to issue after the pulse duration to complete the pulse. Blink will alternate ON/OFF, useful for beacons or flashing leds.
|
||||
channel-type.config.gpio.pigpio-digital-output.pulseCommand.option.OFF = Off
|
||||
channel-type.config.gpio.pigpio-digital-output.pulseCommand.option.ON = On
|
||||
channel-type.config.gpio.pigpio-digital-output.pulseCommand.option.BLINK = Blink
|
||||
|
@ -18,6 +18,7 @@
|
||||
<context>network_address</context>
|
||||
<label>Network Address</label>
|
||||
<description>Network address of the Raspberry Pi.</description>
|
||||
<default>127.0.0.1</default>
|
||||
</parameter>
|
||||
<parameter name="port" type="integer" min="0" max="65535">
|
||||
<context>port</context>
|
||||
@ -25,6 +26,141 @@
|
||||
<description>Port of pigpio on the remote Raspberry Pi.</description>
|
||||
<default>8888</default>
|
||||
</parameter>
|
||||
<parameter name="heartBeatInterval" type="integer" min="100" max="2147483647">
|
||||
<context>time</context>
|
||||
<label>Heart Beat Interval</label>
|
||||
<description>
|
||||
Time in ms to send CMD_TICK calls on the communication socket.
|
||||
Used to detect and recover from pigpiod
|
||||
disconnects.
|
||||
</description>
|
||||
<default>30000</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="inputConnectAction" type="text">
|
||||
<label>Input Channel Connect Action</label>
|
||||
<description>
|
||||
When a pigpiod connection is first established after
|
||||
binding INITIALIZATION.
|
||||
The desired action to
|
||||
perform on input channels.
|
||||
REFRESH: Send a REFRESH command
|
||||
to the channel.
|
||||
NOTHING: Leave
|
||||
all channels
|
||||
at their
|
||||
current
|
||||
state.
|
||||
</description>
|
||||
<options>
|
||||
<option value="REFRESH">Refresh Channel</option>
|
||||
<option value="NOTHING">Do Nothing</option>
|
||||
</options>
|
||||
<limitToOptions>true</limitToOptions>
|
||||
<default>NOTHING</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="inputDisconnectAction" type="text">
|
||||
<label>Input Channel Disconnect Action</label>
|
||||
<description>
|
||||
When a pigpiod disconnect is encountered.
|
||||
The desired action to perform on input channel.
|
||||
SETUNDEF: Set
|
||||
all configured channels to UNDEF.
|
||||
NOTHING: Leave all channels at their current state.
|
||||
</description>
|
||||
<options>
|
||||
<option value="SETUNDEF">Set Undef</option>
|
||||
<option value="NOTHING">Do Nothing</option>
|
||||
</options>
|
||||
<limitToOptions>true</limitToOptions>
|
||||
<default>NOTHING</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="inputReconnectAction" type="text">
|
||||
<label>Input Channel Reconnect Action</label>
|
||||
<description>
|
||||
When a pigpiod connection is re-established after being disconnected.
|
||||
The desired action to perform on
|
||||
input channels.
|
||||
REFRESH: Send a REFRESH command
|
||||
to the channel.
|
||||
NOTHING: Leave all
|
||||
channels at their
|
||||
current
|
||||
state.
|
||||
</description>
|
||||
<options>
|
||||
<option value="REFRESH">Refresh Channel</option>
|
||||
<option value="NOTHING">Do Nothing</option>
|
||||
</options>
|
||||
<limitToOptions>true</limitToOptions>
|
||||
<default>NOTHING</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="outputConnectAction" type="text">
|
||||
<label>Output Channel Connect Action</label>
|
||||
<description>
|
||||
When a pigpiod connection is first established after
|
||||
binding INITIALIZATION.
|
||||
The desired action to
|
||||
perform on outputs.
|
||||
ALLOFF: Update the GPIO pin to OFF.
|
||||
ALLON: Update the GPIO pin to ON.
|
||||
REFRESH: Send a REFRESH
|
||||
command
|
||||
to the channel.
|
||||
NOTHING: Leave all
|
||||
channels at their current
|
||||
state.
|
||||
</description>
|
||||
<options>
|
||||
<option value="ALLOFF">All OFF</option>
|
||||
<option value="ALLON">All ON</option>
|
||||
<option value="REFRESH">Refresh Channel</option>
|
||||
<option value="NOTHING">Do Nothing</option>
|
||||
</options>
|
||||
<limitToOptions>true</limitToOptions>
|
||||
<default>NOTHING</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="outputDisconnectAction" type="text">
|
||||
<label>Output Channel Disconnect Action</label>
|
||||
<description>
|
||||
When a pigpiod disconnect is encountered.
|
||||
The desired action to perform on outputs.
|
||||
SETUNDEF: Set all
|
||||
configured channels to UNDEF.
|
||||
NOTHING: Leave all channels at their current state.
|
||||
</description>
|
||||
<options>
|
||||
<option value="SETUNDEF">Set Undef</option>
|
||||
<option value="NOTHING">Do Nothing</option>
|
||||
</options>
|
||||
<limitToOptions>true</limitToOptions>
|
||||
<default>NOTHING</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="outputReconnectAction" type="text">
|
||||
<label>Output Channel Reconnect Action</label>
|
||||
<description>
|
||||
When a pigpiod connection is re-established after being disconnected.
|
||||
The desired action to perform on
|
||||
outputs.
|
||||
REFRESH: Send a REFRESH command
|
||||
to the channel.
|
||||
NOTHING: Leave all
|
||||
channels at their current
|
||||
state.
|
||||
</description>
|
||||
<options>
|
||||
<option value="REFRESH">Refresh Channel</option>
|
||||
<option value="NOTHING">Do Nothing</option>
|
||||
</options>
|
||||
<limitToOptions>true</limitToOptions>
|
||||
<default>NOTHING</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
@ -35,18 +171,28 @@
|
||||
<state readOnly="true"/>
|
||||
|
||||
<config-description>
|
||||
<parameter name="gpioId" type="integer" required="true">
|
||||
<parameter name="gpioId" type="integer" required="true" min="1" max="2147483647">
|
||||
<label>GPIO Pin</label>
|
||||
<description>GPIO pin to use as input</description>
|
||||
</parameter>
|
||||
<parameter name="invert" type="boolean">
|
||||
<default>false</default>
|
||||
<label>Invert</label>
|
||||
<description>
|
||||
Inverts the GPIO pin state from the channel state.
|
||||
Setting this to true can simulate an active low GPIO
|
||||
pin.
|
||||
</description>
|
||||
</parameter>
|
||||
<parameter name="debouncingTime" type="integer" min="0">
|
||||
<context>time</context>
|
||||
<label>Delay Time</label>
|
||||
<description>Time in ms to double check if value hasn't changed</description>
|
||||
<description>
|
||||
Time in ms to double check if value hasn't changed.
|
||||
Be sure that the maximum latency of your
|
||||
network is
|
||||
not greater than two times this value.
|
||||
</description>
|
||||
<default>10</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
@ -61,6 +207,17 @@
|
||||
<limitToOptions>true</limitToOptions>
|
||||
<default>OFF</default>
|
||||
</parameter>
|
||||
<parameter name="edgeMode" type="text">
|
||||
<label>Edge Detection Mode</label>
|
||||
<description>Edge detection mode of the GPIO pin</description>
|
||||
<options>
|
||||
<option value="EDGE_EITHER">Either Edge</option>
|
||||
<option value="EDGE_FALLING">Falling Edge</option>
|
||||
<option value="EDGE_RISING">Rising Edge</option>
|
||||
</options>
|
||||
<limitToOptions>true</limitToOptions>
|
||||
<default>EDGE_EITHER</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</channel-type>
|
||||
|
||||
@ -76,6 +233,30 @@
|
||||
<parameter name="invert" type="boolean">
|
||||
<default>false</default>
|
||||
<label>Invert</label>
|
||||
<description>
|
||||
Inverts the GPIO pin state from the channel state.
|
||||
Setting this to true can simulate an active low GPIO
|
||||
pin.
|
||||
</description>
|
||||
</parameter>
|
||||
<parameter name="pulse" type="integer" min="0" max="2147483647">
|
||||
<label>Pulse</label>
|
||||
<description>Issues the pulse command after the given number of milliseconds. Used to pulse outputs.</description>
|
||||
<default>0</default>
|
||||
</parameter>
|
||||
<parameter name="pulseCommand" type="text">
|
||||
<label>Pulse Command</label>
|
||||
<description>The command to issue after the pulse duration to complete the pulse. Blink will alternate ON/OFF,
|
||||
useful for beacons
|
||||
or
|
||||
flashing leds.</description>
|
||||
<options>
|
||||
<option value="OFF">Off</option>
|
||||
<option value="ON">On</option>
|
||||
<option value="BLINK">Blink</option>
|
||||
</options>
|
||||
<limitToOptions>true</limitToOptions>
|
||||
<default>OFF</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</channel-type>
|
||||
|
Loading…
Reference in New Issue
Block a user