Repository navigation
Releases
v2.0.13
What's Changed
- fix: stabilize Polestar refresh for 2.0.13 by @NicolasKheirallah in #44
Full Changelog: v2.0.12...v2.0.13
v2.0.12
Large update with quite a few bug fixes as well as decoding alot of the data we recieved but didn't know what it contained
[2.0.12] - 2026-11-01
Added
-
decode the Polestar C3 per-period energy breakdown (driving, climate, battery, other), battery
discharge power limit, manual preconditioning and cold-battery status, and climate warnings;
show them in Battery Diagnostics and Needs Attention, and post a notice once per climate
session when the run time is nearly used up or the battery is too low -
show "Not reported by this vehicle" on the software card when the car does not report update
status, instead of an unavailable badge -
keep a local tyre-pressure history with a four-tyre chart and a possible-slow-leak hint, and a
warning timeline that records when each warning first appeared and when a covering report
cleared it -
estimate the next service date from the last 30 days of driving, and hint at frost or a cold
battery before a scheduled departure using an hourly Open-Meteo forecast fetched in the same
request as current weather -
offer Retry on a failed command when the failure proves it did not run, Undo on a reversible
command for 30 seconds, and saved command routines that run steps in order and stop at the first
failure -
add opt-in notifications for a car left in convenience mode and a daily summary, plus Lock and
Mute This Vehicle actions on vehicle alerts -
add URL routes for cabin cleaning, windows, tailgate, trunk, charge-current limit, charging
override, OTA and engine commands, through the same confirmation and capability checks -
add VoiceOver actions to the menu bar item and an hour-and-price readout on the price curve
-
export a handover pack for selling the car: summary, charging and trip CSVs without locations,
an optional VIN, and a checklist of the steps that happen in the manufacturer app or car -
pace Polestar Data Portal polling so a day stays under 9,000 of the 10,000 daily calls, and show
the projected count in Settings -
name individual failed lamps under the exterior lighting row
-
document the recovered Polestar
GetMyCarsprotobuf schema, with every tag of the app's own
car_informationclasses, its enum tables and the verification against a captured frame, in
docs/research/polestar-protobuf-field-decode.md -
decode the Polestar
GetMyCarslock, unlock, climatization and charge-timer enums and the gross
vehicle weight rating, and gate remote locking, remote unlocking, preconditioning and saved
charge locations on what the car reports instead of the model table -
keep the tailgate control on cars whose unlock type is trunk-only, and explain a disabled
Unlock on cars whose unlock type is none instead of promising an unlock that cannot happen -
show the reported climatization type and maximum preconditioning runtime on the climate card
instead of a fixed claim that fits every car -
surface the per-location charge timer and departure-time limits from
GetMyCarsin the Info
capabilities inspector under "Backend Control Settings" -
show the reported gross vehicle weight on the Info specs card as well as the capabilities card
and the factory passport export, localized in every supported language -
claim plug and charge on the Info capabilities card only when the backend also sends the
plug-and-charge identifier; the wire flag and the REST projection disagree on this car -
name the vehicle type, front grille variant and model year groups of the Polestar
visual_idstring on the Info specs card, and keep the other groups positional until their
code table is verified -
show reported vehicle usage mode in Info connectivity details, including vehicles without
separate network diagnostics -
decode Polestar charging-status V2 and automatic-trip consumption from reconstructed
descriptors, correct climate seat/wheel requests and normalize their heating levels -
add opt-in read-only field research and anonymized idle/active capture regression fixtures
-
decode Polestar C3 vehicle usage mode, use range in miles when kilometre range is absent,
and show separate manual-trip and automatic-trip energy totals in Battery Diagnostics -
document accepted Polestar wire mappings and remaining evidence gaps in
docs/research/POLESTAR_FIELDS.md and docs/research/polestar-live-decoding-2026-10-30.md -
add THIRD_PARTY_NOTICES.md crediting Polaris, pypolestar and polestar_api, Sparkle,
Open-Meteo and Elpriset just nu.se; the app bundle now carries it with LICENSE and
Sparkle's own license -
add regression coverage for privacy erasure, legacy migration failures, charging planner
cancellation and vehicle switching, electricity price coverage, notifications and gestures -
show whether scheduled charging will finish, including "cannot complete" and "smart charging
will not finish", instead of collapsing every plan to "Scheduled" -
add since-charge distance and average speed to the trip computer from the C3 odometer reading
-
compare measured tyre pressures with the car's reported reference pressures
-
explain why a climate session started or failed, including the reported error reasons
-
show "Update status not reported" on cars that do not advertise update status, instead of
claiming that no update is available -
offer windows, Charge Now / Resume Schedule and pre-cleaning from the menu-bar Quick Controls
-
erase cached car artwork from memory, disk and SQLite on sign-out and on "Erase local vehicle
data" -
estimate battery health from high-confidence charging sessions in addition to the 100% range
reading, and show a fused value when both methods agree within tolerance instead of hiding
a method conflict behind one number -
keep the uncapped range-based health figure alongside the displayed value capped at 100%,
so an understated WLTP reference stays visible instead of flattening to 100% -
fit the health trend with a median-based slope that ignores a single bad range reading,
and report the median of recent milestones as the stable figure -
document every backend field Hisingen does not yet wire, with wire type, enum table, the
live capture evidence behind it and the public cross-client sources, in
docs/api/polestar-unwired-fields.md -
decode the remaining named GetMyCars tags into the capabilities inspector: the reporting
flags, notification matrix, end-of-ownership actions, account linking, parking assistance
type, trip reporting, device pairing, bidirectional charging and the preconditioning
duration appear as equipment rows and a new Backend Reporting disclosure, shrinking the raw
"Unwired Backend Fields" list to Charging 35.43, the one field no client schema names -
caption the climate card with the reported heater type and with whether the car heats seats
and the steering wheel on its own in cold weather -
read back the repeats-weekly flag on climate timers that Hisingen itself already writes,
instead of inferring repetition only from the weekday list -
retain unknown wire fields on the air-quality, charge-target and amp-limit reads as
hidden-until-data rows, so a new backend field stays visible instead of being dropped -
render retained raw binary fields as their decoded wire structure (for example
"1: 1787835643") instead of a hex dump, so backend fields no client schema names stay
readable until they are identified
Fixed
- stop Polestar climate start and charge-level changes from failing with "The command may have
reached the vehicle" when the car had answered: the backend keeps these streams open after
replying, so the call deadline ended them, and a complete reply followed by that deadline was
reported as an unknown outcome; it is now accepted, while a deadline inside a frame or before
any reply still reports an unknown outcome - wait at most 15 seconds for a Polestar command to close instead of 60, and report a call
deadline on the HTTP/2 connection as a deadline instead of a cancellation, which callers
could mistake for the user cancelling - replace the Siri phrase value "undefined" in 14 locales and fail the localization check on any
placeholder value - decode Volvo
INITIALIZATION,POWER_AVAILABLE_BUT_NOT_ACTIVATEDand
OTA_INSTALLATION_IN_PROGRESS, read location speed, and stop fuel and engine-status reads for
battery-electric Volvos - advance the database migration cursor after the v5 step, which left later migrations unreachable
- stop a Data Portal health report without tyre fields from counting as a tyre all-clear
- make sign-out stay signed out: an explicit per-brand flag now suppresses auto-restore
from built-in developer credentials plus the lingering server-side grant, and relaunch
no longer re-discovers the vehicle until an explicit save or sign-in clears it - clear all brand credentials on sign-out (email, portal IDs, Volvo custom app, VIN,
Keychain items, memory cache) and invalidate the session cache, so deleting
credentials and restarting does not auto-login - keep password and command grants across a cancelled or failed sign-in: authenticate
and web authorization no longer wipe the warm session to open the window, and a
dismissed login restores the prior session - scope dead-grant clearing to the grant the IdP refused: a dead web session no longer
deletes the independent command grant, and password re-login preserves and re-verifies
it instead of deleting it - discover OIDC before reporting command authorization state, persist a fresh command
grant before verification and only discard it on definitive account mismatch, and
requiremaximumAge: 600on web callbacks - pair custom portal credentials (custom ID requires custom secret, no builtin mixing),
remove the never-read portal token Keychain write, single-flight portal test grants,
retry portal 401 once through the lifecycle with ...
v2.0.10
[2.0.10] - 2026-10-19
Added
- add configurable electricity rates
- add an Electricity section to Settings with a country choice (Sweden or other country);
market-linked pricing and the Charging Planner are offered for Sweden, every other country
uses the typed-in rate and the optional night tariff - add an Automatic electricity rate for Sweden that prices charging from the spot price for
your zone (SE1 to SE4) plus your provider's markup, with optional tax and grid fee in
öre/kWh and a VAT toggle, instead of a single typed-in price - choose the contract type for Automatic pricing: 15-minute price, as the market publishes
it, or hourly price, which averages the four quarters in each hour - choose a Swedish electricity provider (Vattenfall, Fortum, E.ON, Tibber, Bixia, Greenely,
Göteborg Energi, Skellefteå Kraft, Telge Energi, Jämtkraft, or other); the provider names
the contract and carries no prices, because markups differ per agreement - show the current price your settings produce next to the Automatic controls, with
fetching and unavailable states
Fixed
- stop the repeated Keychain password prompts after rebuilding:
make runnow signs the debug
binary with the app's identifier and team, so it has the same Keychain identity as the
installed app instead of a code hash that changes on every build - skip writing a Keychain secret that has not changed, so an unchanged password, email or token
no longer rewrites its item and its access list on every sign-in
Changed
- charge costs follow the chosen rate everywhere: the charge-to-target estimate on the
charging card, session costs, History totals, monthly and per-location breakdowns, cost
per distance, and the CSV export; changing the markup re-prices past sessions that have a
stored market cost, while sessions the market prices do not cover keep their saved cost
or your fixed rate - move the electricity rate and night tariff out of Display, Units & Energy, and the
Charging Planner out of Features, into the new Electricity section - the Charging Planner requires Sweden as well as its feature switch; choosing another
country stops it and keeps its settings for when Sweden is selected again - test: make Volvo renewability setup deterministic
- chore(updates): publish v2.0.6 appcast
v2.0.6
v2.0.5
[2.0.5] - 2026-09-21
Added
- Completely new deisgn
- classify pending Keychain consent (
errSecInteractionRequired) as its own, parked state
with its own message, instead of reporting it as a failed sign-in or storage error - keep the first Keychain read of a launch off the main thread, so an unanswerable ACL
approval can no longer hang the panel before it draws - show a one-sentence verdict of what the car is doing (charging with its finish time,
climate running) at the top of the Vehicle instrument, built only from reported signals - offer Verify now on an acknowledged command receipt, which refreshes so the receipt can
settle against a fresh vehicle reading - place the Smart Charging Planner on the Controls tab by default (feature- and
powertrain-gated as on Vehicle; a rearranged Controls tab keeps its own layout and gains
the planner through Tabs & Cards) - add a selected-trip detail line to the History overview's trip bars, pointer- and
keyboard-reachable - state which credential served a refresh: a snapshot the consumer Polestar ID session
delivered on the Developer Portal's behalf is labelled next to its age - drag card reordering as a real gesture: the dragged card tracks the pointer one-to-one
from where the handle took hold, siblings part as it crosses them, and release settles
it into its slot on the flick spring - lift unselected chips (garage, climate presets, charge presets, schedule days) under the
pointer, so every clickable answers the pointer before the click - tick the pull-to-refresh control when a pull crosses its threshold; hysteresis below the
line keeps a pull hovering at the edge from stuttering the haptic
Fixed
- settle a tab swipe's page on the flick spring from wherever the drag left it, instead of
snapping the offset back with no animation when the swipe lands where it started - give the floating charging panel's close button a 24pt target and press feedback; it was
an 18pt plain-styled icon that answered no press - sweep the Info tab's refresh glyph forward on a cumulative angle instead of spinning it
visibly in reverse when a refresh ended
Changed
- stop the "Showing last-known values" banner from pinning forever: readings carried forward
expire after seven days, so a domain the current provider can never serve (Developer
Portal software and connectivity) ages out instead of holding the banner up indefinitely - validate the fuel fill-up form inline and state what is wrong per field; Save was
previously a silent no-op on invalid input - draw no fake gauge on the Controls instrument map: the climate capsule filled a constant
width and the charge blocks showed the target setting, not progress; the charge rail now
fills only from real progress while actually charging - state both retention horizons with real numbers on the storage card
- move the account card's decisions behind one model: connected, renewable and expired are
classified from typed failure kinds instead of matched message text, and the credential
draft lives with the sheet that edits it instead of a process-wide store, so typed
secrets no longer outlive the Settings window - route every History surface through one workspace: dashboard loading, capacity, edits and
exports reach the history ledgers through a single interface, and no panel view holds the
database directly any more
Changed
- localize the App Intents surface: titles, parameters and dialogs now have keys in every
locale, AppShortcuts.strings exists for all sixteen languages (Swedish and German
translated; other locales keep English entries where translation is still pending) - add
Scripts/audit-l10n-literals.mjswith a committed baseline, so new user-facing
strings cannot ship without an English key again; 322 pre-existing unkeyed literals are
recorded there for retirement - settle the pull-to-refresh chip's collapse on the flick spring, the gesture-carried
spring, rather than a fixed fade - roll the climate target temperature between steps like every other value on the panel
v2.0.4
Polestar finally released their offical API so moved a lot of the fethcing to that, which improves alot of features! You will need to get an client id and secret from your car from their portal:
https://data-portal.polestar.com/se/shared-access
[2.0.4] - 2026-09-18
Added
- redesign the panel as a live instrument
- surface consumer identity, truthful 404s and timer offsets
- surface portal schedules, climate faults and charging estimates
- unify polestar sign-in credential selection
- display usage mode tire delta and trips
- expand portal models and trip mapping
- add eu data act telemetry and controls
- add data portal fixtures and fix tests
- expand Polestar Developer Portal support
- add account id header and live portal tests
- add polestar developer portal provider
Fixed
- harden snapshot merging and command confirmation
- add force-login path for polestar command sign-in
- make polestar augmented session and fallback resilient
- fix charger status and expand v2 states
- rethrow primary error on fallback fail
- support augmented mode in session resume
- fix steering heat default and departure
- clean duplicate tooltip and add a11y hint
- refine control sizes and tab drag metadata
- mark updater delegate methods nonisolated
- synchronize climate button gate with command
- default wire temperature to 22C for auto climate
Changed
- test: create the renders directory the data-portal render refreshes
- docs: add AGENTS.md and DESIGN.md
- chore(l10n): add portal wiring strings to all locales
- docs(docs): update polestar developer portal url
- docs(changelog): update polestar data portal notes
- test(tests): add polestar 2 e2e test and sendable fixes
- test(tests): update decode verification target
- docs(docs): update 2.0.3 changelog with climate fix
[2.0.3] - 2026-09-17
Added
- Polestar Developer Portal (EU Data Act M2M) integration. Dedicated machine-to-machine integration using credentials from the official Polestar Data Portal (
data-portal.polestar.com).- Two-tier primary with fallback architecture:
PolestarDataPortalAPIserves as the primary data provider for all 15 vehicle endpoints (battery, charging, odometer, location, exterior, health, climate, and pre-cleaning), with automatic transparent failover to consumer Polestar ID credentials upon rate limiting (HTTP 429), token expiration, or upstream outages. - Augmented dual-stack mode: Combines official EU Data Act high-precision telemetry with consumer Polestar ID remote actuation commands for door locks (
lock,unlock) and visual alerts (flashLights). - Isolated credential management: Developer credentials (Account ID, Client ID, Client Secret, OAuth Bearer tokens) are isolated in separate Keychain storage from consumer Polestar ID credentials, enabling seamless switching between connection modes without credential conflicts or session invalidation.
- Live connection testing & vehicle linking guidance: Dedicated "Test Connection" button verifies token issuance and vehicle discovery, guiding users when an account is authenticated but pending VIN linking in the portal.
- Daily quota tracking: Built-in tracking for the portal's 10,000 requests/day quota with UTC calendar rollover and live meter display in Settings.
- Official OpenAPI 3.1.0 specification & documentation: Bundled complete OpenAPI schema and step-by-step onboarding guide (
docs/api/polestar-developer-portal.md) with co-located portal screenshots for landing, API credentials overview, and M2M key management. - Build-time secret injection tooling:
Scripts/inject-secrets.shobfuscates developer credentials and compiles fallback accessors inBuiltinPolestarSecrets.
- Two-tier primary with fallback architecture:
- EU Data Act telemetry and smart energy expansion. Complete integration of all telemetry fields and remote charging management capabilities from the official Polestar OpenAPI 3.1.0 specification:
- Battery & Smart Energy:
- Bidirectional / V2X power sharing monitoring (
isBidirectionalChargingEnabled) with vehicle-enforcedminimumSocdischarge thresholds. - Dynamic spot-price optimization indicators (
isOptimizedChargingEnabled,availableOptimizedCharging). - Milestone charging duration estimates for departure readiness (
estimatedChargingTimeMinutesToMinimumSoc) and target distance (estimatedChargingTimeMinutesToTargetDistance). - Cold-weather battery discharge limits (
dischargeInfo.powerLimit,energyAvailable, andenergyAvailableIncreaserestored once warm). - DC fast-charge preconditioning tracking (
manualPreconditioningrunning status, countdown timer, and unavailability reason). - 3-way drive cycle energy breakdowns (driving, climate, battery conditioning, other) across automatic drive, since last charge, and manual trip meters.
- "Charge Now" remote override status and schedule resumption (
/charging/charge-nowand/charging/override-charge-timer).
- Bidirectional / V2X power sharing monitoring (
- Climate & Air Quality:
- Multi-zone seat heating status across all 4 seating positions (
requestedFrontLeftSeat,requestedFrontRightSeat,requestedRearLeftSeat,requestedRearRightSeat) and steering wheel heating. - Live cabin thermal delta tracking comparing interior compartment temperature against requested setpoint with active ventilation mode.
- Precision PM2.5 particulate air quality gauge (
measuredParticulateMatter25in µg/m³) with air cleaning cycle completion and validity tracking.
- Multi-zone seat heating status across all 4 seating positions (
- Diagnostics & Vehicle Health:
- 38-point precision exterior lighting inspector mapping all lamp failure warnings in
lightWarningsinto vehicle health details. - Factory recommended tyre reference pressure comparison (
frontTyresReferencePressureKpa,rearTyresReferencePressureKpa) displaying exact pressure deviations. - 12V low-voltage auxiliary battery warning tracking (
lowVoltageBatteryWarning). - Service countdown tracking for operating hours, days, and distance (
engineHoursToService,daysToService,distanceToServiceKm).
- 38-point precision exterior lighting inspector mapping all lamp failure warnings in
- Trip Computers & Navigation:
- Sub-kilometer high-precision odometer (
odometerMeters). - Triple trip computers (manual, automatic, and since last charge) with corresponding average speeds.
- Altimeter elevation (
altitude), ground speed (speed), and compass heading (heading).
- Sub-kilometer high-precision odometer (
- Closures & Geofencing:
- Charge flap open warning (
exterior.tankLid), sunroof closure state (exterior.sunroof), and vehicle alarm state (exterior.alarm). - Recognized charging location presence and dwell tracking (
is-at-charge-location,locationId,arrivedAtTimestamp). - Vehicle operational usage mode tracking (
availability.usageMode).
- Charge flap open warning (
- Direct M2M Remote Actuation:
- Direct REST API actuation for climate (
startClimate,stopClimate), cabin air purification (startPreCleaning,stopPreCleaning), target state of charge (setChargeTarget), current limits (setAmpLimit), schedule override (startChargingOverride,stopChargingOverride), charging schedules (setGlobalChargeTimer), departure timers (setClimateTimer,deleteClimateTimer), and saved charge locations (createChargeLocationAtCar,updateChargeLocationAlias,updateChargeLocationAmpLimit,updateChargeLocationMinimumSoc,setChargeLocationOptimisedCharging,deleteChargeLocation).
- Direct REST API actuation for climate (
- Battery & Smart Energy:
Fixed
- Polestar 2 remote climate actuation. When starting preconditioning on Polestar 2 (where cabin temperature is vehicle-managed), the remote command now explicitly provides the 22.0 °C comfort default on the wire. Omitting the temperature field previously caused the vehicle's climate control module (CCM) to quietly drop the command, leaving the cabin unconditioned while optimistic UI state masked the refusal.
- Climate card button gating & overlay synchronization. The Start Climate button's disabled state and in-flight spinner overlay now track the actual command being dispatched rather than the probe command, keeping the button interactive and showing clear in-flight progress.
- Sparkle updater Swift 6 concurrency. Conformed
UpdateServicetoSPUStandardUserDriverDelegatewithnonisolateddelegate methods to prevent protocol isolation errors under Swift 6 strict concurrency. - Web sign-in window teardown. Cleaned up WKWebView and window references upon dismissal to prevent retained views during re-authentication.
Changed
- chore(updates): publish v2.0.2 appcast
- test: serialize the suite at every gate, not just ci.yml
v2.0.2
V2.0.1 wasn't released due to some CI issues, so baked both changes into this one
[2.0.2] - 2026-09-16
Added
- Polestar 3 and Polestar 4 owners see their own car in the doors and tyres cards. Both
drew the Polestar 2 outline, so the panels, windows, wheels and lamps lit up wherever a
Polestar 2 has them — the wrong car, and further off the more the two differ. Each model
now carries its own silhouette and its own geometry read off that artwork: the doors,
windows, hood, tailgate and charge flap follow its own panel lines, the glass roof its own
roofline, and the tyre rings and lamp glows sit on its own wheels and lights. The three
outlines are drawn at a matched size, so switching cars no longer changes how large the
car is in the card. Polestar 1, 2, 5 and 6 and every Volvo keep the outline they had. - Tabs & Cards: every card is now the reader's to place. A new Settings pane
(Settings → Tabs & Cards) lists every card, section and banner in the app, per tab,
and lets each one be switched off, moved — by button or by dragging it onto another
card — or removed. The order the pane shows is the order the tab draws, and the
shipped tabs read the same layout record, so what Settings shows is what the panel
draws. - Tabs of your own. Build a tab from anything in Hisingen: name it, pick an icon,
and add cards from any of the shipped tabs — the charging card beside the lock
controls, the render beside the tyre schematic. Cards are drawn by the same views
the shipped tabs use, so a card behaves and looks the same wherever it is placed.
Cards that own a tab's own state (the History charts, the Info section bars) stay on
the tab that gives them that state, and are named as such instead of silently
missing. - Shipped tabs can be hidden. Any of Vehicle, Info, History and Controls can be
hidden from the tab bar; Settings is always there, because it is where the list is
managed. - Card switches double as data switches. A card's own setting is a feature the
provider fetch already consulted, so switching a card off is also the instruction to
stop requesting the telemetry behind it — but only once no card the reader can still
see needs that reading, and never for a reading they switched on by hand. Switching a
card back on resumes the fetch, and the app is told the feature selection moved rather
than guessing. - An installation that predates this reads its existing feature toggles once as its
starting layout, so a card that was already switched off does not reappear. From
then on the layout is the reader's, and the feature list no longer moves cards. - A card shown while its reading is switched off is labelled Data off in the pane
with the reason, instead of drawing empty with no explanation. - A tab emptied on purpose stays empty. "Hide All" switches the cards off where they
live rather than discarding the arrangement, so "Show All" is one click away and a
tab you arranged yourself never has cards reappear in it that you did not put there.
A tab you have never rearranged does keep receiving cards added in later versions.
Fixed
- Cards no longer render as a black field in dark mode. A card was filled with the canvas
— the panel's own colour — so in a dark appearance the cards, the panel and the desktop
behind them composited into one near-black surface, and only a hairline said where a card
ended. A card is a raised surface now: lifted toward white in the dark and off the tinted
canvas in the light, with the chip fill inset from it in turn. The contrast check models the
card surface it actually renders on rather than the canvas it used to sit on, and covers
both appearances for every theme. - The footer gear no longer kicks the panel back to another tab. Settings was reachable
two ways at once — a controller-owned mode and a tab — and the two could disagree: with the
Settings tab already selected, tapping the gear flipped the mode off and the panel fell back
to whichever tab was stored behind it. Settings is one tab now, and every route to it (the
tab, the gear,⌘,, the menu-bar item, a sign-in that ends inside Settings) writes that one
selection, so the two cannot disagree. Backing out returns to the tab Settings was opened
over. - Settings draws the same header navigation as every other tab again. The Settings
screen replaced the panel's tab bar with a header of its own — a back button, a centred
title and a close button — so the one navigation a reader had learned disappeared the
moment they opened Settings. Settings is a tab like the others now: the tab bar stays on
screen and marks the current tab exactly as it does everywhere else. Settings keeps its
own header only before sign-in, where there is no tab bar to leave by. - The card list in Settings → Tabs & Cards no longer appears twice. The card rows were
rendered once by the tab's controls and again by the page below them. - A rejected password no longer replays on every launch. With a password saved, Hisingen
offered it again at each launch even after the sign-in service had refused it, because the
"can resume" flag counted any stored password as a way in. A credential the IdP actually
rejects is dropped now, so the app asks instead of repeating a failed sign-in against
PingFederate's per-client attempt budget. A transient refusal — a 5xx, a timeout — still
keeps the session, so an outage never costs you a sign-in. - A restriction learned on one car no longer switches off the same reading on another.
Volvo stand-downs were recorded under the endpoint alone, so a vehicle that answered
"region restricted" for, say, the environment reading made Hisingen skip that reading on
every other car in the garage for the rest of the stand-down. It is scoped to the vehicle
that earned it now. - One slow vehicle service no longer holds up the whole refresh. The PCCS readings are
optional, and a hung one was allowed the full ten-second session budget before giving up;
they now give up at six, and that service stands down instead of every other reading
waiting on it. - The C3 discovery fallback is no longer recorded as a failure. Every launch wrote one
error for a request that then succeeded: the version-shape rejection that makes Hisingen
fall back is the fallback working, not a fault. It is classified as expected now, so a
support export shows the failures that actually matter.
Changed
- Authorizing remote commands no longer leaves a browser tab behind. The sign-in for
remote commands now runs in a system browser sheet that dismisses itself the moment the
grant is issued, instead of opening a tab you had to close yourself. It is the same
Polestar sign-in page, still loaded outside Hisingen, so the app never sees the Polestar
ID password, and it shares the browser session you already have — which is why a sign-in
you have already completed finishes in a blink. Declining the sheet is not treated as a
failure, and if the sheet cannot be presented at all Hisingen falls back to opening the
page in your own browser as before. - Settings is one entry point again. The Settings tab in the tab bar and the gear
in the footer now take the same path into Settings and the same path back out, so
neither can leave the panel in a state the other cannot leave. - The section row inside Settings reads like the tab bar. The destinations
(All, Accounts, Appearance, …) wore accent-filled pills of their own; they now use the
tab bar's icon-and-label treatment, its ink emphasis and the same indicator moving under
the current section, so the two navigation rows speak one language. The row still
scrolls, so no section label is truncated at the narrowest panel width. - Switching a card off releases its reading only when nothing else you can see needs
it, and a reading you switched on yourself in Settings → Features is never taken
away by moving a card. When a card's reading is released, Hisingen tells the rest of
the app the feature selection moved, so notifications, update checks, the charging
planner and the live stream re-configure instead of running against a selection that
no longer exists. - The Vehicle tab's two side-by-side rows follow the layout: hiding or moving one of a
pair no longer leaves a card trying to share a row with something that is not there. - Diagnostics name the failure rather than the code. A non-zero gRPC status now keeps
its status and the server's own message next to a stable operation name, the token
endpoint's OAuth error code is recorded without retaining the response body, a request
that never reached the server is marked as a transport failure instead of a missing
status, and older entries report where their version is unknown. Existing archives still
load, and the export carries a schema version.
[2.0.1] - 2026-09-13
Added
- First-run setup pass. The first successful sign-in now opens a one-time
"Set up Hisingen" pass instead of dropping the user straight onto the dashboard.
It offers two quick-setup presets, Enable All Safe Features (recommended) and
Enable Remote Controls (with an explicit confirmation spelling out what remote
features can change), plus the one-time Polestar remote-command browser
authorization, reachable from the same screen. Everything it touches can be
changed later in Settings → Features. Completing (or skipping) the pass is
remembered; the flag deliberately stays out of the settings archive, so an
archive restored on a new Mac is a first run there and sees the pass. An install
that upgrades with session material already on the Keychain counts as past the
pass, and a popover closed mid-pass reopens onto it inst...
v2.0.0
[2.0.0] - 2026-09-12
Version 2.0 changes what a remote command means: Hisingen no longer takes the
provider's "accepted" at face value — it waits until fresh vehicle telemetry proves
the requested outcome before calling a command confirmed. The old spot-price
recommendation is reworked into an opt-in Smart Charging Planner, the battery-health
estimate is replaced by one auditable calculation, the vehicle state model is
reorganized into named clusters, and two provider integrations that never worked in
practice are gone.
Also whole architecture has been revamped to be cleaner and easier to work with etc
Added
- Smart Charging Planner (opt-in feature, replaces the old spot-price card). It
fetches Swedish day-ahead spot prices (elprisetjustnu.se, zones SE1 Luleå – SE4
Malmö) and shows the cheapest contiguous whole-hour window that covers the energy
still needed between the current battery level and the set charge limit, with the
window's average price, the price of charging right now, and the estimated saving.
The planner works with both hourly and quarterly price series, always recommends
whole hours, and hides itself entirely once the battery is at the limit.- Prices are fetched once a day any time after 14:15 Stockholm time (when tomorrow's
file is published), covering today plus tomorrow. The series persists in the SQLite
database, so a relaunch mid-day serves from disk without touching the network;
concurrent requests share one fetch, and a late publication is retried a bounded
number of times during the evening instead of waiting a full day. - New Settings card picks the price zone (defaults to SE3, so a first run is one tap
away) and the charger output used to turn missing energy into whole charging hours
(1–250 kW, default 7.4 kW). While the car is charging, the live rate is used
instead. When the vehicle reports a GPS position, Settings suggests the
approximate zone from its latitude — a one-tap "Use" hint, never an automatic
assignment. - The card tells "tomorrow's prices arrive after 14:15" apart from "this charge is
longer than the whole price outlook" instead of showing a generic failure, and a
48-hour price outlook chart shades each interval by cost with the planned window
highlighted. A footnote shows when prices were fetched, and a hint appears when
the car carries its own charging schedules the planner does not use. - Needed energy includes an estimated AC charging-loss factor, so recommended hours
and cost figures reflect grid-side energy — what the meter actually sees. - Optional notifications: one banner per planned window (from 15 minutes before it
opens until it ends, respecting quiet hours, with a "plug in" variant when the
car is unplugged), and an opt-in daily banner when tomorrow's prices arrive,
including the cheapest hour. - Auto-start (separate explicit consent). When enabled, Hisingen sends the same
start-charging command as the Controls tab while the planned window runs — only
when the vehicle is plugged in, not already charging, and below its charge limit,
and only while the Charging Controls feature is on. Auto-start is not part of any
bulk-enable action and never transfers through a settings archive.
- Prices are fetched once a day any time after 14:15 Stockholm time (when tomorrow's
- Market-price session costs. Completed charging sessions are priced against the
actual spot price of every interval they covered (charging power integrated across
price slots, split at rate boundaries, scaled to the session's authoritative energy),
backfilled after each daily price fetch, and shown as "Market Price Cost" on the
session row next to the tariff estimate. Sessions outside the price coverage stay
uncosted rather than being priced with invented rates; spot prices exclude taxes and
grid fees, and the database migrates legacy installs to the new column on first
launch. - Climate commands confirm from telemetry. Start/Stop Climate receipts are
confirmed when a fresh climate-status reading shows the requested state, and hidden
once confirmed because the climate activity itself is the durable confirmation. - Acknowledged — a new receipt outcome for providers without telemetry proof. When
a provider accepts a command but exposes no reading that could verify it (currently
Volvo's climate status), the receipt finishes as acknowledged by the vehicle
service rather than silently polling until timeout. - Nothing the Polestar backend sends is silently dropped. Every
GetMyCarswire
field Hisingen has not decoded — top-level or nested (shown as "Field 35.5") — is
retained raw and listed under a new "Undecoded Backend Fields" disclosure in the
capability inspector, mirroring what the battery parser already did. The same
retention now covers the climate-status fields Hisingen reads but has not decoded
(surfaced in the same disclosure on the Interior & Cabin card) and the
availability frame's timestamp plus its undecoded field, retained on the identity
snapshot. - Factory content codes (paint, wheels, trim groups) are decoded from the
GetMyCars
content-code string and shown in the equipment details and Factory Passport. - Climate sessions now carry their real start and end times (wire fields 14 and 16);
a running session shows "Session Started" and "Cycle Ends" rows on the Specs card. - Air-quality readings carry when the cabin air was last measured (wire field 2,
verified live: the sensor also samples on vehicle wakes, not only during
purification), shown as an "Observed" row and distinct from the frame report time. - Charging sessions carry their market-price cost. Every recorded session is
priced against the hourly spot-price series for the configured zone: recorded
charging power is integrated per interval and priced at that interval's rate, then
scaled to the session's own energy figure. Sessions are priced by a backfill pass
once price coverage reaches them — historical day files are fetched lazily,
bounded to the seven most recent uncovered days — and a session the price series
does not fully cover stays uncosted rather than being priced with invented rates.
The session detail shows "Spot Cost" as a clearly-labelled estimate next to the
flat-tariff "Estimated Cost", and the session summary prefers the spot figure.
Changed
- The command-receipt system was rebuilt end to end.
RefreshCoordinatoris the single owner of every receipt's lifecycle from
awaiting through acknowledged, confirmed, or timed out. Confirmation is
state-driven: a 2-second first check, then a 3-second targeted poll or stream —
exact cadence, no jitter — for at most five minutes.- Only provider-authored state is persisted, entered into history, indexed for
Spotlight, or used as notification evidence. Optimistic command values now live in
a separate display projection that is rebuilt on every refresh and cleared on
relaunch, instead of being patched into the stored snapshot. - Receipts freeze their target: the VIN and provider captured at approval stay fixed
until the command finishes, even if the visible vehicle selection changes, and a
dispatch for another vehicle is stored under that VIN rather than leaking into the
active one. - Opposing commands share a conflict group, so sending Stop Climate supersedes a
pending Start Climate (and likewise for locks, windows, tailgate, pre-cleaning,
charge target, and more) instead of leaving two receipts racing each other. - Multiple commands in flight keep independent receipts with their own deadlines,
dismissal, and relaunch behavior. Receipts are stored per VIN, survive relaunch
without resending, resume their remaining confirmation window, and are restored as
timed out when the deadline passed while the app was closed. - Confirmation selections bypass the normal 15–90 second capability caches, which
otherwise kept answering confirmation polls with the pre-command value. - Diagnostic exports include every receipt's state, audit correlation IDs are
written through to the SQLite command log, and the unified log records each
lifecycle step.
- Shortcuts, deep links, and menu-bar actions target vehicles directly. A command
from Shortcuts or ahisingen://link resolves and prepares its named vehicle
through one awaited boundary (up to 30 seconds) instead of a racy select-then-send,
and reports "Command accepted; waiting for the vehicle to report the result"
instead of claiming the outcome. The menu-bar lock and resume-schedule actions send
against their own vehicle without switching the selection. - Calendar preconditioning awaits the dispatch outcome. A deferred command (for
example, no session yet) leaves the occurrence scheduled for retry instead of being
consumed, and a refused one is consumed with the reason recorded. - Notifications no longer react to command echoes. The optimistic display
projection cannot advance transition baselines or post alerts, a pending Start
Climate cannot trigger a stray "Climate stopped", and a climate stop now requires
two consecutive vehicle-timestamped readings before notifying. - Battery state of health is one auditable number: the vehicle-reported range at
100% charge divided by the configured WLTP range. Hisingen remembers the result and
updates it only after another 100% reading. The multi-signal weighted estimate and
its smoothing are gone. Range estimation needs to be in Standard and not dynamic in car for this to be as close to accurate as possible - The vehicle state snapshot is organized into named clusters — identity, energy
and charging, exterior, health, and snapshot freshness — with the vocabulary
documented in the glossary, replaci...
v1.3.5
Quite a few changes and updates as I'm trying to get the CI works as I like. Also fixed that when starting fan etc the UX didn't update
[1.3.5] - 2026-09-10
Changed
- chore: checkpoint current work before vehicle state refactor
- chore(updates): publish v1.3.4 appcast
Fixed
- run auto release on macOS
- only apply skip release to HEAD commit
- pass the tag explicitly to the publish action [skip release]
- keep the Sparkle private key file in its native base64 format [skip release]
- Remote controls re-render again after a successful command: the coordinator
extraction had dropped the optimistic state patch, so the fan animation, Start/Stop
Climate button, lock icon, and charge-target slider kept showing the pre-command
state until a later telemetry refresh carried the car's answer. A successful command
now patches the visible state immediately and holds it against stale telemetry for
90 seconds viaoptimisticCommandLockUntil, which the merge logic already honored. - The restored patch corrects two quirks the pre-refactor code had: a trunk-only
unlock no longer claims the car is centrally unlocked, and pre-cleaning updates the
air-cleaning reading its button displays instead of synthesizing a climatization
session (which surfaced a "Stop Climate" button that did not target pre-cleaning). - Chronos error-service authorization failures now enter capability backoff instead
of being cached as empty results and retried every five minutes. - Network tests now inject an in-memory API diagnostic store instead of clearing and
writing fixture traffic to the user's persisted diagnostic archive.
Full Changelog: v1.3.4...v1.3.5
v1.3.4
[1.3.4] - 2026-09-09
Fixed
- CI documentation-link validation: the glossary's ADR-0012 target is committed, and
the docs checker now catches files that exist locally but were never committed
before they can break the CI job. - The vehicle connection layer is more stable and makes fewer requests: one persistent
stream per vehicle instead of separate connections per reading, reconnects happen
only when the server closes the stream or the session actually expires, and
background telemetry polls less often.
Changed
- Vehicle history persistence deepened behind ChargingSessionLedger and
VehicleHistoryLedger. - Remote Command dispatch is unified: the Controls tab,
hisingen://deep links, and
Shortcuts intents cross one awaited dispatch authority with brand policy in the
capability gate (ADR-0012). Deep-link remote commands now work on Polestar, and
Shortcuts intents return the awaited provider outcome instead of polling.
Added
make cimirrors every CI job locally (workflows, scripts, localization, docs
links, build, tests, bundle and DMG validation), so a sync cannot fail on checks
that only run on GitHub.- Local Developer ID builds are notarized and stapled automatically when notarization
credentials are configured, andmake notarizestaples an existing build. - Releases cut themselves: after a green CI run on main the patch version bumps, a
changelog entry is generated from commit history, and the release workflow is
dispatched automatically.[skip release]in a commit message opts out.