From fb76d50e419ed11ccc78abdadb659f7c2254042d Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Mon, 16 Sep 2024 10:18:42 -0700 Subject: [PATCH 01/30] Remove old API property --- swagger.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/swagger.json b/swagger.json index 2ee8524b969..75b8dd18d37 100644 --- a/swagger.json +++ b/swagger.json @@ -2773,9 +2773,6 @@ "password": { "type": "string" }, - "twelveHourFormat": { - "type": "boolean" - }, "coordinateFormat": { "type": "string" }, @@ -2850,9 +2847,6 @@ "zoom": { "type": "integer" }, - "twelveHourFormat": { - "type": "boolean" - }, "version": { "type": "string" }, From b89a69251bded7036488c765ec17499cb5e88346 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Mon, 16 Sep 2024 16:11:45 -0700 Subject: [PATCH 02/30] Update git submodule --- traccar-web | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/traccar-web b/traccar-web index 37c2d32b7ba..9a34d3f1454 160000 --- a/traccar-web +++ b/traccar-web @@ -1 +1 @@ -Subproject commit 37c2d32b7ba9da88794fbe004d3388e299cdf0e7 +Subproject commit 9a34d3f1454a1fb66f7f24cd049410ccaea752cd From a986f818cf4815297b82b7498103c79dfd30e5ce Mon Sep 17 00:00:00 2001 From: Lucas Leite Date: Wed, 18 Sep 2024 02:05:49 -0300 Subject: [PATCH 03/30] Allow managers to send announcements --- .../java/org/traccar/api/resource/NotificationResource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/traccar/api/resource/NotificationResource.java b/src/main/java/org/traccar/api/resource/NotificationResource.java index 8bc81a3a40d..5a0fca24dd9 100644 --- a/src/main/java/org/traccar/api/resource/NotificationResource.java +++ b/src/main/java/org/traccar/api/resource/NotificationResource.java @@ -113,7 +113,7 @@ public Response testMessage(@PathParam("notificator") String notificator) public Response sendMessage( @PathParam("notificator") String notificator, @QueryParam("userId") List userIds, NotificationMessage message) throws MessageException, StorageException { - permissionsService.checkAdmin(getUserId()); + permissionsService.checkManager(getUserId()); List users; if (userIds.isEmpty()) { users = storage.getObjects(User.class, new Request(new Columns.All())); From 7301318566fcfc377e672b3d380e33bac9615542 Mon Sep 17 00:00:00 2001 From: Lucas Leite Date: Wed, 18 Sep 2024 02:46:18 -0300 Subject: [PATCH 04/30] Ensure managers get only their managed users --- .../api/resource/NotificationResource.java | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/traccar/api/resource/NotificationResource.java b/src/main/java/org/traccar/api/resource/NotificationResource.java index 5a0fca24dd9..e2d5c59c11b 100644 --- a/src/main/java/org/traccar/api/resource/NotificationResource.java +++ b/src/main/java/org/traccar/api/resource/NotificationResource.java @@ -28,10 +28,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.api.ExtendedObjectResource; -import org.traccar.model.Event; -import org.traccar.model.Notification; -import org.traccar.model.Typed; -import org.traccar.model.User; +import org.traccar.model.*; import org.traccar.notification.MessageException; import org.traccar.notification.NotificationMessage; import org.traccar.notification.NotificatorManager; @@ -42,11 +39,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; @Path("notifications") @@ -116,12 +109,24 @@ public Response sendMessage( permissionsService.checkManager(getUserId()); List users; if (userIds.isEmpty()) { - users = storage.getObjects(User.class, new Request(new Columns.All())); + users = permissionsService.notAdmin(getUserId()) ? + storage.getObjects(User.class, new Request(new Columns.All(), + new Condition.Permission(User.class, getUserId(), ManagedUser.class).excludeGroups())) : + storage.getObjects(User.class, new Request(new Columns.All())); + } else { users = new ArrayList<>(); for (long userId : userIds) { - users.add(storage.getObject( - User.class, new Request(new Columns.All(), new Condition.Equals("id", userId)))); + if (permissionsService.notAdmin(getUserId())) { + var conditions = new LinkedList(); + conditions.add(new Condition.Equals("id", userId)); + conditions.add(new Condition.Permission(User.class, getUserId(), ManagedUser.class).excludeGroups()); + users.add(storage.getObject( + User.class, new Request(new Columns.All(), Condition.merge(conditions)))); + } else { + users.add(storage.getObject( + User.class, new Request(new Columns.All(), new Condition.Equals("id", userId)))); + } } } for (User user : users) { From 2d39df50342a95dd3ce96d3b1193246a2cef0c9d Mon Sep 17 00:00:00 2001 From: Lucas Leite Date: Wed, 18 Sep 2024 02:53:10 -0300 Subject: [PATCH 05/30] Fixed imports --- .../traccar/api/resource/NotificationResource.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/traccar/api/resource/NotificationResource.java b/src/main/java/org/traccar/api/resource/NotificationResource.java index e2d5c59c11b..0e25b9242b3 100644 --- a/src/main/java/org/traccar/api/resource/NotificationResource.java +++ b/src/main/java/org/traccar/api/resource/NotificationResource.java @@ -28,7 +28,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.api.ExtendedObjectResource; -import org.traccar.model.*; +import org.traccar.model.Event; +import org.traccar.model.ManagedUser; +import org.traccar.model.Notification; +import org.traccar.model.Typed; +import org.traccar.model.User; import org.traccar.notification.MessageException; import org.traccar.notification.NotificationMessage; import org.traccar.notification.NotificatorManager; @@ -39,7 +43,11 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; import java.util.stream.Collectors; @Path("notifications") From 916cd59e29290058e1f75a3d8ac5ea8c51eb36d6 Mon Sep 17 00:00:00 2001 From: Lucas Leite Date: Wed, 18 Sep 2024 09:30:18 -0300 Subject: [PATCH 06/30] Updating code to apply suggestions --- .../api/resource/NotificationResource.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/traccar/api/resource/NotificationResource.java b/src/main/java/org/traccar/api/resource/NotificationResource.java index 0e25b9242b3..6d409777a09 100644 --- a/src/main/java/org/traccar/api/resource/NotificationResource.java +++ b/src/main/java/org/traccar/api/resource/NotificationResource.java @@ -117,23 +117,24 @@ public Response sendMessage( permissionsService.checkManager(getUserId()); List users; if (userIds.isEmpty()) { - users = permissionsService.notAdmin(getUserId()) ? - storage.getObjects(User.class, new Request(new Columns.All(), - new Condition.Permission(User.class, getUserId(), ManagedUser.class).excludeGroups())) : - storage.getObjects(User.class, new Request(new Columns.All())); - + if (permissionsService.notAdmin(getUserId())) { + users = storage.getObjects(User.class, new Request(new Columns.All(), + new Condition.Permission(User.class, getUserId(), ManagedUser.class).excludeGroups())); + } else { + users = storage.getObjects(User.class, new Request(new Columns.All())); + } } else { users = new ArrayList<>(); for (long userId : userIds) { + var conditions = new LinkedList(); + conditions.add(new Condition.Equals("id", userId)); if (permissionsService.notAdmin(getUserId())) { - var conditions = new LinkedList(); - conditions.add(new Condition.Equals("id", userId)); conditions.add(new Condition.Permission(User.class, getUserId(), ManagedUser.class).excludeGroups()); users.add(storage.getObject( User.class, new Request(new Columns.All(), Condition.merge(conditions)))); } else { users.add(storage.getObject( - User.class, new Request(new Columns.All(), new Condition.Equals("id", userId)))); + User.class, new Request(new Columns.All(), Condition.merge(conditions)))); } } } From 08ce3f4520acd881229b19d6dd900ed47a323b51 Mon Sep 17 00:00:00 2001 From: Lucas Leite Date: Wed, 18 Sep 2024 09:42:42 -0300 Subject: [PATCH 07/30] Updating code to apply suggestions --- .../org/traccar/api/resource/NotificationResource.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/traccar/api/resource/NotificationResource.java b/src/main/java/org/traccar/api/resource/NotificationResource.java index 6d409777a09..8cfdf8a5b64 100644 --- a/src/main/java/org/traccar/api/resource/NotificationResource.java +++ b/src/main/java/org/traccar/api/resource/NotificationResource.java @@ -130,12 +130,9 @@ public Response sendMessage( conditions.add(new Condition.Equals("id", userId)); if (permissionsService.notAdmin(getUserId())) { conditions.add(new Condition.Permission(User.class, getUserId(), ManagedUser.class).excludeGroups()); - users.add(storage.getObject( - User.class, new Request(new Columns.All(), Condition.merge(conditions)))); - } else { - users.add(storage.getObject( - User.class, new Request(new Columns.All(), Condition.merge(conditions)))); } + users.add(storage.getObject( + User.class, new Request(new Columns.All(), Condition.merge(conditions)))); } } for (User user : users) { From 72e62731c5daad2c4aef0a12497dce72698e4a4e Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Wed, 18 Sep 2024 07:29:45 -0700 Subject: [PATCH 08/30] Add OpenAPI yaml spec --- openapi.yaml | 2351 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2351 insertions(+) create mode 100644 openapi.yaml diff --git a/openapi.yaml b/openapi.yaml new file mode 100644 index 00000000000..ce3e988c33b --- /dev/null +++ b/openapi.yaml @@ -0,0 +1,2351 @@ +openapi: 3.1.0 +info: + title: Traccar + description: Traccar GPS tracking server API documentation. To use the API you need to have a server instance. For testing purposes you can use one of free [demo servers](https://www.traccar.org/demo-server/). For production use you can install your own server or get a [subscription service](https://www.traccar.org/product/tracking-server/). + contact: + name: Traccar Support + url: https://www.traccar.org/ + email: support@traccar.org + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + version: '6.5' +servers: + - url: https://demo.traccar.org/api + description: Demo Server 1 + - url: https://demo2.traccar.org/api + description: Demo Server 2 + - url: https://demo3.traccar.org/api + description: Demo Server 3 + - url: https://demo4.traccar.org/api + description: Demo Server 4 + - url: https://server.traccar.org/api + description: Subscription Server + - url: http://{host}:{port}/api + description: Other Server + variables: + host: + default: localhost + port: + enum: + - '8082' + - '80' + default: '8082' +security: + - BasicAuth: [] + - ApiKey: [] +tags: + - name: Server + description: Server information + - name: Session + description: User session management + - name: Devices + description: Device management + - name: Groups + description: Group management + - name: Users + description: User management + - name: Permissions + description: User permissions and other object linking + - name: Positions + description: Retrieving raw location information + - name: Events + description: Retrieving event information + - name: Reports + description: Reports generation + - name: Notifications + description: User notifications management + - name: Geofences + description: Geofence management + - name: Commands + description: Sending commands to devices and stored command management + - name: Attributes + description: Computed attributes management + - name: Drivers + description: Drivers management + - name: Maintenance + description: Maintenance management + - name: Calendars + description: Calendar management + - name: Statistics + description: Retrieving server statistics +paths: + /commands: + get: + summary: Fetch a list of Saved Commands + tags: + - Commands + description: Without params, it returns a list of Saved Commands the user has access to + parameters: + - name: all + in: query + description: Can only be used by admins or managers to fetch all entities + schema: + type: boolean + - name: userId + in: query + description: Standard users can use this only with their own _userId_ + schema: + type: integer + - name: deviceId + in: query + description: Standard users can use this only with _deviceId_s, they have access to + schema: + type: integer + - name: groupId + in: query + description: >- + Standard users can use this only with _groupId_s, they have access + to + schema: + type: integer + - name: refresh + in: query + schema: + type: boolean + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Command' + post: + summary: Create a Saved Command + tags: + - Commands + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Command' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Command' + x-codegen-request-body-name: body + /commands/{id}: + put: + summary: Update a Saved Command + tags: + - Commands + parameters: + - name: id + in: path + required: true + schema: + type: integer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Command' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Command' + x-codegen-request-body-name: body + delete: + summary: Delete a Saved Command + tags: + - Commands + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + '204': + description: No Content + content: {} + /commands/send: + get: + summary: Fetch a list of Saved Commands supported by Device at the moment + description: >- + Return a list of saved commands linked to Device and its groups, + filtered by current Device protocol support + tags: + - Commands + parameters: + - name: deviceId + in: query + description: >- + Standard users can use this only with _deviceId_s, they have access + to + schema: + type: integer + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Command' + '400': + description: Could happen when the user doesn't have permission for the device + content: {} + post: + summary: Dispatch commands to device + description: Dispatch a new command or Saved Command if _body.id_ set + tags: + - Commands + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Command' + required: true + responses: + '200': + description: Command sent + content: + application/json: + schema: + $ref: '#/components/schemas/Command' + '202': + description: Command queued + content: + application/json: + schema: + $ref: '#/components/schemas/Command' + '400': + description: >- + Could happen when the user doesn't have permission or an incorrect + command _type_ for the device + content: {} + x-codegen-request-body-name: body + /commands/types: + get: + summary: >- + Fetch a list of available Commands for the Device or all possible + Commands if Device ommited + tags: + - Commands + parameters: + - name: deviceId + in: query + description: >- + Internal device identifier. Only works if device has already + reported some locations + schema: + type: integer + - name: protocol + in: query + description: Protocol name. Can be used instead of device id + schema: + type: string + - name: textChannel + in: query + description: >- + When `true` return SMS commands. If not specified or `false` return + data commands + schema: + type: boolean + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CommandType' + '400': + description: >- + Could happen when trying to fetch from a device the user does not + have permission + content: {} + /devices: + get: + summary: Fetch a list of Devices + description: Without any params, returns a list of the user's devices + tags: + - Devices + parameters: + - name: all + in: query + description: Can only be used by admins or managers to fetch all entities + schema: + type: boolean + - name: userId + in: query + description: Standard users can use this only with their own _userId_ + schema: + type: integer + - name: id + in: query + description: >- + To fetch one or more devices. Multiple params can be passed like + `id=31&id=42` + schema: + type: integer + - name: uniqueId + in: query + description: >- + To fetch one or more devices. Multiple params can be passed like + `uniqueId=333331&uniqieId=44442` + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Device' + '400': + description: No permission + content: {} + post: + summary: Create a Device + tags: + - Devices + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Device' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Device' + x-codegen-request-body-name: body + /devices/{id}: + put: + summary: Update a Device + tags: + - Devices + parameters: + - name: id + in: path + required: true + schema: + type: integer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Device' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Device' + x-codegen-request-body-name: body + delete: + summary: Delete a Device + tags: + - Devices + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + '204': + description: No Content + content: {} + /devices/{id}/accumulators: + put: + summary: Update total distance and hours of the Device + tags: + - Devices + parameters: + - name: id + in: path + required: true + schema: + type: integer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/DeviceAccumulators' + required: true + responses: + '204': + description: No Content + content: {} + x-codegen-request-body-name: body + /groups: + get: + summary: Fetch a list of Groups + description: Without any params, returns a list of the Groups the user belongs to + tags: + - Groups + parameters: + - name: all + in: query + description: Can only be used by admins or managers to fetch all entities + schema: + type: boolean + - name: userId + in: query + description: Standard users can use this only with their own _userId_ + schema: + type: integer + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Group' + post: + summary: Create a Group + tags: + - Groups + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Group' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Group' + '400': + description: No permission + content: {} + x-codegen-request-body-name: body + /groups/{id}: + put: + summary: Update a Group + tags: + - Groups + parameters: + - name: id + in: path + required: true + schema: + type: integer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Group' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Group' + x-codegen-request-body-name: body + delete: + summary: Delete a Group + tags: + - Groups + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + '204': + description: No Content + content: {} + /permissions: + post: + summary: Link an Object to another Object + tags: + - Permissions + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Permission' + required: true + responses: + '204': + description: No Content + content: {} + '400': + description: No permission + content: {} + x-codegen-request-body-name: body + delete: + summary: Unlink an Object from another Object + tags: + - Permissions + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Permission' + required: true + responses: + '204': + description: No Content + content: {} + x-codegen-request-body-name: body + /positions: + get: + summary: Fetches a list of Positions + description: >- + We strongly recommend using [Traccar WebSocket + API](https://www.traccar.org/traccar-api/) instead of periodically + polling positions endpoint. Without any params, it returns a list of + last known positions for all the user's Devices. _from_ and _to_ fields + are not required with _id_. + tags: + - Positions + parameters: + - name: deviceId + in: query + description: >- + _deviceId_ is optional, but requires the _from_ and _to_ parameters + when used + schema: + type: integer + - name: from + in: query + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + schema: + type: string + format: date-time + - name: to + in: query + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + schema: + type: string + format: date-time + - name: id + in: query + description: >- + To fetch one or more positions. Multiple params can be passed like + `id=31&id=42` + schema: + type: integer + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Position' + text/csv: + schema: + type: array + items: + $ref: '#/components/schemas/Position' + application/gpx+xml: + schema: + type: array + items: + $ref: '#/components/schemas/Position' + delete: + summary: Deletes all the Positions of a device in the time span specified + description: '' + tags: + - Positions + parameters: + - name: deviceId + in: query + description: '' + schema: + type: integer + required: true + - name: from + in: query + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + schema: + type: string + format: date-time + required: true + - name: to + in: query + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + schema: + type: string + format: date-time + required: true + responses: + '204': + description: No Content + content: {} + '400': + description: Bad Request + content: {} + /server: + get: + summary: Fetch Server information + tags: + - Server + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Server' + put: + summary: Update Server information + tags: + - Server + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Server' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Server' + x-codegen-request-body-name: body + /session: + get: + summary: Fetch Session information + tags: + - Session + parameters: + - name: token + in: query + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/User' + '404': + description: Not Found + content: {} + post: + summary: Create a new Session + tags: + - Session + requestBody: + content: + application/x-www-form-urlencoded: + schema: + required: + - email + - password + properties: + email: + type: string + password: + type: string + format: password + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/User' + '401': + description: Unauthorized + content: {} + delete: + summary: Close the Session + tags: + - Session + responses: + '204': + description: No Content + content: {} + /session/openid/auth: + get: + summary: Fetch Session information + tags: + - Session + responses: + '303': + description: Redirect to OpenID Connect identity provider + content: {} + /session/openid/callback: + get: + summary: OpenID Callback + tags: + - Session + responses: + '303': + description: Successful authentication, redirect to homepage + content: {} + /users: + get: + summary: Fetch a list of Users + tags: + - Users + parameters: + - name: userId + in: query + description: Can only be used by admin or manager users + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' + '400': + description: No Permission + content: {} + post: + summary: Create a User + tags: + - Users + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/User' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/User' + x-codegen-request-body-name: body + /users/{id}: + put: + summary: Update a User + tags: + - Users + parameters: + - name: id + in: path + required: true + schema: + type: integer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/User' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/User' + x-codegen-request-body-name: body + delete: + summary: Delete a User + tags: + - Users + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + '204': + description: No Content + content: {} + /notifications: + get: + summary: Fetch a list of Notifications + description: >- + Without params, it returns a list of Notifications the user has access + to + tags: + - Notifications + parameters: + - name: all + in: query + description: Can only be used by admins or managers to fetch all entities + schema: + type: boolean + - name: userId + in: query + description: Standard users can use this only with their own _userId_ + schema: + type: integer + - name: deviceId + in: query + description: >- + Standard users can use this only with _deviceId_s, they have access + to + schema: + type: integer + - name: groupId + in: query + description: >- + Standard users can use this only with _groupId_s, they have access + to + schema: + type: integer + - name: refresh + in: query + schema: + type: boolean + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Notification' + post: + summary: Create a Notification + tags: + - Notifications + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Notification' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Notification' + x-codegen-request-body-name: body + /notifications/{id}: + put: + summary: Update a Notification + tags: + - Notifications + parameters: + - name: id + in: path + required: true + schema: + type: integer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Notification' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Notification' + x-codegen-request-body-name: body + delete: + summary: Delete a Notification + tags: + - Notifications + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + '204': + description: No Content + content: {} + /notifications/types: + get: + summary: Fetch a list of available Notification types + tags: + - Notifications + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/NotificationType' + /notifications/test: + post: + summary: Send test notification to current user via Email and SMS + tags: + - Notifications + responses: + '204': + description: Successful sending + content: {} + '400': + description: Could happen if sending has failed + content: {} + /geofences: + get: + summary: Fetch a list of Geofences + description: Without params, it returns a list of Geofences the user has access to + tags: + - Geofences + parameters: + - name: all + in: query + description: Can only be used by admins or managers to fetch all entities + schema: + type: boolean + - name: userId + in: query + description: Standard users can use this only with their own _userId_ + schema: + type: integer + - name: deviceId + in: query + description: >- + Standard users can use this only with _deviceId_s, they have access + to + schema: + type: integer + - name: groupId + in: query + description: >- + Standard users can use this only with _groupId_s, they have access + to + schema: + type: integer + - name: refresh + in: query + schema: + type: boolean + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Geofence' + post: + summary: Create a Geofence + tags: + - Geofences + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Geofence' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Geofence' + x-codegen-request-body-name: body + /geofences/{id}: + put: + summary: Update a Geofence + tags: + - Geofences + parameters: + - name: id + in: path + required: true + schema: + type: integer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Geofence' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Geofence' + x-codegen-request-body-name: body + delete: + summary: Delete a Geofence + tags: + - Geofences + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + '204': + description: No Content + content: {} + /events/{id}: + get: + tags: + - Events + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Event' + /reports/route: + get: + summary: >- + Fetch a list of Positions within the time period for the Devices or + Groups + description: At least one _deviceId_ or one _groupId_ must be passed + tags: + - Reports + parameters: + - name: deviceId + in: query + style: form + explode: true + schema: + type: array + items: + type: integer + - name: groupId + in: query + style: form + explode: true + schema: + type: array + items: + type: integer + - name: from + in: query + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + required: true + schema: + type: string + format: date-time + - name: to + in: query + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + required: true + schema: + type: string + format: date-time + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Position' + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet: + schema: + type: array + items: + $ref: '#/components/schemas/Position' + /reports/events: + get: + summary: Fetch a list of Events within the time period for the Devices or Groups + description: At least one _deviceId_ or one _groupId_ must be passed + tags: + - Reports + parameters: + - name: deviceId + in: query + style: form + explode: true + schema: + type: array + items: + type: integer + - name: groupId + in: query + style: form + explode: true + schema: + type: array + items: + type: integer + - name: type + in: query + description: '% can be used to return events of all types' + style: form + explode: false + schema: + type: array + items: + type: string + - name: from + in: query + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + required: true + schema: + type: string + format: date-time + - name: to + in: query + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + required: true + schema: + type: string + format: date-time + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Event' + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet: + schema: + type: array + items: + $ref: '#/components/schemas/Event' + /reports/summary: + get: + summary: >- + Fetch a list of ReportSummary within the time period for the Devices or + Groups + description: At least one _deviceId_ or one _groupId_ must be passed + tags: + - Reports + parameters: + - name: deviceId + in: query + style: form + explode: true + schema: + type: array + items: + type: integer + - name: groupId + in: query + style: form + explode: true + schema: + type: array + items: + type: integer + - name: from + in: query + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + required: true + schema: + type: string + format: date-time + - name: to + in: query + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + required: true + schema: + type: string + format: date-time + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ReportSummary' + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet: + schema: + type: array + items: + $ref: '#/components/schemas/ReportSummary' + /reports/trips: + get: + summary: >- + Fetch a list of ReportTrips within the time period for the Devices or + Groups + description: At least one _deviceId_ or one _groupId_ must be passed + tags: + - Reports + parameters: + - name: deviceId + in: query + style: form + explode: true + schema: + type: array + items: + type: integer + - name: groupId + in: query + style: form + explode: true + schema: + type: array + items: + type: integer + - name: from + in: query + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + required: true + schema: + type: string + format: date-time + - name: to + in: query + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + required: true + schema: + type: string + format: date-time + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ReportTrips' + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet: + schema: + type: array + items: + $ref: '#/components/schemas/ReportTrips' + /reports/stops: + get: + summary: >- + Fetch a list of ReportStops within the time period for the Devices or + Groups + description: At least one _deviceId_ or one _groupId_ must be passed + tags: + - Reports + parameters: + - name: deviceId + in: query + style: form + explode: true + schema: + type: array + items: + type: integer + - name: groupId + in: query + style: form + explode: true + schema: + type: array + items: + type: integer + - name: from + in: query + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + required: true + schema: + type: string + format: date-time + - name: to + in: query + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + required: true + schema: + type: string + format: date-time + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ReportStops' + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet: + schema: + type: array + items: + $ref: '#/components/schemas/ReportStops' + /statistics: + get: + summary: Fetch server Statistics + tags: + - Statistics + parameters: + - name: from + in: query + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + required: true + schema: + type: string + format: date-time + - name: to + in: query + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + required: true + schema: + type: string + format: date-time + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Statistics' + /calendars: + get: + summary: Fetch a list of Calendars + description: Without params, it returns a list of Calendars the user has access to + tags: + - Calendars + parameters: + - name: all + in: query + description: Can only be used by admins or managers to fetch all entities + schema: + type: boolean + - name: userId + in: query + description: Standard users can use this only with their own _userId_ + schema: + type: integer + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Calendar' + post: + summary: Create a Calendar + tags: + - Calendars + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Calendar' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Calendar' + x-codegen-request-body-name: body + /calendars/{id}: + put: + summary: Update a Calendar + tags: + - Calendars + parameters: + - name: id + in: path + required: true + schema: + type: integer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Calendar' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Calendar' + x-codegen-request-body-name: body + delete: + summary: Delete a Calendar + tags: + - Calendars + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + '204': + description: No Content + content: {} + /attributes/computed: + get: + summary: Fetch a list of Attributes + description: Without params, it returns a list of Attributes the user has access to + tags: + - Attributes + parameters: + - name: all + in: query + description: Can only be used by admins or managers to fetch all entities + schema: + type: boolean + - name: userId + in: query + description: Standard users can use this only with their own _userId_ + schema: + type: integer + - name: deviceId + in: query + description: >- + Standard users can use this only with _deviceId_s, they have access + to + schema: + type: integer + - name: groupId + in: query + description: >- + Standard users can use this only with _groupId_s, they have access + to + schema: + type: integer + - name: refresh + in: query + schema: + type: boolean + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Attribute' + post: + summary: Create an Attribute + tags: + - Attributes + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Attribute' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Attribute' + x-codegen-request-body-name: body + /attributes/computed/{id}: + put: + summary: Update an Attribute + tags: + - Attributes + parameters: + - name: id + in: path + required: true + schema: + type: integer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Attribute' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Attribute' + x-codegen-request-body-name: body + delete: + summary: Delete an Attribute + tags: + - Attributes + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + '204': + description: No Content + content: {} + /drivers: + get: + summary: Fetch a list of Drivers + description: Without params, it returns a list of Drivers the user has access to + tags: + - Drivers + parameters: + - name: all + in: query + description: Can only be used by admins or managers to fetch all entities + schema: + type: boolean + - name: userId + in: query + description: Standard users can use this only with their own _userId_ + schema: + type: integer + - name: deviceId + in: query + description: >- + Standard users can use this only with _deviceId_s, they have access + to + schema: + type: integer + - name: groupId + in: query + description: >- + Standard users can use this only with _groupId_s, they have access + to + schema: + type: integer + - name: refresh + in: query + schema: + type: boolean + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Driver' + post: + summary: Create a Driver + tags: + - Drivers + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Driver' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Driver' + x-codegen-request-body-name: body + /drivers/{id}: + put: + summary: Update a Driver + tags: + - Drivers + parameters: + - name: id + in: path + required: true + schema: + type: integer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Driver' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Driver' + x-codegen-request-body-name: body + delete: + summary: Delete a Driver + tags: + - Drivers + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + '204': + description: No Content + content: {} + /maintenance: + get: + summary: Fetch a list of Maintenance + description: Without params, it returns a list of Maintenance the user has access to + tags: + - Maintenance + parameters: + - name: all + in: query + description: Can only be used by admins or managers to fetch all entities + schema: + type: boolean + - name: userId + in: query + description: Standard users can use this only with their own _userId_ + schema: + type: integer + - name: deviceId + in: query + description: >- + Standard users can use this only with _deviceId_s, they have access + to + schema: + type: integer + - name: groupId + in: query + description: >- + Standard users can use this only with _groupId_s, they have access + to + schema: + type: integer + - name: refresh + in: query + schema: + type: boolean + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Maintenance' + post: + summary: Create a Maintenance + tags: + - Maintenance + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Maintenance' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Maintenance' + x-codegen-request-body-name: body + /maintenance/{id}: + put: + summary: Update a Maintenance + tags: + - Maintenance + parameters: + - name: id + in: path + required: true + schema: + type: integer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Maintenance' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Maintenance' + x-codegen-request-body-name: body + delete: + summary: Delete a Maintenance + tags: + - Maintenance + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + '204': + description: No Content + content: {} +components: + schemas: + Position: + type: object + properties: + id: + type: integer + deviceId: + type: integer + protocol: + type: string + deviceTime: + type: string + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + format: date-time + fixTime: + type: string + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + format: date-time + serverTime: + type: string + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + format: date-time + outdated: + type: boolean + valid: + type: boolean + latitude: + type: number + longitude: + type: number + altitude: + type: number + speed: + type: number + description: in knots + course: + type: number + address: + type: string + accuracy: + type: number + network: + type: object + properties: {} + geofenceIds: + type: array + items: + type: integer + attributes: + type: object + properties: {} + User: + type: object + properties: + id: + type: integer + name: + type: string + email: + type: string + phone: + type: string + readonly: + type: boolean + administrator: + type: boolean + map: + type: string + latitude: + type: number + longitude: + type: number + zoom: + type: integer + password: + type: string + coordinateFormat: + type: string + disabled: + type: boolean + expirationTime: + type: string + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + format: date-time + deviceLimit: + type: integer + userLimit: + type: integer + deviceReadonly: + type: boolean + limitCommands: + type: boolean + fixedEmail: + type: boolean + poiLayer: + type: string + attributes: + type: object + properties: {} + Server: + type: object + properties: + id: + type: integer + registration: + type: boolean + readonly: + type: boolean + deviceReadonly: + type: boolean + limitCommands: + type: boolean + map: + type: string + bingKey: + type: string + mapUrl: + type: string + poiLayer: + type: string + latitude: + type: number + longitude: + type: number + zoom: + type: integer + version: + type: string + forceSettings: + type: boolean + coordinateFormat: + type: string + openIdEnabled: + type: boolean + openIdForce: + type: boolean + attributes: + type: object + properties: {} + Command: + type: object + properties: + id: + type: integer + deviceId: + type: integer + description: + type: string + type: + type: string + attributes: + type: object + properties: {} + Device: + type: object + properties: + id: + type: integer + name: + type: string + uniqueId: + type: string + status: + type: string + disabled: + type: boolean + lastUpdate: + type: string + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + format: date-time + positionId: + type: integer + groupId: + type: integer + phone: + type: string + model: + type: string + contact: + type: string + category: + type: string + attributes: + type: object + properties: {} + Group: + type: object + properties: + id: + type: integer + name: + type: string + groupId: + type: integer + attributes: + type: object + properties: {} + Permission: + type: object + properties: + userId: + type: integer + description: User id, can be only first parameter + deviceId: + type: integer + description: >- + Device id, can be first parameter or second only in combination with + userId + groupId: + type: integer + description: >- + Group id, can be first parameter or second only in combination with + userId + geofenceId: + type: integer + description: Geofence id, can be second parameter only + notificationId: + type: integer + description: Notification id, can be second parameter only + calendarId: + type: integer + description: >- + Calendar id, can be second parameter only and only in combination + with userId + attributeId: + type: integer + description: Computed attribute id, can be second parameter only + driverId: + type: integer + description: Driver id, can be second parameter only + managedUserId: + type: integer + description: >- + User id, can be second parameter only and only in combination with + userId + commandId: + type: integer + description: Saved command id, can be second parameter only + description: >- + This is a permission map that contain two object indexes. It is used to + link/unlink objects. Order is important. Example: { deviceId:8, + geofenceId: 16 } + CommandType: + type: object + properties: + type: + type: string + Geofence: + type: object + properties: + id: + type: integer + name: + type: string + description: + type: string + area: + type: string + calendarId: + type: integer + attributes: + type: object + properties: {} + Notification: + type: object + properties: + id: + type: integer + type: + type: string + always: + type: boolean + web: + type: boolean + mail: + type: boolean + sms: + type: boolean + calendarId: + type: integer + attributes: + type: object + properties: {} + NotificationType: + type: object + properties: + type: + type: string + Event: + type: object + properties: + id: + type: integer + type: + type: string + eventTime: + type: string + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + format: date-time + deviceId: + type: integer + positionId: + type: integer + geofenceId: + type: integer + maintenanceId: + type: integer + attributes: + type: object + properties: {} + ReportSummary: + type: object + properties: + deviceId: + type: integer + deviceName: + type: string + maxSpeed: + type: number + description: in knots + averageSpeed: + type: number + description: in knots + distance: + type: number + description: in meters + spentFuel: + type: number + description: in liters + engineHours: + type: integer + ReportTrips: + type: object + properties: + deviceId: + type: integer + deviceName: + type: string + maxSpeed: + type: number + description: in knots + averageSpeed: + type: number + description: in knots + distance: + type: number + description: in meters + spentFuel: + type: number + description: in liters + duration: + type: integer + startTime: + type: string + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + format: date-time + startAddress: + type: string + startLat: + type: number + startLon: + type: number + endTime: + type: string + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + format: date-time + endAddress: + type: string + endLat: + type: number + endLon: + type: number + driverUniqueId: + type: integer + driverName: + type: string + ReportStops: + type: object + properties: + deviceId: + type: integer + deviceName: + type: string + duration: + type: integer + startTime: + type: string + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + format: date-time + address: + type: string + lat: + type: number + lon: + type: number + endTime: + type: string + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + format: date-time + spentFuel: + type: number + description: in liters + engineHours: + type: integer + Statistics: + type: object + properties: + captureTime: + type: string + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + format: date-time + activeUsers: + type: integer + activeDevices: + type: integer + requests: + type: integer + messagesReceived: + type: integer + messagesStored: + type: integer + DeviceAccumulators: + type: object + properties: + deviceId: + type: integer + totalDistance: + type: number + description: in meters + hours: + type: number + Calendar: + type: object + properties: + id: + type: integer + name: + type: string + data: + type: string + description: base64 encoded in iCalendar format + attributes: + type: object + properties: {} + Attribute: + type: object + properties: + id: + type: integer + description: + type: string + attribute: + type: string + expression: + type: string + type: + type: string + description: String|Number|Boolean + Driver: + type: object + properties: + id: + type: integer + name: + type: string + uniqueId: + type: string + attributes: + type: object + properties: {} + Maintenance: + type: object + properties: + id: + type: integer + name: + type: string + type: + type: string + start: + type: number + period: + type: number + attributes: + type: object + properties: {} + parameters: + entityId: + name: id + in: path + required: true + schema: + type: integer + all: + name: all + in: query + description: Can only be used by admins or managers to fetch all entities + schema: + type: boolean + refresh: + name: refresh + in: query + schema: + type: boolean + userId: + name: userId + in: query + description: Standard users can use this only with their own _userId_ + schema: + type: integer + deviceId: + name: deviceId + in: query + description: Standard users can use this only with _deviceId_s, they have access to + schema: + type: integer + groupId: + name: groupId + in: query + description: Standard users can use this only with _groupId_s, they have access to + schema: + type: integer + deviceIdArray: + name: deviceId + in: query + style: form + explode: true + schema: + type: array + items: + type: integer + groupIdArray: + name: groupId + in: query + style: form + explode: true + schema: + type: array + items: + type: integer + fromTime: + name: from + in: query + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + required: true + schema: + type: string + format: date-time + toTime: + name: to + in: query + description: in IS0 8601 format. eg. `1963-11-22T18:30:00Z` + required: true + schema: + type: string + format: date-time + requestBodies: + Device: + content: + application/json: + schema: + $ref: '#/components/schemas/Device' + required: true + Permission: + content: + application/json: + schema: + $ref: '#/components/schemas/Permission' + required: true + Group: + content: + application/json: + schema: + $ref: '#/components/schemas/Group' + required: true + User: + content: + application/json: + schema: + $ref: '#/components/schemas/User' + required: true + Geofence: + content: + application/json: + schema: + $ref: '#/components/schemas/Geofence' + required: true + Calendar: + content: + application/json: + schema: + $ref: '#/components/schemas/Calendar' + required: true + Attribute: + content: + application/json: + schema: + $ref: '#/components/schemas/Attribute' + required: true + Driver: + content: + application/json: + schema: + $ref: '#/components/schemas/Driver' + required: true + Command: + content: + application/json: + schema: + $ref: '#/components/schemas/Command' + required: true + Notification: + content: + application/json: + schema: + $ref: '#/components/schemas/Notification' + required: true + Maintenance: + content: + application/json: + schema: + $ref: '#/components/schemas/Maintenance' + required: true + securitySchemes: + BasicAuth: + type: http + scheme: basic + ApiKey: + type: http + scheme: bearer From 7ea0982598cae6e1b3783b18d55ea9c17dcbf004 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Wed, 18 Sep 2024 07:37:50 -0700 Subject: [PATCH 09/30] Delete swagger file --- swagger.json | 3575 -------------------------------------------------- 1 file changed, 3575 deletions(-) delete mode 100644 swagger.json diff --git a/swagger.json b/swagger.json deleted file mode 100644 index 75b8dd18d37..00000000000 --- a/swagger.json +++ /dev/null @@ -1,3575 +0,0 @@ -{ - "openapi": "3.0.1", - "info": { - "title": "Traccar", - "version": "6.5", - "description": "Traccar GPS tracking server API documentation. To use the API you need to have a server instance. For testing purposes you can use one of free [demo servers](https://www.traccar.org/demo-server/). For production use you can install your own server or get a [subscription service](https://www.traccar.org/product/tracking-server/).", - "contact": { - "name": "Traccar Support", - "url": "https://www.traccar.org/", - "email": "support@traccar.org" - }, - "license": { - "name": "Apache 2.0", - "url": "https://www.apache.org/licenses/LICENSE-2.0.html" - } - }, - "servers": [ - { - "url": "https://demo.traccar.org/api", - "description": "Demo Server 1" - }, - { - "url": "https://demo2.traccar.org/api", - "description": "Demo Server 2" - }, - { - "url": "https://demo3.traccar.org/api", - "description": "Demo Server 3" - }, - { - "url": "https://demo4.traccar.org/api", - "description": "Demo Server 4" - }, - { - "url": "https://server.traccar.org/api", - "description": "Subscription Server" - }, - { - "url": "http://{host}:{port}/api", - "description": "Other Server", - "variables": { - "host": { - "default": "localhost" - }, - "port": { - "enum": [ - "8082", - "80" - ], - "default": "8082" - } - } - } - ], - "security": [ - { - "basicAuth": [] - } - ], - "tags": [ - { - "name": "Server", - "description": "Server information" - }, - { - "name": "Session", - "description": "User session management" - }, - { - "name": "Devices", - "description": "Device management" - }, - { - "name": "Groups", - "description": "Group management" - }, - { - "name": "Users", - "description": "User management" - }, - { - "name": "Permissions", - "description": "User permissions and other object linking" - }, - { - "name": "Positions", - "description": "Retrieving raw location information" - }, - { - "name": "Events", - "description": "Retrieving event information" - }, - { - "name": "Reports", - "description": "Reports generation" - }, - { - "name": "Notifications", - "description": "User notifications management" - }, - { - "name": "Geofences", - "description": "Geofence management" - }, - { - "name": "Commands", - "description": "Sending commands to devices and stored command management" - }, - { - "name": "Attributes", - "description": "Computed attributes management" - }, - { - "name": "Drivers", - "description": "Drivers management" - }, - { - "name": "Maintenance", - "description": "Maintenance management" - }, - { - "name": "Calendars", - "description": "Calendar management" - }, - { - "name": "Statistics", - "description": "Retrieving server statistics" - } - ], - "paths": { - "/commands": { - "get": { - "summary": "Fetch a list of Saved Commands", - "tags": [ - "Commands" - ], - "description": "Without params, it returns a list of Saved Commands the user has access to", - "parameters": [ - { - "name": "all", - "in": "query", - "description": "Can only be used by admins or managers to fetch all entities", - "schema": { - "type": "boolean" - } - }, - { - "name": "userId", - "in": "query", - "description": "Standard users can use this only with their own _userId_", - "schema": { - "type": "integer" - } - }, - { - "name": "deviceId", - "in": "query", - "description": "Standard users can use this only with _deviceId_s, they have access to", - "schema": { - "type": "integer" - } - }, - { - "name": "groupId", - "in": "query", - "description": "Standard users can use this only with _groupId_s, they have access to", - "schema": { - "type": "integer" - } - }, - { - "name": "refresh", - "in": "query", - "schema": { - "type": "boolean" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Command" - } - } - } - } - } - } - }, - "post": { - "summary": "Create a Saved Command", - "tags": [ - "Commands" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Command" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Command" - } - } - } - } - }, - "x-codegen-request-body-name": "body" - } - }, - "/commands/{id}": { - "put": { - "summary": "Update a Saved Command", - "tags": [ - "Commands" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Command" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Command" - } - } - } - } - }, - "x-codegen-request-body-name": "body" - }, - "delete": { - "summary": "Delete a Saved Command", - "tags": [ - "Commands" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "responses": { - "204": { - "description": "No Content", - "content": {} - } - } - } - }, - "/commands/send": { - "get": { - "summary": "Fetch a list of Saved Commands supported by Device at the moment", - "description": "Return a list of saved commands linked to Device and its groups, filtered by current Device protocol support", - "tags": [ - "Commands" - ], - "parameters": [ - { - "name": "deviceId", - "in": "query", - "description": "Standard users can use this only with _deviceId_s, they have access to", - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Command" - } - } - } - } - }, - "400": { - "description": "Could happen when the user doesn't have permission for the device", - "content": {} - } - } - }, - "post": { - "summary": "Dispatch commands to device", - "description": "Dispatch a new command or Saved Command if _body.id_ set", - "tags": [ - "Commands" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Command" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "Command sent", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Command" - } - } - } - }, - "202": { - "description": "Command queued", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Command" - } - } - } - }, - "400": { - "description": "Could happen when the user doesn't have permission or an incorrect command _type_ for the device", - "content": {} - } - }, - "x-codegen-request-body-name": "body" - } - }, - "/commands/types": { - "get": { - "summary": "Fetch a list of available Commands for the Device or all possible Commands if Device ommited", - "tags": [ - "Commands" - ], - "parameters": [ - { - "name": "deviceId", - "in": "query", - "description": "Internal device identifier. Only works if device has already reported some locations", - "schema": { - "type": "integer" - } - }, - { - "name": "protocol", - "in": "query", - "description": "Protocol name. Can be used instead of device id", - "schema": { - "type": "string" - } - }, - { - "name": "textChannel", - "in": "query", - "description": "When `true` return SMS commands. If not specified or `false` return data commands", - "schema": { - "type": "boolean" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CommandType" - } - } - } - } - }, - "400": { - "description": "Could happen when trying to fetch from a device the user does not have permission", - "content": {} - } - } - } - }, - "/devices": { - "get": { - "summary": "Fetch a list of Devices", - "description": "Without any params, returns a list of the user's devices", - "tags": [ - "Devices" - ], - "parameters": [ - { - "name": "all", - "in": "query", - "description": "Can only be used by admins or managers to fetch all entities", - "schema": { - "type": "boolean" - } - }, - { - "name": "userId", - "in": "query", - "description": "Standard users can use this only with their own _userId_", - "schema": { - "type": "integer" - } - }, - { - "name": "id", - "in": "query", - "description": "To fetch one or more devices. Multiple params can be passed like `id=31&id=42`", - "schema": { - "type": "integer" - } - }, - { - "name": "uniqueId", - "in": "query", - "description": "To fetch one or more devices. Multiple params can be passed like `uniqueId=333331&uniqieId=44442`", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Device" - } - } - } - } - }, - "400": { - "description": "No permission", - "content": {} - } - } - }, - "post": { - "summary": "Create a Device", - "tags": [ - "Devices" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Device" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Device" - } - } - } - } - }, - "x-codegen-request-body-name": "body" - } - }, - "/devices/{id}": { - "put": { - "summary": "Update a Device", - "tags": [ - "Devices" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Device" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Device" - } - } - } - } - }, - "x-codegen-request-body-name": "body" - }, - "delete": { - "summary": "Delete a Device", - "tags": [ - "Devices" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "responses": { - "204": { - "description": "No Content", - "content": {} - } - } - } - }, - "/devices/{id}/accumulators": { - "put": { - "summary": "Update total distance and hours of the Device", - "tags": [ - "Devices" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceAccumulators" - } - } - }, - "required": true - }, - "responses": { - "204": { - "description": "No Content", - "content": {} - } - }, - "x-codegen-request-body-name": "body" - } - }, - "/groups": { - "get": { - "summary": "Fetch a list of Groups", - "description": "Without any params, returns a list of the Groups the user belongs to", - "tags": [ - "Groups" - ], - "parameters": [ - { - "name": "all", - "in": "query", - "description": "Can only be used by admins or managers to fetch all entities", - "schema": { - "type": "boolean" - } - }, - { - "name": "userId", - "in": "query", - "description": "Standard users can use this only with their own _userId_", - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Group" - } - } - } - } - } - } - }, - "post": { - "summary": "Create a Group", - "tags": [ - "Groups" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Group" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Group" - } - } - } - }, - "400": { - "description": "No permission", - "content": {} - } - }, - "x-codegen-request-body-name": "body" - } - }, - "/groups/{id}": { - "put": { - "summary": "Update a Group", - "tags": [ - "Groups" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Group" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Group" - } - } - } - } - }, - "x-codegen-request-body-name": "body" - }, - "delete": { - "summary": "Delete a Group", - "tags": [ - "Groups" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "responses": { - "204": { - "description": "No Content", - "content": {} - } - } - } - }, - "/permissions": { - "post": { - "summary": "Link an Object to another Object", - "tags": [ - "Permissions" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Permission" - } - } - }, - "required": true - }, - "responses": { - "204": { - "description": "No Content", - "content": {} - }, - "400": { - "description": "No permission", - "content": {} - } - }, - "x-codegen-request-body-name": "body" - }, - "delete": { - "summary": "Unlink an Object from another Object", - "tags": [ - "Permissions" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Permission" - } - } - }, - "required": true - }, - "responses": { - "204": { - "description": "No Content", - "content": {} - } - }, - "x-codegen-request-body-name": "body" - } - }, - "/positions": { - "get": { - "summary": "Fetches a list of Positions", - "description": "We strongly recommend using [Traccar WebSocket API](https://www.traccar.org/traccar-api/) instead of periodically polling positions endpoint. Without any params, it returns a list of last known positions for all the user's Devices. _from_ and _to_ fields are not required with _id_.", - "tags": [ - "Positions" - ], - "parameters": [ - { - "name": "deviceId", - "in": "query", - "description": "_deviceId_ is optional, but requires the _from_ and _to_ parameters when used", - "schema": { - "type": "integer" - } - }, - { - "name": "from", - "in": "query", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "schema": { - "type": "string", - "format": "date-time" - } - }, - { - "name": "to", - "in": "query", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "schema": { - "type": "string", - "format": "date-time" - } - }, - { - "name": "id", - "in": "query", - "description": "To fetch one or more positions. Multiple params can be passed like `id=31&id=42`", - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Position" - } - } - }, - "text/csv": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Position" - } - } - }, - "application/gpx+xml": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Position" - } - } - } - } - } - } - }, - "delete": { - "summary": "Deletes all the Positions of a device in the time span specified", - "description": "", - "tags": [ - "Positions" - ], - "parameters": [ - { - "name": "deviceId", - "in": "query", - "description": "", - "schema": { - "type": "integer" - }, - "required": true - }, - { - "name": "from", - "in": "query", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "schema": { - "type": "string", - "format": "date-time" - }, - "required": true - }, - { - "name": "to", - "in": "query", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "schema": { - "type": "string", - "format": "date-time" - }, - "required": true - } - ], - "responses": { - "204": { - "description": "No Content", - "content": {} - }, - "400": { - "description": "Bad Request", - "content": {} - } - } - } - }, - "/server": { - "get": { - "summary": "Fetch Server information", - "tags": [ - "Server" - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Server" - } - } - } - } - } - }, - "put": { - "summary": "Update Server information", - "tags": [ - "Server" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Server" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Server" - } - } - } - } - }, - "x-codegen-request-body-name": "body" - } - }, - "/session": { - "get": { - "summary": "Fetch Session information", - "tags": [ - "Session" - ], - "parameters": [ - { - "name": "token", - "in": "query", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - } - } - } - }, - "404": { - "description": "Not Found", - "content": {} - } - } - }, - "post": { - "summary": "Create a new Session", - "tags": [ - "Session" - ], - "requestBody": { - "content": { - "application/x-www-form-urlencoded": { - "schema": { - "required": [ - "email", - "password" - ], - "properties": { - "email": { - "type": "string" - }, - "password": { - "type": "string", - "format": "password" - } - } - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": {} - } - } - }, - "delete": { - "summary": "Close the Session", - "tags": [ - "Session" - ], - "responses": { - "204": { - "description": "No Content", - "content": {} - } - } - } - }, - "/session/openid/auth": { - "get": { - "summary": "Fetch Session information", - "tags": [ - "Session" - ], - "parameters": [ - { - } - ], - "responses": { - "303": { - "description": "Redirect to OpenID Connect identity provider", - "content": { } - } - } - } - }, - "/session/openid/callback": { - "get": { - "summary": "OpenID Callback", - "tags": [ - "Session" - ], - "parameters": [ - { - } - ], - "responses": { - "303": { - "description": "Successful authentication, redirect to homepage", - "content": { } - } - } - } - }, - "/users": { - "get": { - "summary": "Fetch a list of Users", - "tags": [ - "Users" - ], - "parameters": [ - { - "name": "userId", - "in": "query", - "description": "Can only be used by admin or manager users", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/User" - } - } - } - } - }, - "400": { - "description": "No Permission", - "content": {} - } - } - }, - "post": { - "summary": "Create a User", - "tags": [ - "Users" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - } - } - } - } - }, - "x-codegen-request-body-name": "body" - } - }, - "/users/{id}": { - "put": { - "summary": "Update a User", - "tags": [ - "Users" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - } - } - } - } - }, - "x-codegen-request-body-name": "body" - }, - "delete": { - "summary": "Delete a User", - "tags": [ - "Users" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "responses": { - "204": { - "description": "No Content", - "content": {} - } - } - } - }, - "/notifications": { - "get": { - "summary": "Fetch a list of Notifications", - "description": "Without params, it returns a list of Notifications the user has access to", - "tags": [ - "Notifications" - ], - "parameters": [ - { - "name": "all", - "in": "query", - "description": "Can only be used by admins or managers to fetch all entities", - "schema": { - "type": "boolean" - } - }, - { - "name": "userId", - "in": "query", - "description": "Standard users can use this only with their own _userId_", - "schema": { - "type": "integer" - } - }, - { - "name": "deviceId", - "in": "query", - "description": "Standard users can use this only with _deviceId_s, they have access to", - "schema": { - "type": "integer" - } - }, - { - "name": "groupId", - "in": "query", - "description": "Standard users can use this only with _groupId_s, they have access to", - "schema": { - "type": "integer" - } - }, - { - "name": "refresh", - "in": "query", - "schema": { - "type": "boolean" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Notification" - } - } - } - } - } - } - }, - "post": { - "summary": "Create a Notification", - "tags": [ - "Notifications" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Notification" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Notification" - } - } - } - } - }, - "x-codegen-request-body-name": "body" - } - }, - "/notifications/{id}": { - "put": { - "summary": "Update a Notification", - "tags": [ - "Notifications" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Notification" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Notification" - } - } - } - } - }, - "x-codegen-request-body-name": "body" - }, - "delete": { - "summary": "Delete a Notification", - "tags": [ - "Notifications" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "responses": { - "204": { - "description": "No Content", - "content": {} - } - } - } - }, - "/notifications/types": { - "get": { - "summary": "Fetch a list of available Notification types", - "tags": [ - "Notifications" - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NotificationType" - } - } - } - } - } - } - } - }, - "/notifications/test": { - "post": { - "summary": "Send test notification to current user via Email and SMS", - "tags": [ - "Notifications" - ], - "responses": { - "204": { - "description": "Successful sending", - "content": {} - }, - "400": { - "description": "Could happen if sending has failed", - "content": {} - } - } - } - }, - "/geofences": { - "get": { - "summary": "Fetch a list of Geofences", - "description": "Without params, it returns a list of Geofences the user has access to", - "tags": [ - "Geofences" - ], - "parameters": [ - { - "name": "all", - "in": "query", - "description": "Can only be used by admins or managers to fetch all entities", - "schema": { - "type": "boolean" - } - }, - { - "name": "userId", - "in": "query", - "description": "Standard users can use this only with their own _userId_", - "schema": { - "type": "integer" - } - }, - { - "name": "deviceId", - "in": "query", - "description": "Standard users can use this only with _deviceId_s, they have access to", - "schema": { - "type": "integer" - } - }, - { - "name": "groupId", - "in": "query", - "description": "Standard users can use this only with _groupId_s, they have access to", - "schema": { - "type": "integer" - } - }, - { - "name": "refresh", - "in": "query", - "schema": { - "type": "boolean" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Geofence" - } - } - } - } - } - } - }, - "post": { - "summary": "Create a Geofence", - "tags": [ - "Geofences" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Geofence" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Geofence" - } - } - } - } - }, - "x-codegen-request-body-name": "body" - } - }, - "/geofences/{id}": { - "put": { - "summary": "Update a Geofence", - "tags": [ - "Geofences" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Geofence" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Geofence" - } - } - } - } - }, - "x-codegen-request-body-name": "body" - }, - "delete": { - "summary": "Delete a Geofence", - "tags": [ - "Geofences" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "responses": { - "204": { - "description": "No Content", - "content": {} - } - } - } - }, - "/events/{id}": { - "get": { - "tags": [ - "Events" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Event" - } - } - } - } - } - } - }, - "/reports/route": { - "get": { - "summary": "Fetch a list of Positions within the time period for the Devices or Groups", - "description": "At least one _deviceId_ or one _groupId_ must be passed", - "tags": [ - "Reports" - ], - "parameters": [ - { - "name": "deviceId", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "items": { - "type": "integer" - } - } - }, - { - "name": "groupId", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "items": { - "type": "integer" - } - } - }, - { - "name": "from", - "in": "query", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "required": true, - "schema": { - "type": "string", - "format": "date-time" - } - }, - { - "name": "to", - "in": "query", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "required": true, - "schema": { - "type": "string", - "format": "date-time" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Position" - } - } - }, - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Position" - } - } - } - } - } - } - } - }, - "/reports/events": { - "get": { - "summary": "Fetch a list of Events within the time period for the Devices or Groups", - "description": "At least one _deviceId_ or one _groupId_ must be passed", - "tags": [ - "Reports" - ], - "parameters": [ - { - "name": "deviceId", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "items": { - "type": "integer" - } - } - }, - { - "name": "groupId", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "items": { - "type": "integer" - } - } - }, - { - "name": "type", - "in": "query", - "description": "% can be used to return events of all types", - "style": "form", - "explode": false, - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "name": "from", - "in": "query", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "required": true, - "schema": { - "type": "string", - "format": "date-time" - } - }, - { - "name": "to", - "in": "query", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "required": true, - "schema": { - "type": "string", - "format": "date-time" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Event" - } - } - }, - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Event" - } - } - } - } - } - } - } - }, - "/reports/summary": { - "get": { - "summary": "Fetch a list of ReportSummary within the time period for the Devices or Groups", - "description": "At least one _deviceId_ or one _groupId_ must be passed", - "tags": [ - "Reports" - ], - "parameters": [ - { - "name": "deviceId", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "items": { - "type": "integer" - } - } - }, - { - "name": "groupId", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "items": { - "type": "integer" - } - } - }, - { - "name": "from", - "in": "query", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "required": true, - "schema": { - "type": "string", - "format": "date-time" - } - }, - { - "name": "to", - "in": "query", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "required": true, - "schema": { - "type": "string", - "format": "date-time" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ReportSummary" - } - } - }, - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ReportSummary" - } - } - } - } - } - } - } - }, - "/reports/trips": { - "get": { - "summary": "Fetch a list of ReportTrips within the time period for the Devices or Groups", - "description": "At least one _deviceId_ or one _groupId_ must be passed", - "tags": [ - "Reports" - ], - "parameters": [ - { - "name": "deviceId", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "items": { - "type": "integer" - } - } - }, - { - "name": "groupId", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "items": { - "type": "integer" - } - } - }, - { - "name": "from", - "in": "query", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "required": true, - "schema": { - "type": "string", - "format": "date-time" - } - }, - { - "name": "to", - "in": "query", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "required": true, - "schema": { - "type": "string", - "format": "date-time" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ReportTrips" - } - } - }, - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ReportTrips" - } - } - } - } - } - } - } - }, - "/reports/stops": { - "get": { - "summary": "Fetch a list of ReportStops within the time period for the Devices or Groups", - "description": "At least one _deviceId_ or one _groupId_ must be passed", - "tags": [ - "Reports" - ], - "parameters": [ - { - "name": "deviceId", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "items": { - "type": "integer" - } - } - }, - { - "name": "groupId", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "items": { - "type": "integer" - } - } - }, - { - "name": "from", - "in": "query", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "required": true, - "schema": { - "type": "string", - "format": "date-time" - } - }, - { - "name": "to", - "in": "query", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "required": true, - "schema": { - "type": "string", - "format": "date-time" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ReportStops" - } - } - }, - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ReportStops" - } - } - } - } - } - } - } - }, - "/statistics": { - "get": { - "summary": "Fetch server Statistics", - "tags": [ - "Statistics" - ], - "parameters": [ - { - "name": "from", - "in": "query", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "required": true, - "schema": { - "type": "string", - "format": "date-time" - } - }, - { - "name": "to", - "in": "query", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "required": true, - "schema": { - "type": "string", - "format": "date-time" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Statistics" - } - } - } - } - } - } - } - }, - "/calendars": { - "get": { - "summary": "Fetch a list of Calendars", - "description": "Without params, it returns a list of Calendars the user has access to", - "tags": [ - "Calendars" - ], - "parameters": [ - { - "name": "all", - "in": "query", - "description": "Can only be used by admins or managers to fetch all entities", - "schema": { - "type": "boolean" - } - }, - { - "name": "userId", - "in": "query", - "description": "Standard users can use this only with their own _userId_", - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Calendar" - } - } - } - } - } - } - }, - "post": { - "summary": "Create a Calendar", - "tags": [ - "Calendars" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Calendar" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Calendar" - } - } - } - } - }, - "x-codegen-request-body-name": "body" - } - }, - "/calendars/{id}": { - "put": { - "summary": "Update a Calendar", - "tags": [ - "Calendars" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Calendar" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Calendar" - } - } - } - } - }, - "x-codegen-request-body-name": "body" - }, - "delete": { - "summary": "Delete a Calendar", - "tags": [ - "Calendars" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "responses": { - "204": { - "description": "No Content", - "content": {} - } - } - } - }, - "/attributes/computed": { - "get": { - "summary": "Fetch a list of Attributes", - "description": "Without params, it returns a list of Attributes the user has access to", - "tags": [ - "Attributes" - ], - "parameters": [ - { - "name": "all", - "in": "query", - "description": "Can only be used by admins or managers to fetch all entities", - "schema": { - "type": "boolean" - } - }, - { - "name": "userId", - "in": "query", - "description": "Standard users can use this only with their own _userId_", - "schema": { - "type": "integer" - } - }, - { - "name": "deviceId", - "in": "query", - "description": "Standard users can use this only with _deviceId_s, they have access to", - "schema": { - "type": "integer" - } - }, - { - "name": "groupId", - "in": "query", - "description": "Standard users can use this only with _groupId_s, they have access to", - "schema": { - "type": "integer" - } - }, - { - "name": "refresh", - "in": "query", - "schema": { - "type": "boolean" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Attribute" - } - } - } - } - } - } - }, - "post": { - "summary": "Create an Attribute", - "tags": [ - "Attributes" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Attribute" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Attribute" - } - } - } - } - }, - "x-codegen-request-body-name": "body" - } - }, - "/attributes/computed/{id}": { - "put": { - "summary": "Update an Attribute", - "tags": [ - "Attributes" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Attribute" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Attribute" - } - } - } - } - }, - "x-codegen-request-body-name": "body" - }, - "delete": { - "summary": "Delete an Attribute", - "tags": [ - "Attributes" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "responses": { - "204": { - "description": "No Content", - "content": {} - } - } - } - }, - "/drivers": { - "get": { - "summary": "Fetch a list of Drivers", - "description": "Without params, it returns a list of Drivers the user has access to", - "tags": [ - "Drivers" - ], - "parameters": [ - { - "name": "all", - "in": "query", - "description": "Can only be used by admins or managers to fetch all entities", - "schema": { - "type": "boolean" - } - }, - { - "name": "userId", - "in": "query", - "description": "Standard users can use this only with their own _userId_", - "schema": { - "type": "integer" - } - }, - { - "name": "deviceId", - "in": "query", - "description": "Standard users can use this only with _deviceId_s, they have access to", - "schema": { - "type": "integer" - } - }, - { - "name": "groupId", - "in": "query", - "description": "Standard users can use this only with _groupId_s, they have access to", - "schema": { - "type": "integer" - } - }, - { - "name": "refresh", - "in": "query", - "schema": { - "type": "boolean" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Driver" - } - } - } - } - } - } - }, - "post": { - "summary": "Create a Driver", - "tags": [ - "Drivers" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Driver" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Driver" - } - } - } - } - }, - "x-codegen-request-body-name": "body" - } - }, - "/drivers/{id}": { - "put": { - "summary": "Update a Driver", - "tags": [ - "Drivers" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Driver" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Driver" - } - } - } - } - }, - "x-codegen-request-body-name": "body" - }, - "delete": { - "summary": "Delete a Driver", - "tags": [ - "Drivers" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "responses": { - "204": { - "description": "No Content", - "content": {} - } - } - } - }, - "/maintenance": { - "get": { - "summary": "Fetch a list of Maintenance", - "description": "Without params, it returns a list of Maintenance the user has access to", - "tags": [ - "Maintenance" - ], - "parameters": [ - { - "name": "all", - "in": "query", - "description": "Can only be used by admins or managers to fetch all entities", - "schema": { - "type": "boolean" - } - }, - { - "name": "userId", - "in": "query", - "description": "Standard users can use this only with their own _userId_", - "schema": { - "type": "integer" - } - }, - { - "name": "deviceId", - "in": "query", - "description": "Standard users can use this only with _deviceId_s, they have access to", - "schema": { - "type": "integer" - } - }, - { - "name": "groupId", - "in": "query", - "description": "Standard users can use this only with _groupId_s, they have access to", - "schema": { - "type": "integer" - } - }, - { - "name": "refresh", - "in": "query", - "schema": { - "type": "boolean" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Maintenance" - } - } - } - } - } - } - }, - "post": { - "summary": "Create a Maintenance", - "tags": [ - "Maintenance" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Maintenance" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Maintenance" - } - } - } - } - }, - "x-codegen-request-body-name": "body" - } - }, - "/maintenance/{id}": { - "put": { - "summary": "Update a Maintenance", - "tags": [ - "Maintenance" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Maintenance" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Maintenance" - } - } - } - } - }, - "x-codegen-request-body-name": "body" - }, - "delete": { - "summary": "Delete a Maintenance", - "tags": [ - "Maintenance" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "responses": { - "204": { - "description": "No Content", - "content": {} - } - } - } - } - }, - "components": { - "schemas": { - "Position": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "deviceId": { - "type": "integer" - }, - "protocol": { - "type": "string" - }, - "deviceTime": { - "type": "string", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "format": "date-time" - }, - "fixTime": { - "type": "string", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "format": "date-time" - }, - "serverTime": { - "type": "string", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "format": "date-time" - }, - "outdated": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - }, - "latitude": { - "type": "number" - }, - "longitude": { - "type": "number" - }, - "altitude": { - "type": "number" - }, - "speed": { - "type": "number", - "description": "in knots" - }, - "course": { - "type": "number" - }, - "address": { - "type": "string" - }, - "accuracy": { - "type": "number" - }, - "network": { - "type": "object", - "properties": {} - }, - "geofenceIds": { - "type": "array", - "items": { - "type": "integer" - } - }, - "attributes": { - "type": "object", - "properties": {} - } - } - }, - "User": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "email": { - "type": "string" - }, - "phone": { - "type": "string" - }, - "readonly": { - "type": "boolean" - }, - "administrator": { - "type": "boolean" - }, - "map": { - "type": "string" - }, - "latitude": { - "type": "number" - }, - "longitude": { - "type": "number" - }, - "zoom": { - "type": "integer" - }, - "password": { - "type": "string" - }, - "coordinateFormat": { - "type": "string" - }, - "disabled": { - "type": "boolean" - }, - "expirationTime": { - "type": "string", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "format": "date-time" - }, - "deviceLimit": { - "type": "integer" - }, - "userLimit": { - "type": "integer" - }, - "deviceReadonly": { - "type": "boolean" - }, - "limitCommands": { - "type": "boolean" - }, - "fixedEmail": { - "type": "boolean" - }, - "poiLayer": { - "type": "string" - }, - "attributes": { - "type": "object", - "properties": {} - } - } - }, - "Server": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "registration": { - "type": "boolean" - }, - "readonly": { - "type": "boolean" - }, - "deviceReadonly": { - "type": "boolean" - }, - "limitCommands": { - "type": "boolean" - }, - "map": { - "type": "string" - }, - "bingKey": { - "type": "string" - }, - "mapUrl": { - "type": "string" - }, - "poiLayer": { - "type": "string" - }, - "latitude": { - "type": "number" - }, - "longitude": { - "type": "number" - }, - "zoom": { - "type": "integer" - }, - "version": { - "type": "string" - }, - "forceSettings": { - "type": "boolean" - }, - "coordinateFormat": { - "type": "string" - }, - "openIdEnabled": { - "type": "boolean" - }, - "openIdForce": { - "type": "boolean" - }, - "attributes": { - "type": "object", - "properties": {} - } - } - }, - "Command": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "deviceId": { - "type": "integer" - }, - "description": { - "type": "string" - }, - "type": { - "type": "string" - }, - "attributes": { - "type": "object", - "properties": {} - } - } - }, - "Device": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "uniqueId": { - "type": "string" - }, - "status": { - "type": "string" - }, - "disabled": { - "type": "boolean" - }, - "lastUpdate": { - "type": "string", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "format": "date-time" - }, - "positionId": { - "type": "integer" - }, - "groupId": { - "type": "integer" - }, - "phone": { - "type": "string" - }, - "model": { - "type": "string" - }, - "contact": { - "type": "string" - }, - "category": { - "type": "string" - }, - "attributes": { - "type": "object", - "properties": {} - } - } - }, - "Group": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "groupId": { - "type": "integer" - }, - "attributes": { - "type": "object", - "properties": {} - } - } - }, - "Permission": { - "type": "object", - "properties": { - "userId": { - "type": "integer", - "description": "User id, can be only first parameter" - }, - "deviceId": { - "type": "integer", - "description": "Device id, can be first parameter or second only in combination with userId" - }, - "groupId": { - "type": "integer", - "description": "Group id, can be first parameter or second only in combination with userId" - }, - "geofenceId": { - "type": "integer", - "description": "Geofence id, can be second parameter only" - }, - "notificationId": { - "type": "integer", - "description": "Notification id, can be second parameter only" - }, - "calendarId": { - "type": "integer", - "description": "Calendar id, can be second parameter only and only in combination with userId" - }, - "attributeId": { - "type": "integer", - "description": "Computed attribute id, can be second parameter only" - }, - "driverId": { - "type": "integer", - "description": "Driver id, can be second parameter only" - }, - "managedUserId": { - "type": "integer", - "description": "User id, can be second parameter only and only in combination with userId" - }, - "commandId": { - "type": "integer", - "description": "Saved command id, can be second parameter only" - } - }, - "description": "This is a permission map that contain two object indexes. It is used to link/unlink objects. Order is important. Example: { deviceId:8, geofenceId: 16 }" - }, - "CommandType": { - "type": "object", - "properties": { - "type": { - "type": "string" - } - } - }, - "Geofence": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "area": { - "type": "string" - }, - "calendarId": { - "type": "integer" - }, - "attributes": { - "type": "object", - "properties": {} - } - } - }, - "Notification": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "type": { - "type": "string" - }, - "always": { - "type": "boolean" - }, - "web": { - "type": "boolean" - }, - "mail": { - "type": "boolean" - }, - "sms": { - "type": "boolean" - }, - "calendarId": { - "type": "integer" - }, - "attributes": { - "type": "object", - "properties": {} - } - } - }, - "NotificationType": { - "type": "object", - "properties": { - "type": { - "type": "string" - } - } - }, - "Event": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "type": { - "type": "string" - }, - "eventTime": { - "type": "string", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "format": "date-time" - }, - "deviceId": { - "type": "integer" - }, - "positionId": { - "type": "integer" - }, - "geofenceId": { - "type": "integer" - }, - "maintenanceId": { - "type": "integer" - }, - "attributes": { - "type": "object", - "properties": {} - } - } - }, - "ReportSummary": { - "type": "object", - "properties": { - "deviceId": { - "type": "integer" - }, - "deviceName": { - "type": "string" - }, - "maxSpeed": { - "type": "number", - "description": "in knots" - }, - "averageSpeed": { - "type": "number", - "description": "in knots" - }, - "distance": { - "type": "number", - "description": "in meters" - }, - "spentFuel": { - "type": "number", - "description": "in liters" - }, - "engineHours": { - "type": "integer" - } - } - }, - "ReportTrips": { - "type": "object", - "properties": { - "deviceId": { - "type": "integer" - }, - "deviceName": { - "type": "string" - }, - "maxSpeed": { - "type": "number", - "description": "in knots" - }, - "averageSpeed": { - "type": "number", - "description": "in knots" - }, - "distance": { - "type": "number", - "description": "in meters" - }, - "spentFuel": { - "type": "number", - "description": "in liters" - }, - "duration": { - "type": "integer" - }, - "startTime": { - "type": "string", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "format": "date-time" - }, - "startAddress": { - "type": "string" - }, - "startLat": { - "type": "number" - }, - "startLon": { - "type": "number" - }, - "endTime": { - "type": "string", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "format": "date-time" - }, - "endAddress": { - "type": "string" - }, - "endLat": { - "type": "number" - }, - "endLon": { - "type": "number" - }, - "driverUniqueId": { - "type": "integer" - }, - "driverName": { - "type": "string" - } - } - }, - "ReportStops": { - "type": "object", - "properties": { - "deviceId": { - "type": "integer" - }, - "deviceName": { - "type": "string" - }, - "duration": { - "type": "integer" - }, - "startTime": { - "type": "string", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "format": "date-time" - }, - "address": { - "type": "string" - }, - "lat": { - "type": "number" - }, - "lon": { - "type": "number" - }, - "endTime": { - "type": "string", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "format": "date-time" - }, - "spentFuel": { - "type": "number", - "description": "in liters" - }, - "engineHours": { - "type": "integer" - } - } - }, - "Statistics": { - "type": "object", - "properties": { - "captureTime": { - "type": "string", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "format": "date-time" - }, - "activeUsers": { - "type": "integer" - }, - "activeDevices": { - "type": "integer" - }, - "requests": { - "type": "integer" - }, - "messagesReceived": { - "type": "integer" - }, - "messagesStored": { - "type": "integer" - } - } - }, - "DeviceAccumulators": { - "type": "object", - "properties": { - "deviceId": { - "type": "integer" - }, - "totalDistance": { - "type": "number", - "description": "in meters" - }, - "hours": { - "type": "number" - } - } - }, - "Calendar": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "data": { - "type": "string", - "description": "base64 encoded in iCalendar format" - }, - "attributes": { - "type": "object", - "properties": {} - } - } - }, - "Attribute": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "description": { - "type": "string" - }, - "attribute": { - "type": "string" - }, - "expression": { - "type": "string" - }, - "type": { - "type": "string", - "description": "String|Number|Boolean" - } - } - }, - "Driver": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "uniqueId": { - "type": "string" - }, - "attributes": { - "type": "object", - "properties": {} - } - } - }, - "Maintenance": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "type": { - "type": "string" - }, - "start": { - "type": "number" - }, - "period": { - "type": "number" - }, - "attributes": { - "type": "object", - "properties": {} - } - } - } - }, - "parameters": { - "entityId": { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - }, - "all": { - "name": "all", - "in": "query", - "description": "Can only be used by admins or managers to fetch all entities", - "schema": { - "type": "boolean" - } - }, - "refresh": { - "name": "refresh", - "in": "query", - "schema": { - "type": "boolean" - } - }, - "userId": { - "name": "userId", - "in": "query", - "description": "Standard users can use this only with their own _userId_", - "schema": { - "type": "integer" - } - }, - "deviceId": { - "name": "deviceId", - "in": "query", - "description": "Standard users can use this only with _deviceId_s, they have access to", - "schema": { - "type": "integer" - } - }, - "groupId": { - "name": "groupId", - "in": "query", - "description": "Standard users can use this only with _groupId_s, they have access to", - "schema": { - "type": "integer" - } - }, - "deviceIdArray": { - "name": "deviceId", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "items": { - "type": "integer" - } - } - }, - "groupIdArray": { - "name": "groupId", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "items": { - "type": "integer" - } - } - }, - "fromTime": { - "name": "from", - "in": "query", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "required": true, - "schema": { - "type": "string", - "format": "date-time" - } - }, - "toTime": { - "name": "to", - "in": "query", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "required": true, - "schema": { - "type": "string", - "format": "date-time" - } - } - }, - "requestBodies": { - "Device": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Device" - } - } - }, - "required": true - }, - "Permission": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Permission" - } - } - }, - "required": true - }, - "Group": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Group" - } - } - }, - "required": true - }, - "User": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - } - } - }, - "required": true - }, - "Geofence": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Geofence" - } - } - }, - "required": true - }, - "Calendar": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Calendar" - } - } - }, - "required": true - }, - "Attribute": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Attribute" - } - } - }, - "required": true - }, - "Driver": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Driver" - } - } - }, - "required": true - }, - "Command": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Command" - } - } - }, - "required": true - }, - "Notification": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Notification" - } - } - }, - "required": true - }, - "Maintenance": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Maintenance" - } - } - }, - "required": true - } - }, - "securitySchemes": { - "basicAuth": { - "type": "http", - "description": "Basic HTTP authorization with _email_ and _password_", - "scheme": "basic" - } - } - } -} From 9dc855004c69b1d4fe770ddadf44b4a4df52d499 Mon Sep 17 00:00:00 2001 From: Lucas Leite Date: Wed, 18 Sep 2024 12:07:31 -0300 Subject: [PATCH 10/30] Fixed style check issue --- .../java/org/traccar/api/resource/NotificationResource.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/traccar/api/resource/NotificationResource.java b/src/main/java/org/traccar/api/resource/NotificationResource.java index 8cfdf8a5b64..5cc3055d1b0 100644 --- a/src/main/java/org/traccar/api/resource/NotificationResource.java +++ b/src/main/java/org/traccar/api/resource/NotificationResource.java @@ -129,7 +129,8 @@ public Response sendMessage( var conditions = new LinkedList(); conditions.add(new Condition.Equals("id", userId)); if (permissionsService.notAdmin(getUserId())) { - conditions.add(new Condition.Permission(User.class, getUserId(), ManagedUser.class).excludeGroups()); + conditions.add(new Condition.Permission( + User.class, getUserId(), ManagedUser.class).excludeGroups()); } users.add(storage.getObject( User.class, new Request(new Columns.All(), Condition.merge(conditions)))); From 565f6bece54b6fd33df71ae96c89cbeb62a3b2f2 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Wed, 18 Sep 2024 14:30:16 -0700 Subject: [PATCH 11/30] Add notification description (fix #5411) --- schema/changelog-6.6.xml | 17 +++++++++++++++++ schema/changelog-master.xml | 1 + .../java/org/traccar/model/Notification.java | 10 ++++++++++ 3 files changed, 28 insertions(+) create mode 100644 schema/changelog-6.6.xml diff --git a/schema/changelog-6.6.xml b/schema/changelog-6.6.xml new file mode 100644 index 00000000000..8641941605a --- /dev/null +++ b/schema/changelog-6.6.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/schema/changelog-master.xml b/schema/changelog-master.xml index 3ce4b43e6f7..02efdde3033 100644 --- a/schema/changelog-master.xml +++ b/schema/changelog-master.xml @@ -45,5 +45,6 @@ + diff --git a/src/main/java/org/traccar/model/Notification.java b/src/main/java/org/traccar/model/Notification.java index 6dcd9c9de50..465553054ee 100644 --- a/src/main/java/org/traccar/model/Notification.java +++ b/src/main/java/org/traccar/model/Notification.java @@ -26,6 +26,16 @@ @StorageName("tc_notifications") public class Notification extends ExtendedModel implements Schedulable { + private String description; + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + private long calendarId; @Override From e6517ddf21234f32f067f89ffeec22b5447bc775 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Thu, 19 Sep 2024 06:46:13 -0700 Subject: [PATCH 12/30] Fix GV350M ERI handling --- .../java/org/traccar/protocol/Gl200TextProtocolDecoder.java | 2 +- .../org/traccar/protocol/Gl200TextProtocolDecoderTest.java | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java index 99960926bc5..dc86e0cace6 100644 --- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -965,7 +965,7 @@ private Object decodeEri(Channel channel, SocketAddress remoteAddress, String[] position.set(Position.KEY_HOURS, parseHours(v[index++])); position.set(Position.PREFIX_ADC + 1, v[index++].isEmpty() ? null : Integer.parseInt(v[index - 1]) * 0.001); } - if (model.startsWith("GV") && !model.startsWith("GV6")) { + if (model.startsWith("GV") && !model.startsWith("GV6") && !model.equals("GV350M")) { position.set(Position.PREFIX_ADC + 2, v[index++].isEmpty() ? null : Integer.parseInt(v[index - 1]) * 0.001); } if (model.equals("GV200") || model.equals("GV310LAU")) { diff --git a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java index c277c041e59..048b745764e 100644 --- a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java @@ -11,6 +11,9 @@ public void testDecode() throws Exception { var decoder = inject(new Gl200TextProtocolDecoder(null)); + verifyPositions(decoder, buffer( + "+RESP:GTERI,F10413,862599050544301,GV350M,00000002,14164,10,1,1,98.2,93,216.4,-117.484369,33.896640,20240919020058,0311,0480,2C01,00ADBA01,,39539.5,,,100,220100,,0,20240919020058,20A7$")); + verifyPositions(decoder, buffer( "+RESP:GTERI,6E0304,868589060189019,,00000002,,50,1,1,0.0,0,35.5,-98.260961,26.053701,20240912192949,0334,0020,0F1B,04643718,01,12,21.1,,1930,1917,,100,210100,2,0,20240912192950,4BAA$")); From 4671b91b5831e51362b58ac73c07fa33ab1d2147 Mon Sep 17 00:00:00 2001 From: Lucas Leite Date: Thu, 19 Sep 2024 17:03:33 -0300 Subject: [PATCH 13/30] Allow managers to see device's users --- .../java/org/traccar/api/resource/UserResource.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/traccar/api/resource/UserResource.java b/src/main/java/org/traccar/api/resource/UserResource.java index 65ca2c8838d..1dea5a2a0a2 100644 --- a/src/main/java/org/traccar/api/resource/UserResource.java +++ b/src/main/java/org/traccar/api/resource/UserResource.java @@ -45,6 +45,7 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import java.util.Collection; +import java.util.LinkedList; @Path("users") @Produces(MediaType.APPLICATION_JSON) @@ -70,10 +71,13 @@ public Collection get( new Columns.All(), new Condition.Permission(User.class, userId, ManagedUser.class).excludeGroups())); } else if (deviceId > 0) { - permissionsService.checkAdmin(getUserId()); - return storage.getObjects(baseClass, new Request( - new Columns.All(), - new Condition.Permission(User.class, Device.class, deviceId).excludeGroups())); + permissionsService.checkManager(getUserId()); + var conditions = new LinkedList(); + conditions.add(new Condition.Permission(User.class, Device.class, deviceId).excludeGroups()); + if (permissionsService.notAdmin(getUserId())) { + conditions.add(new Condition.Permission(User.class, getUserId(), ManagedUser.class).excludeGroups()); + } + return storage.getObjects(baseClass, new Request(new Columns.All(), Condition.merge(conditions))); } else if (permissionsService.notAdmin(getUserId())) { return storage.getObjects(baseClass, new Request( new Columns.All(), From 66b538f03690c34eae6c74086207b472e8f5c55c Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Thu, 19 Sep 2024 21:34:49 -0700 Subject: [PATCH 14/30] Escape SMS JSON strings --- .../java/org/traccar/sms/HttpSmsClient.java | 21 +++++++++++-------- src/main/java/org/traccar/sms/SmsManager.java | 2 +- .../java/org/traccar/sms/SnsSmsClient.java | 14 ++++++++----- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/traccar/sms/HttpSmsClient.java b/src/main/java/org/traccar/sms/HttpSmsClient.java index c123f9b65d1..e809c4daef9 100644 --- a/src/main/java/org/traccar/sms/HttpSmsClient.java +++ b/src/main/java/org/traccar/sms/HttpSmsClient.java @@ -16,6 +16,7 @@ */ package org.traccar.sms; +import com.fasterxml.jackson.core.io.JsonStringEncoder; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.helper.DataConverter; @@ -37,7 +38,6 @@ public class HttpSmsClient implements SmsManager { private final String authorizationHeader; private final String authorization; private final String template; - private final boolean encode; private final MediaType mediaType; public HttpSmsClient(Config config, Client client) { @@ -58,25 +58,28 @@ public HttpSmsClient(Config config, Client client) { } template = config.getString(Keys.SMS_HTTP_TEMPLATE).trim(); if (template.charAt(0) == '<') { - encode = false; mediaType = MediaType.APPLICATION_XML_TYPE; } else if (template.charAt(0) == '{' || template.charAt(0) == '[') { - encode = false; mediaType = MediaType.APPLICATION_JSON_TYPE; } else { - encode = true; mediaType = MediaType.APPLICATION_FORM_URLENCODED_TYPE; } } private String prepareValue(String value) throws UnsupportedEncodingException { - return encode ? URLEncoder.encode(value, StandardCharsets.UTF_8) : value; + if (mediaType == MediaType.APPLICATION_FORM_URLENCODED_TYPE) { + return URLEncoder.encode(value, StandardCharsets.UTF_8); + } + if (mediaType == MediaType.APPLICATION_JSON_TYPE) { + return new String(JsonStringEncoder.getInstance().quoteAsString(value)); + } + return value; } - private String preparePayload(String destAddress, String message) { + private String preparePayload(String phone, String message) { try { return template - .replace("{phone}", prepareValue(destAddress)) + .replace("{phone}", prepareValue(phone)) .replace("{message}", prepareValue(message)); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); @@ -92,9 +95,9 @@ private Invocation.Builder getRequestBuilder() { } @Override - public void sendMessage(String destAddress, String message, boolean command) throws MessageException { + public void sendMessage(String phone, String message, boolean command) throws MessageException { try (Response response = getRequestBuilder() - .post(Entity.entity(preparePayload(destAddress, message), mediaType))) { + .post(Entity.entity(preparePayload(phone, message), mediaType))) { if (response.getStatus() / 100 != 2) { throw new MessageException(response.readEntity(String.class)); } diff --git a/src/main/java/org/traccar/sms/SmsManager.java b/src/main/java/org/traccar/sms/SmsManager.java index 8cf99c9e8ad..9800e19484e 100644 --- a/src/main/java/org/traccar/sms/SmsManager.java +++ b/src/main/java/org/traccar/sms/SmsManager.java @@ -20,6 +20,6 @@ public interface SmsManager { - void sendMessage(String destAddress, String message, boolean command) throws MessageException; + void sendMessage(String phone, String message, boolean command) throws MessageException; } diff --git a/src/main/java/org/traccar/sms/SnsSmsClient.java b/src/main/java/org/traccar/sms/SnsSmsClient.java index ed5a325cca6..c5c3474a653 100644 --- a/src/main/java/org/traccar/sms/SnsSmsClient.java +++ b/src/main/java/org/traccar/sms/SnsSmsClient.java @@ -47,15 +47,19 @@ public SnsSmsClient(Config config) { } @Override - public void sendMessage(String destAddress, String message, boolean command) { + public void sendMessage(String phone, String message, boolean command) { Map smsAttributes = new HashMap<>(); - smsAttributes.put("AWS.SNS.SMS.SenderID", + smsAttributes.put( + "AWS.SNS.SMS.SenderID", new MessageAttributeValue().withStringValue("SNS").withDataType("String")); - smsAttributes.put("AWS.SNS.SMS.SMSType", + smsAttributes.put( + "AWS.SNS.SMS.SMSType", new MessageAttributeValue().withStringValue("Transactional").withDataType("String")); - PublishRequest publishRequest = new PublishRequest().withMessage(message) - .withPhoneNumber(destAddress).withMessageAttributes(smsAttributes); + PublishRequest publishRequest = new PublishRequest() + .withMessage(message) + .withPhoneNumber(phone) + .withMessageAttributes(smsAttributes); snsClient.publishAsync(publishRequest, new AsyncHandler<>() { @Override From f53d4c24cd7cc4b7cbd3bd043f8d1e95c72d24e4 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Thu, 19 Sep 2024 21:37:33 -0700 Subject: [PATCH 15/30] Remove short templates line break --- templates/short/alarm.vm | 2 +- templates/short/commandResult.vm | 2 +- templates/short/deviceFuelDrop.vm | 2 +- templates/short/deviceFuelIncrease.vm | 2 +- templates/short/deviceInactive.vm | 2 +- templates/short/deviceMoving.vm | 2 +- templates/short/deviceOffline.vm | 2 +- templates/short/deviceOnline.vm | 2 +- templates/short/deviceOverspeed.vm | 2 +- templates/short/deviceStopped.vm | 2 +- templates/short/deviceUnknown.vm | 2 +- templates/short/driverChanged.vm | 2 +- templates/short/geofenceEnter.vm | 2 +- templates/short/geofenceExit.vm | 2 +- templates/short/ignitionOff.vm | 2 +- templates/short/ignitionOn.vm | 2 +- templates/short/maintenance.vm | 2 +- templates/short/media.vm | 2 +- templates/short/queuedCommandSent.vm | 2 +- templates/short/test.vm | 2 +- templates/short/textMessage.vm | 2 +- 21 files changed, 21 insertions(+), 21 deletions(-) diff --git a/templates/short/alarm.vm b/templates/short/alarm.vm index 17349ec2570..db59bd7a871 100644 --- a/templates/short/alarm.vm +++ b/templates/short/alarm.vm @@ -77,4 +77,4 @@ #elseif($alarmName == "removing") #set($alarmName = "Removing") #end -$device.name alarm: $alarmName at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) +$device.name alarm: $alarmName at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) \ No newline at end of file diff --git a/templates/short/commandResult.vm b/templates/short/commandResult.vm index 6dc7a5dec0b..9df18d22dae 100644 --- a/templates/short/commandResult.vm +++ b/templates/short/commandResult.vm @@ -1,2 +1,2 @@ #set($subject = "$device.name: command result received") -$device.name command result received: $position.getString("result") at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) +$device.name command result received: $position.getString("result") at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) \ No newline at end of file diff --git a/templates/short/deviceFuelDrop.vm b/templates/short/deviceFuelDrop.vm index babe1435129..614b39cb425 100644 --- a/templates/short/deviceFuelDrop.vm +++ b/templates/short/deviceFuelDrop.vm @@ -1,2 +1,2 @@ #set($subject = "$device.name: fuel drop") -$device.name fuel drop at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) +$device.name fuel drop at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) \ No newline at end of file diff --git a/templates/short/deviceFuelIncrease.vm b/templates/short/deviceFuelIncrease.vm index 6a11418b100..84ea695f8a9 100644 --- a/templates/short/deviceFuelIncrease.vm +++ b/templates/short/deviceFuelIncrease.vm @@ -1,2 +1,2 @@ #set($subject = "$device.name: fuel increase") -$device.name fuel increase at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) +$device.name fuel increase at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) \ No newline at end of file diff --git a/templates/short/deviceInactive.vm b/templates/short/deviceInactive.vm index c293bf1b22c..615559c23fc 100644 --- a/templates/short/deviceInactive.vm +++ b/templates/short/deviceInactive.vm @@ -1,4 +1,4 @@ #set($subject = "$device.name: inactive") #set($lastUpdate = $dateTool.getDate()) #set($ignore = $lastUpdate.setTime($event.getLong("lastUpdate"))) -$device.name inactive from $dateTool.format("YYYY-MM-dd HH:mm:ss", $lastUpdate, $locale, $timezone) +$device.name inactive from $dateTool.format("YYYY-MM-dd HH:mm:ss", $lastUpdate, $locale, $timezone) \ No newline at end of file diff --git a/templates/short/deviceMoving.vm b/templates/short/deviceMoving.vm index bf6aec3408f..e5b162c4920 100644 --- a/templates/short/deviceMoving.vm +++ b/templates/short/deviceMoving.vm @@ -1,2 +1,2 @@ #set($subject = "$device.name: moving") -$device.name moving at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) +$device.name moving at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) \ No newline at end of file diff --git a/templates/short/deviceOffline.vm b/templates/short/deviceOffline.vm index e663812abb9..f43696c9426 100644 --- a/templates/short/deviceOffline.vm +++ b/templates/short/deviceOffline.vm @@ -1,2 +1,2 @@ #set($subject = "$device.name: offline") -$device.name offline at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) +$device.name offline at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) \ No newline at end of file diff --git a/templates/short/deviceOnline.vm b/templates/short/deviceOnline.vm index bf3b4009679..5221061210a 100644 --- a/templates/short/deviceOnline.vm +++ b/templates/short/deviceOnline.vm @@ -1,2 +1,2 @@ #set($subject = "$device.name: online") -$device.name online at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) +$device.name online at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) \ No newline at end of file diff --git a/templates/short/deviceOverspeed.vm b/templates/short/deviceOverspeed.vm index 849c6ddb776..7d3d8851899 100644 --- a/templates/short/deviceOverspeed.vm +++ b/templates/short/deviceOverspeed.vm @@ -8,4 +8,4 @@ #else #set($speedString = $numberTool.format("0.0 kn", $position.speed)) #end -$device.name exceeds the speed $speedString#{if}($geofence) in $geofence.name#{else}#{end} at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) +$device.name exceeds the speed $speedString#{if}($geofence) in $geofence.name#{else}#{end} at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) \ No newline at end of file diff --git a/templates/short/deviceStopped.vm b/templates/short/deviceStopped.vm index 8fabf89f1e4..0c212394d77 100644 --- a/templates/short/deviceStopped.vm +++ b/templates/short/deviceStopped.vm @@ -1,2 +1,2 @@ #set($subject = "$device.name: stopped") -$device.name stopped at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) +$device.name stopped at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) \ No newline at end of file diff --git a/templates/short/deviceUnknown.vm b/templates/short/deviceUnknown.vm index b6a6e9c9f12..182162c02b2 100644 --- a/templates/short/deviceUnknown.vm +++ b/templates/short/deviceUnknown.vm @@ -1,2 +1,2 @@ #set($subject = "$device.name: status is unknown") -$device.name status is unknown at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) +$device.name status is unknown at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) \ No newline at end of file diff --git a/templates/short/driverChanged.vm b/templates/short/driverChanged.vm index df96b00a1e6..d7a8b7ededd 100644 --- a/templates/short/driverChanged.vm +++ b/templates/short/driverChanged.vm @@ -4,4 +4,4 @@ #else #set($driverName = $event.getString("driverUniqueId")) #end -Driver $driverName has changed in $device.name at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) +Driver $driverName has changed in $device.name at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) \ No newline at end of file diff --git a/templates/short/geofenceEnter.vm b/templates/short/geofenceEnter.vm index 8c250665efa..1098d326ea8 100644 --- a/templates/short/geofenceEnter.vm +++ b/templates/short/geofenceEnter.vm @@ -1,2 +1,2 @@ #set($subject = "$device.name: has entered geofence") -$device.name has entered geofence $geofence.name at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) +$device.name has entered geofence $geofence.name at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) \ No newline at end of file diff --git a/templates/short/geofenceExit.vm b/templates/short/geofenceExit.vm index 7d3ae6f6ef5..55abfcbdc93 100644 --- a/templates/short/geofenceExit.vm +++ b/templates/short/geofenceExit.vm @@ -1,2 +1,2 @@ #set($subject = "$device.name: has exited geofence") -$device.name has exited geofence $geofence.name at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) +$device.name has exited geofence $geofence.name at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) \ No newline at end of file diff --git a/templates/short/ignitionOff.vm b/templates/short/ignitionOff.vm index db5a3f3e18d..10b3d24821a 100644 --- a/templates/short/ignitionOff.vm +++ b/templates/short/ignitionOff.vm @@ -1,2 +1,2 @@ #set($subject = "$device.name: ignition OFF") -$device.name ignition OFF at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) +$device.name ignition OFF at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) \ No newline at end of file diff --git a/templates/short/ignitionOn.vm b/templates/short/ignitionOn.vm index 412ad4d8499..5966175a173 100644 --- a/templates/short/ignitionOn.vm +++ b/templates/short/ignitionOn.vm @@ -1,2 +1,2 @@ #set($subject = "$device.name: ignition ON") -$device.name ignition ON at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) +$device.name ignition ON at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) \ No newline at end of file diff --git a/templates/short/maintenance.vm b/templates/short/maintenance.vm index a58e274b827..4bb887babed 100644 --- a/templates/short/maintenance.vm +++ b/templates/short/maintenance.vm @@ -1,2 +1,2 @@ #set($subject = "$device.name: maintenance is required") -$device.name maintenance $maintenance.name is required at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) +$device.name maintenance $maintenance.name is required at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) \ No newline at end of file diff --git a/templates/short/media.vm b/templates/short/media.vm index 783636f3fa8..342eebc8942 100644 --- a/templates/short/media.vm +++ b/templates/short/media.vm @@ -1,2 +1,2 @@ #set($subject = "$device.name: media file received") -$device.name $event.getString("media") received: $event.getString("file") at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) +$device.name $event.getString("media") received: $event.getString("file") at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) \ No newline at end of file diff --git a/templates/short/queuedCommandSent.vm b/templates/short/queuedCommandSent.vm index 67f03128044..d94de881151 100644 --- a/templates/short/queuedCommandSent.vm +++ b/templates/short/queuedCommandSent.vm @@ -1,2 +1,2 @@ #set($subject = "$device.name: queued command sent") -Queued command sent to $device.name at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) +Queued command sent to $device.name at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) \ No newline at end of file diff --git a/templates/short/test.vm b/templates/short/test.vm index c9d93ce6779..15960b1725c 100644 --- a/templates/short/test.vm +++ b/templates/short/test.vm @@ -1,2 +1,2 @@ #set($subject = "Test message") -Test message +Test message \ No newline at end of file diff --git a/templates/short/textMessage.vm b/templates/short/textMessage.vm index 54c134df46a..70aea297269 100644 --- a/templates/short/textMessage.vm +++ b/templates/short/textMessage.vm @@ -1,2 +1,2 @@ #set($subject = "$device.name: text message received") -Text message received from $device.name at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) +Text message received from $device.name at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) \ No newline at end of file From 7a0eaeafd7c3d399ef91db5a922770d0c479c5a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matja=C5=BE=20=C4=8Crnko?= Date: Fri, 20 Sep 2024 11:01:18 +0200 Subject: [PATCH 16/30] fix: Disable unused Jersey WADL --- src/main/java/org/traccar/web/WebServer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/traccar/web/WebServer.java b/src/main/java/org/traccar/web/WebServer.java index 68fcf52de8d..6d0dad545ff 100644 --- a/src/main/java/org/traccar/web/WebServer.java +++ b/src/main/java/org/traccar/web/WebServer.java @@ -165,6 +165,7 @@ private void initApi(ServletContextHandler servletHandler) { } ResourceConfig resourceConfig = new ResourceConfig(); + resourceConfig.property("jersey.config.server.wadl.disableWadl", true); resourceConfig.registerClasses( JacksonFeature.class, ObjectMapperContextResolver.class, From b1e0484ffd8fe8e71b4027cdb06d23431f5dfac8 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Fri, 20 Sep 2024 21:20:41 -0700 Subject: [PATCH 17/30] Fix GTIGF decoding --- .../java/org/traccar/protocol/Gl200TextProtocolDecoder.java | 2 +- .../org/traccar/protocol/Gl200TextProtocolDecoderTest.java | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java index dc86e0cace6..c9dd0b286f4 100644 --- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -1051,7 +1051,7 @@ private Object decodeIgn( index += 1; // device name index += 1; // duration of ignition on/off - decodeLocation(position, model, v, index); + index = decodeLocation(position, model, v, index); position.set(Position.KEY_IGNITION, type.contains("GN")); position.set(Position.KEY_HOURS, parseHours(v[index++])); diff --git a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java index 048b745764e..52732f49796 100644 --- a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java @@ -11,6 +11,9 @@ public void testDecode() throws Exception { var decoder = inject(new Gl200TextProtocolDecoder(null)); + verifyAttributes(decoder, buffer( + "+RESP:GTIGF,500101,868570000393782,,0,0,,,,,,,0730,0002,A08F,872B36,00,,0.0,20240921021804,E3B1$")); + verifyPositions(decoder, buffer( "+RESP:GTERI,F10413,862599050544301,GV350M,00000002,14164,10,1,1,98.2,93,216.4,-117.484369,33.896640,20240919020058,0311,0480,2C01,00ADBA01,,39539.5,,,100,220100,,0,20240919020058,20A7$")); From 6c82fc6fab45a46f4a09633e2c6f4f1540bb8759 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sun, 22 Sep 2024 07:59:58 -0700 Subject: [PATCH 18/30] Early and late computed attributes --- .../java/org/traccar/ProcessingHandler.java | 3 ++- .../api/resource/AttributeResource.java | 2 +- .../handler/ComputedAttributesHandler.java | 20 +++++++++++++++++-- .../handler/ComputedAttributesTest.java | 2 +- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/traccar/ProcessingHandler.java b/src/main/java/org/traccar/ProcessingHandler.java index d00daf9d55b..869b4b8bbe2 100644 --- a/src/main/java/org/traccar/ProcessingHandler.java +++ b/src/main/java/org/traccar/ProcessingHandler.java @@ -95,6 +95,7 @@ public ProcessingHandler( bufferingManager = new BufferingManager(config, this); positionHandlers = Stream.of( + ComputedAttributesHandler.Early.class, OutdatedHandler.class, TimeHandler.class, GeolocationHandler.class, @@ -105,7 +106,7 @@ public ProcessingHandler( GeocoderHandler.class, SpeedLimitHandler.class, MotionHandler.class, - ComputedAttributesHandler.class, + ComputedAttributesHandler.Late.class, EngineHoursHandler.class, DriverHandler.class, CopyAttributesHandler.class, diff --git a/src/main/java/org/traccar/api/resource/AttributeResource.java b/src/main/java/org/traccar/api/resource/AttributeResource.java index 8debb2e50bf..eb258da7e56 100644 --- a/src/main/java/org/traccar/api/resource/AttributeResource.java +++ b/src/main/java/org/traccar/api/resource/AttributeResource.java @@ -44,7 +44,7 @@ public class AttributeResource extends ExtendedObjectResource { @Inject - private ComputedAttributesHandler computedAttributesHandler; + private ComputedAttributesHandler.Late computedAttributesHandler; public AttributeResource() { super(Attribute.class); diff --git a/src/main/java/org/traccar/handler/ComputedAttributesHandler.java b/src/main/java/org/traccar/handler/ComputedAttributesHandler.java index 9ccb20fe1f4..be777ae436b 100644 --- a/src/main/java/org/traccar/handler/ComputedAttributesHandler.java +++ b/src/main/java/org/traccar/handler/ComputedAttributesHandler.java @@ -48,6 +48,7 @@ public class ComputedAttributesHandler extends BasePositionHandler { private static final Logger LOGGER = LoggerFactory.getLogger(ComputedAttributesHandler.class); private final CacheManager cacheManager; + private boolean early; private final JexlEngine engine; @@ -56,9 +57,23 @@ public class ComputedAttributesHandler extends BasePositionHandler { private final boolean includeDeviceAttributes; private final boolean includeLastAttributes; - @Inject - public ComputedAttributesHandler(Config config, CacheManager cacheManager) { + public static class Early extends ComputedAttributesHandler { + @Inject + public Early(Config config, CacheManager cacheManager) { + super(config, cacheManager, true); + } + } + + public static class Late extends ComputedAttributesHandler { + @Inject + public Late(Config config, CacheManager cacheManager) { + super(config, cacheManager, false); + } + } + + public ComputedAttributesHandler(Config config, CacheManager cacheManager, boolean early) { this.cacheManager = cacheManager; + this.early = early; JexlSandbox sandbox = new JexlSandbox(false); sandbox.allow("com.safe.Functions"); sandbox.allow(Math.class.getName()); @@ -141,6 +156,7 @@ public Object computeAttribute(Attribute attribute, Position position) throws Je @Override public void onPosition(Position position, Callback callback) { var attributes = cacheManager.getDeviceObjects(position.getDeviceId(), Attribute.class).stream() + .filter(attribute -> attribute.getPriority() < 0 == early) .sorted(Comparator.comparing(Attribute::getPriority).reversed()) .toList(); for (Attribute attribute : attributes) { diff --git a/src/test/java/org/traccar/handler/ComputedAttributesTest.java b/src/test/java/org/traccar/handler/ComputedAttributesTest.java index e2af703c22c..d822a0e76ab 100644 --- a/src/test/java/org/traccar/handler/ComputedAttributesTest.java +++ b/src/test/java/org/traccar/handler/ComputedAttributesTest.java @@ -14,7 +14,7 @@ public class ComputedAttributesTest { @Test public void testComputedAttributes() { - ComputedAttributesHandler handler = new ComputedAttributesHandler(new Config(), null); + ComputedAttributesHandler handler = new ComputedAttributesHandler(new Config(), null, false); Date date = new Date(); Position position = new Position(); From bb801da997ddfdc529efce600cc8db76cff7a6df Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Wed, 25 Sep 2024 09:42:07 -0700 Subject: [PATCH 19/30] Add a TODO comment --- src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java b/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java index 50740cbe2ca..a6b2e4bd93a 100644 --- a/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java @@ -103,7 +103,7 @@ public boolean getCompressed(long deviceId) { TAG_LENGTH_MAP.put(0x5b, 7); // variable length TAG_LENGTH_MAP.put(0x5c, 68); TAG_LENGTH_MAP.put(0xfd, 8); - TAG_LENGTH_MAP.put(0xfe, 8); + TAG_LENGTH_MAP.put(0xfe, 8); // TODO this is probably incorrect } private static int getTagLength(int tag) { From d12403926bede0a55c5c01099fe72769f51f62c9 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Wed, 25 Sep 2024 12:02:25 -0700 Subject: [PATCH 20/30] Add SEEWORLD D11L external power --- .../java/org/traccar/protocol/Gt06ProtocolDecoder.java | 8 +++++--- .../org/traccar/protocol/Gt06ProtocolDecoderTest.java | 6 +++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java index fed24c5728d..7eebf09e4d9 100644 --- a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java @@ -846,9 +846,11 @@ private Object decodeBasic(Channel channel, SocketAddress remoteAddress, ByteBuf if (modelLW && type == MSG_STATUS) { position.set(Position.KEY_POWER, BitUtil.to(buf.readUnsignedShort(), 12) / 10.0); } else { - short alarmExtension = buf.readUnsignedByte(); - if (variant != Variant.VXT01) { - position.addAlarm(decodeAlarm(alarmExtension, modelLW)); + short extension = buf.readUnsignedByte(); + if (type == MSG_STATUS) { + position.set(Position.KEY_POWER, (double) extension); + } else if (variant != Variant.VXT01) { + position.addAlarm(decodeAlarm(extension, modelLW)); } } } diff --git a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java index 555903946d2..76d36986301 100644 --- a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java @@ -17,6 +17,10 @@ public void testDecode() throws Exception { verifyNull(decoder, binary( "78780D01086471700328358100093F040D0A")); + verifyAttribute(decoder, binary( + "78780a134506560b0102242b850d0a"), + Position.KEY_POWER, 11.0); + verifyAttribute(decoder, binary( "787817360005040002003201010018020192006a015f0324aeaf0d0a"), Position.KEY_BATTERY, 4.02); @@ -76,7 +80,7 @@ public void testDecode() throws Exception { verifyAttribute(decoder, binary( "78780a130604ea04000006bc8a0d0a"), - Position.KEY_POWER, null); + Position.KEY_POWER, 4.0); verifyAttributes(decoder, binary( "797900849404414c4d313d43353b414c4d323d43433b414c4d333d35433b535441313d43303b4459443d30313b534f533d303133323838333730302c2c3b43454e5445523d303133323838333730303b46454e43453d46656e63652c4f46462c302c302e3030303030302c302e3030303030302c3330302c494e206f72204f55542c313b00b79d120d0a")); From e6c3d52e909bb4be9ee2ac8ba4ba9732cfa4c382 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Thu, 26 Sep 2024 06:16:38 -0700 Subject: [PATCH 21/30] Fix scheduled reports --- src/main/java/org/traccar/model/Calendar.java | 3 +++ src/main/java/org/traccar/schedule/TaskReports.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/traccar/model/Calendar.java b/src/main/java/org/traccar/model/Calendar.java index 3f51731e8dc..d03c718a929 100644 --- a/src/main/java/org/traccar/model/Calendar.java +++ b/src/main/java/org/traccar/model/Calendar.java @@ -29,6 +29,7 @@ import java.io.IOException; import java.time.Duration; import java.time.Instant; +import java.time.OffsetDateTime; import java.time.ZonedDateTime; import java.time.temporal.Temporal; import java.util.Date; @@ -87,6 +88,8 @@ public boolean checkMoment(Date date) { private static Instant temporalToInstant(Temporal temporal) { if (temporal instanceof ZonedDateTime) { return ((ZonedDateTime) temporal).toInstant(); + } else if (temporal instanceof OffsetDateTime) { + return ((OffsetDateTime) temporal).toInstant(); } else if (temporal instanceof Instant) { return (Instant) temporal; } else { diff --git a/src/main/java/org/traccar/schedule/TaskReports.java b/src/main/java/org/traccar/schedule/TaskReports.java index 32f6fea879a..4ee7890e836 100644 --- a/src/main/java/org/traccar/schedule/TaskReports.java +++ b/src/main/java/org/traccar/schedule/TaskReports.java @@ -94,7 +94,7 @@ public void run() { } } } - } catch (StorageException e) { + } catch (Exception e) { LOGGER.warn("Scheduled reports error", e); } } From 1cc23f405cdb79671ca1deefb1de4b5378be5d0f Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Thu, 26 Sep 2024 11:11:38 -0700 Subject: [PATCH 22/30] Navtelecom custom commands --- .../traccar/protocol/NavtelecomProtocol.java | 6 ++- .../protocol/NavtelecomProtocolDecoder.java | 24 ++++++----- .../protocol/NavtelecomProtocolEncoder.java | 40 +++++++++++++++++++ .../NavtelecomProtocolEncoderTest.java | 23 +++++++++++ 4 files changed, 82 insertions(+), 11 deletions(-) create mode 100644 src/main/java/org/traccar/protocol/NavtelecomProtocolEncoder.java create mode 100644 src/test/java/org/traccar/protocol/NavtelecomProtocolEncoderTest.java diff --git a/src/main/java/org/traccar/protocol/NavtelecomProtocol.java b/src/main/java/org/traccar/protocol/NavtelecomProtocol.java index de5f93df1b3..2040af45d02 100644 --- a/src/main/java/org/traccar/protocol/NavtelecomProtocol.java +++ b/src/main/java/org/traccar/protocol/NavtelecomProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2021 - 2024 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,15 +21,19 @@ import org.traccar.config.Config; import jakarta.inject.Inject; +import org.traccar.model.Command; public class NavtelecomProtocol extends BaseProtocol { @Inject public NavtelecomProtocol(Config config) { + setSupportedDataCommands( + Command.TYPE_CUSTOM); addServer(new TrackerServer(config, getName(), false) { @Override protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new NavtelecomFrameDecoder()); + pipeline.addLast(new NavtelecomProtocolEncoder(NavtelecomProtocol.this)); pipeline.addLast(new NavtelecomProtocolDecoder(NavtelecomProtocol.this)); } }); diff --git a/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java b/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java index eef5008d2a3..ad477bcad16 100644 --- a/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2021 - 2024 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -107,19 +107,23 @@ public BitSet getBits() { return bits; } + static ByteBuf encodeContent(int receiver, int sender, ByteBuf content) { + ByteBuf buf = Unpooled.buffer(); + buf.writeCharSequence("@NTC", StandardCharsets.US_ASCII); + buf.writeIntLE(receiver); + buf.writeIntLE(sender); + buf.writeShortLE(content.readableBytes()); + buf.writeByte(Checksum.xor(content.nioBuffer())); + buf.writeByte(Checksum.xor(buf.nioBuffer())); + buf.writeBytes(content); + return buf; + } + private void sendResponse( Channel channel, SocketAddress remoteAddress, int receiver, int sender, ByteBuf content) { if (channel != null) { - ByteBuf response = Unpooled.buffer(); - response.writeCharSequence("@NTC", StandardCharsets.US_ASCII); - response.writeIntLE(sender); - response.writeIntLE(receiver); - response.writeShortLE(content.readableBytes()); - response.writeByte(Checksum.xor(content.nioBuffer())); - response.writeByte(Checksum.xor(response.nioBuffer())); - response.writeBytes(content); + ByteBuf response = encodeContent(sender, receiver, content); content.release(); - channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); } } diff --git a/src/main/java/org/traccar/protocol/NavtelecomProtocolEncoder.java b/src/main/java/org/traccar/protocol/NavtelecomProtocolEncoder.java new file mode 100644 index 00000000000..db86ef054ca --- /dev/null +++ b/src/main/java/org/traccar/protocol/NavtelecomProtocolEncoder.java @@ -0,0 +1,40 @@ +/* + * Copyright 2024 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.buffer.Unpooled; +import org.traccar.BaseProtocolEncoder; +import org.traccar.Protocol; +import org.traccar.model.Command; + +import java.nio.charset.StandardCharsets; + +public class NavtelecomProtocolEncoder extends BaseProtocolEncoder { + + public NavtelecomProtocolEncoder(Protocol protocol) { + super(protocol); + } + + @Override + protected Object encodeCommand(Command command) { + return switch (command.getType()) { + case Command.TYPE_CUSTOM -> NavtelecomProtocolDecoder.encodeContent(0, 1, Unpooled.copiedBuffer( + command.getString(Command.KEY_DATA), StandardCharsets.US_ASCII)); + default -> null; + }; + } + +} diff --git a/src/test/java/org/traccar/protocol/NavtelecomProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/NavtelecomProtocolEncoderTest.java new file mode 100644 index 00000000000..0b58dd33340 --- /dev/null +++ b/src/test/java/org/traccar/protocol/NavtelecomProtocolEncoderTest.java @@ -0,0 +1,23 @@ +package org.traccar.protocol; + +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; +import org.traccar.model.Command; + +public class NavtelecomProtocolEncoderTest extends ProtocolTest { + + @Test + public void testEncode() throws Exception { + + var encoder = inject(new NavtelecomProtocolEncoder(null)); + + Command command = new Command(); + command.setDeviceId(1); + command.setType(Command.TYPE_CUSTOM); + command.set(Command.KEY_DATA, "*!SETOUT 1Y"); + + verifyCommand(encoder, command, binary("404e544300000000010000000b004f5c2a215345544f5554203159")); + + } + +} From a0d5c20d3f527b562e7d766b6a2982cf8281d2e3 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Fri, 27 Sep 2024 09:39:15 -0700 Subject: [PATCH 23/30] Power based on device model --- .../traccar/protocol/Gt06ProtocolDecoder.java | 49 ++++++------------- .../protocol/Gt06ProtocolDecoderTest.java | 18 +++++-- 2 files changed, 28 insertions(+), 39 deletions(-) diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java index 7eebf09e4d9..6289b57fc67 100644 --- a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java @@ -234,21 +234,6 @@ private static boolean hasStatus(int type) { } } - private static boolean hasLanguage(int type) { - switch (type) { - case MSG_GPS_PHONE: - case MSG_HEARTBEAT: - case MSG_GPS_LBS_STATUS_3: - case MSG_LBS_MULTIPLE_1: - case MSG_LBS_MULTIPLE_2: - case MSG_LBS_2: - case MSG_FENCE_MULTI: - return true; - default: - return false; - } - } - private void sendResponse(Channel channel, boolean extended, int type, int index, ByteBuf content) { if (channel != null) { ByteBuf response = Unpooled.buffer(); @@ -836,18 +821,22 @@ private Object decodeBasic(Channel channel, SocketAddress remoteAddress, ByteBuf if (type == MSG_GPS_LBS_STATUS_5) { position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01); } - int battery = buf.readUnsignedByte(); - if (battery <= 6) { - position.set(Position.KEY_BATTERY_LEVEL, battery * 100 / 6); - } else if (battery <= 100) { - position.set(Position.KEY_BATTERY_LEVEL, battery); + if (type == MSG_STATUS && "R11".equals(model)) { + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01); + } else { + int battery = buf.readUnsignedByte(); + if (battery <= 6) { + position.set(Position.KEY_BATTERY_LEVEL, battery * 100 / 6); + } else if (battery <= 100) { + position.set(Position.KEY_BATTERY_LEVEL, battery); + } } position.set(Position.KEY_RSSI, buf.readUnsignedByte()); - if (modelLW && type == MSG_STATUS) { + if (type == MSG_STATUS && modelLW) { position.set(Position.KEY_POWER, BitUtil.to(buf.readUnsignedShort(), 12) / 10.0); } else { short extension = buf.readUnsignedByte(); - if (type == MSG_STATUS) { + if (type == MSG_STATUS && "SEEWORLD".equals(model)) { position.set(Position.KEY_POWER, (double) extension); } else if (variant != Variant.VXT01) { position.addAlarm(decodeAlarm(extension, modelLW)); @@ -987,6 +976,10 @@ private Object decodeBasic(Channel channel, SocketAddress remoteAddress, ByteBuf position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); } + if (type == MSG_GPS_LBS_STATUS_3 || type == MSG_FENCE_MULTI) { + position.set(Position.KEY_GEOFENCE, buf.readUnsignedByte()); + } + } else if (type == MSG_ALARM) { boolean extendedAlarm = dataLength > 7; @@ -1035,18 +1028,6 @@ private Object decodeBasic(Channel channel, SocketAddress remoteAddress, ByteBuf } - if (hasLanguage(type)) { - if (modelLW && type == MSG_STATUS) { - position.set(Position.KEY_POWER, BitUtil.to(buf.readUnsignedShort(), 12) * 0.1); - } else { - buf.readUnsignedShort(); // language - } - } - - if (type == MSG_GPS_LBS_STATUS_3 || type == MSG_FENCE_MULTI) { - position.set(Position.KEY_GEOFENCE, buf.readUnsignedByte()); - } - sendResponse(channel, false, type, buf.getShort(buf.writerIndex() - 6), null); return position; diff --git a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java index 76d36986301..5e28792c720 100644 --- a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java @@ -17,10 +17,6 @@ public void testDecode() throws Exception { verifyNull(decoder, binary( "78780D01086471700328358100093F040D0A")); - verifyAttribute(decoder, binary( - "78780a134506560b0102242b850d0a"), - Position.KEY_POWER, 11.0); - verifyAttribute(decoder, binary( "787817360005040002003201010018020192006a015f0324aeaf0d0a"), Position.KEY_BATTERY, 4.02); @@ -80,7 +76,7 @@ public void testDecode() throws Exception { verifyAttribute(decoder, binary( "78780a130604ea04000006bc8a0d0a"), - Position.KEY_POWER, 4.0); + Position.KEY_POWER, null); verifyAttributes(decoder, binary( "797900849404414c4d313d43353b414c4d323d43433b414c4d333d35433b535441313d43303b4459443d30313b534f533d303133323838333730302c2c3b43454e5445523d303133323838333730303b46454e43453d46656e63652c4f46462c302c302e3030303030302c302e3030303030302c3330302c494e206f72204f55542c313b00b79d120d0a")); @@ -530,6 +526,18 @@ public void testDecode() throws Exception { "78782516180516150812c804b50ee80880e40805dcf909012e000000986633460604190106c393490d0a"), Position.KEY_ALARM, Position.ALARM_ACCELERATION); + decoder.setModelOverride("SEEWORLD"); + + verifyAttribute(decoder, binary( + "78780a134506560b0102242b850d0a"), + Position.KEY_POWER, 11.0); + + decoder.setModelOverride("R11"); + + verifyAttribute(decoder, binary( + "78780a13460b120400160642c20d0a"), + Position.KEY_POWER, 28.34); + } } From b3204eeb95a7cec49e771ea51111bd4ab7773cb4 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Fri, 27 Sep 2024 10:27:55 -0700 Subject: [PATCH 24/30] Ignore unused DT800 attribute --- .../java/org/traccar/protocol/HuabaoProtocolDecoder.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java index f04da9afdb6..7e5489f19db 100644 --- a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java @@ -406,6 +406,8 @@ private Position decodeLocation(DeviceSession deviceSession, ByteBuf buf) { position.addAlarm(decodeAlarm(buf.readUnsignedInt())); + String model = getDeviceModel(deviceSession); + decodeCoordinates(position, deviceSession, buf); position.setAltitude(buf.readShort()); @@ -603,7 +605,9 @@ private Position decodeLocation(DeviceSession deviceSession, ByteBuf buf) { position.set("cover", BitUtil.check(deviceStatus, 3)); break; case 0xE2: - position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedInt() * 0.1); + if (!"DT800".equals(model)) { + position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedInt() * 0.1); + } break; case 0xE3: buf.readUnsignedByte(); // reserved From 8df70f7bd69e9c55daee718040d62e121ec4809b Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Fri, 27 Sep 2024 15:13:55 -0700 Subject: [PATCH 25/30] Collections in computed attributes --- .../java/org/traccar/handler/ComputedAttributesHandler.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/traccar/handler/ComputedAttributesHandler.java b/src/main/java/org/traccar/handler/ComputedAttributesHandler.java index be777ae436b..98b857ec3ff 100644 --- a/src/main/java/org/traccar/handler/ComputedAttributesHandler.java +++ b/src/main/java/org/traccar/handler/ComputedAttributesHandler.java @@ -37,7 +37,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -79,7 +81,8 @@ public ComputedAttributesHandler(Config config, CacheManager cacheManager, boole sandbox.allow(Math.class.getName()); List.of( Double.class, Float.class, Integer.class, Long.class, Short.class, - Character.class, Boolean.class, String.class, Byte.class, Date.class) + Character.class, Boolean.class, String.class, Byte.class, Date.class, + HashMap.class, LinkedHashMap.class, double[].class, int[].class, boolean[].class, String[].class) .forEach((type) -> sandbox.allow(type.getName())); features = new JexlFeatures() .localVar(config.getBoolean(Keys.PROCESSING_COMPUTED_ATTRIBUTES_LOCAL_VARIABLES)) From efdd5fdd44afab2c29a2ed2ffce2a755684eb0d9 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 28 Sep 2024 21:41:41 -0700 Subject: [PATCH 26/30] Support device attributes test --- .../api/resource/AttributeResource.java | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/traccar/api/resource/AttributeResource.java b/src/main/java/org/traccar/api/resource/AttributeResource.java index eb258da7e56..4342f99870e 100644 --- a/src/main/java/org/traccar/api/resource/AttributeResource.java +++ b/src/main/java/org/traccar/api/resource/AttributeResource.java @@ -33,7 +33,7 @@ import org.traccar.model.Device; import org.traccar.model.Position; import org.traccar.handler.ComputedAttributesHandler; -import org.traccar.storage.StorageException; +import org.traccar.session.cache.CacheManager; import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; @@ -43,6 +43,9 @@ @Consumes(MediaType.APPLICATION_JSON) public class AttributeResource extends ExtendedObjectResource { + @Inject + private CacheManager cacheManager; + @Inject private ComputedAttributesHandler.Late computedAttributesHandler; @@ -52,7 +55,7 @@ public AttributeResource() { @POST @Path("test") - public Response test(@QueryParam("deviceId") long deviceId, Attribute entity) throws StorageException { + public Response test(@QueryParam("deviceId") long deviceId, Attribute entity) throws Exception { permissionsService.checkAdmin(getUserId()); permissionsService.checkPermission(Device.class, getUserId(), deviceId); @@ -60,14 +63,20 @@ public Response test(@QueryParam("deviceId") long deviceId, Attribute entity) th new Columns.All(), new Condition.LatestPositions(deviceId))); - Object result = computedAttributesHandler.computeAttribute(entity, position); - if (result != null) { - return switch (entity.getType()) { - case "number", "boolean" -> Response.ok(result).build(); - default -> Response.ok(result.toString()).build(); - }; - } else { - return Response.noContent().build(); + var key = new Object(); + try { + cacheManager.addDevice(position.getDeviceId(), key); + Object result = computedAttributesHandler.computeAttribute(entity, position); + if (result != null) { + return switch (entity.getType()) { + case "number", "boolean" -> Response.ok(result).build(); + default -> Response.ok(result.toString()).build(); + }; + } else { + return Response.noContent().build(); + } + } finally { + cacheManager.removeDevice(position.getDeviceId(), key); } } From a3ad9759bdfc3c71d2e1307b503c3edcbf9d29c5 Mon Sep 17 00:00:00 2001 From: Nikutrax <112165998+Nikutrax@users.noreply.github.com> Date: Mon, 30 Sep 2024 15:52:19 +0200 Subject: [PATCH 27/30] Update TrvProtocolDecoder.java update 2.0 --- src/main/java/org/traccar/protocol/TrvProtocolDecoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java b/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java index a0efdc9825d..d5a69d9b02c 100644 --- a/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java @@ -180,7 +180,7 @@ protected Object decode( channel.writeAndFlush(new NetworkMessage(responseHeader + "," + time + ",0#", remoteAddress)); } else if (type.equals("AP14") && !id.equals("IW")) { channel.writeAndFlush(new NetworkMessage(responseHeader + ",0.000,0.000#", remoteAddress)); - } else if (!type.equals("AP12") && !type.equals("AP14") + } else if (!Set.of("AP12", "AP14", "AP33", "AP34", "AP84", "AP85").contains(type) && !sentence.substring(responseHeader.length() + 1).matches("^\\d{6}$")) { channel.writeAndFlush(new NetworkMessage(responseHeader + "#", remoteAddress)); } From fe0f8bae3c2d4b819a12ec72b84c1722393adaf1 Mon Sep 17 00:00:00 2001 From: Nikutrax <112165998+Nikutrax@users.noreply.github.com> Date: Tue, 1 Oct 2024 12:00:02 +0200 Subject: [PATCH 28/30] Update TrvProtocolDecoder.java No response for following IWAP Commands: AP33 AP34 AP84 AP85 Attached is the protocol where the changes are visible. On page 20, you can see AP33; on page 21, AP34; and on page 23, AP84/AP85. --- src/main/java/org/traccar/protocol/TrvProtocolDecoder.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java b/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java index d5a69d9b02c..7ce1b510cc1 100644 --- a/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java @@ -33,6 +33,7 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.regex.Pattern; +import java.util.Set; public class TrvProtocolDecoder extends BaseProtocolDecoder { From 3d05d50c11a0b9ba24b9b73007a108e2be3fc7cc Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Tue, 1 Oct 2024 07:03:05 -0700 Subject: [PATCH 29/30] Improve Wialon coordinates pattern --- src/main/java/org/traccar/protocol/WialonProtocolDecoder.java | 4 ++-- .../java/org/traccar/protocol/WialonProtocolDecoderTest.java | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java b/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java index 491c5b4dd33..fbb845a7bf2 100644 --- a/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java @@ -50,9 +50,9 @@ public WialonProtocolDecoder(Protocol protocol) { private static final Pattern PATTERN = new PatternBuilder() .number("(?:NA|(dd)(dd)(dd));") // date (ddmmyy) .number("(?:NA|(dd)(dd)(dd));") // time (hhmmss) - .number("(?:NA|(dd)(dd.d+));") // latitude + .number("(?:NA|(d+)(dd.d+));") // latitude .expression("(?:NA|([NS]));") - .number("(?:NA|(ddd)(dd.d+));") // longitude + .number("(?:NA|(d+)(dd.d+));") // longitude .expression("(?:NA|([EW]));") .number("(?:NA|(d+.?d*))?;") // speed .number("(?:NA|(d+.?d*))?;") // course diff --git a/src/test/java/org/traccar/protocol/WialonProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WialonProtocolDecoderTest.java index eac3f8aecd4..7346798f0e6 100644 --- a/src/test/java/org/traccar/protocol/WialonProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/WialonProtocolDecoderTest.java @@ -13,6 +13,9 @@ public void testDecode() throws Exception { verifyNull(decoder, text( "#L#2.0;42001300083;;CE45")); + verifyPosition(decoder, text( + "#SD#300924;154245;5554.350052;N;3644.670410;E;2.92;NA;NA;NA;7A01")); + verifyAttribute(decoder, text( "#D#220323;114150;2234.80479;N;11354.87786;E;0;NA;59;11;NA;NA;NA;;NA;d_battr:1:94,d_csq:1:21,di_light:1:1;E7C9"), "di_light", 1.0); From a11be97deeba6d2b69a8870518bd47bdcd4dda64 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Wed, 2 Oct 2024 15:50:11 -0700 Subject: [PATCH 30/30] Fix Lantrix ACK (fix #5394) --- .../java/org/traccar/protocol/TaipProtocolDecoder.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java b/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java index c09888011a5..4211bc99c06 100644 --- a/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java @@ -301,11 +301,14 @@ private Position decodeAttributes( response = ">SAK;ID=" + uniqueId + ";" + messageIndex + "<"; } else { if (indexFirst) { - response = ">ACK;" + messageIndex + ";ID=" + uniqueId + ";*"; + response = ">ACK;" + messageIndex + ";ID=" + uniqueId + ";"; } else { - response = ">ACK;ID=" + uniqueId + ";" + messageIndex + ";*"; + response = ">ACK;ID=" + uniqueId + ";" + messageIndex + ";"; } - response += String.format("%02X", Checksum.xor(response)) + "<"; + String model = getDeviceModel(deviceSession); + boolean lantrix = model != null && model.toUpperCase().startsWith("LANTRIX"); + int checksum = Checksum.xor(lantrix ? response : response + "*"); + response += String.format("*%02X", checksum) + "<"; } channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); } else {