homebridge-rivian 1.3.0

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 (80) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/LICENSE +21 -0
  3. package/README.md +233 -0
  4. package/config.schema.json +123 -0
  5. package/dist/accessories/battery.d.ts +17 -0
  6. package/dist/accessories/battery.js +52 -0
  7. package/dist/accessories/battery.js.map +1 -0
  8. package/dist/accessories/climate.d.ts +24 -0
  9. package/dist/accessories/climate.js +104 -0
  10. package/dist/accessories/climate.js.map +1 -0
  11. package/dist/accessories/frontSeats.d.ts +18 -0
  12. package/dist/accessories/frontSeats.js +103 -0
  13. package/dist/accessories/frontSeats.js.map +1 -0
  14. package/dist/accessories/frunk.d.ts +16 -0
  15. package/dist/accessories/frunk.js +48 -0
  16. package/dist/accessories/frunk.js.map +1 -0
  17. package/dist/accessories/gearTunnel.d.ts +20 -0
  18. package/dist/accessories/gearTunnel.js +54 -0
  19. package/dist/accessories/gearTunnel.js.map +1 -0
  20. package/dist/accessories/liftgate.d.ts +17 -0
  21. package/dist/accessories/liftgate.js +49 -0
  22. package/dist/accessories/liftgate.js.map +1 -0
  23. package/dist/accessories/lock.d.ts +12 -0
  24. package/dist/accessories/lock.js +52 -0
  25. package/dist/accessories/lock.js.map +1 -0
  26. package/dist/accessories/rearSeatHeat.d.ts +13 -0
  27. package/dist/accessories/rearSeatHeat.js +40 -0
  28. package/dist/accessories/rearSeatHeat.js.map +1 -0
  29. package/dist/accessories/steeringHeat.d.ts +13 -0
  30. package/dist/accessories/steeringHeat.js +35 -0
  31. package/dist/accessories/steeringHeat.js.map +1 -0
  32. package/dist/accessories/tailgate.d.ts +16 -0
  33. package/dist/accessories/tailgate.js +42 -0
  34. package/dist/accessories/tailgate.js.map +1 -0
  35. package/dist/accessories/thirdRowHeat.d.ts +17 -0
  36. package/dist/accessories/thirdRowHeat.js +44 -0
  37. package/dist/accessories/thirdRowHeat.js.map +1 -0
  38. package/dist/accessories/tonneau.d.ts +17 -0
  39. package/dist/accessories/tonneau.js +49 -0
  40. package/dist/accessories/tonneau.js.map +1 -0
  41. package/dist/accessories/util.d.ts +11 -0
  42. package/dist/accessories/util.js +20 -0
  43. package/dist/accessories/util.js.map +1 -0
  44. package/dist/accessories/windows.d.ts +17 -0
  45. package/dist/accessories/windows.js +51 -0
  46. package/dist/accessories/windows.js.map +1 -0
  47. package/dist/auth-cli.d.ts +2 -0
  48. package/dist/auth-cli.js +155 -0
  49. package/dist/auth-cli.js.map +1 -0
  50. package/dist/commands.d.ts +35 -0
  51. package/dist/commands.js +80 -0
  52. package/dist/commands.js.map +1 -0
  53. package/dist/crypto.d.ts +21 -0
  54. package/dist/crypto.js +43 -0
  55. package/dist/crypto.js.map +1 -0
  56. package/dist/enroll.d.ts +17 -0
  57. package/dist/enroll.js +72 -0
  58. package/dist/enroll.js.map +1 -0
  59. package/dist/index.d.ts +3 -0
  60. package/dist/index.js +7 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/persist.d.ts +34 -0
  63. package/dist/persist.js +54 -0
  64. package/dist/persist.js.map +1 -0
  65. package/dist/platform.d.ts +58 -0
  66. package/dist/platform.js +272 -0
  67. package/dist/platform.js.map +1 -0
  68. package/dist/rivianClient.d.ts +97 -0
  69. package/dist/rivianClient.js +226 -0
  70. package/dist/rivianClient.js.map +1 -0
  71. package/dist/settings.d.ts +18 -0
  72. package/dist/settings.js +22 -0
  73. package/dist/settings.js.map +1 -0
  74. package/dist/state.d.ts +24 -0
  75. package/dist/state.js +96 -0
  76. package/dist/state.js.map +1 -0
  77. package/homebridge-ui/public/index.html +242 -0
  78. package/homebridge-ui/server.js +194 -0
  79. package/package.json +75 -0
  80. package/scripts/pair_rivian_ble.py +292 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,102 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. This project adheres
