diff --git a/bundles/org.openhab.binding.opengarage/README.md b/bundles/org.openhab.binding.opengarage/README.md index 2fc30e1e1..155372a0f 100644 --- a/bundles/org.openhab.binding.opengarage/README.md +++ b/bundles/org.openhab.binding.opengarage/README.md @@ -4,7 +4,7 @@ The OpenGarage binding allows you to control an OpenGarage controller ( are supported. +OpenGarage controllers from are supported. ## Discovery @@ -19,6 +19,13 @@ As a minimum, the IP address is needed: - `port` - the port the OpenGarage is listening on. Defaults to port 80 - `refresh` - The frequency with which to refresh information from the OpenGarage controller specified in seconds. Defaults to 10 seconds. - `password` - The password to send commands to the OpenGarage. Defaults to "opendoor" +- `doorTransitionTimeSeconds` - Specifies how long it takes the garage door +to fully open / close after triggering it from OpenGarage, including auditory +beeps. Recommend to round up or pad by a second or two. +- `doorOpeningState` - Text state to report when garage is opening. Defaults to "OPENING". +- `doorOpenState` - Text state to report when garage is open (and not in transition). Defaults to "OPEN". +- `doorClosingState` - Text state to report when garage is closing. Defaults to "CLOSING". +- `doorClosedState` - Text state to report when garage is closed (and not in transition). Defaults to "CLOSED". ## Channels @@ -26,6 +33,7 @@ As a minimum, the IP address is needed: |----------------------|---------------|---------------------------------------------------------------------------------------| | distance | Number:Length | Distance reading from the OpenGarage controller (default in cm) | | status-switch | Switch | Door status (OFF = Closed, ON = Open), set "invert=true" on channel to invert switch | +| status-text | String | Text status of the current door state, including transition, using values from configuration: doorOpeningState, doorOpenState, doorClosingState, doorClosedState. | | status-contact | Contact | Door status (Open or Closed) | | status-rollershutter | Rollershutter | Door status (DOWN = Closed, UP = Open) | | vehicle-status | Number | Report vehicle presence (0=Not Detected, 1=Detected, 2=Unknown) | @@ -46,11 +54,13 @@ Contact OpenGarage_Status_Contact { channel="opengarage:opengarage:OpenGarage:st Rollershutter OpenGarage_Status_Rollershutter { channel="opengarage:opengarage:OpenGarage:status-rollershutter" } Number:Length OpenGarage_Distance { channel="opengarage:opengarage:OpenGarage:setpoint" } String OpenGarage_Vehicle { channel="opengarage:opengarage:OpenGarage:vehicle" } +String OpenGarage_StatusText { channel="opengarage:opengarage:OpenGarage:status-text" } ``` opengarage.sitemap: ```perl +Text item=OpenGarage_StatusText label="Status" Switch item=OpenGarage_Status icon="garagedoorclosed" mappings=[ON=Open] visibility=[OpenGarage_Status == OFF] Switch item=OpenGarage_Status icon="garagedooropen" mappings=[OFF=Close] visibility=[OpenGarage_Status == ON] Switch item=OpenGarage_Status icon="garage" @@ -58,4 +68,26 @@ Contact item=OpenGarage_Status_Contact icon="garage" Rollershutter item=OpenGarage_Status_Rollershutter icon="garage" Text item=OpenGarage_Distance label="OG distance" Text item=OpenGarage_Vehicle label="Vehicle Presence" + ``` + +## Adding to HomeKit + +If you have the HomeKit extension installed, you can control your OpenGarage instance via your iPhone. +To wire it up to HomeKit, you might specify the following: + +opengarage.items + +``` +Group gOpenGarage "OpenGarage Door" {homekit="GarageDoorOpener"} +Switch OpenGarage_TargetState "Target state" (gOpenGarage) {homekit="GarageDoorOpener.TargetDoorState", channel="opengarage:opengarage:deadbeef:status-switch"} +String OpenGarage_CurrentState "Current state" (gOpenGarage) {homekit="GarageDoorOpener.CurrentDoorState", channel="opengarage:opengarage:deadbeef:status-text"} +Switch OpenGarage_xxObstruction "Obstruction (do not use)" (gOpenGarage) {homekit="GarageDoorOpener.ObstructionStatus"} +``` + +The obstruction channel is not bound to any channel. +It's needed because HomeKit requires it, and OpenGarage does not provide it. +HomeKit requires a status for the garage door of `OPEN`, `CLOSED`, `CLOSING`, `OPENING`. +In order to report that, we must provide state transition information. +State transition information is inferred when the garage door state is changed. +For `doorTransitionTimeSeconds` since the last open/close command was issued, the binding reports the state as either "closing" or "opening". diff --git a/bundles/org.openhab.binding.opengarage/src/main/java/org/openhab/binding/opengarage/internal/OpenGarageBindingConstants.java b/bundles/org.openhab.binding.opengarage/src/main/java/org/openhab/binding/opengarage/internal/OpenGarageBindingConstants.java index 02704100d..e1a9267fc 100644 --- a/bundles/org.openhab.binding.opengarage/src/main/java/org/openhab/binding/opengarage/internal/OpenGarageBindingConstants.java +++ b/bundles/org.openhab.binding.opengarage/src/main/java/org/openhab/binding/opengarage/internal/OpenGarageBindingConstants.java @@ -34,6 +34,7 @@ public class OpenGarageBindingConstants { // List of all Channel ids public static final String CHANNEL_OG_DISTANCE = "distance"; public static final String CHANNEL_OG_STATUS = "status"; // now deprecated + public static final String CHANNEL_OG_STATUS_TEXT = "status-text"; public static final String CHANNEL_OG_STATUS_SWITCH = "status-switch"; public static final String CHANNEL_OG_STATUS_CONTACT = "status-contact"; public static final String CHANNEL_OG_STATUS_ROLLERSHUTTER = "status-rollershutter"; diff --git a/bundles/org.openhab.binding.opengarage/src/main/java/org/openhab/binding/opengarage/internal/OpenGarageConfiguration.java b/bundles/org.openhab.binding.opengarage/src/main/java/org/openhab/binding/opengarage/internal/OpenGarageConfiguration.java index 6f1473e8f..84ae82771 100644 --- a/bundles/org.openhab.binding.opengarage/src/main/java/org/openhab/binding/opengarage/internal/OpenGarageConfiguration.java +++ b/bundles/org.openhab.binding.opengarage/src/main/java/org/openhab/binding/opengarage/internal/OpenGarageConfiguration.java @@ -12,14 +12,23 @@ */ package org.openhab.binding.opengarage.internal; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * The OpenGarageConfiguration class contains fields mapping thing configuration parameters. * * @author Paul Smedley - Initial contribution */ +@NonNullByDefault public class OpenGarageConfiguration { - public String hostname; - public long port = 80; + public String hostname = ""; + public int port = 80; public String password = "opendoor"; - public long refresh = 10; + public int refresh = 10; + + public String doorOpeningState = "OPENING"; + public String doorOpenState = "OPEN"; + public String doorClosedState = "CLOSED"; + public String doorClosingState = "CLOSING"; + public int doorTransitionTimeSeconds = 17; } diff --git a/bundles/org.openhab.binding.opengarage/src/main/java/org/openhab/binding/opengarage/internal/OpenGarageHandler.java b/bundles/org.openhab.binding.opengarage/src/main/java/org/openhab/binding/opengarage/internal/OpenGarageHandler.java index ce0ca8752..dab22dbce 100644 --- a/bundles/org.openhab.binding.opengarage/src/main/java/org/openhab/binding/opengarage/internal/OpenGarageHandler.java +++ b/bundles/org.openhab.binding.opengarage/src/main/java/org/openhab/binding/opengarage/internal/OpenGarageHandler.java @@ -13,12 +13,14 @@ package org.openhab.binding.opengarage.internal; import java.io.IOException; +import java.time.Duration; +import java.time.Instant; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; -import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.function.Function; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.opengarage.internal.api.ControllerVariables; import org.openhab.binding.opengarage.internal.api.Enums.OpenGarageCommand; import org.openhab.core.library.types.DecimalType; @@ -52,34 +54,47 @@ public class OpenGarageHandler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(OpenGarageHandler.class); - private long refreshInterval; - private @NonNullByDefault({}) OpenGarageWebTargets webTargets; - private @Nullable ScheduledFuture pollFuture; + + // reference to periodically scheduled poll task + private Future pollScheduledFuture = CompletableFuture.completedFuture(null); + + // reference to one-shot poll task which gets scheduled after a garage state change command + private Future pollScheduledFutureTransition = CompletableFuture.completedFuture(null); + private Instant lastTransition; + private String lastTransitionText; + + private OpenGarageConfiguration config = new OpenGarageConfiguration(); public OpenGarageHandler(Thing thing) { super(thing); + this.lastTransition = Instant.MIN; + this.lastTransitionText = ""; } @Override - public void handleCommand(ChannelUID channelUID, Command command) { + public synchronized void handleCommand(ChannelUID channelUID, Command command) { try { logger.debug("Received command {} for thing '{}' on channel {}", command, thing.getUID().getAsString(), channelUID.getId()); - boolean invert = isChannelInverted(channelUID.getId()); + Function maybeInvert = getInverter(channelUID.getId()); switch (channelUID.getId()) { case OpenGarageBindingConstants.CHANNEL_OG_STATUS: case OpenGarageBindingConstants.CHANNEL_OG_STATUS_SWITCH: case OpenGarageBindingConstants.CHANNEL_OG_STATUS_ROLLERSHUTTER: - if (command.equals(OnOffType.ON) || command.equals(UpDownType.UP)) { - changeStatus(invert ? OpenGarageCommand.CLOSE : OpenGarageCommand.OPEN); - return; - } else if (command.equals(OnOffType.OFF) || command.equals(UpDownType.DOWN)) { - changeStatus(invert ? OpenGarageCommand.OPEN : OpenGarageCommand.CLOSE); - return; - } else if (command.equals(StopMoveType.STOP) || command.equals(StopMoveType.MOVE)) { + if (command.equals(StopMoveType.STOP) || command.equals(StopMoveType.MOVE)) { changeStatus(OpenGarageCommand.CLICK); - return; + } else { + boolean doorOpen = command.equals(OnOffType.ON) || command.equals(UpDownType.UP); + changeStatus(maybeInvert.apply(doorOpen) ? OpenGarageCommand.OPEN : OpenGarageCommand.CLOSE); + this.lastTransition = Instant.now(); + this.lastTransitionText = doorOpen ? this.config.doorOpeningState + : this.config.doorClosingState; + + this.poll(); // invoke poll directly to communicate the door transition state + this.pollScheduledFutureTransition.cancel(false); + this.pollScheduledFutureTransition = this.scheduler.schedule(this::poll, + this.config.doorTransitionTimeSeconds, TimeUnit.SECONDS); } break; default: @@ -91,107 +106,111 @@ public class OpenGarageHandler extends BaseThingHandler { @Override public void initialize() { - OpenGarageConfiguration config = getConfigAs(OpenGarageConfiguration.class); + this.config = getConfigAs(OpenGarageConfiguration.class); logger.debug("config.hostname = {}, refresh = {}, port = {}", config.hostname, config.refresh, config.port); - if (config.hostname == null) { + if (config.hostname.isEmpty()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Hostname/IP address must be set"); } else { - webTargets = new OpenGarageWebTargets(config.hostname, config.port, config.password); - refreshInterval = config.refresh; - - schedulePoll(); + updateStatus(ThingStatus.UNKNOWN); + int requestTimeout = Math.max(OpenGarageWebTargets.DEFAULT_TIMEOUT_MS, config.refresh * 1000); + webTargets = new OpenGarageWebTargets(config.hostname, config.port, config.password, requestTimeout); + this.pollScheduledFuture = this.scheduler.scheduleWithFixedDelay(this::poll, 1, config.refresh, + TimeUnit.SECONDS); } } @Override public void dispose() { + this.pollScheduledFuture.cancel(true); + this.pollScheduledFutureTransition.cancel(true); super.dispose(); - stopPoll(); } - private void schedulePoll() { - if (pollFuture != null) { - pollFuture.cancel(false); - } - logger.debug("Scheduling poll for 1 second out, then every {} s", refreshInterval); - pollFuture = scheduler.scheduleWithFixedDelay(this::poll, 1, refreshInterval, TimeUnit.SECONDS); - } - - private void poll() { + /** + * Update the state of the controller. + * + * + */ + private synchronized void poll() { try { logger.debug("Polling for state"); - pollStatus(); + ControllerVariables controllerVariables = webTargets.getControllerVariables(); + long lastTransitionAgoSecs = Duration.between(lastTransition, Instant.now()).getSeconds(); + boolean inTransition = lastTransitionAgoSecs < this.config.doorTransitionTimeSeconds; + if (controllerVariables != null) { + updateStatus(ThingStatus.ONLINE); + updateState(OpenGarageBindingConstants.CHANNEL_OG_DISTANCE, + new QuantityType<>(controllerVariables.dist, MetricPrefix.CENTI(SIUnits.METRE))); + Function maybeInvert = getInverter( + OpenGarageBindingConstants.CHANNEL_OG_STATUS_SWITCH); + + if ((controllerVariables.door != 0) && (controllerVariables.door != 1)) { + logger.debug("Received unknown door value: {}", controllerVariables.door); + } else { + boolean doorOpen = controllerVariables.door == 1; + OnOffType onOff = maybeInvert.apply(doorOpen) ? OnOffType.ON : OnOffType.OFF; + UpDownType upDown = doorOpen ? UpDownType.UP : UpDownType.DOWN; + OpenClosedType contact = doorOpen ? OpenClosedType.OPEN : OpenClosedType.CLOSED; + + String transitionText; + if (inTransition) { + transitionText = this.lastTransitionText; + } else { + transitionText = doorOpen ? this.config.doorOpenState : this.config.doorClosedState; + } + if (!inTransition) { + updateState(OpenGarageBindingConstants.CHANNEL_OG_STATUS, onOff); // deprecated channel + updateState(OpenGarageBindingConstants.CHANNEL_OG_STATUS_SWITCH, onOff); + } + updateState(OpenGarageBindingConstants.CHANNEL_OG_STATUS_ROLLERSHUTTER, upDown); + updateState(OpenGarageBindingConstants.CHANNEL_OG_STATUS_CONTACT, contact); + updateState(OpenGarageBindingConstants.CHANNEL_OG_STATUS_TEXT, new StringType(transitionText)); + } + + switch (controllerVariables.vehicle) { + case 0: + updateState(OpenGarageBindingConstants.CHANNEL_OG_VEHICLE, + new StringType("No vehicle detected")); + break; + case 1: + updateState(OpenGarageBindingConstants.CHANNEL_OG_VEHICLE, new StringType("Vehicle detected")); + break; + case 2: + updateState(OpenGarageBindingConstants.CHANNEL_OG_VEHICLE, + new StringType("Vehicle status unknown")); + break; + case 3: + updateState(OpenGarageBindingConstants.CHANNEL_OG_VEHICLE, + new StringType("Vehicle status not available")); + break; + + default: + logger.debug("Received unknown vehicle value: {}", controllerVariables.vehicle); + } + updateState(OpenGarageBindingConstants.CHANNEL_OG_VEHICLE_STATUS, + new DecimalType(controllerVariables.vehicle)); + } } catch (IOException e) { logger.debug("Could not connect to OpenGarage controller", e); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Could not connect to OpenGarage controller"); } catch (RuntimeException e) { - logger.warn("Unexpected error connecting to OpenGarage controller", e); + logger.debug("Unexpected error connecting to OpenGarage controller", e); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); } } - private void stopPoll() { - final Future future = pollFuture; - if (future != null && !future.isCancelled()) { - future.cancel(true); - pollFuture = null; - } - } - - private void pollStatus() throws IOException { - ControllerVariables controllerVariables = webTargets.getControllerVariables(); - updateStatus(ThingStatus.ONLINE); - if (controllerVariables != null) { - updateState(OpenGarageBindingConstants.CHANNEL_OG_DISTANCE, - new QuantityType<>(controllerVariables.dist, MetricPrefix.CENTI(SIUnits.METRE))); - boolean invert = isChannelInverted(OpenGarageBindingConstants.CHANNEL_OG_STATUS_SWITCH); - switch (controllerVariables.door) { - case 0: - updateState(OpenGarageBindingConstants.CHANNEL_OG_STATUS, invert ? OnOffType.ON : OnOffType.OFF); - updateState(OpenGarageBindingConstants.CHANNEL_OG_STATUS_SWITCH, - invert ? OnOffType.ON : OnOffType.OFF); - updateState(OpenGarageBindingConstants.CHANNEL_OG_STATUS_ROLLERSHUTTER, UpDownType.DOWN); - updateState(OpenGarageBindingConstants.CHANNEL_OG_STATUS_CONTACT, OpenClosedType.CLOSED); - break; - case 1: - updateState(OpenGarageBindingConstants.CHANNEL_OG_STATUS, invert ? OnOffType.OFF : OnOffType.ON); - updateState(OpenGarageBindingConstants.CHANNEL_OG_STATUS_SWITCH, - invert ? OnOffType.OFF : OnOffType.ON); - updateState(OpenGarageBindingConstants.CHANNEL_OG_STATUS_ROLLERSHUTTER, UpDownType.UP); - updateState(OpenGarageBindingConstants.CHANNEL_OG_STATUS_CONTACT, OpenClosedType.OPEN); - break; - default: - logger.warn("Received unknown door value: {}", controllerVariables.door); - } - switch (controllerVariables.vehicle) { - case 0: - updateState(OpenGarageBindingConstants.CHANNEL_OG_VEHICLE, new StringType("No vehicle detected")); - break; - case 1: - updateState(OpenGarageBindingConstants.CHANNEL_OG_VEHICLE, new StringType("Vehicle detected")); - break; - case 2: - updateState(OpenGarageBindingConstants.CHANNEL_OG_VEHICLE, - new StringType("Vehicle status unknown")); - break; - case 3: - updateState(OpenGarageBindingConstants.CHANNEL_OG_VEHICLE, - new StringType("Vehicle status not available")); - break; - default: - logger.warn("Received unknown vehicle value: {}", controllerVariables.vehicle); - } - updateState(OpenGarageBindingConstants.CHANNEL_OG_VEHICLE_STATUS, - new DecimalType(controllerVariables.vehicle)); - } - } - private void changeStatus(OpenGarageCommand status) throws OpenGarageCommunicationException { webTargets.setControllerVariables(status); } - private boolean isChannelInverted(String channelUID) { + private Function getInverter(String channelUID) { Channel channel = getThing().getChannel(channelUID); - return channel != null && channel.getConfiguration().as(OpenGarageChannelConfiguration.class).invert; + boolean invert = channel != null && channel.getConfiguration().as(OpenGarageChannelConfiguration.class).invert; + if (invert) { + return onOff -> !onOff; + } else { + return Function.identity(); + } } } diff --git a/bundles/org.openhab.binding.opengarage/src/main/java/org/openhab/binding/opengarage/internal/OpenGarageWebTargets.java b/bundles/org.openhab.binding.opengarage/src/main/java/org/openhab/binding/opengarage/internal/OpenGarageWebTargets.java index feb9c195c..ea313e771 100644 --- a/bundles/org.openhab.binding.opengarage/src/main/java/org/openhab/binding/opengarage/internal/OpenGarageWebTargets.java +++ b/bundles/org.openhab.binding.opengarage/src/main/java/org/openhab/binding/opengarage/internal/OpenGarageWebTargets.java @@ -27,16 +27,18 @@ import org.slf4j.LoggerFactory; * */ public class OpenGarageWebTargets { - private static final int TIMEOUT_MS = 30000; + public static int DEFAULT_TIMEOUT_MS = 30000; private String getControllerVariablesUri; private String changeControllerVariablesUri; private final Logger logger = LoggerFactory.getLogger(OpenGarageWebTargets.class); + private int timeoutMs; - public OpenGarageWebTargets(String ipAddress, long port, String password) { + public OpenGarageWebTargets(String ipAddress, long port, String password, int timeoutMs) { String baseUri = "http://" + ipAddress + ":" + port + "/"; - getControllerVariablesUri = baseUri + "jc"; - changeControllerVariablesUri = baseUri + "cc?dkey=" + password; + this.timeoutMs = timeoutMs; + this.getControllerVariablesUri = baseUri + "jc"; + this.changeControllerVariablesUri = baseUri + "cc?dkey=" + password; } public ControllerVariables getControllerVariables() throws OpenGarageCommunicationException { @@ -73,7 +75,7 @@ public class OpenGarageWebTargets { String response; synchronized (this) { try { - response = HttpUtil.executeUrl("GET", uriWithParams, TIMEOUT_MS); + response = HttpUtil.executeUrl("GET", uriWithParams, this.timeoutMs); } catch (IOException ex) { logger.debug("{}", ex.getLocalizedMessage(), ex); // Response will also be set to null if parsing in executeUrl fails so we use null here to make the diff --git a/bundles/org.openhab.binding.opengarage/src/main/resources/OH-INF/i18n/opengarage.properties b/bundles/org.openhab.binding.opengarage/src/main/resources/OH-INF/i18n/opengarage.properties index a83666c9c..5bf5c729d 100644 --- a/bundles/org.openhab.binding.opengarage/src/main/resources/OH-INF/i18n/opengarage.properties +++ b/bundles/org.openhab.binding.opengarage/src/main/resources/OH-INF/i18n/opengarage.properties @@ -18,6 +18,16 @@ thing-type.config.opengarage.opengarage.port.label = Port thing-type.config.opengarage.opengarage.port.description = Port of the OpenGarage Web API interface. thing-type.config.opengarage.opengarage.refresh.label = Refresh Interval thing-type.config.opengarage.opengarage.refresh.description = Specifies the refresh interval in seconds. +thing-type.config.opengarage.opengarage.doorTransitionTimeSeconds.label = Door Transition Time +thing-type.config.opengarage.opengarage.doorTransitionTimeSeconds.description = Specifies number of seconds that it takes for the garage door to fully open / close, including the time it takes for OpenHab to emit beeps. Round up. +thing-type.config.opengarage.opengarage.doorOpeningState.label = Door Opening State +thing-type.config.opengarage.opengarage.doorOpeningState.description = Text state to report when garage is opening. Defaults to "OPENING". +thing-type.config.opengarage.opengarage.doorOpenState.label = Door Open State +thing-type.config.opengarage.opengarage.doorOpenState.description = Text state to report when garage is open (and not in transition). Defaults to "OPEN". +thing-type.config.opengarage.opengarage.doorClosingState.label = Door Closing State +thing-type.config.opengarage.opengarage.doorClosingState.description = Text state to report when garage is closing. Defaults to "CLOSING". +thing-type.config.opengarage.opengarage.doorClosedState.label = Door Closed State +thing-type.config.opengarage.opengarage.doorClosedState.description = Text state to report when garage is closed (and not in transition). Defaults to "CLOSED". # channel types @@ -31,6 +41,8 @@ channel-type.opengarage.opengarage-status-switch.label = Status channel-type.opengarage.opengarage-status-switch.description = On/Off Status of the OG unit channel-type.opengarage.opengarage-status.label = Status channel-type.opengarage.opengarage-status.description = On/Off Status of the OG unit (now deprecated, use status-switch instead) +channel-type.opengarage.opengarage-status-text.label = Text status +channel-type.opengarage.opengarage-status-text.description = Text status of the current door state, including transition, using values from configuration: doorOpeningState, doorOpenState, doorClosingState, doorClosedState. channel-type.opengarage.opengarage-vehicle-status.label = Vehicle Presence channel-type.opengarage.opengarage-vehicle-status.description = Vehicle presence detection channel-type.opengarage.opengarage-vehicle-status.state.option.0 = No vehicle detected diff --git a/bundles/org.openhab.binding.opengarage/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.opengarage/src/main/resources/OH-INF/thing/thing-types.xml index 9395f6f0c..7cde3d295 100644 --- a/bundles/org.openhab.binding.opengarage/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.opengarage/src/main/resources/OH-INF/thing/thing-types.xml @@ -16,8 +16,13 @@ + + + 1 + + @@ -34,13 +39,38 @@ password opendoor - + Specifies the refresh interval in seconds. 60 + + + Specifies number of seconds that it takes for the garage door to fully open / close, including the time + it takes for OpenHab to emit beeps. Round up. + 17 + + + + Text state to report when garage is opening + OPENING + + + + Text state to report when garage is open (and not in transition) + OPEN + + + + Text state to report when garage is closing + CLOSING + + + + Text state to report when garage is closed (and not in transition) + CLOSED + - @@ -113,4 +143,11 @@ + + String + + Text status of the current door state, including transition, using values from configuration: + doorOpeningState, doorOpenState, doorClosingState, doorClosedState. + + diff --git a/bundles/org.openhab.binding.opengarage/src/main/resources/OH-INF/update/instructions.xml b/bundles/org.openhab.binding.opengarage/src/main/resources/OH-INF/update/instructions.xml new file mode 100644 index 000000000..65663e15a --- /dev/null +++ b/bundles/org.openhab.binding.opengarage/src/main/resources/OH-INF/update/instructions.xml @@ -0,0 +1,16 @@ + + + + + + + + opengarage:opengarage-status-text + + + + + +