Readme

An API for mysubaru.com to enable remote start and stop.

Requires env vars (val.town secrets) named: SUBARU_USERNAME, SUBARU_PASSWORD, SUBARU_DEVICEID, SUBARU_VEHICLEKEY, SUBARU_PIN. The username, password, and PIN should match your mysubaru.com credentials. The device ID and vehicle key can be found by inspecting the request body of a login network request at mysubaru.com.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import assert from "node:assert/strict";
import process from "node:process";
const { wrapFetch: FetchWithCookie } = await import("https://deno.land/x/another_cookiejar@v5.0.3/mod.ts");
const fetch = FetchWithCookie();
type ServiceResponse = { id: string; success: boolean };
export async function login() {
const body = new URLSearchParams({
"username": process.env.SUBARU_USERNAME,
"password": process.env.SUBARU_PASSWORD,
"deviceId": process.env.SUBARU_DEVICEID,
"lastSelectedVehicleKey": process.env.SUBARU_VEHICLEKEY,
});
const dest = "https://www.mysubaru.com/login";
const resp = await fetch(dest, { method: "POST", body });
assert(resp.status == 200, `unexpected login response status: ${resp.status}`);
}
export async function status(id: string): Promise<ServiceResponse> {
const body = new URLSearchParams({
serviceRequestId: id,
});
const url = "https://www.mysubaru.com/service/g2/remoteService/status.json";
const res = await fetch(url, { method: "POST", body });
return await parseServiceResponse(res);
}
export async function start(overrides?: Record<string, string>): Promise<ServiceResponse> {
const opts = Object.assign({
"now": `${Date.now()}`,
"pin": process.env.SUBARU_PIN,
"delay": "0",
"horn": "true",
"unlockDoorType": "ALL_DOORS_CMD",
"name": "Auto",
"runTimeMinutes": "10",
"climateZoneFrontTemp": "74",
"climateZoneFrontAirMode": "AUTO",
"climateZoneFrontAirVolume": "AUTO",
"outerAirCirculation": "auto",
"heatedRearWindowActive": "false",
"airConditionOn": "false",
"heatedSeatFrontLeft": "off",
"heatedSeatFrontRight": "off",
"startConfiguration": "START_ENGINE_ALLOW_KEY_IN_IGNITION",
"canEdit": "true",
"disabled": "false",
"vehicleType": "gas",
"presetType": "subaruPreset",
}, overrides);
const url = "https://www.mysubaru.com/service/g2/engineStart/execute.json";
const res = await fetch(url, { method: "POST", body: new URLSearchParams(opts) });
return await parseServiceResponse(res);
}
export async function stop(overrides?: Record<string, string>): Promise<ServiceResponse> {
const opts = Object.assign({
"now": `${Date.now()}`,
"pin": process.env.SUBARU_PIN,
"delay": "0",
"horn": "true",
"unlockDoorType": "ALL_DOORS_CMD",
}, overrides);
const url = "https://www.mysubaru.com/service/g2/engineStop/execute.json";
const res = await fetch(url, { method: "POST", body: new URLSearchParams(opts) });
return await parseServiceResponse(res);
}
async function parseServiceResponse(res: Response): Promise<ServiceResponse> {
assert(res.status == 200, `unexpected service response status: ${res.status}`);
const ctype = res.headers.get("Content-Type");
assert(ctype == "application/json", `unexpected service response content type: ${ctype}`);
const json = await res.json();
assert(json.success, `service request was not successful`);
return { id: json.data.serviceRequestId, success: json.data.success };
}
👆 This is a val. Vals are TypeScript snippets of code, written in the browser and run on our servers. Create scheduled functions, email yourself, and persist small pieces of data — all from the browser.