added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
32
bundles/org.openhab.binding.gpstracker/.classpath
Normal file
32
bundles/org.openhab.binding.gpstracker/.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.gpstracker/.project
Normal file
23
bundles/org.openhab.binding.gpstracker/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.openhab.binding.gpstracker</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.gpstracker/NOTICE
Normal file
13
bundles/org.openhab.binding.gpstracker/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
|
||||
281
bundles/org.openhab.binding.gpstracker/README.md
Normal file
281
bundles/org.openhab.binding.gpstracker/README.md
Normal file
@@ -0,0 +1,281 @@
|
||||
# GPSTracker Binding
|
||||
|
||||
This binding allows you to connect mobile GPS tracker applications to openHAB and process GPS location reports.
|
||||
|
||||
Currently two applications are supported:
|
||||
|
||||
* [OwnTracks](https://owntracks.org/booklet/) - iOS, Android
|
||||
* [GPSLogger](https://gpslogger.app/) - Android
|
||||
|
||||
GPS location reports are sent to openHAB using HTTP.
|
||||
Please be aware that this communication uses the public network so make sure your openHAB installation is [secured](https://www.openhab.org/docs/installation/security.html#encrypted-communication) (but accessible from public internet through myopenhab.org or using a reverse proxy) and you configured HTTP**S** access in tracking applications.
|
||||
The easiest way to achieve this is to use the [openHAB Cloud Connector](https://www.openhab.org/addons/integrations/openhabcloud/) in conjunction with [myopenHAB.org](https://www.myopenhab.org/).
|
||||
|
||||
The binding can process two message types received from trackers:
|
||||
|
||||
* **Location** - This is a simple location report with coordinates extended with some extra information about the tracker (e.g. tracker device battery level). [OwnTracks, GPSLogger]
|
||||
* **Transition** - This report is based on regions defined in tracker application only. A message is sent every time the tracker enters or leaves a region. [OwnTracks only] See "Distance Channel and Presence Switch" how this behavior can be achieved with openHAB's on-board tools.
|
||||
|
||||
## Configuration
|
||||
|
||||
### OwnTracks
|
||||
|
||||
Install [OwnTracks for Android](https://play.google.com/store/apps/details?id=org.owntracks.android) or [OwnTracks for iOS](https://itunes.apple.com/us/app/owntracks/id692424691) on your device.
|
||||
|
||||
Go to Preferences/Connection and set:
|
||||
|
||||
* **Mode** - select Private HTTP
|
||||
* **Host**
|
||||
* https://<your.ip.address>/gpstracker/owntracks or
|
||||
* https://home.myopenhab.org/gpstracker/owntracks
|
||||
* **Identification**
|
||||
* Turn Authentication ON
|
||||
* Set username and password to be able to reach your openHAB server (myopenhab.org credential, if choosen as host)
|
||||
* Device ID is not important. Set it to e.g. phone
|
||||
* Tracker ID - This id identifies the tracker as a thing. This must be unique for each tracker connected to the same openHAB instance (e.g. family members).
|
||||
|
||||
### GPSLogger
|
||||
|
||||
Install [GPSLogger for Android](https://play.google.com/store/apps/details?id=com.mendhak.gpslogger) on your device.
|
||||
After the launch, go to General Options.
|
||||
Enable **Start on boot-up** and **Start on app launch**.
|
||||
|
||||
Go to *Logging details* and enable **Log to custom URL**.
|
||||
If you only want to use GPSLogger for this binding, you can disable all other "*Log to*" entries.
|
||||
Right after enabling, the app takes you to the *Log to custom URL* settings:
|
||||
|
||||
* **URL**
|
||||
* https://<your.ip.address>/gpstracker/gpslogger or
|
||||
* https://home.myopenhab.org/gpstracker/gpslogger
|
||||
* **HTTP Body** - type in: { "_type":"location", "lat":%LAT, "lon":%LON, "tid":"XY", "acc":%ACC, "batt":%BATT, "tst":%TIMESTAMP }
|
||||
* Note: "*tid*" is the tracker id that identifies the tracker as a thing. This must be unique for each tracker connected to the same openHAB instance (e.g. family members).
|
||||
* **HTTP Headers** - type in: *Content-Type: application/json*
|
||||
* **HTTP Method** - type in: *POST*
|
||||
* **Basic Authentication** - Set username and password to be able to reach your openHAB server (myopenhab.org credential, if choosen as URL)
|
||||
* Check if everything is ok by clicking on **Validate SSL Certificate**.
|
||||
|
||||
### Things
|
||||
|
||||
It is possible to define things manually or to use the discovery feature of the openHAB.
|
||||
An important detail for both methods is that a tracker is identified by a **tracker id** configured on mobile devices.
|
||||
Make sure these tracker ids are unique in group of trackers connected to a single openHAB instance.
|
||||
|
||||
#### Discovery
|
||||
|
||||
If the things are not defined in **.things** files the first time the tracker sends a GPS log record the binding recognizes it as new tracker and inserts an entry into the Inbox as new tracker with name **GPS Tracker ??**.
|
||||
|
||||
### Channels
|
||||
|
||||
Basic channels provided by the tracker things:
|
||||
|
||||
* **Location** - Current location of the tracker
|
||||
* **Accuracy** - GPS accuracy
|
||||
* **Last Report** - Timestamp of the last location report
|
||||
* **Battery Level** - Battery level of the device running the tracker application
|
||||
* **Region trigger channel** - Used by regions defined in tracker application. Event is fired with payload of the region name when the binding receives a **transition** log record or a distance calculation for a **location** record indicates that the tracker is outside of the region circle. Payload is suffixed with `/enter` for entering and with `/leave` for leaving events.
|
||||
|
||||
#### Distance Calculation
|
||||
|
||||
Tracker thing can be extended with **Distance** channels (channel type is `regionDistance`) if a distance calculation is needed for a region.
|
||||
These dynamic channels require the following parameters:
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|---------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Region Name | String | Region name. If the region is configured in the tracker app as well use the same name. Distance channels can also be defined as binding only regions (not configured in trackers) |
|
||||
| Region center | Location | Region center location |
|
||||
| Region Radius | Integer | Geofence radius |
|
||||
| Accuracy Threshold | Integer | Location accuracy threshold (0 to disable) |
|
||||
|
||||
Distance values will be updated each time a GPS location log record is received from the tracker if the accuracy is below the threshold or if the threshold is disabled.
|
||||
|
||||
When this calculated distance is less than the defined geofence radius the binding also fires event on Region Trigger channel.
|
||||
|
||||
* When the tracker is approaching (the new calculated distance is less then the previous one) the payload is <<region_name>>/enter.
|
||||
* If the tracker is distancing (the new calculated distance is greater then the previous one) payload is <<region_name>>/leave.
|
||||
|
||||
If the tracker is moving inside/outside the region (both the previous and the current calculated distance value is less/greater than the radius) no events are fired.
|
||||
This means that the region events are triggered **ONLY IN CASE THE REGION BORDER IS CROSSED**.
|
||||
|
||||
**Note**: In case the location is set for openHAB installation (Configuration/System/Regional Settings) the binding automatically creates the System Distance channel (gpstracker:tracker:??:distanceSystem) with region name set to **System**.
|
||||
|
||||
#### Tracker Location Status
|
||||
|
||||
In case external regions are defined in mobile application the binding fires entering/leaving events on region trigger channel.
|
||||
These events are fired in case of distance channels as well when the tracker crosses the geofence (the distance from the region center becomes less/greater than the radius).
|
||||
In order to have this state available in stateful switch items (e.g. for rule logic) switch type items can be linked to **regionTrigger** channel.
|
||||
There is a special profile (gpstracker:trigger-geofence) that transforms trigger enter/leave events to switch states:
|
||||
|
||||
* **<<region_name>>/enter** will update the switch state to **ON**
|
||||
* **<<region_name>>/leave** will update the switch state to **OFF**
|
||||
|
||||
To link a switch item to regionTrigger channel the following parameters are required by the item link:
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|--------------|-----------|----------------------------------------------------------------------------------------------------------|
|
||||
| Profile Name | Selection | Select the Geofence(gpstracker:trigger-geofence) from dropdown |
|
||||
| Region Name | String | Region name which should be the same as used in the tracker application or defined for distance channels |
|
||||
|
||||
## Manual Configuration
|
||||
|
||||
### Things
|
||||
|
||||
```
|
||||
//tracker definition
|
||||
Thing gpstracker:tracker:1 "XY tracker" [trackerId="XY"]
|
||||
|
||||
//tracker definition with extra distance channel
|
||||
Thing gpstracker:tracker:EX "EX tracker" [trackerId="EX"] {
|
||||
Channels:
|
||||
Type regionDistance : homeDistance "Distance from Home" [
|
||||
regionName="Home",
|
||||
regionCenterLocation="11.1111,22.2222",
|
||||
regionRadius=100,
|
||||
accuracyThreshold=30
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Items
|
||||
|
||||
```
|
||||
//items for basic channels
|
||||
Location locationEX "Location" {channel="gpstracker:tracker:1:lastLocation"}
|
||||
DateTime lastSeenEX "Last seen" {channel="gpstracker:tracker:1:lastReport"}
|
||||
Number batteryEX "Battery level" {channel="gpstracker:tracker:1:batteryLevel"}
|
||||
Number:Length accuracyEX "GPS Accuracy [%d m]" {channel="gpstracker:tracker:1:gpsAccuracy"}
|
||||
|
||||
//linking switch item to regionTrigger channel. assuming the Home distance channel is defined in the binding config (see above)
|
||||
Switch atHomeEX "Home presence" {channel="gpstracker:tracker:EX:regionTrigger" [profile="gpstracker:trigger-geofence", regionName="Home"]}
|
||||
|
||||
//another switch for work region. assuming the OTWork is defined in OwnTracks application (no distance channel is needed like for Home)
|
||||
Switch atWorkEX "Work presence" {channel="gpstracker:tracker:EX:regionTrigger" [profile="gpstracker:trigger-geofence", regionName="OTWork"]}
|
||||
```
|
||||
|
||||
### Sitemaps
|
||||
|
||||
```
|
||||
sitemap gpstracker label="GPSTracker Binding" {
|
||||
Text item=distanceEX
|
||||
Text item=atWorkEX
|
||||
Text item=atHomeEX
|
||||
Text item=lastSeenEX
|
||||
Text item=batteryEX
|
||||
Text item=accuracyEX
|
||||
Mapview item=locationEX height=4
|
||||
}
|
||||
```
|
||||
|
||||
## Debug
|
||||
|
||||
As the setup is not that simple here are some hints for debugging.
|
||||
In order to see detailed debug information [set TRACE debug level](https://www.openhab.org/docs/administration/logging.html) for `org.openhab.binding.gpstracker` package.
|
||||
|
||||
### Binding Start
|
||||
|
||||
```
|
||||
2018-10-03 18:12:38.950 [DEBUG] [org.openhab.binding.gpstracker ] - ServiceEvent REGISTERED - {org.openhab.core.config.discovery.DiscoveryService, org.openhab.binding.gpstracker.internal.discovery.TrackerDiscoveryService}={service.id=425, service.bundleid=183, service.scope=bundle, component.name=org.openhab.binding.gpstracker.internal.discovery.TrackerDiscoveryService, component.id=268} - org.openhab.binding.gpstracker
|
||||
2018-10-03 18:12:38.965 [DEBUG] [org.openhab.binding.gpstracker ] - ServiceEvent REGISTERED - {org.openhab.core.thing.binding.ThingHandlerFactory, org.openhab.core.config.core.ConfigOptionProvider}={location=47.536178,19.169812, service.id=426, service.bundleid=183, service.scope=bundle, radius=100, name=Home, component.name=org.openhab.binding.gpstracker.internal.GPSTrackerHandlerFactory, component.id=267, additionalRegionsJSON=[
|
||||
], triggerEvent=false, service.pid=binding.gpstracker} - org.openhab.binding.gpstracker
|
||||
2018-10-03 18:12:38.994 [DEBUG] [er.internal.GPSTrackerHandlerFactory] - Initializing callback servlets
|
||||
2018-10-03 18:12:39.013 [DEBUG] [org.openhab.binding.gpstracker ] - ServiceEvent REGISTERED - {javax.servlet.ServletContext}={osgi.web.version=2.4.0.201809241418, osgi.web.contextpath=/, service.id=427, osgi.web.symbolicname=org.openhab.binding.gpstracker, service.bundleid=183, service.scope=singleton, osgi.web.contextname=default} - org.openhab.binding.gpstracker
|
||||
2018-10-03 18:12:39.047 [DEBUG] [er.internal.GPSTrackerHandlerFactory] - Started GPSTracker Callback servlet on /gpstracker/owntracks
|
||||
2018-10-03 18:12:39.058 [DEBUG] [er.internal.GPSTrackerHandlerFactory] - Started GPSTracker Callback servlet on /gpstracker/gpslogger
|
||||
2018-10-03 18:12:39.072 [DEBUG] [org.openhab.binding.gpstracker ] - ServiceEvent REGISTERED - {org.openhab.core.thing.profiles.ProfileFactory, org.openhab.core.thing.profiles.ProfileAdvisor, org.openhab.core.thing.profiles.ProfileTypeProvider}={service.id=428, service.bundleid=183, service.scope=bundle, component.name=org.openhab.binding.gpstracker.internal.profile.GPSTrackerProfileFactory, component.id=269} - org.openhab.binding.gpstracker
|
||||
2018-10-03 18:12:39.092 [DEBUG] [org.openhab.binding.gpstracker ] - BundleEvent STARTING - org.openhab.binding.gpstracker
|
||||
2018-10-03 18:12:39.098 [DEBUG] [org.openhab.binding.gpstracker ] - BundleEvent STARTED - org.openhab.binding.gpstracker
|
||||
```
|
||||
|
||||
Please note the lines about started servlets:
|
||||
|
||||
```
|
||||
Started GPSTracker Callback servlet on /gpstracker/owntracks
|
||||
Started GPSTracker Callback servlet on /gpstracker/gpslogger
|
||||
```
|
||||
|
||||
### Registration
|
||||
|
||||
In case the discovery is used the first message from a tracker registers it in the inbox:
|
||||
|
||||
```
|
||||
2018-10-05 08:36:14.283 [DEBUG] [nal.provider.AbstractCallbackServlet] - Post message received from OwnTracks tracker: {"_type":"location","tid":"XX","acc":10.0,"lat":41.53,"lon":16.16,"tst":1527966973,"wtst":1524244195,"batt":96}
|
||||
2018-10-05 08:36:14.286 [DEBUG] [nal.provider.AbstractCallbackServlet] - There is no handler for tracker XX. Check the inbox for the new tracker.
|
||||
```
|
||||
|
||||
### Location Update
|
||||
|
||||
The next location message already calculates the distance for System location:
|
||||
|
||||
```
|
||||
2018-10-05 08:38:33.916 [DEBUG] [nal.provider.AbstractCallbackServlet] - Post message received from OwnTracks tracker: {"_type":"location","tid":"XX","acc":10.0,"lat":41.53,"lon":16.16,"tst":1527966973,"wtst":1524244195,"batt":96}
|
||||
2018-10-05 08:38:33.917 [DEBUG] [cker.internal.handler.TrackerHandler] - Update base channels for tracker XX from message: org.openhab.binding.gpstracker.internal.message.LocationMessage@31dfed63
|
||||
2018-10-05 08:38:33.941 [TRACE] [cker.internal.handler.TrackerHandler] - batteryLevel -> 96
|
||||
2018-10-05 08:38:33.942 [TRACE] [cker.internal.handler.TrackerHandler] - lastLocation -> 41.53,16.16
|
||||
2018-10-05 08:38:33.943 [TRACE] [cker.internal.handler.TrackerHandler] - lastReport -> 2018-06-02T19:16:13.000+0000
|
||||
2018-10-05 08:38:33.943 [DEBUG] [cker.internal.handler.TrackerHandler] - Updating distance channels tracker XX
|
||||
2018-10-05 08:38:33.944 [TRACE] [cker.internal.handler.TrackerHandler] - Region center distance from tracker location 41.53,16.16 is 709835.1673811453m
|
||||
2018-10-05 08:38:33.944 [TRACE] [cker.internal.handler.TrackerHandler] - System uses SI measurement units. No conversion is needed.
|
||||
```
|
||||
|
||||
### Distance Channel and Presence Switch
|
||||
|
||||
Assumptions:
|
||||
|
||||
* A Home distance channel is added to the tracker with parameters:
|
||||
* Channel ID: distanceHome
|
||||
* Region Name: Home
|
||||
* Region Radius: 100
|
||||
* Region Center: 42.53,16.16
|
||||
* Presence switch is linked to the regionTrigger channel with parameters:
|
||||
* Profile: Geofence(gpstracker:trigger-geofence)
|
||||
* Region Name: Home
|
||||
|
||||

|
||||
|
||||
After a location message received from the tracker the log should contain these lines:
|
||||
|
||||
```
|
||||
2018-10-05 09:27:58.768 [DEBUG] [nal.provider.AbstractCallbackServlet] - Post message received from OwnTracks tracker: {"_type":"location","tid":"XX","acc":10.0,"lat":42.53,"lon":17.16,"tst":1527966973,"wtst":1524244195,"batt":96}
|
||||
2018-10-05 09:27:58.769 [DEBUG] [cker.internal.handler.TrackerHandler] - Update base channels for tracker XX from message: org.openhab.binding.gpstracker.internal.message.LocationMessage@67e5d438
|
||||
2018-10-05 09:27:58.770 [TRACE] [cker.internal.handler.TrackerHandler] - batteryLevel -> 96
|
||||
2018-10-05 09:27:58.771 [TRACE] [cker.internal.handler.TrackerHandler] - lastLocation -> 42.53,17.16
|
||||
2018-10-05 09:27:58.772 [TRACE] [cker.internal.handler.TrackerHandler] - lastReport -> 2018-06-02T19:16:13.000+0000
|
||||
2018-10-05 09:27:58.773 [DEBUG] [cker.internal.handler.TrackerHandler] - Updating distance channels tracker XX
|
||||
2018-10-05 09:27:58.774 [TRACE] [cker.internal.handler.TrackerHandler] - Region Home center distance from tracker location 42.53,17.16 is 82033.47272145993m
|
||||
2018-10-05 09:27:58.775 [TRACE] [cker.internal.handler.TrackerHandler] - System uses SI measurement units. No conversion is needed.
|
||||
2018-10-05 09:27:58.779 [DEBUG] [ofile.GPSTrackerTriggerSwitchProfile] - Trigger switch profile created for region Home
|
||||
2018-10-05 09:27:58.779 [DEBUG] [ofile.GPSTrackerTriggerSwitchProfile] - Transition trigger Home/leave handled for region Home by profile: OFF
|
||||
2018-10-05 09:27:58.792 [TRACE] [cker.internal.handler.TrackerHandler] - Triggering Home for XX/Home/leave
|
||||
2018-10-05 09:27:58.793 [TRACE] [cker.internal.handler.TrackerHandler] - Region System center distance from tracker location 42.53,17.16 is 579224.192171576m
|
||||
2018-10-05 09:27:58.794 [TRACE] [cker.internal.handler.TrackerHandler] - System uses SI measurement units. No conversion is needed.
|
||||
```
|
||||
|
||||
**Note**: If the binding was restarted or the distance channel is new (this is the first location message for the channel) only the second location update will trigger event as the binding has to know the previous state.
|
||||
|
||||
### External Region and Presence Switch
|
||||
|
||||
Assumptions:
|
||||
|
||||
* A **shared** region is defined in OwnTracks application. Lets call it Work.
|
||||
* Presence switch is linked to the regionTrigger channel with parameters:
|
||||
* Profile: Geofence(gpstracker:trigger-geofence)
|
||||
* Region Name: Work
|
||||
|
||||
```
|
||||
2018-10-05 09:35:27.203 [DEBUG] [nal.provider.AbstractCallbackServlet] - Post message received from OwnTracks tracker: {"_type":"transition","tid":"XX","acc":10.0,"desc":"Work","event":"enter","lat":42.53,"lon":18.22,"tst":1527966973,"wtst":1524244195,"t":"c"}
|
||||
2018-10-05 09:35:27.204 [DEBUG] [cker.internal.handler.TrackerHandler] - ConfigHelper transition event received: Work
|
||||
2018-10-05 09:35:27.204 [DEBUG] [cker.internal.handler.TrackerHandler] - Update base channels for tracker XX from message: org.openhab.binding.gpstracker.internal.message.TransitionMessage@5e5b0d59
|
||||
2018-10-05 09:35:27.204 [TRACE] [cker.internal.handler.TrackerHandler] - lastLocation -> 42.53,18.22
|
||||
2018-10-05 09:35:27.205 [TRACE] [cker.internal.handler.TrackerHandler] - lastReport -> 2018-06-02T19:16:13.000+0000
|
||||
2018-10-05 09:35:27.205 [DEBUG] [cker.internal.handler.TrackerHandler] - Updating distance channels tracker XX
|
||||
2018-10-05 09:35:27.206 [TRACE] [cker.internal.handler.TrackerHandler] - Region Home center distance from tracker location 42.53,18.22 is 168985.77453131412m
|
||||
2018-10-05 09:35:27.207 [TRACE] [cker.internal.handler.TrackerHandler] - Region System center distance from tracker location 42.53,18.22 is 562259.467093007m
|
||||
2018-10-05 09:35:27.208 [TRACE] [cker.internal.handler.TrackerHandler] - Triggering Work for XX/Work/enter
|
||||
2018-10-05 09:35:27.208 [DEBUG] [ofile.GPSTrackerTriggerSwitchProfile] - Trigger switch profile created for region Home
|
||||
2018-10-05 09:35:27.210 [DEBUG] [ofile.GPSTrackerTriggerSwitchProfile] - Trigger switch profile created for region Work
|
||||
2018-10-05 09:35:27.210 [DEBUG] [ofile.GPSTrackerTriggerSwitchProfile] - Transition trigger Work/enter handled for region Work by profile: ON
|
||||
```
|
||||
|
||||
**Note**:
|
||||
|
||||
* If the binding was restarted only the second transition update will trigger event as the binding has to know the previous state.
|
||||
* The distance is not calculated for Work as the binding doesn't know the Work region center.
|
||||
BIN
bundles/org.openhab.binding.gpstracker/doc/example.png
Normal file
BIN
bundles/org.openhab.binding.gpstracker/doc/example.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 274 KiB |
17
bundles/org.openhab.binding.gpstracker/pom.xml
Normal file
17
bundles/org.openhab.binding.gpstracker/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.gpstracker</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: GPSTracker Binding</name>
|
||||
|
||||
</project>
|
||||
@@ -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