4
+ to [Semantic Versioning](https://semver.org/).
5
+
6
+ ## 1.3.0
7
+
8
+ - Front seats are now **Heater/Cooler tiles** (Driver + Passenger) that switch
9
+ between Off / Heat / Cool, replacing the old front seat-cooling switch
10
+ (`enableSeatCooling` still works as an alias for `enableFrontSeats`).
11
+ - Add optional **second-row seat heating** and **heated steering wheel** switches
12
+ (both models).
13
+ - Note: Rivian's unofficial API exposes no suspension/ride-height command, so
14
+ that can't be controlled.
15
+
16
+ ## 1.2.0
17
+
18
+ - Add optional **third-row seat heating** switch for the R1S.
19
+ - Add optional **gear tunnel** side-bin controls for the R1T (left/right release
20
+ switches; open-only, since the API can't close the bins - they reflect actual
21
+ state). Both are off by default and shown only for the matching model.
22
+
23
+ ## 1.1.0
24
+
25
+ - R1S support: the rear closure is now model-aware. R1S gets a powered
26
+ **liftgate (trunk)** Garage Door tile that opens AND closes with state; R1T
27
+ keeps the drop **tailgate** switch. The **tonneau** control is automatically
28
+ hidden on the R1S.
29
+ - Frunk, lock, climate, windows, seat cooling, and battery already work on both
30
+ R1T and R1S.
31
+
32
+ ## 1.0.7
33
+
34
+ - Pairing is now documented and presented as a laptop-browser step (Chrome/Edge,
35
+ or Android Chrome). iPhone/iPad browsers can't do Web Bluetooth, so they can't
36
+ pair - but control works from iPhone normally once paired.
37
+ - Wizard now offers a direct "Open Bluetooth pairing page" link (data pre-filled)
38
+ in addition to copy/paste, and clearer device guidance.
39
+
40
+ ## 1.0.6
41
+
42
+ - Add a phone-based Bluetooth pairing flow built into the setup wizard: a
43
+ "Show pairing steps" panel hands your per-vehicle pairing data to a hosted
44
+ HTTPS Web Bluetooth page you open on your phone (Bluefy on iOS, Chrome on
45
+ Android) and tap Pair - no laptop, Raspberry Pi Bluetooth, or Python needed.
46
+ - Add the hosted pairing page (docs/pair.html) and a `/pairing-data` UI server
47
+ endpoint (looks up vasVehicleId live if not stored).
48
+ - Improve the standalone pairing script's diagnostics (BLE GATT map dump).
49
+
50
+ ## 1.0.5
51
+
52
+ - Cabin climate is now a Thermostat tile: shows current cabin temperature and
53
+ lets you pick Off/Heat/Cool/Auto and a target temperature (16-29 C). Maps to
54
+ Rivian cabin preconditioning + set-temp.
55
+ - Add an optional front Seat Cooling switch (off by default).
56
+ - Revert Windows to a single vent (open all) / close all switch, since Rivian's
57
+ API has no per-window or partial-vent control.
58
+ - Add `scripts/pair_rivian_ble.py` to perform the one-time Bluetooth pairing of
59
+ the enrolled phone key. This is REQUIRED for commands to work - an enrolled
60
+ but unpaired key is accepted by the cloud but ignored by the vehicle.
61
+ - Store the VAS vehicle id at enrollment (used by BLE pairing).
62
+
63
+ ## 1.0.4
64
+
65
+ - Windows are now a Window tile (slider): 100% opens all windows, 0% closes
66
+ all (values snap, since Rivian's API has no per-window or partial/vent
67
+ position). Position reflects the reported open/closed state.
68
+
69
+ ## 1.0.3
70
+
71
+ - Frunk and tonneau are now exposed as Garage Door tiles (clear Open / Closed
72
+ with opening/closing state) instead of plain switches. The old switch
73
+ services are removed automatically on upgrade.
74
+
75
+ ## 1.0.2
76
+
77
+ - Fix a HomeKit warning by sending `FirmwareRevision` (model year) as a string.
78
+
79
+ ## 1.0.1
80
+
81
+ - Fix accessory naming so each control shows a proper name in the Home app
82
+ (e.g. "Preconditioning", "Windows", "Frunk") instead of "Switch 1/2/3", by
83
+ setting the `ConfiguredName` characteristic on every service.
84
+ - Fix charging detection: `charging_ready` (plugged in, not charging) is no
85
+ longer reported as actively charging.
86
+ - Refresh a vehicle's state shortly after sending a command so switches
87
+ reflect the real state quickly instead of waiting for the next poll.
88
+ - Support Node.js 24 in `engines`.
89
+
90
+ ## 1.0.0
91
+
92
+ Initial release.
93
+
94
+ - Dynamic platform plugin for Rivian vehicles (R1T / R1S / R2).
95
+ - In-browser setup wizard (no command line): sign in, MFA, vehicle selection,
96
+ and phone-key enrollment from the plugin's settings page.
97
+ - `rivian-homebridge-auth` CLI as a headless alternative (enroll / status /
98
+ disenroll).
99
+ - HomeKit accessories: lock/unlock, battery % + charging, cabin
100
+ preconditioning, all windows, frunk, tailgate/liftgate, powered tonneau.
101
+ - Command signing via secp256r1 ECDH + HKDF-SHA256 + HMAC-SHA256.
102
+ - Per-control enable/disable toggles and configurable polling interval.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 homebridge-rivian contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,233 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/homebridge/branding/latest/logos/homebridge-wordmark-logo-vertical.png" width="120">
3
+ </p>
4
+
5
+ # Homebridge Rivian
6
+
7
+ [![npm](https://img.shields.io/npm/v/homebridge-rivian.svg)](https://www.npmjs.com/package/homebridge-rivian)
8
+ [![npm downloads](https://img.shields.io/npm/dt/homebridge-rivian.svg)](https://www.npmjs.com/package/homebridge-rivian)
9
+ [![license](https://img.shields.io/npm/l/homebridge-rivian.svg)](LICENSE)
10
+
11
+ Control your **Rivian** (R1T, R1S, R2) from the Apple **Home** app and Siri, through [Homebridge](https://homebridge.io).
12
+
13
+ - Lock / unlock
14
+ - Battery % and charging status
15
+ - Cabin preconditioning (warm up / cool down)
16
+ - Open / close all windows
17
+ - Front trunk (frunk)
18
+ - Tailgate / liftgate
19
+ - Powered tonneau cover (R1T)
20
+
21
+ > **Unofficial.** This project is not affiliated with, endorsed by, or supported by Rivian. It uses the same private API the Rivian mobile app uses. APIs can change at any time and break the plugin.
22
+
23
+ ---
24
+
25
+ ## Requirements
26
+
27
+ - A working [Homebridge](https://github.com/homebridge/homebridge/wiki) install (the [Homebridge UI](https://github.com/homebridge/homebridge-config-ui-x) is strongly recommended).
28
+ - Your **Rivian account** email and password (the same one you use in the Rivian app).
29
+ - A free **phone-key slot**. Rivian allows **2 phone keys per vehicle**; this plugin uses one of them. If both are in use, remove one in the Rivian app first (Profile -> your vehicle -> Digital Keys), or remove it later from this plugin.
30
+ - **For the one-time key pairing: a laptop/desktop with Bluetooth running Google Chrome or Microsoft Edge** (or Chrome on Android), that you can bring within Bluetooth range of the vehicle.
31
+ - This is **required** and is a hard browser limitation: pairing uses the [Web Bluetooth API](https://developer.mozilla.org/docs/Web/API/Web_Bluetooth_API), which **does not work in any iPhone/iPad browser** (Apple does not implement it in iOS WebKit). Safari and Firefox are not supported on any platform.
32
+ - You only need this once. After the key is paired, all control runs through Rivian's cloud and works from any Apple Home / iPhone normally - no laptop or Bluetooth needed again.
33
+ - **Gen1 vehicles only** (the BLE pairing protocol for Gen2 has not been reverse-engineered).
34
+
35
+ ---
36
+
37
+ ## Install
38
+
39
+ ### Option A - Homebridge UI (recommended)
40
+
41
+ 1. Open the Homebridge UI in your browser.
42
+ 2. Go to the **Plugins** tab and search for **`homebridge-rivian`**.
43
+ 3. Click **Install**.
44
+
45
+ ### Option B - Command line
46
+
47
+ ```bash
48
+ sudo hb-service add homebridge-rivian
49
+ # or, for a manual Homebridge install:
50
+ sudo npm install -g homebridge-rivian
51
+ ```
52
+
53
+ ---
54
+
55
+ ## Connect your Rivian (the setup wizard)
56
+
57
+ After installing, you connect your account from the plugin's settings page. **No command line needed.**
58
+
59
+ 1. In the Homebridge UI **Plugins** tab, find **Homebridge Rivian** and click **Settings**.
60
+ 2. At the top you'll see the **Sign in & enroll** panel.
61
+ 3. Enter your **Rivian email and password**, then click **Continue**.
62
+ - Your password is sent only to Rivian to log in. It is **never saved** to disk.
63
+ 4. If your account uses two-factor authentication, you'll be asked for the **verification code** Rivian texts/emails you. Enter it and click **Verify**.
64
+ 5. Pick which **vehicle(s)** you want to control, give the key a name (default `Homebridge`), and click **Enroll & finish**.
65
+ - This registers a digital "phone key" for each selected vehicle. It will appear in the Rivian app under your vehicle's Digital Keys, and **uses one of your two key slots per vehicle**.
66
+ 6. **Restart Homebridge** (or the child bridge) when prompted. Your Rivian accessories now appear in the Home app.
67
+
68
+ The plugin stores a session token and a key that lives only on your Homebridge machine.
69
+
70
+ ### Step 2 - Pair the key over Bluetooth from a laptop (REQUIRED for commands)
71
+
72
+ Enrolling registers the key, but Rivian will not execute commands from it until
73
+ it is **paired with the vehicle over Bluetooth** (the key shows `isPaired: false`
74
+ until then). Until you do this, **reads work but lock / climate / windows / etc.
75
+ silently do nothing** (the cloud accepts the command, but the truck ignores it).
76
+
77
+ This is a **one-time** step. You need a **laptop/desktop with Bluetooth running
78
+ Chrome or Edge** (Android Chrome also works). **iPhones/iPads cannot do this** -
79
+ no iOS browser supports Web Bluetooth (see Requirements).
80
+
81
+ 1. Bring the laptop within Bluetooth range of the vehicle (sitting in or right
82
+ next to it is best).
83
+ 2. In the Homebridge UI, open the plugin **Settings** and click
84
+ **Show pairing steps** under "Enable vehicle control".
85
+ 3. Click **Open Bluetooth pairing page** for your vehicle. It opens a secure
86
+ page (`https://<your-fork>.github.io/homebridge-rivian/pair.html`) with your
87
+ data already loaded. (If you opened the Homebridge UI on a different machine,
88
+ use **Copy pairing data** and paste it into that page on the laptop instead.)
89
+ 4. On the **vehicle touchscreen**: Settings -> Drivers & Keys, select the
90
+ `Homebridge` key and tap **Set Up**.
91
+ 5. On the pairing page click **Connect to Bluetooth & pair**, choose
92
+ **Rivian Phone Key** in the browser's Bluetooth chooser, and approve.
93
+
94
+ When it shows `SUCCESS`, the key is paired and commands work. From then on
95
+ everything works from your iPhone / Apple Home over the cloud.
96
+
97
+ ### Advanced / headless alternative (Python)
98
+
99
+ If you'd rather pair from a command line on a Bluetooth-capable computer next to
100
+ the vehicle (instead of a browser):
101
+
102
+ ```bash
103
+ pip install bleak cryptography # Linux also: pip install dbus-fast
104
+ python scripts/pair_rivian_ble.py --auth /var/lib/homebridge/rivian-auth.json
105
+ ```
106
+
107
+ Tap **Set Up** for the `Homebridge` key on the vehicle screen when it starts
108
+ scanning. It prints `SUCCESS` when paired.
109
+
110
+ ### Headless / Docker alternative (CLI)
111
+
112
+ If you can't use the UI, run the bundled CLI on the Homebridge host:
113
+
114
+ ```bash
115
+ # enroll (interactive: email, password, MFA code, vehicle selection)
116
+ rivian-homebridge-auth --storage /var/lib/homebridge
117
+
118
+ # check status
119
+ rivian-homebridge-auth status --storage /var/lib/homebridge
120
+
121
+ # remove the key from your account and delete local credentials
122
+ rivian-homebridge-auth disenroll --storage /var/lib/homebridge
123
+ ```
124
+
125
+ `--storage` should point at your Homebridge storage directory (where `config.json` lives). It defaults to `~/.homebridge`.
126
+
127
+ ---
128
+
129
+ ## What you get in HomeKit
130
+
131
+ The plugin creates one accessory per vehicle, with these services (each can be turned off in settings):
132
+
133
+ | Control | HomeKit type | Notes |
134
+ | --- | --- | --- |
135
+ | Lock / unlock | Lock | Locks/unlocks all closures. State reflects your doors. |
136
+ | Battery % | Humidity sensor + Battery | Shows state of charge as a percentage. |
137
+ | Charging | Contact sensor | "Open" while the vehicle is charging. |
138
+ | Cabin climate | Thermostat | Current cabin temp, Off/Heat/Cool/Auto, and a target temp (16-29 C / ~61-84 F). Maps to Rivian preconditioning. |
139
+ | Front seats | Heater/Cooler (Driver + Passenger) | One tile per seat: Off / Heat / Cool. Off by default. |
140
+ | Second-row seat heat | Switch | Heats both second-row seats. Off by default. |
141
+ | Steering wheel heat | Switch | Off by default. |
142
+ | Windows | Switch | On = open/vent all windows, off = close all (no per-window or partial vent via Rivian's API). |
143
+ | Frunk | Garage Door | Open / close with Open/Closed state (R1T and R1S). |
144
+ | Rear trunk | Garage Door (R1S liftgate) or Switch (R1T tailgate) | Auto-selected by model. R1S powered liftgate opens AND closes with state; R1T tailgate opens/drops (the API can't close it or report its position). |
145
+ | Tonneau | Garage Door | R1T with the powered tonneau only; automatically hidden on R1S. |
146
+
147
+ ### Vehicle differences (auto-detected)
148
+
149
+ The plugin reads your vehicle's model and adjusts:
150
+
151
+ - **R1S:** rear closure is a powered **liftgate** (open + close); **no tonneau**.
152
+ - **R1T:** rear closure is the **tailgate** (open/drop); optional **tonneau** if equipped.
153
+ - Frunk, lock, climate, windows, battery, and (optional) front-seat heat/cool, second-row seat heat, and heated steering wheel work the same on both.
154
+ - **R1S only (optional, off by default):** third-row seat heating switch.
155
+ - **R1T only (optional, off by default):** gear tunnel side-bin release (two switches, left/right). The API can only open a bin - you close it by hand - so the switch reflects the bin's actual state.
156
+
157
+ ### Settings
158
+
159
+ - **Polling interval** - how often vehicle state refreshes (default 60s, minimum 30s). Lower = faster updates but more API calls.
160
+ - **Controls to expose** - enable/disable each accessory above.
161
+ - **Verbose debug logging** - extra logs for troubleshooting.
162
+
163
+ ---
164
+
165
+ ## Important caveats (please read)
166
+
167
+ These are limits of Rivian's API, not bugs:
168
+
169
+ - **No partial window "vent."** Rivian's cloud API only supports opening or closing *all* windows together. There is no partial-vent command, so the Windows switch fully opens/closes.
170
+ - **No suspension / ride-height control.** Rivian's unofficial API has no command for ride height (kneel/low/standard/high), so it can't be exposed - even though the official app can do it.
171
+ - **Cabin preconditioning** is exposed as a Thermostat (current temp + target temp + on/off); Rivian decides whether to heat or cool to reach the target. Seat heat/cool and steering heat are simple on/off (level is fixed when on).
172
+ - **The vehicle may be asleep.** Commands first try directly, then wake the vehicle and retry. The first command after a long idle period can take a few extra seconds.
173
+ - **Some controls depend on your vehicle/options** (e.g. powered tonneau is R1T-only; liftgate close is R1S). Unsupported commands simply do nothing on the car.
174
+
175
+ ---
176
+
177
+ ## Security & privacy
178
+
179
+ - Your **password is never stored**. It's used once to obtain session tokens.
180
+ - The plugin generates a **secp256r1 key pair locally**. Only the *public* half is sent to Rivian (for enrollment). The private key stays on your machine and is used to sign commands.
181
+ - Tokens and the key are saved to `rivian-auth.json` in your Homebridge storage directory, with restrictive file permissions. Keep that file private and never commit it to a repo.
182
+ - To revoke access, click **Disconnect & remove key** in the plugin settings (or run `rivian-homebridge-auth disenroll`). This removes the phone key from your Rivian account and deletes the local credentials. You can also remove it any time from the Rivian app.
183
+
184
+ ---
185
+
186
+ ## Troubleshooting
187
+
188
+ - **"Not signed in yet" in the logs** - open the plugin settings and complete the Sign in & enroll wizard.
189
+ - **Commands do nothing (reads still work)** - the phone key almost certainly isn't paired yet. Complete "Step 2 - Pair the key over Bluetooth" above. You can confirm in the Rivian app that the `Homebridge` key shows as set up/paired.
190
+ - **"Rivian session expired"** - re-run the sign-in wizard to refresh your session.
191
+ - **Phone key limit reached** - remove an unused key in the Rivian app (Profile -> vehicle -> Digital Keys) and enroll again.
192
+ - **Enable Verbose debug logging** in settings to see detailed request/error logs, then check the Homebridge logs.
193
+
194
+ ---
195
+
196
+ ## How it works
197
+
198
+ ```
199
+ Setup wizard (login + MFA + EnrollPhone)
200
+ |
201
+ v
202
+ rivian-auth.json (session tokens + local key + vehicle ids)
203
+ |
204
+ v
205
+ Homebridge platform --- poll state ---> Rivian GraphQL API
206
+ --- signed commands ->
207
+ |
208
+ v
209
+ HomeKit accessories (Apple Home app)
210
+ ```
211
+
212
+ Commands are signed exactly like the Rivian app: an ECDH shared secret between your enrolled key and the vehicle's key is run through HKDF-SHA256, then used as the HMAC-SHA256 key over `command + timestamp`.
213
+
214
+ ---
215
+
216
+ ## Credits
217
+
218
+ - The community that reverse-engineered the Rivian API, especially [bretterer/rivian-python-client](https://github.com/bretterer/rivian-python-client) and the [RivDocs](https://rivian-api.kaedenb.org/) project.
219
+
220
+ ## Contributing
221
+
222
+ Issues and PRs welcome. Please don't include any tokens, VINs, or the contents of `rivian-auth.json` in bug reports.
223
+
224
+ ## Support this project
225
+
226
+ This plugin is free and open source. If it's useful to you, a tip is appreciated (entirely optional):
227
+
228
+ - **Venmo:** [@thomas-whippld](https://venmo.com/u/thomas-whippld)
229
+ - **GitHub Sponsors:** [github.com/sponsors/tvearl](https://github.com/sponsors/tvearl) _(pending approval)_
230
+
231
+ ## License
232
+
233
+ [MIT](LICENSE)
@@ -0,0 +1,123 @@
1
+ {
2
+ "pluginAlias": "RivianHomebridge",
3
+ "pluginType": "platform",
4
+ "singular": true,
5
+ "customUi": true,
6
+ "headerDisplay": "Control your Rivian from Apple Home. Use the **Sign in & enroll** panel above to connect your account, then choose which controls to expose below.",
7
+ "footerDisplay": "Unofficial plugin - not affiliated with Rivian. Enrolling uses one of your two phone-key slots per vehicle.",
8
+ "schema": {
9
+ "type": "object",
10
+ "properties": {
11
+ "name": {
12
+ "title": "Name",
13
+ "type": "string",
14
+ "default": "Rivian",
15
+ "required": true
16
+ },
17
+ "pollIntervalSeconds": {
18
+ "title": "Polling interval (seconds)",
19
+ "type": "integer",
20
+ "default": 60,
21
+ "minimum": 30,
22
+ "maximum": 3600,
23
+ "description": "How often to refresh vehicle state. Lower values update faster but use more API calls and battery."
24
+ },
25
+ "enableLock": {
26
+ "title": "Lock / unlock (all closures)",
27
+ "type": "boolean",
28
+ "default": true
29
+ },
30
+ "enableBattery": {
31
+ "title": "Battery % and charging status",
32
+ "type": "boolean",
33
+ "default": true
34
+ },
35
+ "enableClimate": {
36
+ "title": "Cabin climate (thermostat: current temp + preconditioning)",
37
+ "type": "boolean",
38
+ "default": true
39
+ },
40
+ "enableFrontSeats": {
41
+ "title": "Front seats heating + cooling (Driver & Passenger tiles)",
42
+ "type": "boolean",
43
+ "default": false
44
+ },
45
+ "enableRearSeatHeat": {
46
+ "title": "Second-row seat heating",
47
+ "type": "boolean",
48
+ "default": false
49
+ },
50
+ "enableSteeringHeat": {
51
+ "title": "Heated steering wheel",
52
+ "type": "boolean",
53
+ "default": false
54
+ },
55
+ "enableThirdRowHeat": {
56
+ "title": "Third-row seat heating (R1S only)",
57
+ "type": "boolean",
58
+ "default": false
59
+ },
60
+ "enableGearTunnel": {
61
+ "title": "Gear tunnel side bins (R1T only)",
62
+ "type": "boolean",
63
+ "default": false
64
+ },
65
+ "enableWindows": {
66
+ "title": "Open / close all windows",
67
+ "type": "boolean",
68
+ "default": true
69
+ },
70
+ "enableFrunk": {
71
+ "title": "Front trunk (frunk)",
72
+ "type": "boolean",
73
+ "default": true
74
+ },
75
+ "enableTailgate": {
76
+ "title": "Rear trunk - tailgate (R1T) or powered liftgate (R1S)",
77
+ "type": "boolean",
78
+ "default": false
79
+ },
80
+ "enableTonneau": {
81
+ "title": "Powered tonneau cover (R1T only; ignored on R1S)",
82
+ "type": "boolean",
83
+ "default": false
84
+ },
85
+ "debug": {
86
+ "title": "Verbose debug logging",
87
+ "type": "boolean",
88
+ "default": false
89
+ }
90
+ }
91
+ },
92
+ "layout": [
93
+ "name",
94
+ "pollIntervalSeconds",
95
+ {
96
+ "type": "fieldset",
97
+ "title": "Controls to expose",
98
+ "expandable": true,
99
+ "expanded": true,
100
+ "items": [
101
+ "enableLock",
102
+ "enableBattery",
103
+ "enableClimate",
104
+ "enableFrontSeats",
105
+ "enableRearSeatHeat",
106
+ "enableSteeringHeat",
107
+ "enableThirdRowHeat",
108
+ "enableWindows",
109
+ "enableFrunk",
110
+ "enableTailgate",
111
+ "enableTonneau",
112
+ "enableGearTunnel"
113
+ ]
114
+ },
115
+ {
116
+ "type": "fieldset",
117
+ "title": "Advanced",
118
+ "expandable": true,
119
+ "expanded": false,
120
+ "items": ["debug"]
121
+ }
122
+ ]
123
+ }
@@ -0,0 +1,17 @@
1
+ import type { PlatformAccessory } from 'homebridge';
2
+ import type { RivianHomebridgePlatform, RivianAccessory } from '../platform';
3
+ import type { StoredVehicle } from '../persist';
4
+ import { VehicleStateValues } from '../commands';
5
+ /**
6
+ * Exposes state of charge as a Humidity sensor (so it shows a readable %),
7
+ * a Battery service (level + charging + low-battery), and a Contact sensor
8
+ * that "opens" while charging.
9
+ */
10
+ export declare class BatteryAccessory implements RivianAccessory {
11
+ private readonly platform;
12
+ private readonly humidity;
13
+ private readonly battery;
14
+ private readonly charging;
15
+ constructor(platform: RivianHomebridgePlatform, accessory: PlatformAccessory, vehicle: StoredVehicle);
16
+ update(values: VehicleStateValues): void;
17
+ }
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BatteryAccessory = void 0;
4
+ const state_1 = require("../state");
5
+ const util_1 = require("./util");
6
+ const LOW_BATTERY_THRESHOLD = 20;
7
+ /**
8
+ * Exposes state of charge as a Humidity sensor (so it shows a readable %),
9
+ * a Battery service (level + charging + low-battery), and a Contact sensor
10
+ * that "opens" while charging.
11
+ */
12
+ class BatteryAccessory {
13
+ platform;
14
+ humidity;
15
+ battery;
16
+ charging;
17
+ constructor(platform, accessory, vehicle) {
18
+ this.platform = platform;
19
+ const { Service } = this.platform;
20
+ this.humidity =
21
+ accessory.getServiceById(Service.HumiditySensor, 'rivian-soc') ||
22
+ accessory.addService(Service.HumiditySensor, `${vehicle.name} Battery`, 'rivian-soc');
23
+ this.battery =
24
+ accessory.getServiceById(Service.Battery, 'rivian-battery') ||
25
+ accessory.addService(Service.Battery, `${vehicle.name} Battery Level`, 'rivian-battery');
26
+ this.charging =
27
+ accessory.getServiceById(Service.ContactSensor, 'rivian-charging') ||
28
+ accessory.addService(Service.ContactSensor, `${vehicle.name} Charging`, 'rivian-charging');
29
+ (0, util_1.nameService)(this.platform, this.humidity, `${vehicle.name} Battery`);
30
+ (0, util_1.nameService)(this.platform, this.battery, `${vehicle.name} Battery Level`);
31
+ (0, util_1.nameService)(this.platform, this.charging, `${vehicle.name} Charging`);
32
+ }
33
+ update(values) {
34
+ const { Characteristic } = this.platform;
35
+ const soc = (0, state_1.toNumber)(values.batteryLevel);
36
+ const charging = (0, state_1.isCharging)(values.chargerStatus, values.chargerState);
37
+ if (soc !== undefined) {
38
+ const clamped = Math.max(0, Math.min(100, Math.round(soc)));
39
+ this.humidity.updateCharacteristic(Characteristic.CurrentRelativeHumidity, clamped);
40
+ this.battery.updateCharacteristic(Characteristic.BatteryLevel, clamped);
41
+ this.battery.updateCharacteristic(Characteristic.StatusLowBattery, clamped <= LOW_BATTERY_THRESHOLD
42
+ ? Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW
43
+ : Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL);
44
+ }
45
+ this.battery.updateCharacteristic(Characteristic.ChargingState, charging ? Characteristic.ChargingState.CHARGING : Characteristic.ChargingState.NOT_CHARGING);
46
+ this.charging.updateCharacteristic(Characteristic.ContactSensorState, charging
47
+ ? Characteristic.ContactSensorState.CONTACT_NOT_DETECTED
48
+ : Characteristic.ContactSensorState.CONTACT_DETECTED);
49
+ }
50
+ }
51
+ exports.BatteryAccessory = BatteryAccessory;
52
+ //# sourceMappingURL=battery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"battery.js","sourceRoot":"","sources":["../../src/accessories/battery.ts"],"names":[],"mappings":";;;AAIA,oCAAgD;AAChD,iCAAqC;AAErC,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAEjC;;;;GAIG;AACH,MAAa,gBAAgB;IAMR;IALF,QAAQ,CAAU;IAClB,OAAO,CAAU;IACjB,QAAQ,CAAU;IAEnC,YACmB,QAAkC,EACnD,SAA4B,EAC5B,OAAsB;QAFL,aAAQ,GAAR,QAAQ,CAA0B;QAInD,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC;QAElC,IAAI,CAAC,QAAQ;YACX,SAAS,CAAC,cAAc,CAAC,OAAO,CAAC,cAAc,EAAE,YAAY,CAAC;gBAC9D,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC,cAAc,EAAE,GAAG,OAAO,CAAC,IAAI,UAAU,EAAE,YAAY,CAAC,CAAC;QAExF,IAAI,CAAC,OAAO;YACV,SAAS,CAAC,cAAc,CAAC,OAAO,CAAC,OAAO,EAAE,gBAAgB,CAAC;gBAC3D,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,gBAAgB,EAAE,gBAAgB,CAAC,CAAC;QAE3F,IAAI,CAAC,QAAQ;YACX,SAAS,CAAC,cAAc,CAAC,OAAO,CAAC,aAAa,EAAE,iBAAiB,CAAC;gBAClE,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,WAAW,EAAE,iBAAiB,CAAC,CAAC;QAE7F,IAAA,kBAAW,EAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,UAAU,CAAC,CAAC;QACrE,IAAA,kBAAW,EAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,gBAAgB,CAAC,CAAC;QAC1E,IAAA,kBAAW,EAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,WAAW,CAAC,CAAC;IACxE,CAAC;IAED,MAAM,CAAC,MAA0B;QAC/B,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC;QACzC,MAAM,GAAG,GAAG,IAAA,gBAAQ,EAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC1C,MAAM,QAAQ,GAAG,IAAA,kBAAU,EAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;QAEvE,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAC5D,IAAI,CAAC,QAAQ,CAAC,oBAAoB,CAAC,cAAc,CAAC,uBAAuB,EAAE,OAAO,CAAC,CAAC;YACpF,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC,cAAc,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;YACxE,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAC/B,cAAc,CAAC,gBAAgB,EAC/B,OAAO,IAAI,qBAAqB;gBAC9B,CAAC,CAAC,cAAc,CAAC,gBAAgB,CAAC,iBAAiB;gBACnD,CAAC,CAAC,cAAc,CAAC,gBAAgB,CAAC,oBAAoB,CACzD,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAC/B,cAAc,CAAC,aAAa,EAC5B,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,aAAa,CAAC,YAAY,CAC7F,CAAC;QAEF,IAAI,CAAC,QAAQ,CAAC,oBAAoB,CAChC,cAAc,CAAC,kBAAkB,EACjC,QAAQ;YACN,CAAC,CAAC,cAAc,CAAC,kBAAkB,CAAC,oBAAoB;YACxD,CAAC,CAAC,cAAc,CAAC,kBAAkB,CAAC,gBAAgB,CACvD,CAAC;IACJ,CAAC;CACF;AA1DD,4CA0DC"}
@@ -0,0 +1,24 @@
1
+ import type { PlatformAccessory } from 'homebridge';
2
+ import type { RivianHomebridgePlatform, RivianAccessory } from '../platform';
3
+ import type { StoredVehicle } from '../persist';
4
+ import { VehicleStateValues } from '../commands';
5
+ /**
6
+ * Cabin preconditioning as a Thermostat tile.
7
+ *
8
+ * Rivian's cloud API does not expose a true live thermostat: it can enable /
9
+ * disable preconditioning and set a target temperature, and the vehicle decides
10
+ * whether to heat or cool to reach it. We therefore map:
11
+ * - Off -> disable preconditioning
12
+ * - Heat / Cool / Auto -> enable preconditioning + set target temperature
13
+ * Current temperature is the reported cabin interior temperature.
14
+ */
15
+ export declare class ClimateAccessory implements RivianAccessory {
16
+ private readonly platform;
17
+ private readonly vehicle;
18
+ private readonly service;
19
+ private targetTempC;
20
+ constructor(platform: RivianHomebridgePlatform, accessory: PlatformAccessory, vehicle: StoredVehicle);
21
+ private setMode;
22
+ private setTargetTemp;
23
+ update(values: VehicleStateValues): void;
24
+ }