added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.gpstracker-${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-gpstracker" description="GPSTracker Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.gpstracker/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* 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.gpstracker.internal;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
|
||||
/**
|
||||
* Binding constants
|
||||
*
|
||||
* @author Gabor Bicskei - Initial contribution
|
||||
*/
|
||||
public abstract class GPSTrackerBindingConstants {
|
||||
public static final String BINDING_ID = "gpstracker";
|
||||
static final String CONFIG_PID = "binding." + BINDING_ID;
|
||||
private static final String THING_TYPE = "tracker";
|
||||
public static final ThingTypeUID THING_TYPE_TRACKER = new ThingTypeUID(BINDING_ID, THING_TYPE);
|
||||
|
||||
// channels
|
||||
public static final String CHANNEL_REGION_TRIGGER = "regionTrigger";
|
||||
public static final String CHANNEL_LAST_REPORT = "lastReport";
|
||||
public static final String CHANNEL_LAST_LOCATION = "lastLocation";
|
||||
public static final String CHANNEL_BATTERY_LEVEL = "batteryLevel";
|
||||
private static final String CHANNEL_REGION_DISTANCE = "regionDistance";
|
||||
public static final String CHANNEL_GPS_ACCURACY = "gpsAccuracy";
|
||||
|
||||
// system distance channel
|
||||
public static final String CHANNEL_DISTANCE_SYSTEM_ID = "distanceSystem";
|
||||
public static final String CHANNEL_DISTANCE_SYSTEM_NAME = "System";
|
||||
public static final Integer CHANNEL_DISTANCE_SYSTEM_RADIUS = 100;
|
||||
|
||||
public static final ChannelTypeUID CHANNEL_TYPE_DISTANCE = new ChannelTypeUID(BINDING_ID, CHANNEL_REGION_DISTANCE);
|
||||
public static final ChannelTypeUID CHANNEL_TYPE_REGION = new ChannelTypeUID(BINDING_ID, CHANNEL_REGION_TRIGGER);
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Stream.of(THING_TYPE_TRACKER)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
/**
|
||||
* 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.gpstracker.internal;
|
||||
|
||||
import static org.openhab.binding.gpstracker.internal.GPSTrackerBindingConstants.CONFIG_PID;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.gpstracker.internal.config.ConfigHelper;
|
||||
import org.openhab.binding.gpstracker.internal.discovery.TrackerDiscoveryService;
|
||||
import org.openhab.binding.gpstracker.internal.handler.TrackerHandler;
|
||||
import org.openhab.binding.gpstracker.internal.message.NotificationBroker;
|
||||
import org.openhab.binding.gpstracker.internal.provider.TrackerRegistry;
|
||||
import org.openhab.binding.gpstracker.internal.provider.gpslogger.GPSLoggerCallbackServlet;
|
||||
import org.openhab.binding.gpstracker.internal.provider.owntracks.OwnTracksCallbackServlet;
|
||||
import org.openhab.core.config.core.ConfigOptionProvider;
|
||||
import org.openhab.core.config.core.ParameterOption;
|
||||
import org.openhab.core.i18n.LocationProvider;
|
||||
import org.openhab.core.i18n.UnitProvider;
|
||||
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.ComponentContext;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.osgi.service.http.HttpService;
|
||||
import org.osgi.service.http.NamespaceException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Main component
|
||||
*
|
||||
* @author Gabor Bicskei - Initial contribution
|
||||
*/
|
||||
@Component(configurationPid = CONFIG_PID, service = { ThingHandlerFactory.class, ConfigOptionProvider.class })
|
||||
@NonNullByDefault
|
||||
public class GPSTrackerHandlerFactory extends BaseThingHandlerFactory implements TrackerRegistry, ConfigOptionProvider {
|
||||
/**
|
||||
* Config URI
|
||||
*/
|
||||
private static final String URI_STR = "profile:gpstracker:trigger-geofence";
|
||||
|
||||
/**
|
||||
* Class logger
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(GPSTrackerHandlerFactory.class);
|
||||
|
||||
/**
|
||||
* Discovery service instance
|
||||
*/
|
||||
private final TrackerDiscoveryService discoveryService;
|
||||
|
||||
/**
|
||||
* Unit provider
|
||||
*/
|
||||
private final UnitProvider unitProvider;
|
||||
|
||||
/**
|
||||
* Location provider
|
||||
*/
|
||||
private final LocationProvider locationProvider;
|
||||
|
||||
/**
|
||||
* HTTP service reference
|
||||
*/
|
||||
private final HttpService httpService;
|
||||
|
||||
/**
|
||||
* Endpoint called by tracker applications
|
||||
*/
|
||||
private @NonNullByDefault({}) OwnTracksCallbackServlet otHTTPEndpoint;
|
||||
|
||||
/**
|
||||
* Endpoint called by tracker applications
|
||||
*/
|
||||
private @NonNullByDefault({}) GPSLoggerCallbackServlet glHTTPEndpoint;
|
||||
|
||||
/**
|
||||
* Notification broker
|
||||
*/
|
||||
private final NotificationBroker notificationBroker = new NotificationBroker();
|
||||
|
||||
/**
|
||||
* Handler registry
|
||||
*/
|
||||
private final Map<String, TrackerHandler> trackerHandlers = new HashMap<>();
|
||||
|
||||
/**
|
||||
* All regions.
|
||||
*/
|
||||
private final Set<String> regions = new HashSet<>();
|
||||
|
||||
@Activate
|
||||
public GPSTrackerHandlerFactory(final @Reference HttpService httpService, //
|
||||
final @Reference TrackerDiscoveryService discoveryService, //
|
||||
final @Reference UnitProvider unitProvider, //
|
||||
final @Reference LocationProvider locationProvider) {
|
||||
this.httpService = httpService;
|
||||
this.discoveryService = discoveryService;
|
||||
this.unitProvider = unitProvider;
|
||||
this.locationProvider = locationProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the framework to find out if thing type is supported by the handler factory.
|
||||
*
|
||||
* @param thingTypeUID Thing type UID
|
||||
* @return True if supported.
|
||||
*/
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return GPSTrackerBindingConstants.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new handler for tracker.
|
||||
*
|
||||
* @param thing Tracker thing
|
||||
* @return Handler instance
|
||||
*/
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
if (GPSTrackerBindingConstants.THING_TYPE_TRACKER.equals(thingTypeUID)
|
||||
&& ConfigHelper.getTrackerId(thing.getConfiguration()) != null) {
|
||||
TrackerHandler trackerHandler = new TrackerHandler(thing, notificationBroker, regions,
|
||||
locationProvider.getLocation(), unitProvider);
|
||||
discoveryService.removeTracker(trackerHandler.getTrackerId());
|
||||
trackerHandlers.put(trackerHandler.getTrackerId(), trackerHandler);
|
||||
return trackerHandler;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeHandler(ThingHandler thingHandler) {
|
||||
String trackerId = ConfigHelper.getTrackerId(thingHandler.getThing().getConfiguration());
|
||||
trackerHandlers.remove(trackerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate the binding. It starts the tracker discovery service and the HTTP callback endpoint.
|
||||
*
|
||||
* @param componentContext Component context.
|
||||
*/
|
||||
@Override
|
||||
protected void activate(ComponentContext componentContext) {
|
||||
super.activate(componentContext);
|
||||
|
||||
logger.debug("Initializing callback servlets");
|
||||
try {
|
||||
otHTTPEndpoint = new OwnTracksCallbackServlet(discoveryService, this);
|
||||
this.httpService.registerServlet(otHTTPEndpoint.getPath(), otHTTPEndpoint, null,
|
||||
this.httpService.createDefaultHttpContext());
|
||||
logger.debug("Started GPSTracker Callback servlet on {}", otHTTPEndpoint.getPath());
|
||||
|
||||
glHTTPEndpoint = new GPSLoggerCallbackServlet(discoveryService, this);
|
||||
this.httpService.registerServlet(glHTTPEndpoint.getPath(), glHTTPEndpoint, null,
|
||||
this.httpService.createDefaultHttpContext());
|
||||
logger.debug("Started GPSTracker Callback servlet on {}", glHTTPEndpoint.getPath());
|
||||
} catch (NamespaceException | ServletException e) {
|
||||
logger.error("Could not start GPSTracker Callback servlet: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate the binding. It stops the HTTP callback endpoint and stops the tracker discovery service.
|
||||
*
|
||||
* @param componentContext Component context.
|
||||
*/
|
||||
@Override
|
||||
protected void deactivate(ComponentContext componentContext) {
|
||||
logger.debug("Deactivating GPSTracker Binding");
|
||||
|
||||
this.httpService.unregister(otHTTPEndpoint.getPath());
|
||||
logger.debug("GPSTracker callback servlet stopped on {}", otHTTPEndpoint.getPath());
|
||||
|
||||
this.httpService.unregister(glHTTPEndpoint.getPath());
|
||||
logger.debug("GPSTracker callback servlet stopped on {}", glHTTPEndpoint.getPath());
|
||||
|
||||
super.deactivate(componentContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Collection<ParameterOption> getParameterOptions(URI uri, String param, @Nullable Locale locale) {
|
||||
return getParameterOptions(uri, param, null, locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Collection<ParameterOption> getParameterOptions(URI uri, String param, @Nullable String context,
|
||||
@Nullable Locale locale) {
|
||||
if (URI_STR.equals(uri.toString()) && ConfigHelper.CONFIG_REGION_NAME.equals(param)) {
|
||||
Set<ParameterOption> ret = new HashSet<>();
|
||||
regions.forEach(r -> ret.add(new ParameterOption(r, r)));
|
||||
return ret;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable TrackerHandler getTrackerHandler(String trackerId) {
|
||||
return trackerHandlers.get(trackerId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* 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.gpstracker.internal.config;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.library.types.PointType;
|
||||
|
||||
/**
|
||||
* The {@link ConfigHelper} class is a configuration helper for channels and profiles.
|
||||
*
|
||||
* @author Gabor Bicskei - Initial contribution
|
||||
*/
|
||||
public class ConfigHelper {
|
||||
// configuration constants
|
||||
public static final String CONFIG_TRACKER_ID = "trackerId";
|
||||
public static final String CONFIG_REGION_NAME = "regionName";
|
||||
public static final String CONFIG_REGION_RADIUS = "regionRadius";
|
||||
public static final String CONFIG_REGION_CENTER_LOCATION = "regionCenterLocation";
|
||||
public static final String CONFIG_ACCURACY_THRESHOLD = "accuracyThreshold";
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
private ConfigHelper() {
|
||||
}
|
||||
|
||||
public static double getRegionRadius(Configuration config) {
|
||||
return ((BigDecimal) config.get(CONFIG_REGION_RADIUS)).doubleValue();
|
||||
}
|
||||
|
||||
public static double getAccuracyThreshold(Configuration config) {
|
||||
Object value = config.get(CONFIG_ACCURACY_THRESHOLD);
|
||||
return value != null ? ((BigDecimal) value).doubleValue() : 0;
|
||||
}
|
||||
|
||||
public static String getRegionName(Configuration config) {
|
||||
return (String) config.get(CONFIG_REGION_NAME);
|
||||
}
|
||||
|
||||
public static String getTrackerId(Configuration config) {
|
||||
return (String) config.get(CONFIG_TRACKER_ID);
|
||||
}
|
||||
|
||||
public static PointType getRegionCenterLocation(Configuration config) {
|
||||
String location = (String) config.get(CONFIG_REGION_CENTER_LOCATION);
|
||||
return location != null ? new PointType(location) : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* 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.gpstracker.internal.discovery;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.gpstracker.internal.GPSTrackerBindingConstants;
|
||||
import org.openhab.binding.gpstracker.internal.config.ConfigHelper;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Deactivate;
|
||||
import org.osgi.service.component.annotations.Modified;
|
||||
|
||||
/**
|
||||
* The {@link TrackerDiscoveryService} class provides discovery service for the binding to discover trackers. Discovery
|
||||
* process is initiated by the tracker by sending a GPS log record. Based on the tracker id received in thin record an
|
||||
* entry is created in the Inbox for the thing representing the tracker.
|
||||
*
|
||||
* @author Gabor Bicskei - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = { DiscoveryService.class,
|
||||
TrackerDiscoveryService.class }, immediate = true, configurationPid = "discovery.gpstracker")
|
||||
public class TrackerDiscoveryService extends AbstractDiscoveryService {
|
||||
/**
|
||||
* Discovery timeout
|
||||
*/
|
||||
private static final int TIMEOUT = 1;
|
||||
|
||||
/**
|
||||
* Registry of tracker to discover next time
|
||||
*/
|
||||
private Set<String> trackersToDiscover = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @throws IllegalArgumentException thrown by the super constructor
|
||||
*/
|
||||
public TrackerDiscoveryService() throws IllegalArgumentException {
|
||||
super(GPSTrackerBindingConstants.SUPPORTED_THING_TYPES_UIDS, TIMEOUT, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the source tracker is not registered as a thing. These undiscovered trackers will be registered by
|
||||
* the discovery service.
|
||||
*
|
||||
* @param trackerId Tracker id.
|
||||
*/
|
||||
public void addTracker(String trackerId) {
|
||||
trackersToDiscover.add(trackerId);
|
||||
if (isBackgroundDiscoveryEnabled()) {
|
||||
createDiscoveryResult(trackerId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the tracker after the thing handles is created.
|
||||
*
|
||||
* @param trackerId Tracker id to unregister
|
||||
*/
|
||||
public void removeTracker(String trackerId) {
|
||||
trackersToDiscover.remove(trackerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
trackersToDiscover.forEach(this::createDiscoveryResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create discovery result form the tracker id.
|
||||
*
|
||||
* @param trackerId Tracker id.
|
||||
*/
|
||||
private void createDiscoveryResult(String trackerId) {
|
||||
ThingUID id = new ThingUID(GPSTrackerBindingConstants.THING_TYPE_TRACKER, trackerId);
|
||||
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(id)
|
||||
.withProperty(ConfigHelper.CONFIG_TRACKER_ID, trackerId)
|
||||
.withThingType(GPSTrackerBindingConstants.THING_TYPE_TRACKER).withLabel("GPS Tracker " + trackerId)
|
||||
.build();
|
||||
this.thingDiscovered(discoveryResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Activate
|
||||
protected void activate(@Nullable Map<String, @Nullable Object> configProperties) {
|
||||
super.activate(configProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modified
|
||||
protected void modified(@Nullable Map<String, @Nullable Object> configProperties) {
|
||||
super.modified(configProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deactivate
|
||||
protected void deactivate() {
|
||||
removeOlderResults(new Date().getTime());
|
||||
super.deactivate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopScan() {
|
||||
super.stopScan();
|
||||
removeOlderResults(getTimestampOfLastScan());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,431 @@
|
||||
/**
|
||||
* 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.gpstracker.internal.handler;
|
||||
|
||||
import static org.openhab.binding.gpstracker.internal.GPSTrackerBindingConstants.*;
|
||||
import static org.openhab.binding.gpstracker.internal.config.ConfigHelper.CONFIG_REGION_CENTER_LOCATION;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.measure.Unit;
|
||||
import javax.measure.quantity.Length;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.gpstracker.internal.config.ConfigHelper;
|
||||
import org.openhab.binding.gpstracker.internal.message.LocationMessage;
|
||||
import org.openhab.binding.gpstracker.internal.message.NotificationBroker;
|
||||
import org.openhab.binding.gpstracker.internal.message.NotificationHandler;
|
||||
import org.openhab.binding.gpstracker.internal.message.TransitionMessage;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.i18n.UnitProvider;
|
||||
import org.openhab.core.library.types.PointType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.ImperialUnits;
|
||||
import org.openhab.core.library.unit.MetricPrefix;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
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.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerCallback;
|
||||
import org.openhab.core.thing.binding.builder.ChannelBuilder;
|
||||
import org.openhab.core.thing.binding.builder.ThingBuilder;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link TrackerHandler} class is a tracker thing handler.
|
||||
*
|
||||
* @author Gabor Bicskei - Initial contribution
|
||||
*/
|
||||
public class TrackerHandler extends BaseThingHandler {
|
||||
/**
|
||||
* Trigger events
|
||||
*/
|
||||
private static final String EVENT_ENTER = "enter";
|
||||
private static final String EVENT_LEAVE = "leave";
|
||||
|
||||
/**
|
||||
* Class logger
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(TrackerHandler.class);
|
||||
|
||||
/**
|
||||
* Notification handler
|
||||
*/
|
||||
private NotificationHandler notificationHandler;
|
||||
|
||||
/**
|
||||
* Notification broker
|
||||
*/
|
||||
private NotificationBroker notificationBroker;
|
||||
|
||||
/**
|
||||
* Id of the tracker represented by the thing
|
||||
*/
|
||||
private String trackerId;
|
||||
|
||||
/**
|
||||
* Map of regionName/distance channels
|
||||
*/
|
||||
private Map<String, Channel> distanceChannelMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Map of last trigger events per region
|
||||
*/
|
||||
private Map<String, Boolean> lastTriggeredStates = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Set of all regions referenced by distance channels and extended by the received transition messages.
|
||||
*/
|
||||
private Set<String> regions;
|
||||
|
||||
/**
|
||||
* System location
|
||||
*/
|
||||
private PointType sysLocation;
|
||||
|
||||
/**
|
||||
* Unit provider
|
||||
*/
|
||||
private UnitProvider unitProvider;
|
||||
|
||||
/**
|
||||
* Last message received from the tracker
|
||||
*/
|
||||
private LocationMessage lastMessage;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param thing Thing.
|
||||
* @param notificationBroker Notification broker
|
||||
* @param regions Global region set
|
||||
* @param sysLocation Location of the system
|
||||
* @param unitProvider Unit provider
|
||||
*/
|
||||
public TrackerHandler(Thing thing, NotificationBroker notificationBroker, Set<String> regions,
|
||||
PointType sysLocation, UnitProvider unitProvider) {
|
||||
super(thing);
|
||||
|
||||
this.notificationBroker = notificationBroker;
|
||||
this.notificationHandler = new NotificationHandler();
|
||||
this.regions = regions;
|
||||
this.sysLocation = sysLocation;
|
||||
this.unitProvider = unitProvider;
|
||||
|
||||
trackerId = ConfigHelper.getTrackerId(thing.getConfiguration());
|
||||
notificationBroker.registerHandler(trackerId, notificationHandler);
|
||||
|
||||
logger.debug("Tracker handler created: {}", trackerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns tracker id configuration of the thing.
|
||||
*
|
||||
* @return Tracker id
|
||||
*/
|
||||
public String getTrackerId() {
|
||||
return trackerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
if (sysLocation != null) {
|
||||
createBasicDistanceChannel();
|
||||
} else {
|
||||
logger.debug("System location is not set. Skipping system distance channel setup.");
|
||||
}
|
||||
|
||||
mapDistanceChannels();
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create distance channel for measuring the distance between the tracker and the szstem.
|
||||
*/
|
||||
private void createBasicDistanceChannel() {
|
||||
@Nullable
|
||||
ThingHandlerCallback callback = getCallback();
|
||||
if (callback != null) {
|
||||
// find the system distance channel
|
||||
ChannelUID systemDistanceChannelUID = new ChannelUID(thing.getUID(), CHANNEL_DISTANCE_SYSTEM_ID);
|
||||
Channel systemDistance = thing.getChannel(CHANNEL_DISTANCE_SYSTEM_ID);
|
||||
ChannelBuilder channelBuilder = null;
|
||||
if (systemDistance != null) {
|
||||
if (!systemDistance.getConfiguration().get(CONFIG_REGION_CENTER_LOCATION)
|
||||
.equals(sysLocation.toFullString())) {
|
||||
logger.trace("Existing distance channel for system. Changing system location config parameter: {}",
|
||||
sysLocation.toFullString());
|
||||
|
||||
channelBuilder = callback.editChannel(thing, systemDistanceChannelUID);
|
||||
Configuration configToUpdate = systemDistance.getConfiguration();
|
||||
configToUpdate.put(CONFIG_REGION_CENTER_LOCATION, sysLocation.toFullString());
|
||||
channelBuilder.withConfiguration(configToUpdate);
|
||||
} else {
|
||||
logger.trace("Existing distance channel for system. No change.");
|
||||
}
|
||||
} else {
|
||||
logger.trace("Creating missing distance channel for system.");
|
||||
|
||||
Configuration config = new Configuration();
|
||||
config.put(ConfigHelper.CONFIG_REGION_NAME, CHANNEL_DISTANCE_SYSTEM_NAME);
|
||||
config.put(CONFIG_REGION_CENTER_LOCATION, sysLocation.toFullString());
|
||||
config.put(ConfigHelper.CONFIG_REGION_RADIUS, CHANNEL_DISTANCE_SYSTEM_RADIUS);
|
||||
config.put(ConfigHelper.CONFIG_ACCURACY_THRESHOLD, 0);
|
||||
|
||||
channelBuilder = callback.createChannelBuilder(systemDistanceChannelUID, CHANNEL_TYPE_DISTANCE)
|
||||
.withLabel("System Distance").withConfiguration(config);
|
||||
}
|
||||
|
||||
// update the thing with system distance channel
|
||||
if (channelBuilder != null) {
|
||||
List<Channel> channels = new ArrayList<>(thing.getChannels());
|
||||
if (systemDistance != null) {
|
||||
channels.remove(systemDistance);
|
||||
}
|
||||
channels.add(channelBuilder.build());
|
||||
|
||||
ThingBuilder thingBuilder = editThing();
|
||||
thingBuilder.withChannels(channels);
|
||||
updateThing(thingBuilder.build());
|
||||
|
||||
logger.debug("Distance channel created for system: {}", systemDistanceChannelUID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a map of all configured distance channels to handle channel updates easily.
|
||||
*/
|
||||
private void mapDistanceChannels() {
|
||||
distanceChannelMap = thing.getChannels().stream()
|
||||
.filter(c -> CHANNEL_TYPE_DISTANCE.equals(c.getChannelTypeUID()))
|
||||
.collect(Collectors.toMap(c -> ConfigHelper.getRegionName(c.getConfiguration()), Function.identity()));
|
||||
// register the collected regions
|
||||
regions.addAll(distanceChannelMap.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (command instanceof RefreshType && lastMessage != null) {
|
||||
String channelId = channelUID.getId();
|
||||
switch (channelId) {
|
||||
case CHANNEL_LAST_REPORT:
|
||||
updateBaseChannels(lastMessage, CHANNEL_LAST_REPORT);
|
||||
break;
|
||||
case CHANNEL_LAST_LOCATION:
|
||||
updateBaseChannels(lastMessage, CHANNEL_LAST_LOCATION);
|
||||
break;
|
||||
case CHANNEL_BATTERY_LEVEL:
|
||||
updateBaseChannels(lastMessage, CHANNEL_BATTERY_LEVEL);
|
||||
break;
|
||||
case CHANNEL_GPS_ACCURACY:
|
||||
updateBaseChannels(lastMessage, CHANNEL_GPS_ACCURACY);
|
||||
break;
|
||||
default: // distance channels
|
||||
@Nullable
|
||||
Channel channel = thing.getChannel(channelId);
|
||||
if (channel != null) {
|
||||
updateDistanceChannelFromMessage(lastMessage, channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle transition messages by firing the trigger channel with regionName/event payload.
|
||||
*
|
||||
* @param message TransitionMessage message.
|
||||
*/
|
||||
private void updateTriggerChannelsWithTransition(TransitionMessage message) {
|
||||
String regionName = message.getRegionName();
|
||||
triggerRegionChannel(regionName, message.getEvent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire trigger event with regionName/enter|leave payload but only if the event differs from the last event.
|
||||
*
|
||||
* @param regionName Region name
|
||||
* @param event Occurred event
|
||||
*/
|
||||
private void triggerRegionChannel(@NonNull String regionName, @NonNull String event) {
|
||||
Boolean lastState = lastTriggeredStates.get(regionName);
|
||||
Boolean newState = EVENT_ENTER.equals(event);
|
||||
if (!newState.equals(lastState) && lastState != null) {
|
||||
String payload = regionName + "/" + event;
|
||||
triggerChannel(CHANNEL_REGION_TRIGGER, payload);
|
||||
lastTriggeredStates.put(regionName, newState);
|
||||
logger.trace("Triggering {} for {}/{}", regionName, trackerId, payload);
|
||||
}
|
||||
lastTriggeredStates.put(regionName, newState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update state channels from location message. This includes basic channel updates and recalculations of all
|
||||
* distances.
|
||||
*
|
||||
* @param message Message.
|
||||
*/
|
||||
private void updateChannelsWithLocation(LocationMessage message) {
|
||||
updateBaseChannels(message, CHANNEL_BATTERY_LEVEL, CHANNEL_LAST_LOCATION, CHANNEL_LAST_REPORT,
|
||||
CHANNEL_GPS_ACCURACY);
|
||||
|
||||
String trackerId = message.getTrackerId();
|
||||
logger.debug("Updating distance channels tracker {}", trackerId);
|
||||
distanceChannelMap.values().forEach(c -> updateDistanceChannelFromMessage(message, c));
|
||||
}
|
||||
|
||||
private void updateDistanceChannelFromMessage(LocationMessage message, Channel c) {
|
||||
Configuration currentConfig = c.getConfiguration();
|
||||
// convert into meters which is the unit of the threshold
|
||||
Double accuracyThreshold = convertToMeters(ConfigHelper.getAccuracyThreshold(currentConfig));
|
||||
State messageAccuracy = message.getGpsAccuracy();
|
||||
Double accuracy = messageAccuracy != UnDefType.UNDEF ? ((QuantityType<?>) messageAccuracy).doubleValue()
|
||||
: accuracyThreshold;
|
||||
|
||||
if (accuracyThreshold >= accuracy || accuracyThreshold.intValue() == 0) {
|
||||
if (accuracyThreshold > 0) {
|
||||
logger.debug("Location accuracy is below required threshold: {}<={}", accuracy, accuracyThreshold);
|
||||
} else {
|
||||
logger.debug("Location accuracy threshold check is disabled.");
|
||||
}
|
||||
|
||||
String regionName = ConfigHelper.getRegionName(currentConfig);
|
||||
PointType center = ConfigHelper.getRegionCenterLocation(currentConfig);
|
||||
State newLocation = message.getTrackerLocation();
|
||||
if (center != null && newLocation != UnDefType.UNDEF) {
|
||||
double newDistance = center.distanceFrom((PointType) newLocation).doubleValue();
|
||||
updateState(c.getUID(), new QuantityType<>(newDistance / 1000, MetricPrefix.KILO(SIUnits.METRE)));
|
||||
logger.trace("Region {} center distance from tracker location {} is {}m", regionName, newLocation,
|
||||
newDistance);
|
||||
|
||||
// fire trigger based on distance calculation only in case of pure location message
|
||||
if (!(message instanceof TransitionMessage)) {
|
||||
// convert into meters which is the unit of the calculated distance
|
||||
double radiusMeter = convertToMeters(ConfigHelper.getRegionRadius(c.getConfiguration()));
|
||||
if (radiusMeter > newDistance) {
|
||||
triggerRegionChannel(regionName, EVENT_ENTER);
|
||||
} else {
|
||||
triggerRegionChannel(regionName, EVENT_LEAVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.debug("Skip update as location accuracy is above required threshold: {}>{}", accuracy,
|
||||
accuracyThreshold);
|
||||
}
|
||||
}
|
||||
|
||||
private double convertToMeters(double valueToConvert) {
|
||||
if (unitProvider != null) {
|
||||
@Nullable
|
||||
Unit<Length> unit = unitProvider.getUnit(Length.class);
|
||||
if (unit != null && !SIUnits.METRE.equals(unit)) {
|
||||
double value = ImperialUnits.YARD.getConverterTo(SIUnits.METRE).convert(valueToConvert);
|
||||
logger.trace("Value converted: {}yd->{}m", valueToConvert, value);
|
||||
return value;
|
||||
} else {
|
||||
logger.trace("System uses SI measurement units. No conversion is needed.");
|
||||
}
|
||||
} else {
|
||||
logger.trace("No unit provider. Considering region radius {} in meters.", valueToConvert);
|
||||
}
|
||||
return valueToConvert;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update basic channels: batteryLevel, lastLocation, lastReport
|
||||
*
|
||||
* @param message Received message.
|
||||
*/
|
||||
private void updateBaseChannels(LocationMessage message, String... channels) {
|
||||
logger.debug("Update base channels for tracker {} from message: {}", trackerId, message);
|
||||
|
||||
for (String channel : channels) {
|
||||
switch (channel) {
|
||||
case CHANNEL_LAST_REPORT:
|
||||
State timestamp = message.getTimestamp();
|
||||
updateState(CHANNEL_LAST_REPORT, timestamp);
|
||||
logger.trace("{} -> {}", CHANNEL_LAST_REPORT, timestamp);
|
||||
break;
|
||||
case CHANNEL_LAST_LOCATION:
|
||||
State newLocation = message.getTrackerLocation();
|
||||
updateState(CHANNEL_LAST_LOCATION, newLocation);
|
||||
logger.trace("{} -> {}", CHANNEL_LAST_LOCATION, newLocation);
|
||||
break;
|
||||
case CHANNEL_BATTERY_LEVEL:
|
||||
State batteryLevel = message.getBatteryLevel();
|
||||
updateState(CHANNEL_BATTERY_LEVEL, batteryLevel);
|
||||
logger.trace("{} -> {}", CHANNEL_BATTERY_LEVEL, batteryLevel);
|
||||
break;
|
||||
case CHANNEL_GPS_ACCURACY:
|
||||
State accuracy = message.getGpsAccuracy();
|
||||
updateState(CHANNEL_GPS_ACCURACY, accuracy);
|
||||
logger.trace("{} -> {}", CHANNEL_GPS_ACCURACY, accuracy);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Location message handling.
|
||||
*
|
||||
* @param lm Location message
|
||||
*/
|
||||
public void updateLocation(LocationMessage lm) {
|
||||
this.lastMessage = lm;
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
updateChannelsWithLocation(lm);
|
||||
notificationBroker.sendNotification(lm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transition message handling
|
||||
*
|
||||
* @param tm Transition message
|
||||
*/
|
||||
public void doTransition(TransitionMessage tm) {
|
||||
this.lastMessage = tm;
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
String regionName = tm.getRegionName();
|
||||
logger.debug("ConfigHelper transition event received: {}", regionName);
|
||||
regions.add(regionName);
|
||||
|
||||
updateChannelsWithLocation(tm);
|
||||
updateTriggerChannelsWithTransition(tm);
|
||||
|
||||
notificationBroker.sendNotification(tm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get notification to return to the tracker (supported by OwnTracks only)
|
||||
*
|
||||
* @return List of notifications received from other trackers
|
||||
*/
|
||||
public List<LocationMessage> getNotifications() {
|
||||
return notificationHandler.getNotifications();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* 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.gpstracker.internal.message;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.PointType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* The {@link LocationMessage} is a POJO for location messages sent bz trackers.
|
||||
*
|
||||
* @author Gabor Bicskei - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class LocationMessage {
|
||||
|
||||
/**
|
||||
* Message type
|
||||
*/
|
||||
@SerializedName("_type")
|
||||
private String type = "";
|
||||
|
||||
/**
|
||||
* Tracker ID used to display the initials of a user (iOS,Android/string/optional) required for http mode
|
||||
*/
|
||||
@SerializedName("tid")
|
||||
private String trackerId = "";
|
||||
|
||||
/**
|
||||
* Latitude (iOS, Android/float/meters/required)
|
||||
*/
|
||||
@SerializedName("lat")
|
||||
private BigDecimal latitude = BigDecimal.ZERO;
|
||||
|
||||
/**
|
||||
* Longitude (iOS,Android/float/meters/required)
|
||||
*/
|
||||
@SerializedName("lon")
|
||||
private BigDecimal longitude = BigDecimal.ZERO;
|
||||
|
||||
/**
|
||||
* GPS accuracy
|
||||
*/
|
||||
@SerializedName("acc")
|
||||
private @Nullable BigDecimal gpsAccuracy;
|
||||
|
||||
/**
|
||||
* Battery level (iOS,Android/integer/percent/optional)
|
||||
*/
|
||||
@SerializedName("batt")
|
||||
private Integer batteryLevel = Integer.MIN_VALUE;
|
||||
|
||||
/**
|
||||
* Timestamp at which the event occurred (iOS,Android/integer/epoch/required)
|
||||
*/
|
||||
@SerializedName("tst")
|
||||
private Long timestampMillis = Long.MIN_VALUE;
|
||||
|
||||
public String getTrackerId() {
|
||||
return trackerId.replaceAll("[^a-zA-Z0-9_]", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts event timestamp onto DateTimeType
|
||||
*
|
||||
* @return Conversion result
|
||||
*/
|
||||
public State getTimestamp() {
|
||||
if (timestampMillis != Long.MIN_VALUE) {
|
||||
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(new Date(timestampMillis * 1000).toInstant(),
|
||||
ZoneId.systemDefault());
|
||||
return new DateTimeType(zonedDateTime);
|
||||
}
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts tracker coordinates into PointType
|
||||
*
|
||||
* @return Conversion result
|
||||
*/
|
||||
public State getTrackerLocation() {
|
||||
if (latitude != BigDecimal.ZERO && longitude != BigDecimal.ZERO) {
|
||||
return new PointType(new DecimalType(latitude), new DecimalType(longitude));
|
||||
}
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts battery level into DecimalType
|
||||
*
|
||||
* @return Conversion result
|
||||
*/
|
||||
public State getBatteryLevel() {
|
||||
if (batteryLevel != Integer.MIN_VALUE) {
|
||||
return new DecimalType(batteryLevel);
|
||||
}
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
public State getGpsAccuracy() {
|
||||
if (gpsAccuracy != null) {
|
||||
return new QuantityType<>(gpsAccuracy.intValue(), SIUnits.METRE);
|
||||
}
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LocationMessage [" + ("type=" + type + ", ") + ("trackerId=" + trackerId + ", ")
|
||||
+ ("latitude=" + latitude + ", ") + ("longitude=" + longitude + ", ")
|
||||
+ (gpsAccuracy != null ? "gpsAccuracy=" + gpsAccuracy + ", " : "")
|
||||
+ ("batteryLevel=" + batteryLevel + ", ") + ("timestampMillis=" + timestampMillis) + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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.gpstracker.internal.message;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* Message handling utility
|
||||
*
|
||||
* @author Gabor Bicskei - Initial contribution
|
||||
*/
|
||||
public class MessageUtil {
|
||||
/**
|
||||
* Patterns to identify incoming JSON payload.
|
||||
*/
|
||||
private static final String[] PATTERNS = new String[] { ".*\"_type\"\\s*:\\s*\"transition\".*", // transition
|
||||
".*\"_type\"\\s*:\\s*\"location\".*", // location
|
||||
};
|
||||
|
||||
/**
|
||||
* Supported message types
|
||||
*/
|
||||
private static final Map<String, Class<? extends LocationMessage>> MESSAGE_TYPES = new HashMap<>();
|
||||
|
||||
static {
|
||||
MESSAGE_TYPES.put(PATTERNS[0], TransitionMessage.class);
|
||||
MESSAGE_TYPES.put(PATTERNS[1], LocationMessage.class);
|
||||
}
|
||||
|
||||
private final Gson gson = new Gson();
|
||||
|
||||
/**
|
||||
* Parses JSON message into an object with type determined by message pattern.
|
||||
*
|
||||
* @param json JSON string.
|
||||
* @return Parsed message POJO or null without pattern match
|
||||
*/
|
||||
public LocationMessage fromJson(String json) {
|
||||
for (String pattern : PATTERNS) {
|
||||
Class<? extends LocationMessage> c = MESSAGE_TYPES.get(pattern);
|
||||
if (json.matches(pattern)) {
|
||||
return gson.fromJson(json, c);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts object to JSON sting.
|
||||
*
|
||||
* @param o Object to convert
|
||||
* @return JSON string
|
||||
*/
|
||||
public String toJson(Object o) {
|
||||
return gson.toJson(o);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 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.gpstracker.internal.message;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Notification broker.
|
||||
*
|
||||
* @author Gabor Bicskei - Initial contribution
|
||||
*/
|
||||
public class NotificationBroker {
|
||||
/**
|
||||
* Handlers
|
||||
*/
|
||||
private Map<String, NotificationHandler> handlers = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Register new handler.
|
||||
*
|
||||
* @param trackerId Tracker id
|
||||
* @param handler Notification handler
|
||||
*/
|
||||
public void registerHandler(String trackerId, NotificationHandler handler) {
|
||||
handlers.put(trackerId, handler);
|
||||
}
|
||||
|
||||
public void sendNotification(LocationMessage msg) {
|
||||
String trackerId = msg.getTrackerId();
|
||||
handlers.entrySet().stream().filter(e -> !e.getKey().equals(trackerId))
|
||||
.forEach(e -> e.getValue().handleNotification(msg));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* 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.gpstracker.internal.message;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Handler for notification messages between trackers.
|
||||
*
|
||||
* @author Gabor Bicskei - Initial contribution
|
||||
*/
|
||||
public class NotificationHandler {
|
||||
/**
|
||||
* Location notifications need to be sent to the own tracker. Only the last location is saved for each tracker
|
||||
* in the group.
|
||||
*/
|
||||
private Map<String, LocationMessage> locationNotifications = new HashMap<>();
|
||||
|
||||
/**
|
||||
* TransitionMessage notification to send out to the own tracker. Notifications are saved in order they were
|
||||
* received.
|
||||
*/
|
||||
private Map<String, List<TransitionMessage>> transitionNotifications = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Handling notification sent by other trackers.
|
||||
*
|
||||
* @param msg Notification message.
|
||||
*/
|
||||
public void handleNotification(LocationMessage msg) {
|
||||
synchronized (this) {
|
||||
String trackerId = msg.getTrackerId();
|
||||
if (msg instanceof TransitionMessage) {
|
||||
List<TransitionMessage> transitionMessages = transitionNotifications.computeIfAbsent(trackerId,
|
||||
k -> new ArrayList<>());
|
||||
transitionMessages.add((TransitionMessage) msg);
|
||||
} else {
|
||||
locationNotifications.put(trackerId, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect all notifications about friend trackers.
|
||||
*
|
||||
* @return List of notification messages from friend trackers need to sent out
|
||||
*/
|
||||
public List<LocationMessage> getNotifications() {
|
||||
List<LocationMessage> ret;
|
||||
synchronized (this) {
|
||||
ret = new ArrayList<>(locationNotifications.values());
|
||||
transitionNotifications.values().forEach(ret::addAll);
|
||||
locationNotifications.clear();
|
||||
transitionNotifications.clear();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -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.gpstracker.internal.message;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* TransitionMessage message POJO
|
||||
*
|
||||
* @author Gabor Bicskei - Initial contribution
|
||||
*/
|
||||
public class TransitionMessage extends LocationMessage {
|
||||
|
||||
/**
|
||||
* Event that triggered the transition (iOS,Android/string/required)
|
||||
* enter The tracker entered the defined geographical region or BLE Beacon range (iOS)
|
||||
* leave The tracker left the defined geographical region or BLE Beacon range (iOS)
|
||||
*/
|
||||
@SerializedName("event")
|
||||
String event;
|
||||
|
||||
/**
|
||||
* Name of the waypoint (iOS,Android/string/optional)
|
||||
*/
|
||||
@SerializedName("desc")
|
||||
String regionName;
|
||||
|
||||
public String getRegionName() {
|
||||
return regionName;
|
||||
}
|
||||
|
||||
public String getEvent() {
|
||||
return event;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* 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.gpstracker.internal.profile;
|
||||
|
||||
import static org.openhab.binding.gpstracker.internal.GPSTrackerBindingConstants.CHANNEL_TYPE_REGION;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.gpstracker.internal.GPSTrackerBindingConstants;
|
||||
import org.openhab.core.library.CoreItemFactory;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.profiles.*;
|
||||
import org.openhab.core.thing.type.ChannelType;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
|
||||
/**
|
||||
* The {@link GPSTrackerProfileFactory} class defines and provides switch profile and its type of this binding.
|
||||
*
|
||||
* @author Gabor Bicskei - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component
|
||||
public class GPSTrackerProfileFactory implements ProfileFactory, ProfileAdvisor, ProfileTypeProvider {
|
||||
/**
|
||||
* Profile UID for trigger events
|
||||
*/
|
||||
static final ProfileTypeUID UID_TRIGGER_SWITCH = new ProfileTypeUID(GPSTrackerBindingConstants.BINDING_ID,
|
||||
"trigger-geofence");
|
||||
|
||||
/**
|
||||
* Profile type for trigger events
|
||||
*/
|
||||
private static final TriggerProfileType TRIGGER_SWITCH_TYPE = ProfileTypeBuilder
|
||||
.newTrigger(UID_TRIGGER_SWITCH, "Geofence").withSupportedItemTypes(CoreItemFactory.SWITCH)
|
||||
.withSupportedChannelTypeUIDs(CHANNEL_TYPE_REGION).build();
|
||||
|
||||
@Override
|
||||
public Collection<ProfileTypeUID> getSupportedProfileTypeUIDs() {
|
||||
return Stream.of(UID_TRIGGER_SWITCH).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ProfileType> getProfileTypes(@Nullable Locale locale) {
|
||||
return Stream.of(TRIGGER_SWITCH_TYPE).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ProfileTypeUID getSuggestedProfileTypeUID(Channel channel, @Nullable String itemType) {
|
||||
return getSuggestedProfileTypeUID(channel.getChannelTypeUID(), itemType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ProfileTypeUID getSuggestedProfileTypeUID(ChannelType channelType, @Nullable String itemType) {
|
||||
return getSuggestedProfileTypeUID(channelType.getUID(), itemType);
|
||||
}
|
||||
|
||||
private @Nullable ProfileTypeUID getSuggestedProfileTypeUID(@Nullable ChannelTypeUID channelTypeUID,
|
||||
@Nullable String itemType) {
|
||||
if (CoreItemFactory.SWITCH.equals(itemType) && CHANNEL_TYPE_REGION.equals(channelTypeUID)) {
|
||||
return UID_TRIGGER_SWITCH;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Profile createProfile(ProfileTypeUID profileTypeUID, ProfileCallback callback,
|
||||
ProfileContext profileContext) {
|
||||
if (UID_TRIGGER_SWITCH.equals(profileTypeUID)) {
|
||||
return new GPSTrackerTriggerSwitchProfile(callback, profileContext);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* 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.gpstracker.internal.profile;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.gpstracker.internal.config.ConfigHelper;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.profiles.ProfileCallback;
|
||||
import org.openhab.core.thing.profiles.ProfileContext;
|
||||
import org.openhab.core.thing.profiles.ProfileTypeUID;
|
||||
import org.openhab.core.thing.profiles.TriggerProfile;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link GPSTrackerTriggerSwitchProfile} class implements the behavior when being linked to a Switch item.
|
||||
*
|
||||
* @author Gabor Bicskei - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class GPSTrackerTriggerSwitchProfile implements TriggerProfile {
|
||||
/**
|
||||
* Class logger
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(GPSTrackerTriggerSwitchProfile.class);
|
||||
|
||||
/**
|
||||
* Callback
|
||||
*/
|
||||
private ProfileCallback callback;
|
||||
|
||||
/**
|
||||
* Link region name
|
||||
*/
|
||||
private String regionName;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param callback Callback
|
||||
* @param context Context
|
||||
*/
|
||||
GPSTrackerTriggerSwitchProfile(ProfileCallback callback, ProfileContext context) {
|
||||
this.callback = callback;
|
||||
this.regionName = ConfigHelper.getRegionName(context.getConfiguration());
|
||||
logger.debug("Trigger switch profile created for region {}", regionName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileTypeUID getProfileTypeUID() {
|
||||
return GPSTrackerProfileFactory.UID_TRIGGER_SWITCH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateUpdateFromItem(State state) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTriggerFromHandler(String payload) {
|
||||
if (payload.startsWith(regionName)) {
|
||||
OnOffType state = payload.endsWith("enter") ? OnOffType.ON : OnOffType.OFF;
|
||||
callback.sendCommand(state);
|
||||
logger.debug("Transition trigger {} handled for region {} by profile: {}", payload, regionName, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* 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.gpstracker.internal.provider;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.openhab.binding.gpstracker.internal.discovery.TrackerDiscoveryService;
|
||||
import org.openhab.binding.gpstracker.internal.handler.TrackerHandler;
|
||||
import org.openhab.binding.gpstracker.internal.message.LocationMessage;
|
||||
import org.openhab.binding.gpstracker.internal.message.MessageUtil;
|
||||
import org.openhab.binding.gpstracker.internal.message.TransitionMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Abstract callback servlet used by the trackers.
|
||||
*
|
||||
* @author Gabor Bicskei - Initial contribution
|
||||
*/
|
||||
public abstract class AbstractCallbackServlet extends HttpServlet {
|
||||
|
||||
private static final long serialVersionUID = -2725161358635927815L;
|
||||
|
||||
/**
|
||||
* Class logger
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(AbstractCallbackServlet.class);
|
||||
|
||||
/**
|
||||
* Discovery service to handle new trackers
|
||||
*/
|
||||
private TrackerDiscoveryService discoveryService;
|
||||
|
||||
/**
|
||||
* Utility to process messages
|
||||
*/
|
||||
private MessageUtil messageUtil = new MessageUtil();
|
||||
|
||||
/**
|
||||
* Tracker registry
|
||||
*/
|
||||
private TrackerRegistry trackerRegistry;
|
||||
|
||||
/**
|
||||
* Constructor called at binding startup.
|
||||
*
|
||||
* @param discoveryService Discovery service for new trackers.
|
||||
* @param trackerRegistry Tracker handler registry
|
||||
*/
|
||||
protected AbstractCallbackServlet(TrackerDiscoveryService discoveryService, TrackerRegistry trackerRegistry) {
|
||||
this.discoveryService = discoveryService;
|
||||
this.trackerRegistry = trackerRegistry;
|
||||
}
|
||||
|
||||
protected abstract String getPath();
|
||||
|
||||
/**
|
||||
* Process the HTTP requests from tracker applications
|
||||
*
|
||||
* @param req HTTP request
|
||||
* @param resp HTTP response
|
||||
*/
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
|
||||
try {
|
||||
StringBuilder jb = new StringBuilder();
|
||||
BufferedReader reader = req.getReader();
|
||||
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
jb.append(line);
|
||||
}
|
||||
|
||||
// clear the whitespaces from the message
|
||||
String json = jb.toString().replaceAll("\\p{Z}", "");
|
||||
logger.debug("Post message received from {} tracker: {}", getProvider(), json);
|
||||
|
||||
LocationMessage message = messageUtil.fromJson(json);
|
||||
if (message != null) {
|
||||
List<? extends LocationMessage> response = processMessage(message);
|
||||
if (response != null) {
|
||||
resp.getWriter().append(messageUtil.toJson(response)).flush();
|
||||
}
|
||||
}
|
||||
resp.setStatus(HttpServletResponse.SC_OK);
|
||||
} catch (Exception e) {
|
||||
logger.error("Error processing location report:", e);
|
||||
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the message received by the servlet. If the tracker is unknown the discovery service is notified
|
||||
* so that the next search will pop up the new tracker as result.
|
||||
*
|
||||
* @param message The message
|
||||
* @return Response message.
|
||||
*/
|
||||
private List<? extends LocationMessage> processMessage(LocationMessage message) {
|
||||
String trackerId = message.getTrackerId();
|
||||
if (!trackerId.isEmpty()) {
|
||||
TrackerHandler recorder = getHandlerById(trackerId);
|
||||
if (recorder != null) {
|
||||
if (message instanceof TransitionMessage) {
|
||||
TransitionMessage tm = (TransitionMessage) message;
|
||||
recorder.doTransition(tm);
|
||||
} else {
|
||||
recorder.updateLocation(message);
|
||||
}
|
||||
return recorder.getNotifications();
|
||||
} else {
|
||||
logger.debug("There is no handler for tracker {}. Check the inbox for the new tracker.", trackerId);
|
||||
}
|
||||
} else {
|
||||
logger.debug("Message without tracker id. Dropping message. {}", messageUtil.toJson(message));
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find handler for tracker. If the handler does not exist it is registered with discovery service.
|
||||
*
|
||||
* @param trackerId Tracker id.
|
||||
* @return Handler for tracker.
|
||||
*/
|
||||
private TrackerHandler getHandlerById(String trackerId) {
|
||||
if (trackerId != null) {
|
||||
TrackerHandler handler = trackerRegistry.getTrackerHandler(trackerId);
|
||||
if (handler == null) {
|
||||
// handler was not found - adding the tracker to discovery service.
|
||||
discoveryService.addTracker(trackerId);
|
||||
} else {
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected abstract String getProvider();
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 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.gpstracker.internal.provider;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.gpstracker.internal.handler.TrackerHandler;
|
||||
|
||||
/**
|
||||
* Functional interface for checking tracker registration.
|
||||
*
|
||||
* @author Gabor Bicskei - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface TrackerRegistry {
|
||||
|
||||
/**
|
||||
* Returns a handler for a given id
|
||||
*
|
||||
* @param trackerId the id of the tracker
|
||||
* @return the handler
|
||||
*/
|
||||
@Nullable
|
||||
TrackerHandler getTrackerHandler(String trackerId);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 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.gpstracker.internal.provider.gpslogger;
|
||||
|
||||
import org.openhab.binding.gpstracker.internal.discovery.TrackerDiscoveryService;
|
||||
import org.openhab.binding.gpstracker.internal.provider.AbstractCallbackServlet;
|
||||
import org.openhab.binding.gpstracker.internal.provider.TrackerRegistry;
|
||||
|
||||
/**
|
||||
* Callback servlet for GPSLogger
|
||||
*
|
||||
* @author Gabor Bicskei - Initial contribution
|
||||
*/
|
||||
public class GPSLoggerCallbackServlet extends AbstractCallbackServlet {
|
||||
|
||||
private static final long serialVersionUID = -6992472786850682196L;
|
||||
|
||||
/**
|
||||
* Servlet path
|
||||
*/
|
||||
private static final String CALLBACK_PATH = "/gpstracker/gpslogger";
|
||||
|
||||
/**
|
||||
* Provider name
|
||||
*/
|
||||
private static final String PROVIDER = "GPSLogger";
|
||||
|
||||
/**
|
||||
* Constructor called at binding startup.
|
||||
*
|
||||
* @param discoveryService Discovery service for new trackers.
|
||||
* @param trackerRegistry Tracker registry
|
||||
*/
|
||||
public GPSLoggerCallbackServlet(TrackerDiscoveryService discoveryService, TrackerRegistry trackerRegistry) {
|
||||
super(discoveryService, trackerRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return CALLBACK_PATH;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getProvider() {
|
||||
return PROVIDER;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 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.gpstracker.internal.provider.owntracks;
|
||||
|
||||
import org.openhab.binding.gpstracker.internal.discovery.TrackerDiscoveryService;
|
||||
import org.openhab.binding.gpstracker.internal.provider.AbstractCallbackServlet;
|
||||
import org.openhab.binding.gpstracker.internal.provider.TrackerRegistry;
|
||||
|
||||
/**
|
||||
* Callback servlet for OwnTracks trackers
|
||||
*
|
||||
* @author Gabor Bicskei - Initial contribution
|
||||
*/
|
||||
public class OwnTracksCallbackServlet extends AbstractCallbackServlet {
|
||||
|
||||
private static final long serialVersionUID = -4053305903339688036L;
|
||||
|
||||
/**
|
||||
* Servlet path
|
||||
*/
|
||||
private static final String CALLBACK_PATH = "/gpstracker/owntracks";
|
||||
|
||||
/**
|
||||
* Provider name
|
||||
*/
|
||||
private static final String PROVIDER = "OwnTracks";
|
||||
|
||||
/**
|
||||
* Constructor called at binding startup.
|
||||
*
|
||||
* @param discoveryService Discovery service for new trackers.
|
||||
* @param trackerRegistry Tracker registry
|
||||
*/
|
||||
public OwnTracksCallbackServlet(TrackerDiscoveryService discoveryService, TrackerRegistry trackerRegistry) {
|
||||
super(discoveryService, trackerRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return CALLBACK_PATH;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getProvider() {
|
||||
return PROVIDER;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="gpstracker" 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>GPSTracker Binding</name>
|
||||
<description>GPS tracking with OwnTracks and GPSLogger support over HTTP</description>
|
||||
<author>Gabor Bicskei</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,40 @@
|
||||
<?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="thing-type:gpstracker:tracker">
|
||||
<parameter name="trackerId" type="text" required="true">
|
||||
<label>Tracker Id</label>
|
||||
<description>Id configured in tracker application.</description>
|
||||
<advanced>false</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
<config-description uri="channel-type:gpstracker:distance">
|
||||
<parameter name="regionName" type="text" required="true">
|
||||
<label>Region Name</label>
|
||||
<description>Region name payload for trigger channel event</description>
|
||||
<advanced>false</advanced>
|
||||
<limitToOptions>false</limitToOptions>
|
||||
</parameter>
|
||||
<parameter name="regionRadius" type="decimal" required="true" min="0">
|
||||
<label>Region Radius</label>
|
||||
<description>Region circle radius in m or yd</description>
|
||||
<default>100</default>
|
||||
<advanced>false</advanced>
|
||||
</parameter>
|
||||
<parameter name="regionCenterLocation" type="text" required="true">
|
||||
<context>location</context>
|
||||
<label>Region Center</label>
|
||||
<description>Location of the region center</description>
|
||||
<advanced>false</advanced>
|
||||
</parameter>
|
||||
<parameter name="accuracyThreshold" type="decimal" required="true" min="0">
|
||||
<label>Accuracy Threshold</label>
|
||||
<description>Location accuracy threshold in m or yd (0 to disable)</description>
|
||||
<default>0</default>
|
||||
<advanced>false</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</config-description:config-descriptions>
|
||||
@@ -0,0 +1,15 @@
|
||||
<?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="profile:gpstracker:trigger-geofence">
|
||||
<parameter name="regionName" type="text" required="true">
|
||||
<label>Region Name</label>
|
||||
<description>Region name to trigger the switch</description>
|
||||
<advanced>false</advanced>
|
||||
<limitToOptions>false</limitToOptions>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</config-description:config-descriptions>
|
||||
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="gpstracker"
|
||||
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 Types -->
|
||||
<thing-type id="tracker" extensible="regionDistance">
|
||||
<label>Tracker Device</label>
|
||||
<description>Device running tracker application</description>
|
||||
|
||||
<channels>
|
||||
<channel id="lastLocation" typeId="system.location"/>
|
||||
<channel id="batteryLevel" typeId="system.battery-level"/>
|
||||
<channel id="regionTrigger" typeId="regionTrigger"/>
|
||||
<channel id="lastReport" typeId="lastReport"/>
|
||||
<channel id="gpsAccuracy" typeId="gpsAccuracy"/>
|
||||
</channels>
|
||||
|
||||
<config-description-ref uri="thing-type:gpstracker:tracker"/>
|
||||
</thing-type>
|
||||
|
||||
<!-- Channel Types -->
|
||||
<channel-type id="regionDistance">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Distance</label>
|
||||
<description>Distance from region</description>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
<config-description-ref uri="channel-type:gpstracker:distance"/>
|
||||
</channel-type>
|
||||
<channel-type id="gpsAccuracy">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Accuracy</label>
|
||||
<description>GPS accuracy</description>
|
||||
<state pattern="%d %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="lastReport">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Last Seen</label>
|
||||
<description>Last report timestamp</description>
|
||||
<state pattern="%1$tF %1$tR" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="regionTrigger">
|
||||
<kind>trigger</kind>
|
||||
<label>Region Trigger</label>
|
||||
<description>Trigger channel for entering/leaving regions. Payload is the region name with prefix > for entering
|
||||
and < for leaving.</description>
|
||||
<event/>
|
||||
</channel-type>
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user