added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
32
bundles/org.openhab.binding.onebusaway/.classpath
Normal file
32
bundles/org.openhab.binding.onebusaway/.classpath
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
23
bundles/org.openhab.binding.onebusaway/.project
Normal file
23
bundles/org.openhab.binding.onebusaway/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.openhab.binding.onebusaway</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
13
bundles/org.openhab.binding.onebusaway/NOTICE
Normal file
13
bundles/org.openhab.binding.onebusaway/NOTICE
Normal file
@@ -0,0 +1,13 @@
|
||||
This content is produced and maintained by the openHAB project.
|
||||
|
||||
* Project home: https://www.openhab.org
|
||||
|
||||
== Declared Project Licenses
|
||||
|
||||
This program and the accompanying materials are made available under the terms
|
||||
of the Eclipse Public License 2.0 which is available at
|
||||
https://www.eclipse.org/legal/epl-2.0/.
|
||||
|
||||
== Source Code
|
||||
|
||||
https://github.com/openhab/openhab-addons
|
||||
86
bundles/org.openhab.binding.onebusaway/README.md
Normal file
86
bundles/org.openhab.binding.onebusaway/README.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# OneBusAway Binding
|
||||
|
||||
[OneBusAway](https://onebusaway.org/) is an open source, real-time, transit-information service. This binding allows you to get events based on transit arrival and departures, so you can create rules to do something based on that data.
|
||||
|
||||
## Preparation
|
||||
|
||||
You will need to obtain an API key from the transit provider you want to load data from.
|
||||
Different providers of the service have different policies, so you will have to figure this part out for each [deployment](https://github.com/OneBusAway/onebusaway/wiki/OneBusAway-Deployments).
|
||||
|
||||
## Supported Things
|
||||
|
||||
This binding supports route arrival and departure times for all stops provided from a OneBusAway deployment.
|
||||
|
||||
## Binding Configuration
|
||||
|
||||
The following configuration options are available for the API binding:
|
||||
|
||||
| Parameter | Name | Description | Required |
|
||||
|-------------|------------|-------------------------------------------------------------------------------------|----------|
|
||||
| `apiKey` | API Key | The API key given to you by a transit provider for their deployment. | yes |
|
||||
| `apiServer` | API Server | The domain name of the deployment to talk to, e.g. `api.pugetsound.onebusaway.org`. | yes |
|
||||
|
||||
|
||||
The following configuration options are available for the Stop binding (which requires an API binding):
|
||||
|
||||
| Parameter | Name | Description | Required |
|
||||
|-----------|------|-------------|----------|
|
||||
| `stopId` | Stop ID | The OneBusAway ID of the stop to obtain data for, e.g. `1_26860`. | yes |
|
||||
| `interval` | Update Interval | The number of seconds between updates. | no |
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
The following configuration options are available for a Route (which requires a Stop binding):
|
||||
|
||||
| Parameter | Name | Description | Required |
|
||||
|-----------|----------|---------------------------------------------------------------------|----------|
|
||||
| `routeId` | Route ID | The OneBusAway ID of the route to obtain data for, e.g. `1_102574`. | yes |
|
||||
|
||||
|
||||
## Channels
|
||||
|
||||
The Route Thing supports the following state channels:
|
||||
|
||||
| Channel Type ID | Channel Kind | Item Type | Description |
|
||||
|------------------|--------------|-----------|----------------------------------------------------------------------------------------------------------|
|
||||
| arrival | state | DateTime | The arrival time of a Route at a Stop. |
|
||||
| departure | state | DateTime | The departure time of a Route at a Stop. |
|
||||
| update | state | DateTime | The last time this data was updated (per the data provider, not the last time openHAB updated the data). |
|
||||
| arrivalDeparture | trigger | DateTime | Triggered when a Route arrives or departs a Stop. |
|
||||
|
||||
|
||||
### Channel Configurations
|
||||
|
||||
The `arrival`, `departure`, and `arrivalDeparture` channels can be configured with an `offset` specifying the number of seconds to move an event back in time.
|
||||
|
||||
## Full Example
|
||||
|
||||
Here is an example of a configuration for a bus stop in Seattle, WA, USA that has three routes configured.
|
||||
|
||||
`demo.things`:
|
||||
|
||||
```
|
||||
Bridge onebusaway:api:pugetsound [apiKey="your-api-key", apiServer="api.pugetsound.onebusaway.org"] {
|
||||
Bridge onebusaway:stop:1_26860 [stopId="1_26860"] {
|
||||
Thing onebusaway:route:1_100193 [routeId="1_100193"]
|
||||
Thing onebusaway:route:1_102574 [routeId="1_102574"]
|
||||
Thing onebusaway:route:1_100252 [routeId="1_100252"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`demo.items`:
|
||||
|
||||
```
|
||||
// Route 1_100193 (#32)
|
||||
DateTime Fremont_32_Arrival "32 - University District" { channel="onebusaway:route:1_100193:arrival" }
|
||||
DateTime Fremont_32_Departure "32 - University District" { channel="onebusaway:route:1_100193:departure" }
|
||||
|
||||
// Route 1_102574 (#40)
|
||||
DateTime Fremont_40_Arrival "40 - Ballard" { channel="onebusaway:route:1_102574:arrival" }
|
||||
DateTime Fremont_40_Departure "40 - Ballard" { channel="onebusaway:route:1_102574:departure" }
|
||||
|
||||
// Route 1_100252 (#62)
|
||||
DateTime Fremont_62_Arrival "62 - Sand Point East Green Lake" { channel="onebusaway:route:1_100252:arrival" }
|
||||
DateTime Fremont_62_Departure "62 - Sand Point East Green Lake" { channel="onebusaway:route:1_100252:departure" }
|
||||
```
|
||||
17
bundles/org.openhab.binding.onebusaway/pom.xml
Normal file
17
bundles/org.openhab.binding.onebusaway/pom.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.onebusaway</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: OneBusAway Binding</name>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.onebusaway-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-binding-onebusaway" description="OneBusAway Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.onebusaway/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.onebusaway.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link OneBusAwayBinding} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Shawn Wilsher - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OneBusAwayBindingConstants {
|
||||
|
||||
public static final String BINDING_ID = "onebusaway";
|
||||
|
||||
// Things
|
||||
public static final ThingTypeUID THING_TYPE_API = new ThingTypeUID(BINDING_ID, "api");
|
||||
public static final ThingTypeUID THING_TYPE_ROUTE = new ThingTypeUID(BINDING_ID, "route");
|
||||
public static final ThingTypeUID THING_TYPE_STOP = new ThingTypeUID(BINDING_ID, "stop");
|
||||
|
||||
// Channel IDs
|
||||
public static final String CHANNEL_ID_ARRIVAL = "arrival";
|
||||
public static final String CHANNEL_ID_DEPARTURE = "departure";
|
||||
public static final String CHANNEL_ID_UPDATE = "update";
|
||||
|
||||
// Events
|
||||
public static final String EVENT_ARRIVAL = "ARRIVAL";
|
||||
public static final String EVENT_DEPARTURE = "DEPARTURE";
|
||||
|
||||
// Event channel IDs
|
||||
public static final String EVENT_CHANNEL_ID_ARRIVAL = "arrivalDeparture#event";
|
||||
|
||||
// Channel configs
|
||||
public static final String CHANNEL_CONFIG_OFFSET = "offset";
|
||||
|
||||
// API configs
|
||||
public static final String API_CONFIG_API_KEY = "apiKey";
|
||||
public static final String API_CONFIG_API_SERVER = "apiServer";
|
||||
|
||||
// Route configs
|
||||
public static final String ROUTE_CONFIG_ROUTE_ID = "routeId";
|
||||
|
||||
// Stop configs
|
||||
public static final String STOP_CONFIG_ID = "stopId";
|
||||
public static final String STOP_CONFIG_INTERVAL = "interval";
|
||||
|
||||
// Route properties
|
||||
public static final String ROUTE_PROPERTY_HEADSIGN = "headsign";
|
||||
public static final String ROUTE_PROPERTY_LONG_NAME = "longName";
|
||||
public static final String ROUTE_PROPERTY_SHORT_NAME = "shortName";
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.onebusaway.internal;
|
||||
|
||||
import static org.openhab.binding.onebusaway.internal.OneBusAwayBindingConstants.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.binding.onebusaway.internal.handler.ApiHandler;
|
||||
import org.openhab.binding.onebusaway.internal.handler.RouteHandler;
|
||||
import org.openhab.binding.onebusaway.internal.handler.StopHandler;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* The {@link OneBusAwayHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Shawn Wilsher - Initial contribution
|
||||
*/
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.onebusaway")
|
||||
@NonNullByDefault
|
||||
public class OneBusAwayHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet(Stream
|
||||
.of(ApiHandler.SUPPORTED_THING_TYPE, RouteHandler.SUPPORTED_THING_TYPE, StopHandler.SUPPORTED_THING_TYPE)
|
||||
.collect(Collectors.toSet()));
|
||||
|
||||
private final HttpClient httpClient;
|
||||
|
||||
@Activate
|
||||
public OneBusAwayHandlerFactory(@Reference final HttpClientFactory httpClientFactory) {
|
||||
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (thingTypeUID.equals(THING_TYPE_API)) {
|
||||
return new ApiHandler((Bridge) thing);
|
||||
} else if (thingTypeUID.equals(THING_TYPE_ROUTE)) {
|
||||
return new RouteHandler(thing);
|
||||
} else if (thingTypeUID.equals(THING_TYPE_STOP)) {
|
||||
return new StopHandler((Bridge) thing, httpClient);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.onebusaway.internal.config;
|
||||
|
||||
import static org.openhab.binding.onebusaway.internal.OneBusAwayBindingConstants.*;
|
||||
|
||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||
|
||||
/**
|
||||
* The {@link ApiConfiguration} defines the model for a API bridge configuration.
|
||||
*
|
||||
* @author Shawn Wilsher - Initial contribution
|
||||
*/
|
||||
public class ApiConfiguration {
|
||||
private String apiKey;
|
||||
private String apiServer;
|
||||
|
||||
/**
|
||||
* @return the API Key to access the OneBusAway server.
|
||||
*/
|
||||
public String getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the API Key for the OneBusAway server.
|
||||
*/
|
||||
public void setApiKey(String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the OneBusAway API server to use.
|
||||
*/
|
||||
public String getApiServer() {
|
||||
return apiServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the OneBusAway API server.
|
||||
*/
|
||||
public void setApiServer(String apiServer) {
|
||||
this.apiServer = apiServer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this).append(API_CONFIG_API_KEY, this.getApiKey())
|
||||
.append(API_CONFIG_API_SERVER, this.getApiServer()).toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.onebusaway.internal.config;
|
||||
|
||||
import static org.openhab.binding.onebusaway.internal.OneBusAwayBindingConstants.CHANNEL_CONFIG_OFFSET;
|
||||
|
||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||
|
||||
/**
|
||||
* The {@link ChannelConfig} defines the model for a channel configuration.
|
||||
*
|
||||
* @author Shawn Wilsher - Initial contribution
|
||||
*/
|
||||
public class ChannelConfig {
|
||||
private Integer offset = 0;
|
||||
|
||||
/**
|
||||
* @return the offset (in seconds).
|
||||
*/
|
||||
public Integer getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the offset (in seconds).
|
||||
*/
|
||||
public void setOffset(Integer offset) {
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this).append(CHANNEL_CONFIG_OFFSET, this.getOffset()).toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.onebusaway.internal.config;
|
||||
|
||||
import static org.openhab.binding.onebusaway.internal.OneBusAwayBindingConstants.ROUTE_CONFIG_ROUTE_ID;
|
||||
|
||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||
|
||||
/**
|
||||
* The {@link RouteConfiguration} defines the model for a route stop configuration.
|
||||
*
|
||||
* @author Shawn Wilsher - Initial contribution
|
||||
*/
|
||||
public class RouteConfiguration {
|
||||
|
||||
private String routeId;
|
||||
|
||||
/**
|
||||
* @return the route ID.
|
||||
*/
|
||||
public String getRouteId() {
|
||||
return routeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the route ID.
|
||||
*/
|
||||
public void setRouteId(String routeId) {
|
||||
this.routeId = routeId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this).append(ROUTE_CONFIG_ROUTE_ID, this.getRouteId()).toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.onebusaway.internal.config;
|
||||
|
||||
import static org.openhab.binding.onebusaway.internal.OneBusAwayBindingConstants.*;
|
||||
|
||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||
|
||||
/**
|
||||
* The {@link StopConfiguration} defines the model for a stop bridge configuration.
|
||||
*
|
||||
* @author Shawn Wilsher - Initial contribution
|
||||
*/
|
||||
public class StopConfiguration {
|
||||
|
||||
private Integer interval;
|
||||
private String stopId;
|
||||
|
||||
/**
|
||||
* @return the update interval (in seconds).
|
||||
*/
|
||||
public Integer getInterval() {
|
||||
return interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the update interval (in seconds).
|
||||
*/
|
||||
public void setInterval(Integer interval) {
|
||||
this.interval = interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the stop ID.
|
||||
*/
|
||||
public String getStopId() {
|
||||
return stopId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the stop ID.
|
||||
*/
|
||||
public void setStopId(String stopId) {
|
||||
this.stopId = stopId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this).append(STOP_CONFIG_INTERVAL, this.getInterval())
|
||||
.append(STOP_CONFIG_ID, this.getStopId()).toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.onebusaway.internal.handler;
|
||||
|
||||
import static org.openhab.binding.onebusaway.internal.OneBusAwayBindingConstants.THING_TYPE_API;
|
||||
|
||||
import org.openhab.binding.onebusaway.internal.config.ApiConfiguration;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link ApiHandler} is responsible for storing basic configuration data for talking to a OneBusAway API server.
|
||||
*
|
||||
* @author Shawn Wilsher - Initial contribution
|
||||
*/
|
||||
public class ApiHandler extends BaseBridgeHandler {
|
||||
public static final ThingTypeUID SUPPORTED_THING_TYPE = THING_TYPE_API;
|
||||
|
||||
private ApiConfiguration config;
|
||||
private Logger logger = LoggerFactory.getLogger(ApiHandler.class);
|
||||
|
||||
public ApiHandler(Bridge bridge) {
|
||||
super(bridge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
logger.warn("The API bridge is a read-only and can not handle commands.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing OneBusAway bridge...");
|
||||
|
||||
config = loadAndCheckConfiguration();
|
||||
if (config == null) {
|
||||
logger.debug("Initialization of OneBusAway API bridge failed!");
|
||||
return;
|
||||
}
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
|
||||
protected String getApiKey() {
|
||||
return config.getApiKey();
|
||||
}
|
||||
|
||||
protected String getApiServer() {
|
||||
return config.getApiServer();
|
||||
}
|
||||
|
||||
private ApiConfiguration loadAndCheckConfiguration() {
|
||||
ApiConfiguration config = getConfigAs(ApiConfiguration.class);
|
||||
if (config.getApiKey() == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "apiKey is not set");
|
||||
return null;
|
||||
}
|
||||
if (config.getApiServer() == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "apiServer is not set");
|
||||
return null;
|
||||
}
|
||||
return config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.onebusaway.internal.handler;
|
||||
|
||||
/**
|
||||
* The {@link ObaStopArrivalResponse} is a representation of the OneBusAway response for requesting a stops arrival
|
||||
* data.
|
||||
*
|
||||
* @see <a href=
|
||||
* "http://developer.onebusaway.org/modules/onebusaway-application-modules/current/api/where/methods/arrivals-and-departures-for-stop.html">arrivals-and-departures-for-stop
|
||||
* documentation</a>
|
||||
*
|
||||
* @author Shawn Wilsher - Initial contribution
|
||||
*/
|
||||
public class ObaStopArrivalResponse {
|
||||
public long currentTime;
|
||||
public Data data;
|
||||
|
||||
public class Data {
|
||||
public Entry entry;
|
||||
}
|
||||
|
||||
public class Entry {
|
||||
public ArrivalAndDeparture[] arrivalsAndDepartures;
|
||||
}
|
||||
|
||||
public class ArrivalAndDeparture implements Comparable<ArrivalAndDeparture> {
|
||||
public boolean predicted;
|
||||
public long predictedArrivalTime;
|
||||
public long predictedDepartureTime;
|
||||
public long scheduledArrivalTime;
|
||||
public long scheduledDepartureTime;
|
||||
public String routeLongName;
|
||||
public String routeShortName;
|
||||
public String routeId;
|
||||
public String stopId;
|
||||
public String tripHeadsign;
|
||||
|
||||
/**
|
||||
* Assumes other is for the same routeId and stopId. Sorts based on arrival time.
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(ArrivalAndDeparture other) {
|
||||
// Prefer predicated over scheduled times for order
|
||||
return (int) ((predicted ? predictedArrivalTime : scheduledArrivalTime)
|
||||
- (other.predicted ? other.predictedArrivalTime : other.scheduledArrivalTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.onebusaway.internal.handler;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The {@link RouteDataListener} is the interface used for the stop (a bridge) to communicate information about route
|
||||
* arrivals.
|
||||
*
|
||||
* @author Shawn Wilsher - Initial contribution
|
||||
*/
|
||||
interface RouteDataListener {
|
||||
/**
|
||||
* @return The routeId for this listener. {@link #onNewRouteData(List)} should only receive updates for
|
||||
* this route.
|
||||
*/
|
||||
String getRouteId();
|
||||
|
||||
/**
|
||||
* Called when new arrival and departure data is available for this listeners route (as specified by
|
||||
* {@link #getRouteId()}).
|
||||
*
|
||||
* @param lastUpdateTime a {@link long} representing the time the data was last updated.
|
||||
* @param data a {@link List} of data from the OneBusAway API.
|
||||
*/
|
||||
void onNewRouteData(long lastUpdateTime, List<ObaStopArrivalResponse.ArrivalAndDeparture> data);
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.onebusaway.internal.handler;
|
||||
|
||||
import static org.openhab.binding.onebusaway.internal.OneBusAwayBindingConstants.*;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.openhab.binding.onebusaway.internal.config.ChannelConfig;
|
||||
import org.openhab.binding.onebusaway.internal.config.RouteConfiguration;
|
||||
import org.openhab.binding.onebusaway.internal.handler.ObaStopArrivalResponse.ArrivalAndDeparture;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.type.ChannelKind;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link RouteHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Shawn Wilsher - Initial contribution
|
||||
*/
|
||||
public class RouteHandler extends BaseThingHandler implements RouteDataListener {
|
||||
|
||||
public static final ThingTypeUID SUPPORTED_THING_TYPE = THING_TYPE_ROUTE;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(RouteHandler.class);
|
||||
|
||||
private RouteConfiguration config;
|
||||
private List<ScheduledFuture<?>> scheduledFutures = new CopyOnWriteArrayList<>();
|
||||
|
||||
public RouteHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (RefreshType.REFRESH == command) {
|
||||
logger.debug("Refreshing {}...", channelUID);
|
||||
switch (channelUID.getId()) {
|
||||
case CHANNEL_ID_ARRIVAL:
|
||||
case CHANNEL_ID_DEPARTURE:
|
||||
case CHANNEL_ID_UPDATE:
|
||||
StopHandler stopHandler = getStopHandler();
|
||||
if (stopHandler != null) {
|
||||
stopHandler.forceUpdate();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logger.warn("Unnknown channel UID {} with comamnd {}", channelUID.getId(), command);
|
||||
}
|
||||
} else {
|
||||
logger.debug("The OneBusAway route is read-only and can not handle commands.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing OneBusAway route stop...");
|
||||
|
||||
config = loadAndCheckConfiguration();
|
||||
if (config == null) {
|
||||
logger.debug("Initialization of OneBusAway route stop failed!");
|
||||
return;
|
||||
}
|
||||
StopHandler stopHandler = getStopHandler();
|
||||
if (stopHandler != null) {
|
||||
// We will be marked as ONLINE when we get data in our callback, which should be immediately because the
|
||||
// StopHandler, our bridge, won't be marked as online until it has data itself.
|
||||
stopHandler.registerRouteDataListener(this);
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, "Bridge unavailable");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
cancelAllScheduledFutures();
|
||||
StopHandler stopHandler = getStopHandler();
|
||||
if (stopHandler != null) {
|
||||
stopHandler.unregisterRouteDataListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRouteId() {
|
||||
return config.getRouteId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewRouteData(long lastUpdateTime, List<ArrivalAndDeparture> data) {
|
||||
if (data.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// Publish to all of our linked channels.
|
||||
Calendar now = Calendar.getInstance();
|
||||
for (Channel channel : getThing().getChannels()) {
|
||||
if (channel.getKind() == ChannelKind.TRIGGER) {
|
||||
scheduleTriggerEvents(channel.getUID(), now, data);
|
||||
} else {
|
||||
publishChannel(channel.getUID(), now, lastUpdateTime, data);
|
||||
}
|
||||
}
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
|
||||
private StopHandler getStopHandler() {
|
||||
return (StopHandler) getBridge().getHandler();
|
||||
}
|
||||
|
||||
private RouteConfiguration loadAndCheckConfiguration() {
|
||||
RouteConfiguration config = getConfigAs(RouteConfiguration.class);
|
||||
if (config.getRouteId() == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "routeId is not set");
|
||||
return null;
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
private void cancelAllScheduledFutures() {
|
||||
for (ScheduledFuture<?> future : scheduledFutures) {
|
||||
if (!future.isDone() || !future.isCancelled()) {
|
||||
future.cancel(true);
|
||||
}
|
||||
}
|
||||
scheduledFutures = new CopyOnWriteArrayList<>();
|
||||
}
|
||||
|
||||
private void updatePropertiesFromArrivalAndDeparture(ArrivalAndDeparture data) {
|
||||
Map<String, String> props = editProperties();
|
||||
props.put(ROUTE_PROPERTY_HEADSIGN, data.tripHeadsign);
|
||||
props.put(ROUTE_PROPERTY_LONG_NAME, data.routeLongName);
|
||||
props.put(ROUTE_PROPERTY_SHORT_NAME, data.routeShortName);
|
||||
updateProperties(props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes the channel with data and possibly schedules work to update it again when the next event has passed.
|
||||
*/
|
||||
private void publishChannel(ChannelUID channelUID, Calendar now, long lastUpdateTime,
|
||||
List<ArrivalAndDeparture> arrivalAndDepartures) {
|
||||
if (channelUID.getId().equals(CHANNEL_ID_UPDATE)) {
|
||||
updateState(channelUID, new DateTimeType(
|
||||
ZonedDateTime.ofInstant(Instant.ofEpochMilli(lastUpdateTime), ZoneId.systemDefault())));
|
||||
return;
|
||||
}
|
||||
|
||||
ChannelConfig channelConfig = getThing().getChannel(channelUID.getId()).getConfiguration()
|
||||
.as(ChannelConfig.class);
|
||||
long offsetMs = TimeUnit.SECONDS.toMillis(channelConfig.getOffset());
|
||||
for (int i = 0; i < arrivalAndDepartures.size(); i++) {
|
||||
ArrivalAndDeparture data = arrivalAndDepartures.get(i);
|
||||
Calendar time;
|
||||
switch (channelUID.getId()) {
|
||||
case CHANNEL_ID_ARRIVAL:
|
||||
time = (new Calendar.Builder())
|
||||
.setInstant(
|
||||
(data.predicted ? data.predictedArrivalTime : data.scheduledArrivalTime) - offsetMs)
|
||||
.build();
|
||||
break;
|
||||
case CHANNEL_ID_DEPARTURE:
|
||||
time = (new Calendar.Builder()).setInstant(
|
||||
(data.predicted ? data.predictedDepartureTime : data.scheduledDepartureTime) - offsetMs)
|
||||
.build();
|
||||
break;
|
||||
default:
|
||||
logger.warn("No code to handle publishing to {}", channelUID.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not publish this if it's already passed.
|
||||
if (time.before(now)) {
|
||||
logger.debug("Not notifying {} because it is in the past.", channelUID.getId());
|
||||
continue;
|
||||
}
|
||||
updateState(channelUID,
|
||||
new DateTimeType(ZonedDateTime.ofInstant(time.toInstant(), ZoneId.systemDefault())));
|
||||
|
||||
// Update properties only when we update arrival information. This is not perfect.
|
||||
if (channelUID.getId().equals(CHANNEL_ID_ARRIVAL)) {
|
||||
updatePropertiesFromArrivalAndDeparture(data);
|
||||
}
|
||||
|
||||
// Schedule updates in the future. These may be canceled if we are notified about new data in the future.
|
||||
List<ArrivalAndDeparture> remaining = arrivalAndDepartures.subList(i + 1, arrivalAndDepartures.size());
|
||||
if (remaining.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
scheduledFutures.add(scheduler.schedule(() -> {
|
||||
publishChannel(channelUID, Calendar.getInstance(), lastUpdateTime, remaining);
|
||||
}, time.getTimeInMillis() - now.getTimeInMillis(), TimeUnit.MILLISECONDS));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleTriggerEvents(ChannelUID channelUID, Calendar now,
|
||||
List<ArrivalAndDeparture> arrivalAndDepartures) {
|
||||
scheduleTriggerEvents(channelUID, now, arrivalAndDepartures,
|
||||
(ArrivalAndDeparture data) -> data.predicted ? data.predictedArrivalTime : data.scheduledArrivalTime,
|
||||
EVENT_ARRIVAL);
|
||||
scheduleTriggerEvents(channelUID, now, arrivalAndDepartures, new Function<ArrivalAndDeparture, Long>() {
|
||||
@Override
|
||||
public Long apply(ArrivalAndDeparture data) {
|
||||
return data.predicted ? data.predictedDepartureTime : data.scheduledDepartureTime;
|
||||
}
|
||||
}, EVENT_DEPARTURE);
|
||||
}
|
||||
|
||||
private void scheduleTriggerEvents(ChannelUID channelUID, Calendar now,
|
||||
List<ArrivalAndDeparture> arrivalAndDepartures, Function<ArrivalAndDeparture, Long> dataPiece,
|
||||
String event) {
|
||||
ChannelConfig channelConfig = getThing().getChannel(channelUID.getId()).getConfiguration()
|
||||
.as(ChannelConfig.class);
|
||||
long offsetMs = TimeUnit.SECONDS.toMillis(channelConfig.getOffset());
|
||||
for (ArrivalAndDeparture data : arrivalAndDepartures) {
|
||||
long time = dataPiece.apply(data);
|
||||
// Do not schedule this if it's already passed.
|
||||
Calendar cal = (new Calendar.Builder()).setInstant(time - offsetMs).build();
|
||||
if (cal.before(now)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Schedule this trigger
|
||||
scheduledFutures.add(scheduler.schedule(() -> {
|
||||
triggerChannel(channelUID, event);
|
||||
}, cal.getTimeInMillis() - now.getTimeInMillis(), TimeUnit.MILLISECONDS));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.onebusaway.internal.handler;
|
||||
|
||||
import static org.openhab.binding.onebusaway.internal.OneBusAwayBindingConstants.THING_TYPE_STOP;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.openhab.binding.onebusaway.internal.config.StopConfiguration;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* The {@link StopHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Shawn Wilsher - Initial contribution
|
||||
*/
|
||||
public class StopHandler extends BaseBridgeHandler {
|
||||
|
||||
public static final ThingTypeUID SUPPORTED_THING_TYPE = THING_TYPE_STOP;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(StopHandler.class);
|
||||
|
||||
private StopConfiguration config;
|
||||
private Gson gson;
|
||||
private HttpClient httpClient;
|
||||
private ScheduledFuture<?> pollingJob;
|
||||
private AtomicBoolean fetchInProgress = new AtomicBoolean(false);
|
||||
private long routeDataLastUpdateMs = 0;
|
||||
private final Map<String, List<ObaStopArrivalResponse.ArrivalAndDeparture>> routeData = new HashMap<>();
|
||||
private List<RouteDataListener> routeDataListeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
public StopHandler(Bridge bridge, HttpClient httpClient) {
|
||||
super(bridge);
|
||||
this.httpClient = httpClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (RefreshType.REFRESH == command) {
|
||||
logger.debug("Refreshing {}...", channelUID);
|
||||
forceUpdate();
|
||||
} else {
|
||||
logger.debug("The OneBusAway Stop is a read-only and can not handle commands.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing OneBusAway stop bridge...");
|
||||
|
||||
config = loadAndCheckConfiguration();
|
||||
if (config == null) {
|
||||
logger.debug("Initialization of OneBusAway bridge failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Do the rest of the work asynchronously because it can take a while.
|
||||
scheduler.submit(() -> {
|
||||
gson = new Gson();
|
||||
|
||||
pollingJob = scheduler.scheduleWithFixedDelay(this::fetchAndUpdateStopData, 0, config.getInterval(),
|
||||
TimeUnit.SECONDS);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (pollingJob != null && !pollingJob.isCancelled()) {
|
||||
pollingJob.cancel(true);
|
||||
pollingJob = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the listener to receive updates about arrival and departure times for its route.
|
||||
*
|
||||
* @param listener
|
||||
* @return true if successful.
|
||||
*/
|
||||
protected boolean registerRouteDataListener(RouteDataListener listener) {
|
||||
if (listener == null) {
|
||||
throw new IllegalArgumentException("It makes no sense to register a null listener!");
|
||||
}
|
||||
boolean added = routeDataListeners.add(listener);
|
||||
if (added) {
|
||||
String routeId = listener.getRouteId();
|
||||
List<ObaStopArrivalResponse.ArrivalAndDeparture> copiedRouteData;
|
||||
synchronized (routeData) {
|
||||
copiedRouteData = new ArrayList<>(routeData.get(routeId));
|
||||
}
|
||||
Collections.sort(copiedRouteData);
|
||||
listener.onNewRouteData(routeDataLastUpdateMs, copiedRouteData);
|
||||
}
|
||||
return added;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the listener so it no longer receives updates about arrival and departure times for its route.
|
||||
*
|
||||
* @param listener
|
||||
* @return true if successful.
|
||||
*/
|
||||
protected boolean unregisterRouteDataListener(RouteDataListener listener) {
|
||||
return routeDataListeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forced an update to be scheduled immediately.
|
||||
*/
|
||||
protected void forceUpdate() {
|
||||
scheduler.execute(this::fetchAndUpdateStopData);
|
||||
}
|
||||
|
||||
private ApiHandler getApiHandler() {
|
||||
return (ApiHandler) getBridge().getHandler();
|
||||
}
|
||||
|
||||
private StopConfiguration loadAndCheckConfiguration() {
|
||||
StopConfiguration config = getConfigAs(StopConfiguration.class);
|
||||
if (config.getInterval() == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "interval is not set");
|
||||
return null;
|
||||
}
|
||||
if (config.getStopId() == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "stopId is not set");
|
||||
return null;
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
private boolean fetchAndUpdateStopData() {
|
||||
try {
|
||||
ApiHandler apiHandler = getApiHandler();
|
||||
if (apiHandler == null) {
|
||||
// We must be offline.
|
||||
return false;
|
||||
}
|
||||
boolean alreadyFetching = !fetchInProgress.compareAndSet(false, true);
|
||||
if (alreadyFetching) {
|
||||
return false;
|
||||
}
|
||||
logger.debug("Fetching data for stop ID {}", config.getStopId());
|
||||
String url = String.format("http://%s/api/where/arrivals-and-departures-for-stop/%s.json?key=%s",
|
||||
apiHandler.getApiServer(), config.getStopId(), apiHandler.getApiKey());
|
||||
URI uri;
|
||||
try {
|
||||
uri = new URI(url);
|
||||
} catch (URISyntaxException e) {
|
||||
logger.error("Unable to parse {} as a URI.", url);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
|
||||
"stopId or apiKey is set to a bogus value");
|
||||
return false;
|
||||
}
|
||||
ContentResponse response;
|
||||
try {
|
||||
response = httpClient.newRequest(uri).send();
|
||||
} catch (InterruptedException | TimeoutException | ExecutionException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
|
||||
return false;
|
||||
}
|
||||
if (response.getStatus() != HttpStatus.OK_200) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
|
||||
String.format("While fetching stop data: %d: %s", response.getStatus(), response.getReason()));
|
||||
return false;
|
||||
}
|
||||
ObaStopArrivalResponse data = gson.fromJson(response.getContentAsString(), ObaStopArrivalResponse.class);
|
||||
routeDataLastUpdateMs = data.currentTime;
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
|
||||
Map<String, List<ObaStopArrivalResponse.ArrivalAndDeparture>> copiedRouteData = new HashMap<>();
|
||||
synchronized (routeData) {
|
||||
routeData.clear();
|
||||
for (ObaStopArrivalResponse.ArrivalAndDeparture d : data.data.entry.arrivalsAndDepartures) {
|
||||
routeData.put(d.routeId, Arrays.asList(d));
|
||||
}
|
||||
for (String key : routeData.keySet()) {
|
||||
List<ObaStopArrivalResponse.ArrivalAndDeparture> copy = new ArrayList<>(routeData.get(key));
|
||||
Collections.sort(copy);
|
||||
copiedRouteData.put(key, copy);
|
||||
}
|
||||
}
|
||||
for (RouteDataListener listener : routeDataListeners) {
|
||||
listener.onNewRouteData(routeDataLastUpdateMs, copiedRouteData.get(listener.getRouteId()));
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
logger.debug("Exception refreshing route data", e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
return false;
|
||||
} finally {
|
||||
fetchInProgress.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="onebusaway" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>OneBusAway Binding</name>
|
||||
<description>This is the binding for OneBusAway, an open system to provide transit data.</description>
|
||||
<author>Shawn Wilsher</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
|
||||
https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
|
||||
<config-description uri="channel-type:onebusaway:config">
|
||||
<parameter name="offset" type="integer" min="0" max="3600">
|
||||
<label>Offset</label>
|
||||
<description>Moves an event or datetime value backward (in seconds)</description>
|
||||
<default>0</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="onebusaway"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<bridge-type id="api">
|
||||
<label>OneBusAway Service</label>
|
||||
<description>The Service settings to talk to a OneBusAway deployment.</description>
|
||||
<config-description>
|
||||
<parameter name="apiKey" type="text" required="true">
|
||||
<label>API Key</label>
|
||||
<description>The OneBusAway API key to use.</description>
|
||||
<context>password</context>
|
||||
</parameter>
|
||||
<parameter name="apiServer" type="text" required="true">
|
||||
<label>API Server</label>
|
||||
<description>The OneBusAway API Server to use.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</bridge-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="onebusaway"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<channel-type id="arrival">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Arrival Time</label>
|
||||
<description>The arrival time</description>
|
||||
<state readOnly="true" pattern="%1$tF %1$tR"/>
|
||||
<config-description-ref uri="channel-type:onebusaway:config"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="departure">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Departure Time</label>
|
||||
<description>The departure time</description>
|
||||
<state readOnly="true" pattern="%1$tF %1$tR"/>
|
||||
<config-description-ref uri="channel-type:onebusaway:config"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="routeEvent">
|
||||
<kind>trigger</kind>
|
||||
<label>Route Event</label>
|
||||
<description>Route event</description>
|
||||
<event>
|
||||
<options>
|
||||
<option value="ARRIVAL">arrival</option>
|
||||
<option value="DEPARTURE">departure</option>
|
||||
</options>
|
||||
</event>
|
||||
<config-description-ref uri="channel-type:onebusaway:config"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="update">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Last Update Time</label>
|
||||
<description>The last time information was updated</description>
|
||||
<state readOnly="true" pattern="%1$tF %1$tR"/>
|
||||
</channel-type>
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="onebusaway"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<thing-type id="route">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="stop"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>Route</label>
|
||||
<description>Provides data about a route at a specific stop.</description>
|
||||
|
||||
<channels>
|
||||
<channel id="arrival" typeId="arrival"/>
|
||||
<channel id="departure" typeId="departure"/>
|
||||
<channel id="event" typeId="routeEvent"/>
|
||||
<channel id="update" typeId="update"/>
|
||||
</channels>
|
||||
|
||||
<config-description uri="thing-type:onebusaway:config">
|
||||
<parameter name="routeId" type="text" required="true">
|
||||
<label>Route ID</label>
|
||||
<description>The OneBusAway route ID.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="onebusaway"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<bridge-type id="stop">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="api"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>Stop</label>
|
||||
<description>Provides data about different routes at a specific stop.</description>
|
||||
<config-description>
|
||||
<parameter name="interval" type="integer" min="60" max="7200">
|
||||
<label>Interval</label>
|
||||
<description>Refresh interval for arrival data in seconds.</description>
|
||||
<default>300</default>
|
||||
</parameter>
|
||||
<parameter name="stopId" type="text" required="true">
|
||||
<label>Stop ID</label>
|
||||
<description>The OneBusAway stop ID.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</bridge-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user