Check if your car needs to move for Somerville, MA street sweeping.
Looks up the street sweeping schedule for any Somerville address using the city's Recollect data. Tells you if sweeping is happening today, tomorrow, or when the next event is — and which side of the street it covers, with OSM-based detection of which side of the street you're actually parked on.
Two ways to check:
- Tesla Login — sign in with your Tesla account; the app pulls the car's GPS, geocodes it, runs the sweep check, and shows a map + side-detection diagnostic.
- Manual — drag a pin on the map. Same results view as the Tesla flow, just driven by you instead of your car. Useful for testing addresses you're considering parking at, or for users who don't have a Tesla on the account.
Older versions guessed your side from house-number parity (houseNum % 2). That breaks when a car is parked across from its own address (oversized lots, no opposing house, etc.). The current detection:
- Queries OSM Overpass for the closest named drivable highway segment to your pin
- Computes a 2D cross-product to decide which side of the road segment your pin is on
- Queries OSM buildings tagged with that street name and tallies even-vs-odd house numbers per side
- Maps your cross-product side to even/odd via the building parity vote
Falls back to the parity heuristic if OSM has no building data for the street. The diagnostic card on the results view shows exactly which segment + buildings the algorithm saw, so you can sanity-check it.
Once connected, opt-in via "🔔 Daily Slack Pings" to receive a Slack DM 1, 2, and 3 days before each sweeping event that affects your side. Sign in with Slack (OIDC) auto-fills your user id; subscriptions store the Tesla refresh_token server-side (mode 0600) so a noon-ET cron can refresh, locate, and notify without the user keeping the page open.
A confirmation DM is sent the moment you click Enable so you know the wiring works. If a sub starts failing (Tesla token revoked, vehicle removed), you get a one-time "your sub broke, re-enable" DM after 3 consecutive failures, then once per day until you re-enable.
Subsequent page opens within 6 hours of a successful "Check My Car" hydrate from localStorage instead of waking the car. The button still bypasses cache for an explicit live check.
- Backend: Node.js / Express, proxies Tesla Fleet API + Recollect + Nominatim + OSM Overpass
- Frontend: Preact + Vite, Leaflet for maps (lazy-loaded)
- Assets: Vite-built bundle plus brotli-precompressed
.brsiblings served by an Express middleware (falls through to gzip via whatever fronts it)
The repo is an npm workspace coordinating two packages:
src/server/ — @tesla-sweeper/server (express backend)
src/client/ — @tesla-sweeper/client (preact + vite SPA)
Common commands run from the repo root and fan out to the workspaces.
cp .env.example .env # Add your Tesla app + Slack OIDC credentials
npm install # installs both workspaces
npm run dev # runs server (20040) + vite (5173) concurrentlyOpen http://localhost:5173/.
For production:
npm run build # builds the client bundle into dist/
npm start # runs the server, which serves dist/
npm test # runs vitest in both workspaces| Variable | Required for | Description |
|---|---|---|
TESLA_CLIENT_ID |
Tesla Login | Tesla developer app client ID |
TESLA_CLIENT_SECRET |
Tesla Login | Tesla developer app client secret |
TESLA_REDIRECT_URI |
Tesla Login | OAuth redirect URI registered with the Tesla developer app (must match exactly) |
SLACK_CLIENT_ID |
Sign in with Slack | OIDC client id from a Slack app (api.slack.com/apps) |
SLACK_CLIENT_SECRET |
Sign in with Slack | OIDC client secret |
SLACK_REDIRECT_URI |
Sign in with Slack | Same URL added under app's Redirect URLs |
SLACK_BOT_TOKEN |
Confirmation + cron DMs | xoxb-… bot token with chat:write scope |
SESSION_HMAC_KEY |
/enable + /disable gate | 32-byte hex (openssl rand -hex 32). Without it, those endpoints reject every request. |
NOTIFICATIONS_RUN_TOKEN |
Daily cron | Bearer token guarding POST /api/notifications/run. Generate with openssl rand -hex 32. |
STUB_VEHICLE_ENABLED |
Test mode | Set to 1 to inject a stub "Test Vehicle" when Tesla returns 0 vehicles. Off in normal prod. |
STUB_VEHICLE_LAT / _LNG / _NAME |
Test mode | Optional overrides for the stub's location/name. Defaults to a Somerville address. |
- Address is matched via Recollect's
address-suggestendpoint for Somerville (service 349) - Sweeping events for the next 30 days are fetched from the matched place
- Lat/lng (from Tesla GPS or Manual pin) is run through
whichSide()for OSM-based side detection; falls back tohouseNum % 2if OSM has no building data for the street - Status is determined: danger (move now), warning (tomorrow/other side), safe (no upcoming sweep)
- After noon, today's sweep status is demoted to "done" since sweeping runs 8AM-12PM
Sweeping season runs April 1 – December 31. Outside that window, most addresses will show no scheduled events.
Uses the Tesla Fleet API OAuth2 authorization_code flow:
- User clicks "Connect Tesla Account"
- Redirected to Tesla's auth page to grant vehicle data + location access
- On callback, token is exchanged server-side (credentials never exposed to browser)
- Vehicle list is fetched — user selects which car if multiple
- Car's GPS is reverse-geocoded to a street address via Nominatim
- Address is checked against Recollect's sweeping database; OSM is queried for side-detection geometry
Tokens are stored in localStorage for session persistence. Refresh tokens are used to maintain access without re-login. The app is registered with Tesla Fleet API and hosts a public key at /.well-known/appspecific/com.tesla.3p.public-key.pem.
SESSION_HMAC_KEYgate:/api/notifications/enableand/disablerequire an HMAC session token issued by/api/slack/oauth/callback. Without this binding, anyone with a Tesla refresh_token could subscribe an arbitraryslack_user_id. Without the env var set, both endpoints reject every request.- Bearer auth on
/api/notifications/run:crypto.timingSafeEqualcomparison. - Body size limit: Express bodies capped at 10kb.
- No CORS headers: server binds to
127.0.0.1by default; expose via a same-origin reverse proxy. - Refresh tokens live in
data/subscriptions.json(mode 0600, dir 0700). Never logged. Thewrap()async-error helper returns a generic 502 to the client without leaking error details. - Stub vehicle:
STUB_VEHICLE_ENABLED=1injects a test vehicle when Tesla returns 0; only intended for dev/testing. The cron path branches on aSTUB_REFRESH_TOKENsentinel to short-circuit Tesla calls for stub subs.
MIT