fleetpull 0.1.0__py3-none-any.whl
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.
- fleetpull/__init__.py +36 -0
- fleetpull/api/__init__.py +32 -0
- fleetpull/api/auth_ingress.py +216 -0
- fleetpull/api/catalog.py +139 -0
- fleetpull/api/fetch.py +229 -0
- fleetpull/api/identity.py +90 -0
- fleetpull/api/sync.py +735 -0
- fleetpull/cli.py +146 -0
- fleetpull/config/__init__.py +47 -0
- fleetpull/config/base.py +18 -0
- fleetpull/config/example.py +90 -0
- fleetpull/config/geotab.py +78 -0
- fleetpull/config/http.py +32 -0
- fleetpull/config/loading.py +195 -0
- fleetpull/config/logger.py +139 -0
- fleetpull/config/providers.py +520 -0
- fleetpull/config/rate_limit.py +50 -0
- fleetpull/config/resolution.py +156 -0
- fleetpull/config/retry.py +69 -0
- fleetpull/config/root.py +157 -0
- fleetpull/config/sections.py +146 -0
- fleetpull/endpoints/__init__.py +24 -0
- fleetpull/endpoints/geotab/__init__.py +12 -0
- fleetpull/endpoints/geotab/_requests.py +427 -0
- fleetpull/endpoints/geotab/annotation_logs.py +73 -0
- fleetpull/endpoints/geotab/audits.py +70 -0
- fleetpull/endpoints/geotab/devices.py +79 -0
- fleetpull/endpoints/geotab/driver_changes.py +73 -0
- fleetpull/endpoints/geotab/duty_status_logs.py +75 -0
- fleetpull/endpoints/geotab/dvir_logs.py +76 -0
- fleetpull/endpoints/geotab/exception_events.py +119 -0
- fleetpull/endpoints/geotab/fault_data.py +72 -0
- fleetpull/endpoints/geotab/fill_ups.py +79 -0
- fleetpull/endpoints/geotab/fuel_and_energy_used.py +74 -0
- fleetpull/endpoints/geotab/fuel_tax_details.py +75 -0
- fleetpull/endpoints/geotab/log_records.py +74 -0
- fleetpull/endpoints/geotab/media_files.py +74 -0
- fleetpull/endpoints/geotab/shipment_logs.py +71 -0
- fleetpull/endpoints/geotab/status_data.py +71 -0
- fleetpull/endpoints/geotab/text_messages.py +77 -0
- fleetpull/endpoints/geotab/trips.py +102 -0
- fleetpull/endpoints/geotab/users.py +81 -0
- fleetpull/endpoints/motive/__init__.py +12 -0
- fleetpull/endpoints/motive/_spec_builders.py +94 -0
- fleetpull/endpoints/motive/driver_idle_rollups.py +113 -0
- fleetpull/endpoints/motive/driving_periods.py +85 -0
- fleetpull/endpoints/motive/groups.py +61 -0
- fleetpull/endpoints/motive/idle_events.py +94 -0
- fleetpull/endpoints/motive/users.py +64 -0
- fleetpull/endpoints/motive/vehicle_locations.py +170 -0
- fleetpull/endpoints/motive/vehicle_utilizations.py +122 -0
- fleetpull/endpoints/motive/vehicles.py +106 -0
- fleetpull/endpoints/registry.py +280 -0
- fleetpull/endpoints/samsara/__init__.py +12 -0
- fleetpull/endpoints/samsara/_spec_builders.py +202 -0
- fleetpull/endpoints/samsara/addresses.py +77 -0
- fleetpull/endpoints/samsara/asset_locations.py +217 -0
- fleetpull/endpoints/samsara/driver_fuel_energy_reports.py +124 -0
- fleetpull/endpoints/samsara/driver_vehicle_assignments.py +211 -0
- fleetpull/endpoints/samsara/drivers.py +169 -0
- fleetpull/endpoints/samsara/engine_states.py +114 -0
- fleetpull/endpoints/samsara/gps_readings.py +113 -0
- fleetpull/endpoints/samsara/idling_events.py +195 -0
- fleetpull/endpoints/samsara/odometer_readings.py +116 -0
- fleetpull/endpoints/samsara/trips.py +217 -0
- fleetpull/endpoints/samsara/vehicle_fuel_energy_reports.py +139 -0
- fleetpull/endpoints/samsara/vehicles.py +121 -0
- fleetpull/endpoints/shared/__init__.py +57 -0
- fleetpull/endpoints/shared/base.py +529 -0
- fleetpull/endpoints/shared/request_shape.py +227 -0
- fleetpull/endpoints/shared/resume.py +74 -0
- fleetpull/endpoints/shared/spec_builders.py +59 -0
- fleetpull/endpoints/shared/sync_mode.py +159 -0
- fleetpull/endpoints/shared/url_paths.py +149 -0
- fleetpull/exceptions.py +320 -0
- fleetpull/incremental/__init__.py +27 -0
- fleetpull/incremental/cursor.py +66 -0
- fleetpull/incremental/resolution.py +152 -0
- fleetpull/incremental/seed.py +53 -0
- fleetpull/incremental/window.py +94 -0
- fleetpull/logger/__init__.py +5 -0
- fleetpull/logger/setup.py +145 -0
- fleetpull/model_contract/__init__.py +6 -0
- fleetpull/model_contract/coercions.py +33 -0
- fleetpull/model_contract/response.py +45 -0
- fleetpull/models/__init__.py +7 -0
- fleetpull/models/geotab/__init__.py +166 -0
- fleetpull/models/geotab/annotation_log.py +109 -0
- fleetpull/models/geotab/audit.py +56 -0
- fleetpull/models/geotab/device.py +217 -0
- fleetpull/models/geotab/driver_change.py +102 -0
- fleetpull/models/geotab/duty_status_log.py +200 -0
- fleetpull/models/geotab/dvir_log.py +184 -0
- fleetpull/models/geotab/exception_event.py +135 -0
- fleetpull/models/geotab/fault_data.py +168 -0
- fleetpull/models/geotab/fill_up.py +189 -0
- fleetpull/models/geotab/fuel_and_energy_used.py +81 -0
- fleetpull/models/geotab/fuel_tax_detail.py +150 -0
- fleetpull/models/geotab/log_record.py +68 -0
- fleetpull/models/geotab/media_file.py +133 -0
- fleetpull/models/geotab/shared.py +221 -0
- fleetpull/models/geotab/shipment_log.py +105 -0
- fleetpull/models/geotab/status_data.py +108 -0
- fleetpull/models/geotab/text_message.py +125 -0
- fleetpull/models/geotab/trip.py +162 -0
- fleetpull/models/geotab/user.py +187 -0
- fleetpull/models/motive/__init__.py +43 -0
- fleetpull/models/motive/driver_idle_rollup.py +109 -0
- fleetpull/models/motive/driving_period.py +82 -0
- fleetpull/models/motive/group.py +49 -0
- fleetpull/models/motive/idle_event.py +77 -0
- fleetpull/models/motive/shared.py +192 -0
- fleetpull/models/motive/user.py +217 -0
- fleetpull/models/motive/vehicle.py +162 -0
- fleetpull/models/motive/vehicle_location.py +127 -0
- fleetpull/models/motive/vehicle_utilization.py +128 -0
- fleetpull/models/samsara/__init__.py +108 -0
- fleetpull/models/samsara/address.py +149 -0
- fleetpull/models/samsara/asset_location.py +131 -0
- fleetpull/models/samsara/driver.py +232 -0
- fleetpull/models/samsara/driver_fuel_energy_report.py +153 -0
- fleetpull/models/samsara/driver_vehicle_assignment.py +168 -0
- fleetpull/models/samsara/engine_state.py +92 -0
- fleetpull/models/samsara/gps_reading.py +146 -0
- fleetpull/models/samsara/idling_event.py +174 -0
- fleetpull/models/samsara/odometer_reading.py +90 -0
- fleetpull/models/samsara/trip.py +199 -0
- fleetpull/models/samsara/vehicle.py +167 -0
- fleetpull/models/samsara/vehicle_fuel_energy_report.py +191 -0
- fleetpull/network/__init__.py +9 -0
- fleetpull/network/auth/__init__.py +15 -0
- fleetpull/network/auth/authenticate.py +271 -0
- fleetpull/network/auth/manager.py +223 -0
- fleetpull/network/auth/models.py +57 -0
- fleetpull/network/auth/strategies.py +169 -0
- fleetpull/network/classifiers/__init__.py +11 -0
- fleetpull/network/classifiers/geotab.py +145 -0
- fleetpull/network/classifiers/motive.py +79 -0
- fleetpull/network/classifiers/samsara.py +80 -0
- fleetpull/network/client/__init__.py +25 -0
- fleetpull/network/client/page.py +32 -0
- fleetpull/network/client/profile.py +27 -0
- fleetpull/network/client/registry.py +103 -0
- fleetpull/network/client/registry_base.py +150 -0
- fleetpull/network/client/runtime.py +44 -0
- fleetpull/network/client/transport.py +381 -0
- fleetpull/network/contract/__init__.py +50 -0
- fleetpull/network/contract/auth.py +48 -0
- fleetpull/network/contract/classifier.py +189 -0
- fleetpull/network/contract/envelope_fetcher.py +33 -0
- fleetpull/network/contract/envelopes.py +189 -0
- fleetpull/network/contract/outcome.py +43 -0
- fleetpull/network/contract/page_decoder.py +101 -0
- fleetpull/network/contract/request.py +97 -0
- fleetpull/network/decoders/__init__.py +40 -0
- fleetpull/network/decoders/_window_stamp.py +78 -0
- fleetpull/network/decoders/geotab.py +309 -0
- fleetpull/network/decoders/motive.py +204 -0
- fleetpull/network/decoders/motive_reports.py +114 -0
- fleetpull/network/decoders/samsara.py +343 -0
- fleetpull/network/decoders/samsara_reports.py +119 -0
- fleetpull/network/decoders/single_page.py +55 -0
- fleetpull/network/limits/__init__.py +13 -0
- fleetpull/network/limits/bucket_math.py +58 -0
- fleetpull/network/limits/limiter.py +153 -0
- fleetpull/network/limits/registry.py +96 -0
- fleetpull/network/posture/__init__.py +6 -0
- fleetpull/network/posture/client_options.py +96 -0
- fleetpull/network/retry/__init__.py +13 -0
- fleetpull/network/retry/decision.py +155 -0
- fleetpull/network/tls/__init__.py +5 -0
- fleetpull/network/tls/truststore_context.py +35 -0
- fleetpull/orchestrator/__init__.py +30 -0
- fleetpull/orchestrator/backfill.py +127 -0
- fleetpull/orchestrator/batch.py +155 -0
- fleetpull/orchestrator/bisection.py +271 -0
- fleetpull/orchestrator/drivers.py +350 -0
- fleetpull/orchestrator/entry.py +347 -0
- fleetpull/orchestrator/executors.py +128 -0
- fleetpull/orchestrator/fanout.py +163 -0
- fleetpull/orchestrator/feed_drive.py +266 -0
- fleetpull/orchestrator/metadata_projection.py +195 -0
- fleetpull/orchestrator/outcome.py +53 -0
- fleetpull/orchestrator/recording.py +85 -0
- fleetpull/orchestrator/resume.py +143 -0
- fleetpull/orchestrator/roster_harvest.py +108 -0
- fleetpull/orchestrator/roster_refresh.py +363 -0
- fleetpull/orchestrator/runner.py +262 -0
- fleetpull/orchestrator/shape_resolution.py +221 -0
- fleetpull/orchestrator/spine.py +166 -0
- fleetpull/orchestrator/streaming.py +153 -0
- fleetpull/orchestrator/unit_loop.py +273 -0
- fleetpull/orchestrator/watermark_drive.py +455 -0
- fleetpull/paths/__init__.py +13 -0
- fleetpull/paths/datasets.py +38 -0
- fleetpull/paths/partitions.py +41 -0
- fleetpull/paths/resolution.py +98 -0
- fleetpull/polars_typing/__init__.py +20 -0
- fleetpull/py.typed +0 -0
- fleetpull/records/__init__.py +19 -0
- fleetpull/records/convert.py +53 -0
- fleetpull/records/dataframe.py +63 -0
- fleetpull/records/event_time.py +70 -0
- fleetpull/records/fields.py +190 -0
- fleetpull/records/flatten.py +54 -0
- fleetpull/records/roster_members.py +74 -0
- fleetpull/records/schema.py +73 -0
- fleetpull/records/validation.py +62 -0
- fleetpull/resources/__init__.py +10 -0
- fleetpull/resources/config.example.yaml +222 -0
- fleetpull/roster/__init__.py +16 -0
- fleetpull/roster/definition.py +46 -0
- fleetpull/roster/key.py +40 -0
- fleetpull/roster/registry.py +93 -0
- fleetpull/state/__init__.py +36 -0
- fleetpull/state/cursors.py +417 -0
- fleetpull/state/database.py +463 -0
- fleetpull/state/migrations.py +430 -0
- fleetpull/state/reconcile.py +127 -0
- fleetpull/state/rosters.py +159 -0
- fleetpull/state/run_ledger.py +572 -0
- fleetpull/state/work_units.py +648 -0
- fleetpull/storage/__init__.py +30 -0
- fleetpull/storage/append.py +187 -0
- fleetpull/storage/atomic.py +161 -0
- fleetpull/storage/files.py +176 -0
- fleetpull/storage/frames.py +98 -0
- fleetpull/storage/metadata.py +161 -0
- fleetpull/storage/partitioned.py +205 -0
- fleetpull/storage/pruning.py +169 -0
- fleetpull/storage/read.py +35 -0
- fleetpull/storage/result.py +34 -0
- fleetpull/storage/single_file.py +130 -0
- fleetpull/storage/splitting.py +88 -0
- fleetpull/storage/staging.py +178 -0
- fleetpull/storage/writers.py +145 -0
- fleetpull/timing/__init__.py +21 -0
- fleetpull/timing/canon.py +92 -0
- fleetpull/timing/clock.py +192 -0
- fleetpull/timing/codec.py +122 -0
- fleetpull/timing/sleeper.py +65 -0
- fleetpull/vocabulary/__init__.py +16 -0
- fleetpull/vocabulary/json_types.py +20 -0
- fleetpull/vocabulary/provider.py +29 -0
- fleetpull/vocabulary/quota_scope.py +47 -0
- fleetpull/vocabulary/response_category.py +32 -0
- fleetpull-0.1.0.dist-info/METADATA +248 -0
- fleetpull-0.1.0.dist-info/RECORD +252 -0
- fleetpull-0.1.0.dist-info/WHEEL +5 -0
- fleetpull-0.1.0.dist-info/entry_points.txt +2 -0
- fleetpull-0.1.0.dist-info/licenses/LICENSE +202 -0
- fleetpull-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# src/fleetpull/models/samsara/idling_event.py
|
|
2
|
+
"""Samsara IdlingEvent response model (``GET /idling/events``).
|
|
3
|
+
|
|
4
|
+
Written from captured live responses (2026-07-20 probe session: a
|
|
5
|
+
2,200-event census walked over 11 cursor pages), never from docs. The
|
|
6
|
+
census observed ZERO real nulls -- optionality is absence-shaped
|
|
7
|
+
(Samsara omits keys, the vehicles/drivers posture): every key below
|
|
8
|
+
except the three partial blocks was present on every record, so those
|
|
9
|
+
fields are required; ``operator`` (1546/2200 -- driver attribution when
|
|
10
|
+
known), ``airTemperatureMillicelsius`` (1833/2200), and ``address``
|
|
11
|
+
(552/2200) are optional. Within a carrying ``address`` block,
|
|
12
|
+
``addressTypes`` is itself absent on ~31 of the 552 blocks, so it is
|
|
13
|
+
optional inside the block too.
|
|
14
|
+
|
|
15
|
+
There is NO end key: the interval is start plus duration. Events were
|
|
16
|
+
only ever observed complete, with implied ends in the past even in a
|
|
17
|
+
last-30-minutes probe -- in-progress idles appear to materialize on
|
|
18
|
+
completion; the watermark lookback absorbs late materialization
|
|
19
|
+
(accepted residual, DESIGN §8). ``durationMilliseconds`` stays a
|
|
20
|
+
verbatim unit-suffixed int mirror -- no timedelta recovery: the value
|
|
21
|
+
is directly consumable, and recovery would presume a use.
|
|
22
|
+
|
|
23
|
+
``startTime`` is an RFC3339 string recovered as a tz-aware UTC datetime
|
|
24
|
+
by Pydantic's standard parse (the vehicles/drivers pattern, NOT the
|
|
25
|
+
trips epoch-ms path). ``ptoState`` is a plain ``str``, NOT an enum:
|
|
26
|
+
only ``'inactive'`` was observed in 2,200 records, but the value set is
|
|
27
|
+
not closed by evidence (unlike ``driverActivationStatus``'s 400-proven
|
|
28
|
+
closure), so membership is not enforced. ``fuelConsumedMilliliters`` is
|
|
29
|
+
MIXED int|float on the wire -- modeled ``float``, lax coercion lifting
|
|
30
|
+
the int shape. The two money blocks mirror verbatim as strings
|
|
31
|
+
(``{amount, currency}``) -- never parsed to a numeric type. NOTE the id
|
|
32
|
+
type split, mirrored exactly: ``address.id`` is a STRING while
|
|
33
|
+
``asset.id`` and ``operator.id`` are BARE INTs.
|
|
34
|
+
|
|
35
|
+
Wire keys are camelCase; fields are snake_case via the ``to_camel``
|
|
36
|
+
alias generator.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
from datetime import datetime
|
|
40
|
+
|
|
41
|
+
from pydantic import ConfigDict
|
|
42
|
+
from pydantic.alias_generators import to_camel
|
|
43
|
+
|
|
44
|
+
from fleetpull.model_contract import ResponseModel
|
|
45
|
+
|
|
46
|
+
__all__: list[str] = [
|
|
47
|
+
'AssetRef',
|
|
48
|
+
'FuelCost',
|
|
49
|
+
'IdlingAddress',
|
|
50
|
+
'IdlingEvent',
|
|
51
|
+
'OperatorRef',
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class AssetRef(ResponseModel):
|
|
56
|
+
"""The ``asset`` block: the event's vehicle reference.
|
|
57
|
+
|
|
58
|
+
Present on every captured record -- events are fleet-wide with
|
|
59
|
+
per-record asset attribution, which is what makes the endpoint a
|
|
60
|
+
single fetch with no fan-out.
|
|
61
|
+
|
|
62
|
+
Attributes:
|
|
63
|
+
id: Samsara's asset id -- a BARE int on the wire, unlike the
|
|
64
|
+
numeric-string ids of the vehicles/drivers surfaces.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
id: int
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class OperatorRef(ResponseModel):
|
|
71
|
+
"""The ``operator`` block: driver attribution when known (1546/2200).
|
|
72
|
+
|
|
73
|
+
Attributes:
|
|
74
|
+
id: Samsara's operator id -- a BARE int on the wire, like
|
|
75
|
+
``asset.id``.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
id: int
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class IdlingAddress(ResponseModel):
|
|
82
|
+
"""The ``address`` block: a matched defined-address reference (552/2200).
|
|
83
|
+
|
|
84
|
+
Attributes:
|
|
85
|
+
id: Samsara's address id -- a STRING on the wire, unlike the
|
|
86
|
+
bare-int ``asset.id``/``operator.id`` beside it (mirrored
|
|
87
|
+
exactly; never coerced to match its siblings).
|
|
88
|
+
address_types: The defined address's type tags (element
|
|
89
|
+
``'yard'`` observed); absent on ~31 of the 552 captured
|
|
90
|
+
address blocks, so optional within the block.
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
model_config = ConfigDict(alias_generator=to_camel)
|
|
94
|
+
|
|
95
|
+
id: str
|
|
96
|
+
address_types: list[str] | None = None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class FuelCost(ResponseModel):
|
|
100
|
+
"""A money block: the ``{amount, currency}`` shape.
|
|
101
|
+
|
|
102
|
+
The one shape both cost fields (``fuelCost``, ``gaseousFuelCost``)
|
|
103
|
+
carry, both keys present in every captured block. Mirrored verbatim
|
|
104
|
+
as strings -- parsing money would presume a use.
|
|
105
|
+
|
|
106
|
+
Attributes:
|
|
107
|
+
amount: The monetary amount, a decimal string.
|
|
108
|
+
currency: The currency code string.
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
amount: str
|
|
112
|
+
currency: str
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class IdlingEvent(ResponseModel):
|
|
116
|
+
"""One Samsara idling event, start-anchored per window.
|
|
117
|
+
|
|
118
|
+
A pure mirror of the captured fields (module docstring). Field
|
|
119
|
+
semantics and units are Samsara's; no value is derived or
|
|
120
|
+
interpreted here. The interval is ``start_time`` plus
|
|
121
|
+
``duration_milliseconds`` -- there is no end key on the wire.
|
|
122
|
+
|
|
123
|
+
Attributes:
|
|
124
|
+
event_uuid: The event's id, a UUID string.
|
|
125
|
+
start_time: Idle start (RFC3339, recovered tz-aware UTC) -- the
|
|
126
|
+
endpoint's event time; retrieval is START-anchored on UTC
|
|
127
|
+
(DESIGN §8), so retrieval and routing coincide natively.
|
|
128
|
+
duration_milliseconds: Idle duration in milliseconds, a bare
|
|
129
|
+
int mirrored verbatim (no end key exists; module
|
|
130
|
+
docstring).
|
|
131
|
+
asset: The vehicle reference (bare-int id).
|
|
132
|
+
latitude: Event latitude, decimal degrees.
|
|
133
|
+
longitude: Event longitude, decimal degrees.
|
|
134
|
+
pto_state: Power-take-off state -- a plain string; only
|
|
135
|
+
``'inactive'`` observed in 2,200 records, but the value set
|
|
136
|
+
is not evidence-closed, so no enum (module docstring).
|
|
137
|
+
fuel_consumed_milliliters: Fuel consumed while idling, in
|
|
138
|
+
milliliters -- MIXED int|float on the wire, modeled float.
|
|
139
|
+
fuel_cost: The fuel cost money block, strings verbatim.
|
|
140
|
+
gaseous_fuel_consumed_grams: Gaseous fuel consumed, in grams, a
|
|
141
|
+
bare int.
|
|
142
|
+
gaseous_fuel_cost: The gaseous-fuel cost money block, strings
|
|
143
|
+
verbatim.
|
|
144
|
+
operator: Driver attribution when known (1546/2200; bare-int
|
|
145
|
+
id).
|
|
146
|
+
air_temperature_millicelsius: Ambient air temperature in
|
|
147
|
+
millidegrees Celsius, a bare int (1833/2200).
|
|
148
|
+
address: The matched defined-address block (552/2200; STRING
|
|
149
|
+
id).
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
model_config = ConfigDict(alias_generator=to_camel)
|
|
153
|
+
|
|
154
|
+
# Identity and the start+duration interval (no end key).
|
|
155
|
+
event_uuid: str
|
|
156
|
+
start_time: datetime
|
|
157
|
+
duration_milliseconds: int
|
|
158
|
+
|
|
159
|
+
# Attribution and position.
|
|
160
|
+
asset: AssetRef
|
|
161
|
+
latitude: float
|
|
162
|
+
longitude: float
|
|
163
|
+
|
|
164
|
+
# State and consumption (provider units mirrored verbatim).
|
|
165
|
+
pto_state: str
|
|
166
|
+
fuel_consumed_milliliters: float
|
|
167
|
+
fuel_cost: FuelCost
|
|
168
|
+
gaseous_fuel_consumed_grams: int
|
|
169
|
+
gaseous_fuel_cost: FuelCost
|
|
170
|
+
|
|
171
|
+
# The partial blocks (absence-shaped).
|
|
172
|
+
operator: OperatorRef | None = None
|
|
173
|
+
air_temperature_millicelsius: int | None = None
|
|
174
|
+
address: IdlingAddress | None = None
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# src/fleetpull/models/samsara/odometer_reading.py
|
|
2
|
+
"""Samsara OdometerReading response model
|
|
3
|
+
(``GET /fleet/vehicles/stats/history``, ``types=obdOdometerMeters``,
|
|
4
|
+
post-decoder reading grain).
|
|
5
|
+
|
|
6
|
+
Written from captured live responses (2026-07-20 probe session: a
|
|
7
|
+
9,480-reading census over a 24-hour window, 135 vehicles -- every
|
|
8
|
+
vehicle returned per the requested type carried data; no empty-array
|
|
9
|
+
padding was observed), never from docs. The model mirrors the FLAT
|
|
10
|
+
record ``SamsaraVehicleSeriesPageDecoder`` emits, one row per reading
|
|
11
|
+
-- the grain the records pipeline represents (scalars, not
|
|
12
|
+
list-of-objects; DESIGN section 9) -- and its two field families have
|
|
13
|
+
different provenance:
|
|
14
|
+
|
|
15
|
+
- ``vehicle_id`` / ``vehicle_name`` / ``vehicle_serial`` /
|
|
16
|
+
``vehicle_vin`` are DECODER-SYNTHESIZED: the unnesting lifts them
|
|
17
|
+
from the per-vehicle ``id``/``name`` keys and the ``externalIds``
|
|
18
|
+
object's literal dotted ``samsara.serial``/``samsara.vin`` wire keys
|
|
19
|
+
onto every reading. ``vehicle_id`` and ``vehicle_name`` were 74/74
|
|
20
|
+
on the censused mixed-type page and are required; ``vehicle_serial``
|
|
21
|
+
and ``vehicle_vin`` were also 74/74 there, but one page is not a
|
|
22
|
+
whole-population oath (an unplugged or serial-less unit could omit
|
|
23
|
+
its ``externalIds`` block -- the vehicles surface shows exactly that
|
|
24
|
+
variance), so both stay OPTIONAL -- the drivers conservative posture.
|
|
25
|
+
- ``time`` / ``value`` are WIRE-VERBATIM reading keys: the series
|
|
26
|
+
census observed exactly ``{time, value}`` on every one of the 9,480
|
|
27
|
+
readings, so both are required.
|
|
28
|
+
|
|
29
|
+
``value`` is the OBD odometer in METERS -- a bare int on every observed
|
|
30
|
+
reading (range 3,552,000..1,012,456,215 in census), mirrored verbatim
|
|
31
|
+
under the model's unitless wire name; the unit lives here and in the
|
|
32
|
+
series' stat-type name (``obdOdometerMeters``), never converted.
|
|
33
|
+
|
|
34
|
+
``time`` is an RFC3339 string recovered as a tz-aware UTC datetime by
|
|
35
|
+
Pydantic's standard parse. Readings fall strictly inside the requested
|
|
36
|
+
``[startTime, endTime)`` window (probe-proven), so ``time`` is the
|
|
37
|
+
endpoint's event-time column with retrieval and routing coinciding
|
|
38
|
+
natively.
|
|
39
|
+
|
|
40
|
+
Wire keys are camelCase; fields are snake_case via the ``to_camel``
|
|
41
|
+
alias generator.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
from datetime import datetime
|
|
45
|
+
|
|
46
|
+
from pydantic import ConfigDict
|
|
47
|
+
from pydantic.alias_generators import to_camel
|
|
48
|
+
|
|
49
|
+
from fleetpull.model_contract import ResponseModel
|
|
50
|
+
|
|
51
|
+
__all__: list[str] = ['OdometerReading']
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class OdometerReading(ResponseModel):
|
|
55
|
+
"""One OBD odometer reading of one vehicle, at the reading grain.
|
|
56
|
+
|
|
57
|
+
A pure mirror of the flat post-decoder record (module docstring:
|
|
58
|
+
the identity fields are decoder-synthesized, the reading fields
|
|
59
|
+
wire-verbatim). Field semantics and units are Samsara's; no value
|
|
60
|
+
is derived or interpreted here.
|
|
61
|
+
|
|
62
|
+
Attributes:
|
|
63
|
+
vehicle_id: The vehicle's Samsara id -- a numeric string,
|
|
64
|
+
decoder-synthesized from the vehicle record's ``id``.
|
|
65
|
+
vehicle_name: The vehicle's display name, decoder-synthesized
|
|
66
|
+
from the vehicle record's ``name``.
|
|
67
|
+
vehicle_serial: The gateway serial, decoder-synthesized from
|
|
68
|
+
``externalIds['samsara.serial']`` (74/74 on the censused
|
|
69
|
+
page; optional -- module docstring).
|
|
70
|
+
vehicle_vin: The VIN, decoder-synthesized from
|
|
71
|
+
``externalIds['samsara.vin']`` (74/74 on the censused page;
|
|
72
|
+
optional -- module docstring).
|
|
73
|
+
time: The reading instant (RFC3339, recovered tz-aware UTC) --
|
|
74
|
+
the event-time column; readings fall strictly inside the
|
|
75
|
+
requested window.
|
|
76
|
+
value: The OBD odometer in meters -- a bare int on every
|
|
77
|
+
observed reading, mirrored verbatim (module docstring).
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
model_config = ConfigDict(alias_generator=to_camel)
|
|
81
|
+
|
|
82
|
+
# Decoder-synthesized vehicle identity.
|
|
83
|
+
vehicle_id: str
|
|
84
|
+
vehicle_name: str
|
|
85
|
+
vehicle_serial: str | None = None
|
|
86
|
+
vehicle_vin: str | None = None
|
|
87
|
+
|
|
88
|
+
# Wire-verbatim reading payload.
|
|
89
|
+
time: datetime
|
|
90
|
+
value: int
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# src/fleetpull/models/samsara/trip.py
|
|
2
|
+
"""Samsara Trip response model (``GET /v1/fleet/trips`` -- the legacy v1
|
|
3
|
+
surface; every modern candidate path 404s).
|
|
4
|
+
|
|
5
|
+
Written from captured live responses (2026-07-20 probe session: a
|
|
6
|
+
725-trip census across 60 vehicles), never from docs. The census
|
|
7
|
+
observed ZERO nulls anywhere: every key below except the two address
|
|
8
|
+
blocks was present on every record, so those fields are required; the
|
|
9
|
+
address blocks (``startAddress`` 177/725, ``endAddress`` 185/725) are
|
|
10
|
+
present only when the trip endpoint matched a defined address/geofence
|
|
11
|
+
and are optional.
|
|
12
|
+
|
|
13
|
+
``startMs``/``endMs`` are epoch-MILLISECOND ints on the wire. The
|
|
14
|
+
mirror RECOVERS them as tz-aware UTC datetimes via a mode='before'
|
|
15
|
+
validator -- type recovery is structural and belongs on the mirror (the
|
|
16
|
+
``GeotabTimeSpan`` precedent), not interpretation. The field names
|
|
17
|
+
(``start_time``/``end_time``) drop the wire's unit suffix because the
|
|
18
|
+
recovered type carries the unit; naming is the model's to own. ``endMs``
|
|
19
|
+
was present on every observed trip including the two most recent --
|
|
20
|
+
in-progress trips were never observed and appear to materialize on
|
|
21
|
+
completion; the watermark lookback absorbs late materialization
|
|
22
|
+
(accepted residual, DESIGN §8), so ``end_time`` stays required and a
|
|
23
|
+
trip ever arriving without it fails validation loudly.
|
|
24
|
+
|
|
25
|
+
Everything else mirrors verbatim: the unit-suffixed int family
|
|
26
|
+
(``distanceMeters``, ``fuelConsumedMl``, ``tollMeters``) and the
|
|
27
|
+
odometer pair keep provider units untouched; ``driverId`` is an int
|
|
28
|
+
whose ``0`` is the UNASSIGNED sentinel (110/725) -- mirrored verbatim,
|
|
29
|
+
never nulled or interpreted. ``assetIds``/``codriverIds`` were observed
|
|
30
|
+
ONLY EMPTY across all 725 records; they are typed ``list[int]`` --
|
|
31
|
+
the int-id family, in the ``list[scalar]`` form the records layer's
|
|
32
|
+
schema derivation represents (DESIGN §9; the §9 pipeline has no tuple
|
|
33
|
+
form) -- so a first non-empty capture lands as data, not a crash.
|
|
34
|
+
|
|
35
|
+
The record does NOT echo the requested ``vehicleId``: per-vehicle
|
|
36
|
+
attribution lives in the request parameter (the roster fan-out member),
|
|
37
|
+
not on the wire record. The response *wrapper* (the ``{"trips": [...]}``
|
|
38
|
+
envelope, unpaginated) is the endpoints layer's decoder concern; this
|
|
39
|
+
module mirrors only the inner per-trip object.
|
|
40
|
+
|
|
41
|
+
Wire keys are camelCase; fields are snake_case via the ``to_camel``
|
|
42
|
+
alias generator (``startMs``/``endMs`` carry explicit aliases, since
|
|
43
|
+
their field names diverge by design).
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
from datetime import UTC, datetime, timedelta
|
|
47
|
+
from typing import Annotated, Final
|
|
48
|
+
|
|
49
|
+
from pydantic import BeforeValidator, ConfigDict, Field
|
|
50
|
+
from pydantic.alias_generators import to_camel
|
|
51
|
+
|
|
52
|
+
from fleetpull.model_contract import ResponseModel
|
|
53
|
+
from fleetpull.vocabulary import JsonValue
|
|
54
|
+
|
|
55
|
+
__all__: list[str] = [
|
|
56
|
+
'Trip',
|
|
57
|
+
'TripAddress',
|
|
58
|
+
'TripCoordinates',
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
_EPOCH_START: Final[datetime] = datetime(1970, 1, 1, tzinfo=UTC)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _epoch_milliseconds_to_datetime(value: JsonValue | datetime) -> datetime:
|
|
65
|
+
"""The epoch-ms ingress: recover a tz-aware UTC datetime from a wire int.
|
|
66
|
+
|
|
67
|
+
Strict by design: the census observed BARE ints only, so anything
|
|
68
|
+
else -- a quoted number, a float, a bool -- is a wire drift that
|
|
69
|
+
should fail validation loudly, not pass mangled. Exact integer
|
|
70
|
+
arithmetic (no float epoch math), so millisecond precision is
|
|
71
|
+
preserved verbatim.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
value: The raw wire value, or an already-recovered datetime on a
|
|
75
|
+
Pydantic revalidation path.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
The instant as a timezone-aware UTC datetime (passthrough for an
|
|
79
|
+
already-recovered value).
|
|
80
|
+
|
|
81
|
+
Raises:
|
|
82
|
+
ValueError: ``value`` is not a bare int (bools excluded).
|
|
83
|
+
"""
|
|
84
|
+
if isinstance(value, datetime):
|
|
85
|
+
return value
|
|
86
|
+
if isinstance(value, bool) or not isinstance(value, int):
|
|
87
|
+
raise ValueError(
|
|
88
|
+
f'expected a bare epoch-millisecond int, got {type(value).__name__}'
|
|
89
|
+
)
|
|
90
|
+
return _EPOCH_START + timedelta(milliseconds=value)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# The epoch-ms datetime field type. Plain assignment, not a `type`
|
|
94
|
+
# statement: Pydantic must evaluate the Annotated form eagerly so the
|
|
95
|
+
# metadata lifts into FieldInfo and the records field walk sees the bare
|
|
96
|
+
# `datetime` leaf (the GeotabTimeSpan stance).
|
|
97
|
+
_EpochMillisecondsDatetime = Annotated[
|
|
98
|
+
datetime, BeforeValidator(_epoch_milliseconds_to_datetime)
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class TripCoordinates(ResponseModel):
|
|
103
|
+
"""A ``{latitude, longitude}`` coordinate pair, decimal-degree floats.
|
|
104
|
+
|
|
105
|
+
The one shape both trip endpoints' coordinate blocks
|
|
106
|
+
(``startCoordinates``, ``endCoordinates``) carry -- both keys
|
|
107
|
+
present in every captured block.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
latitude: float
|
|
111
|
+
longitude: float
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class TripAddress(ResponseModel):
|
|
115
|
+
"""A matched address/geofence block: the ``{address, id, name}`` shape.
|
|
116
|
+
|
|
117
|
+
Present only when a trip endpoint matched a defined address/geofence
|
|
118
|
+
(``startAddress`` 177/725, ``endAddress`` 185/725); all three keys
|
|
119
|
+
present in every carrying block.
|
|
120
|
+
|
|
121
|
+
Attributes:
|
|
122
|
+
address: The defined address's street address string.
|
|
123
|
+
id: Samsara's address id -- a BARE int on the wire, unlike the
|
|
124
|
+
string ids of the vehicles/drivers surfaces.
|
|
125
|
+
name: The defined address's display name.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
address: str
|
|
129
|
+
id: int
|
|
130
|
+
name: str
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class Trip(ResponseModel):
|
|
134
|
+
"""One Samsara vehicle trip, overlap-retrieved per (vehicle, window).
|
|
135
|
+
|
|
136
|
+
A pure mirror of the captured fields (module docstring). Field
|
|
137
|
+
semantics and units are Samsara's; no value is derived or
|
|
138
|
+
interpreted here. The requested ``vehicleId`` is not echoed on the
|
|
139
|
+
record.
|
|
140
|
+
|
|
141
|
+
Attributes:
|
|
142
|
+
start_time: Trip start, recovered from the wire's epoch-ms
|
|
143
|
+
``startMs`` -- the endpoint's event time (start-anchored
|
|
144
|
+
ownership, DESIGN §4).
|
|
145
|
+
end_time: Trip end, recovered from the wire's epoch-ms
|
|
146
|
+
``endMs``; present on every observed trip (in-progress trips
|
|
147
|
+
appear to materialize on completion).
|
|
148
|
+
distance_meters: Trip distance in meters, a bare int.
|
|
149
|
+
fuel_consumed_ml: Fuel consumed in milliliters, a bare int.
|
|
150
|
+
toll_meters: Distance on toll roads in meters, a bare int.
|
|
151
|
+
start_odometer: Odometer at trip start -- provider units
|
|
152
|
+
mirrored verbatim, a bare int.
|
|
153
|
+
end_odometer: Odometer at trip end -- provider units mirrored
|
|
154
|
+
verbatim, a bare int.
|
|
155
|
+
driver_id: The assigned driver's id; ``0`` is the UNASSIGNED
|
|
156
|
+
sentinel (110/725), mirrored verbatim.
|
|
157
|
+
start_location: Reverse-geocoded start location string.
|
|
158
|
+
end_location: Reverse-geocoded end location string.
|
|
159
|
+
start_coordinates: The start ``{latitude, longitude}`` block.
|
|
160
|
+
end_coordinates: The end ``{latitude, longitude}`` block.
|
|
161
|
+
asset_ids: Attached asset ids; observed ONLY EMPTY across all
|
|
162
|
+
725 census records.
|
|
163
|
+
codriver_ids: Co-driver ids; observed ONLY EMPTY across all 725
|
|
164
|
+
census records.
|
|
165
|
+
start_address: The matched start address/geofence block
|
|
166
|
+
(177/725); null when no defined address matched.
|
|
167
|
+
end_address: The matched end address/geofence block (185/725);
|
|
168
|
+
null when no defined address matched.
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
model_config = ConfigDict(alias_generator=to_camel)
|
|
172
|
+
|
|
173
|
+
# The trip interval (epoch-ms recovered, module docstring).
|
|
174
|
+
start_time: _EpochMillisecondsDatetime = Field(alias='startMs')
|
|
175
|
+
end_time: _EpochMillisecondsDatetime = Field(alias='endMs')
|
|
176
|
+
|
|
177
|
+
# Distances, fuel, and odometers (provider units, bare ints).
|
|
178
|
+
distance_meters: int
|
|
179
|
+
fuel_consumed_ml: int
|
|
180
|
+
toll_meters: int
|
|
181
|
+
start_odometer: int
|
|
182
|
+
end_odometer: int
|
|
183
|
+
|
|
184
|
+
# Assignment (0 = unassigned, mirrored verbatim).
|
|
185
|
+
driver_id: int
|
|
186
|
+
|
|
187
|
+
# Endpoints: geocoded strings and coordinate blocks.
|
|
188
|
+
start_location: str
|
|
189
|
+
end_location: str
|
|
190
|
+
start_coordinates: TripCoordinates
|
|
191
|
+
end_coordinates: TripCoordinates
|
|
192
|
+
|
|
193
|
+
# The int-id lists (only empties observed).
|
|
194
|
+
asset_ids: list[int]
|
|
195
|
+
codriver_ids: list[int]
|
|
196
|
+
|
|
197
|
+
# The partial address/geofence matches.
|
|
198
|
+
start_address: TripAddress | None = None
|
|
199
|
+
end_address: TripAddress | None = None
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# src/fleetpull/models/samsara/vehicle.py
|
|
2
|
+
"""Samsara Vehicle response model (``GET /fleet/vehicles``).
|
|
3
|
+
|
|
4
|
+
Written from captured live responses (2026-07-17 probe session: a
|
|
5
|
+
full-population sweep of all 608 records plus six captured record
|
|
6
|
+
variants), never from docs. The sweep observed no null value and no
|
|
7
|
+
type variance on any of the 20 observed keys -- Samsara omits absent
|
|
8
|
+
keys rather than nulling them (the GeoTab posture), while also using
|
|
9
|
+
empty strings (``notes: ""`` on every captured record), which mirror
|
|
10
|
+
verbatim and normalize to null at the DataFrame boundary. Fields the
|
|
11
|
+
sweep proved on every record are required; the partial-presence fields
|
|
12
|
+
are optional (presence counts in the attribute docs, out of 608). The
|
|
13
|
+
minimal captured shape is the bare 7-key form -- units with no gateway
|
|
14
|
+
carry serial-shaped default names.
|
|
15
|
+
|
|
16
|
+
``year`` arrives as a quoted integer (``"2013"``); lax coercion types
|
|
17
|
+
it. No empty-string ``year`` was observed in 608 records -- if one ever
|
|
18
|
+
arrives, the loud ``int_parsing`` failure names it (the Motive ``year``
|
|
19
|
+
history, replayed on purpose).
|
|
20
|
+
|
|
21
|
+
Excluded fields (``extra='ignore'`` makes exclusion exactly "don't
|
|
22
|
+
model it"):
|
|
23
|
+
|
|
24
|
+
- ``tags`` -- a list of ``{id, name, parentTagId}`` objects (549/608,
|
|
25
|
+
``parentTagId`` itself partial within elements); the records layer's
|
|
26
|
+
schema derivation supports scalars, enums, ``list[scalar]``, and
|
|
27
|
+
nested models only (the Device/User exclusion precedent) -- modeled
|
|
28
|
+
when the list-of-structs derivation vertical lands.
|
|
29
|
+
|
|
30
|
+
``externalIds`` is an OPEN, user-definable map whose keys are dotted
|
|
31
|
+
namespace names -- not a struct. The two Samsara-managed keys observed
|
|
32
|
+
in every carrying record are modeled as aliased fields
|
|
33
|
+
(``samsara.serial``, ``samsara.vin``, each mirroring its top-level
|
|
34
|
+
sibling exactly in capture); user-defined keys are absorbed by
|
|
35
|
+
``extra='ignore'`` until a capture shows one, per the union-of-observed
|
|
36
|
+
discipline.
|
|
37
|
+
|
|
38
|
+
Wire keys are camelCase; fields are snake_case via the ``to_camel``
|
|
39
|
+
alias generator (the dotted ``externalIds`` keys carry explicit
|
|
40
|
+
aliases, above the generator's reach).
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
from datetime import datetime
|
|
44
|
+
|
|
45
|
+
from pydantic import ConfigDict, Field
|
|
46
|
+
from pydantic.alias_generators import to_camel
|
|
47
|
+
|
|
48
|
+
from fleetpull.model_contract import ResponseModel
|
|
49
|
+
|
|
50
|
+
__all__: list[str] = [
|
|
51
|
+
'Vehicle',
|
|
52
|
+
'VehicleExternalIds',
|
|
53
|
+
'VehicleGatewayRef',
|
|
54
|
+
'VehicleStaticAssignedDriverRef',
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class VehicleExternalIds(ResponseModel):
|
|
59
|
+
"""The ``externalIds`` block: an open map of namespaced external ids.
|
|
60
|
+
|
|
61
|
+
Only the two Samsara-managed keys are modeled (see the module
|
|
62
|
+
docstring's open-map caveat). Each key is independently optional --
|
|
63
|
+
the sweep observed carriers with a serial and no VIN (595 blocks:
|
|
64
|
+
``samsara.serial`` on 509, ``samsara.vin`` on 576).
|
|
65
|
+
|
|
66
|
+
Attributes:
|
|
67
|
+
samsara_serial: The gateway serial, undashed (wire key
|
|
68
|
+
``samsara.serial``; equal to the record's top-level
|
|
69
|
+
``serial`` in every capture).
|
|
70
|
+
samsara_vin: The VIN (wire key ``samsara.vin``; equal to the
|
|
71
|
+
record's top-level ``vin`` in every capture).
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
samsara_serial: str | None = Field(default=None, alias='samsara.serial')
|
|
75
|
+
samsara_vin: str | None = Field(default=None, alias='samsara.vin')
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class VehicleGatewayRef(ResponseModel):
|
|
79
|
+
"""The installed telematics gateway: serial and hardware model.
|
|
80
|
+
|
|
81
|
+
Attributes:
|
|
82
|
+
serial: The gateway serial in Samsara's dashed 4-3-3 rendering
|
|
83
|
+
(the record's top-level ``serial`` carries the undashed
|
|
84
|
+
form of the same value in every capture).
|
|
85
|
+
model: The gateway hardware model (``"VG34"`` captured).
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
serial: str
|
|
89
|
+
model: str
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class VehicleStaticAssignedDriverRef(ResponseModel):
|
|
93
|
+
"""The statically assigned driver reference: id and display name."""
|
|
94
|
+
|
|
95
|
+
id: str
|
|
96
|
+
name: str
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class Vehicle(ResponseModel):
|
|
100
|
+
"""One Samsara fleet vehicle.
|
|
101
|
+
|
|
102
|
+
A pure mirror of the captured fields (``tags`` excluded, module
|
|
103
|
+
docstring). Field semantics are Samsara's; no value is derived or
|
|
104
|
+
interpreted here.
|
|
105
|
+
|
|
106
|
+
Attributes:
|
|
107
|
+
id: Samsara's vehicle id -- a numeric string, mirrored as
|
|
108
|
+
string.
|
|
109
|
+
name: The unit's display name; units with no gateway carry
|
|
110
|
+
serial-shaped default names (captured).
|
|
111
|
+
notes: Free-form notes; ``""`` on every captured record,
|
|
112
|
+
mirrored verbatim.
|
|
113
|
+
harsh_acceleration_setting_type: Settings vocabulary
|
|
114
|
+
(``"automatic"`` captured), mirrored, never interpreted.
|
|
115
|
+
vehicle_regulation_mode: ``"regulated"`` / ``"unregulated"``
|
|
116
|
+
captured, mirrored, never interpreted.
|
|
117
|
+
created_at_time: Record creation (UTC).
|
|
118
|
+
updated_at_time: Last record update (UTC).
|
|
119
|
+
camera_serial: Dash-cam serial, dashed rendering (404/608).
|
|
120
|
+
external_ids: The open external-id map's modeled slice
|
|
121
|
+
(595/608).
|
|
122
|
+
gateway: The installed gateway reference (509/608; absent on
|
|
123
|
+
unplugged units -- the minimal shape).
|
|
124
|
+
serial: The gateway serial, undashed (509/608; the dashed twin
|
|
125
|
+
lives on ``gateway.serial``).
|
|
126
|
+
esn: Electronic serial number; both captured shapes are
|
|
127
|
+
alphanumeric strings (255/608).
|
|
128
|
+
license_plate: Registration plate (410/608).
|
|
129
|
+
make: Manufacturer (576/608).
|
|
130
|
+
model: Vehicle model name (576/608).
|
|
131
|
+
vin: Vehicle identification number (576/608).
|
|
132
|
+
year: Model year; a quoted integer on the wire, typed by lax
|
|
133
|
+
coercion (576/608).
|
|
134
|
+
static_assigned_driver: Statically assigned driver reference
|
|
135
|
+
(198/608).
|
|
136
|
+
aux_input_type1: Auxiliary input assignment (12/608;
|
|
137
|
+
``"powerTakeOff"`` captured), mirrored, never interpreted.
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
model_config = ConfigDict(alias_generator=to_camel)
|
|
141
|
+
|
|
142
|
+
# Identity and lifecycle.
|
|
143
|
+
id: str
|
|
144
|
+
name: str
|
|
145
|
+
notes: str
|
|
146
|
+
harsh_acceleration_setting_type: str
|
|
147
|
+
vehicle_regulation_mode: str
|
|
148
|
+
created_at_time: datetime
|
|
149
|
+
updated_at_time: datetime
|
|
150
|
+
|
|
151
|
+
# Installed hardware.
|
|
152
|
+
camera_serial: str | None = None
|
|
153
|
+
external_ids: VehicleExternalIds | None = None
|
|
154
|
+
gateway: VehicleGatewayRef | None = None
|
|
155
|
+
serial: str | None = None
|
|
156
|
+
esn: str | None = None
|
|
157
|
+
|
|
158
|
+
# Vehicle identity.
|
|
159
|
+
license_plate: str | None = None
|
|
160
|
+
make: str | None = None
|
|
161
|
+
model: str | None = None
|
|
162
|
+
vin: str | None = None
|
|
163
|
+
year: int | None = None
|
|
164
|
+
|
|
165
|
+
# Assignment.
|
|
166
|
+
static_assigned_driver: VehicleStaticAssignedDriverRef | None = None
|
|
167
|
+
aux_input_type1: str | None = None
|