@fleetbase/fleetops-engine 0.6.20 → 0.6.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/addon/components/custom-entity/form.hbs +14 -14
  2. package/addon/components/device/card.hbs +1 -0
  3. package/addon/components/device/card.js +3 -0
  4. package/addon/components/device/details.hbs +92 -43
  5. package/addon/components/device/form.hbs +108 -60
  6. package/addon/components/device/form.js +36 -8
  7. package/addon/components/device/manager.hbs +29 -0
  8. package/addon/components/device/manager.js +95 -0
  9. package/addon/components/device/panel-header.hbs +32 -0
  10. package/addon/components/device/panel-header.js +3 -0
  11. package/addon/components/device/pill.hbs +16 -0
  12. package/addon/components/device/pill.js +3 -0
  13. package/addon/components/driver/details.hbs +4 -0
  14. package/addon/components/driver/details.js +19 -1
  15. package/addon/components/driver/form.hbs +14 -3
  16. package/addon/components/driver/form.js +49 -47
  17. package/addon/components/driver/pill.hbs +17 -0
  18. package/addon/components/driver/pill.js +3 -0
  19. package/addon/components/entity/form.hbs +7 -5
  20. package/addon/components/layout/fleet-ops-sidebar.js +12 -12
  21. package/addon/components/map/drawer/device-event-listing.hbs +64 -0
  22. package/addon/components/map/drawer/device-event-listing.js +181 -0
  23. package/addon/components/map/drawer/position-listing.hbs +100 -0
  24. package/addon/components/map/drawer/position-listing.js +455 -0
  25. package/addon/components/map/drawer.js +2 -0
  26. package/addon/components/map/leaflet-live-map.hbs +7 -2
  27. package/addon/components/modals/attach-device.hbs +18 -0
  28. package/addon/components/modals/attach-device.js +3 -0
  29. package/addon/components/order/details/detail.hbs +2 -54
  30. package/addon/components/order/details/detail.js +1 -0
  31. package/addon/components/order/details/payload.hbs +6 -4
  32. package/addon/components/order/details/payload.js +2 -0
  33. package/addon/components/order/pill.hbs +34 -0
  34. package/addon/components/order/pill.js +3 -0
  35. package/addon/components/order-config-manager/custom-fields.js +1 -1
  36. package/addon/components/positions-replay.hbs +339 -0
  37. package/addon/components/positions-replay.js +409 -0
  38. package/addon/components/sensor/details.hbs +64 -38
  39. package/addon/components/sensor/form.hbs +112 -63
  40. package/addon/components/sensor/form.js +36 -24
  41. package/addon/components/sensor/panel-header.hbs +32 -0
  42. package/addon/components/sensor/panel-header.js +3 -0
  43. package/addon/components/telematic/details.hbs +40 -16
  44. package/addon/components/telematic/form.hbs +63 -64
  45. package/addon/components/telematic/form.js +73 -4
  46. package/addon/components/vehicle/card.hbs +2 -2
  47. package/addon/components/vehicle/details.hbs +4 -0
  48. package/addon/components/vehicle/details.js +19 -1
  49. package/addon/components/vehicle/form.hbs +4 -0
  50. package/addon/components/vehicle/pill.hbs +34 -0
  51. package/addon/components/vehicle/pill.js +3 -0
  52. package/addon/controllers/analytics/reports/index/edit.js +1 -1
  53. package/addon/controllers/connectivity/devices/index/details.js +22 -1
  54. package/addon/controllers/connectivity/devices/index/edit.js +66 -1
  55. package/addon/controllers/connectivity/devices/index.js +51 -9
  56. package/addon/controllers/connectivity/events/index.js +65 -16
  57. package/addon/controllers/connectivity/sensors/index/details.js +22 -1
  58. package/addon/controllers/connectivity/sensors/index/edit.js +66 -1
  59. package/addon/controllers/connectivity/sensors/index.js +66 -6
  60. package/addon/controllers/connectivity/telematics/index/details.js +22 -1
  61. package/addon/controllers/connectivity/telematics/index/edit.js +66 -1
  62. package/addon/controllers/connectivity/telematics/index.js +20 -11
  63. package/addon/controllers/management/fleets/index/details.js +26 -21
  64. package/addon/controllers/management/fleets/index/edit.js +9 -6
  65. package/addon/controllers/management/vehicles/index/details.js +26 -13
  66. package/addon/controllers/settings/custom-fields.js +6 -0
  67. package/addon/helpers/get-fleet-ops-option-label.js +11 -0
  68. package/addon/routes/connectivity/devices/index/details.js +27 -1
  69. package/addon/routes/connectivity/devices/index/edit.js +27 -1
  70. package/addon/routes/connectivity/sensors/index/details.js +27 -1
  71. package/addon/routes/connectivity/sensors/index/edit.js +27 -1
  72. package/addon/routes/connectivity/telematics/index/details.js +27 -1
  73. package/addon/routes/connectivity/telematics/index/edit.js +27 -1
  74. package/addon/routes/management/drivers/index/details/positions.js +3 -0
  75. package/addon/routes/management/vehicles/index/details/positions.js +3 -0
  76. package/addon/routes.js +4 -0
  77. package/addon/services/movement-tracker.js +81 -30
  78. package/addon/services/position-playback.js +486 -0
  79. package/addon/services/resource-metadata.js +46 -0
  80. package/addon/styles/fleetops-engine.css +157 -0
  81. package/addon/templates/connectivity/devices/index/details/index.hbs +2 -2
  82. package/addon/templates/connectivity/devices/index/details.hbs +15 -2
  83. package/addon/templates/connectivity/devices/index/edit.hbs +1 -1
  84. package/addon/templates/connectivity/events/index.hbs +1 -1
  85. package/addon/templates/connectivity/sensors/index/details/index.hbs +2 -2
  86. package/addon/templates/connectivity/sensors/index/details.hbs +15 -2
  87. package/addon/templates/connectivity/sensors/index/edit.hbs +1 -1
  88. package/addon/templates/connectivity/telematics/index/details/index.hbs +2 -2
  89. package/addon/templates/connectivity/telematics/index/details.hbs +14 -2
  90. package/addon/templates/connectivity/telematics/index/edit.hbs +1 -1
  91. package/addon/templates/management/drivers/index/details/positions.hbs +2 -0
  92. package/addon/templates/management/vehicles/index/details/devices.hbs +1 -2
  93. package/addon/templates/management/vehicles/index/details/positions.hbs +1 -0
  94. package/addon/utils/fleet-ops-options.js +95 -0
  95. package/app/components/device/card.js +1 -0
  96. package/app/components/device/manager.js +1 -0
  97. package/app/components/device/panel-header.js +1 -0
  98. package/app/components/device/pill.js +1 -0
  99. package/app/components/driver/pill.js +1 -0
  100. package/app/components/map/drawer/device-event-listing.js +1 -0
  101. package/app/components/map/drawer/position-listing.js +1 -0
  102. package/app/components/modals/attach-device.js +1 -0
  103. package/app/components/order/pill.js +1 -0
  104. package/app/components/positions-replay.js +1 -0
  105. package/app/components/sensor/panel-header.js +1 -0
  106. package/app/components/vehicle/pill.js +1 -0
  107. package/app/helpers/get-fleet-ops-option-label.js +1 -0
  108. package/app/routes/management/drivers/index/details/positions.js +1 -0
  109. package/app/routes/management/vehicles/index/details/positions.js +1 -0
  110. package/app/services/position-playback.js +1 -0
  111. package/app/services/resource-metadata.js +1 -0
  112. package/app/templates/management/drivers/index/details/positions.js +1 -0
  113. package/app/templates/management/vehicles/index/details/positions.js +1 -0
  114. package/composer.json +1 -1
  115. package/extension.json +1 -1
  116. package/package.json +4 -4
  117. package/server/config/telematics.php +111 -0
  118. package/server/migrations/2025_10_27_000001_add_telematics_integration_fields.php +70 -0
  119. package/server/migrations/2025_10_27_171322_fix_device_column_names.php +107 -0
  120. package/server/migrations/2025_10_27_203023_add_company_uuid_to_device_events_table.php +28 -0
  121. package/server/src/Console/Commands/ReplayVehicleLocations.php +225 -0
  122. package/server/src/Contracts/TelematicProviderDescriptor.php +72 -0
  123. package/server/src/Contracts/TelematicProviderInterface.php +119 -0
  124. package/server/src/Exceptions/TelematicProviderException.php +14 -0
  125. package/server/src/Exceptions/TelematicRateLimitExceededException.php +12 -0
  126. package/server/src/Http/Controllers/Api/v1/DriverController.php +24 -14
  127. package/server/src/Http/Controllers/Api/v1/VehicleController.php +27 -7
  128. package/server/src/Http/Controllers/Internal/v1/DeviceController.php +22 -0
  129. package/server/src/Http/Controllers/Internal/v1/PositionController.php +240 -0
  130. package/server/src/Http/Controllers/Internal/v1/SensorController.php +11 -0
  131. package/server/src/Http/Controllers/Internal/v1/TelematicController.php +141 -0
  132. package/server/src/Http/Controllers/TelematicWebhookController.php +169 -0
  133. package/server/src/Http/Filter/DeviceEventFilter.php +68 -0
  134. package/server/src/Http/Filter/PositionFilter.php +35 -0
  135. package/server/src/Http/Resources/v1/Position.php +44 -0
  136. package/server/src/Jobs/ReplayPositions.php +64 -0
  137. package/server/src/Jobs/SendPositionReplay.php +65 -0
  138. package/server/src/Jobs/SyncTelematicDevicesJob.php +106 -0
  139. package/server/src/Jobs/TestTelematicConnectionJob.php +102 -0
  140. package/server/src/Models/Asset.php +10 -8
  141. package/server/src/Models/Device.php +79 -12
  142. package/server/src/Models/DeviceEvent.php +33 -3
  143. package/server/src/Models/Driver.php +28 -1
  144. package/server/src/Models/Maintenance.php +15 -12
  145. package/server/src/Models/Part.php +2 -0
  146. package/server/src/Models/Payload.php +0 -1
  147. package/server/src/Models/Place.php +4 -1
  148. package/server/src/Models/Position.php +27 -17
  149. package/server/src/Models/Sensor.php +78 -13
  150. package/server/src/Models/Telematic.php +116 -6
  151. package/server/src/Models/TrackingNumber.php +3 -1
  152. package/server/src/Models/Vehicle.php +8 -11
  153. package/server/src/Models/WorkOrder.php +8 -5
  154. package/server/src/Providers/FleetOpsServiceProvider.php +2 -0
  155. package/server/src/Support/Telematics/Providers/AbstractProvider.php +151 -0
  156. package/server/src/Support/Telematics/Providers/FlespiProvider.php +182 -0
  157. package/server/src/Support/Telematics/Providers/GeotabProvider.php +181 -0
  158. package/server/src/Support/Telematics/Providers/SamsaraProvider.php +177 -0
  159. package/server/src/Support/Telematics/TelematicProviderRegistry.php +147 -0
  160. package/server/src/Support/Telematics/TelematicService.php +223 -0
  161. package/server/src/Support/Utils.php +1 -1
  162. package/server/src/routes.php +24 -1
@@ -0,0 +1,223 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\FleetOps\Support\Telematics;
4
+
5
+ use Fleetbase\FleetOps\Jobs\SyncDevicesJob;
6
+ use Fleetbase\FleetOps\Jobs\TestConnectionJob;
7
+ use Fleetbase\FleetOps\Models\Device;
8
+ use Fleetbase\FleetOps\Models\Telematic;
9
+ use Illuminate\Support\Facades\Crypt;
10
+ use Illuminate\Support\Facades\Validator;
11
+ use Illuminate\Validation\ValidationException;
12
+
13
+ /**
14
+ * Class TelematicService.
15
+ *
16
+ * Business logic for telematics management.
17
+ * Handles CRUD operations, connection testing, and device discovery.
18
+ */
19
+ class TelematicService
20
+ {
21
+ protected TelematicProviderRegistry $registry;
22
+
23
+ public function __construct(TelematicProviderRegistry $registry)
24
+ {
25
+ $this->registry = $registry;
26
+ }
27
+
28
+ /**
29
+ * Create a new telematic integration.
30
+ *
31
+ * @throws ValidationException
32
+ */
33
+ public function create(array $data): Telematic
34
+ {
35
+ // Validate provider exists
36
+ $providerKey = $data['provider_key'];
37
+ $descriptor = $this->registry->findByKey($providerKey);
38
+
39
+ if (!$descriptor) {
40
+ throw ValidationException::withMessages(['provider_key' => ['Invalid provider key.']]);
41
+ }
42
+
43
+ // Validate credentials against provider schema
44
+ $this->validateCredentials($data['credentials'] ?? [], $descriptor->requiredFields);
45
+
46
+ // Test connection if requested
47
+ if ($data['test_connection'] ?? false) {
48
+ $provider = $this->registry->resolve($providerKey);
49
+ $result = $provider->testConnection($data['credentials']);
50
+
51
+ if (!$result['success']) {
52
+ throw ValidationException::withMessages(['credentials' => [$result['message']]]);
53
+ }
54
+ }
55
+
56
+ // Create telematic record
57
+ $telematic = new Telematic();
58
+ $telematic->company_uuid = session('company');
59
+ $telematic->name = $data['name'];
60
+ $telematic->provider = $providerKey;
61
+ $telematic->credentials = Crypt::encryptString(json_encode($data['credentials']));
62
+ $telematic->status = 'active';
63
+ $telematic->meta = $data['meta'] ?? [];
64
+ $telematic->save();
65
+
66
+ return $telematic;
67
+ }
68
+
69
+ /**
70
+ * Update a telematic integration.
71
+ */
72
+ public function update(Telematic $telematic, array $data): Telematic
73
+ {
74
+ if (isset($data['name'])) {
75
+ $telematic->name = $data['name'];
76
+ }
77
+
78
+ if (isset($data['credentials'])) {
79
+ $descriptor = $this->registry->findByKey($telematic->provider);
80
+ $this->validateCredentials($data['credentials'], $descriptor->requiredFields);
81
+ $telematic->credentials = Crypt::encryptString(json_encode($data['credentials']));
82
+ }
83
+
84
+ if (isset($data['status'])) {
85
+ $telematic->status = $data['status'];
86
+ }
87
+
88
+ if (isset($data['meta'])) {
89
+ $telematic->meta = array_merge($telematic->meta ?? [], $data['meta']);
90
+ }
91
+
92
+ $telematic->save();
93
+
94
+ return $telematic;
95
+ }
96
+
97
+ /**
98
+ * Delete a telematic integration.
99
+ */
100
+ public function delete(Telematic $telematic): bool
101
+ {
102
+ return $telematic->delete();
103
+ }
104
+
105
+ /**
106
+ * Test connection to a provider.
107
+ *
108
+ * @return array|string
109
+ */
110
+ public function testConnection(Telematic $telematic, bool $async = false)
111
+ {
112
+ if ($async) {
113
+ $job = new TestConnectionJob($telematic);
114
+ dispatch($job);
115
+
116
+ return ['job_id' => $job->getJobId(), 'message' => 'Connection test queued'];
117
+ }
118
+
119
+ $provider = $this->registry->resolve($telematic->provider);
120
+ $provider->connect($telematic);
121
+
122
+ $credentials = json_decode(Crypt::decryptString($telematic->credentials), true);
123
+
124
+ return $provider->testConnection($credentials);
125
+ }
126
+
127
+ /**
128
+ * Discover devices from a provider.
129
+ *
130
+ * @return string Job ID
131
+ */
132
+ public function discoverDevices(Telematic $telematic, array $options = []): string
133
+ {
134
+ $job = new SyncDevicesJob($telematic, $options);
135
+ dispatch($job);
136
+
137
+ return $job->getJobId();
138
+ }
139
+
140
+ /**
141
+ * Link a device to a telematic.
142
+ */
143
+ public function linkDevice(Telematic $telematic, array $deviceData): Device
144
+ {
145
+ $device = Device::firstOrNew([
146
+ 'telematic_uuid' => $telematic->uuid,
147
+ 'external_id' => $deviceData['external_id'],
148
+ ]);
149
+
150
+ $device->device_name = $deviceData['device_name'] ?? 'Unknown Device';
151
+ $device->device_model = $deviceData['device_model'] ?? null;
152
+ $device->device_provider = $telematic->provider;
153
+ $device->status = $deviceData['status'] ?? 'active';
154
+ $device->meta = array_merge($device->meta ?? [], $deviceData['meta'] ?? []);
155
+
156
+ $device->save();
157
+
158
+ return $device;
159
+ }
160
+
161
+ /**
162
+ * Get devices for a telematic.
163
+ *
164
+ * @return \Illuminate\Database\Eloquent\Collection
165
+ */
166
+ public function getDevices(Telematic $telematic, array $filters = [])
167
+ {
168
+ $query = Device::where('telematic_uuid', $telematic->uuid);
169
+
170
+ if (isset($filters['status'])) {
171
+ $query->where('status', $filters['status']);
172
+ }
173
+
174
+ if (isset($filters['search'])) {
175
+ $query->where(function ($q) use ($filters) {
176
+ $q->where('device_name', 'like', "%{$filters['search']}%")
177
+ ->orWhere('external_id', 'like', "%{$filters['search']}%");
178
+ });
179
+ }
180
+
181
+ return $query->get();
182
+ }
183
+
184
+ /**
185
+ * Validate credentials against provider schema.
186
+ *
187
+ * @throws ValidationException
188
+ */
189
+ protected function validateCredentials(array $credentials, array $schema): void
190
+ {
191
+ $rules = [];
192
+
193
+ foreach ($schema as $field) {
194
+ $fieldRules = [];
195
+
196
+ if ($field['required'] ?? true) {
197
+ $fieldRules[] = 'required';
198
+ }
199
+
200
+ if (isset($field['validation'])) {
201
+ $fieldRules[] = $field['validation'];
202
+ } else {
203
+ $fieldRules[] = 'string';
204
+ }
205
+
206
+ $rules[$field['name']] = implode('|', $fieldRules);
207
+ }
208
+
209
+ $validator = Validator::make($credentials, $rules);
210
+
211
+ if ($validator->fails()) {
212
+ throw new ValidationException($validator);
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Get decrypted credentials for a telematic.
218
+ */
219
+ public function getCredentials(Telematic $telematic): array
220
+ {
221
+ return json_decode(Crypt::decryptString($telematic->credentials), true);
222
+ }
223
+ }
@@ -908,7 +908,7 @@ class Utils extends FleetbaseUtils
908
908
 
909
909
  /**
910
910
  * Calculates the great-circle distance between two points, with
911
- * the Vincenty formula. (Using over haversine tdue to antipodal point issues).
911
+ * the Vincenty formula. (Using over haversine due to antipodal point issues).
912
912
  *
913
913
  * https://en.wikipedia.org/wiki/Great-circle_distance#Formulas
914
914
  * https://en.wikipedia.org/wiki/Antipodal_point
@@ -213,6 +213,18 @@ Route::prefix(config('fleetops.api.routing.prefix', null))->namespace('Fleetbase
213
213
  $router->get('organizations', 'OrganizationController@listOrganizations');
214
214
  });
215
215
 
216
+ /*
217
+ |--------------------------------------------------------------------------
218
+ | Webhook Integration Routes
219
+ |--------------------------------------------------------------------------
220
+ |
221
+ | End-user API routes, these are routes used for Webhook integrations.
222
+ */
223
+ $router->group(['prefix' => 'webhooks'], function () use ($router) {
224
+ $router->any('telematics/{providerKey}', 'TelematicWebhookController@handle');
225
+ $router->any('telematics/ingest/{id}', 'TelematicWebhookController@ingest');
226
+ });
227
+
216
228
  /*
217
229
  |--------------------------------------------------------------------------
218
230
  | Internal FleetOps API Routes
@@ -345,6 +357,10 @@ Route::prefix(config('fleetops.api.routing.prefix', null))->namespace('Fleetbase
345
357
  $router->fleetbaseRoutes('proofs');
346
358
  $router->fleetbaseRoutes('purchase-rates');
347
359
  $router->fleetbaseRoutes('routes');
360
+ $router->fleetbaseRoutes('positions', function ($router, $controller) {
361
+ $router->post('replay', $controller('replay'));
362
+ $router->post('metrics', $controller('metrics'));
363
+ });
348
364
  $router->fleetbaseRoutes(
349
365
  'service-areas',
350
366
  function ($router, $controller) {
@@ -399,7 +415,14 @@ Route::prefix(config('fleetops.api.routing.prefix', null))->namespace('Fleetbase
399
415
  $router->fleetbaseRoutes('devices');
400
416
  $router->fleetbaseRoutes('device-events');
401
417
  $router->fleetbaseRoutes('sensors');
402
- $router->fleetbaseRoutes('telematics');
418
+ $router->fleetbaseRoutes('telematics', function ($router, $controller) {
419
+ $router->get('providers', $controller('providers'));
420
+ $router->get('devices', $controller('devices'));
421
+ $router->post('link-device', $controller('linkDevice'));
422
+ $router->post('discover', $controller('discover'));
423
+ $router->post('{id}/test-connection', $controller('testConnection'));
424
+ $router->post('{key}/test-credentials', $controller('testCredentials'));
425
+ });
403
426
  $router->fleetbaseRoutes('work-orders');
404
427
  $router->fleetbaseRoutes('maintenance');
405
428
  $router->fleetbaseRoutes('equipment');