Skip to content

Support half-degree setpoints driven by device metadata#148

Open
jeffscottmtl wants to merge 1 commit into
nickustinov:mainfrom
jeffscottmtl:feature/half-degree-setpoints
Open

Support half-degree setpoints driven by device metadata#148
jeffscottmtl wants to merge 1 commit into
nickustinov:mainfrom
jeffscottmtl:feature/half-degree-setpoints

Conversation

@jeffscottmtl

Copy link
Copy Markdown

Closes #60, and covers the Eve Thermo case from #102.

What this does

Temperature setpoints (thermostat target, heater-cooler heating and cooling thresholds, and Home Assistant climate targets) now step and display in the increment the device actually advertises, typically 0.5 °C, instead of hardcoded whole degrees. A 21.5° setpoint set from Apple Home finally reads 21.5° rather than 22°, and the ± steppers can reach it.

It is metadata-driven, so it adapts per device: a 0.5° radiator steps by 0.5°, and a device that only reports whole degrees is completely unchanged.

In Celsius mode, a heat-mode setpoint of 21.5° now displays 21.5° (previously 22°), one + click snaps it to 22°, and an Auto-mode range of 20.5° / 24.5° shows both halves in the widened labels. Fahrenheit is deliberately untouched: the same 21.5 °C setpoint still reads 71° and still steps one displayed degree per click (the #137 behaviour). Happy to attach rendered screenshots if useful.

How it works

  • Metadata through the bridge. ServiceData carries step/min/max for the target temperature and both thresholds, following the existing rotationSpeedMin/Max and colorTemperatureMin/Max precedent. On HomeKit they come from HMCharacteristicMetadata (stepValue/minimumValue/maximumValue); on Home Assistant from target_temp_step/min_temp/max_temp. HA values are normalized to internal Celsius, and because the step is a delta it scales by 5/9 without the freezing-point offset on Fahrenheit servers.
  • Sane UI step. TemperatureFormatter.uiStep rounds the advertised step up to the nearest 0.5 °C. HomeKit's spec default step is 0.1 °C, which is unusable one click at a time, so 0.5 is the floor. Missing metadata falls back to 1.0, i.e. no behaviour change.
  • Grid-snap stepping. In Celsius the value snaps to the step grid, so an off-grid 21.3 steps to 21.5 or 21.0. The Fahrenheit branch of step is untouched.
  • Honest display. A new formatSetpoint shows one decimal only when the value is fractional (21° vs 21.5°); the existing whole-degree format and its ceil are left exactly as they are, so sensor readouts and Fahrenheit are unaffected.
  • Metadata-driven clamps. Setpoint clamps use the device min/max when present, with the previous constants (10/30, 16/30) as fallback. The 1 °C heat/cool spread is kept, since ecobee rejects writes with a smaller gap.
  • Bonus fix. ACMenuItem stepped the raw Celsius value by 1 (1.8 °F per click), the same class of bug as [Bug] Changing Temperature Skips from 72 to 70 #137 but never fixed for heater-coolers. It now goes through TemperatureFormatter.step like the other climate items, so Fahrenheit AC stepping moves one displayed degree.
  • Layout. Auto-mode range labels widen to fit half-degree strings; mode buttons narrow slightly to keep the row inside its width budget.

Tests

The suite goes from 987 to 1023 (36 added), all green on xcodebuild test -scheme Itsyhome -destination "platform=macOS". Coverage includes the uiStep derivation table, Celsius grid-stepping and float-noise cases, formatSetpoint whole/half/negative, HA unit scaling, ServiceData codable round trips and legacy-JSON decode, and real label-text assertions through the menu-item views. The AC Fahrenheit fix and the new threshold-clamp behaviour are pinned with mutation-verified regression tests (reverting the fix makes them fail).

The three existing #137 Fahrenheit regression tests are byte-identical and still pass.

Notes

  • Half degrees are a Celsius-mode feature, matching the [Feature request] add support for 0.5 degree setting for heating #60 request. Fahrenheit users keep whole-degree stepping and display by design.
  • HAClimateMenuItem intentionally keeps its trailing margin, so its Auto layout differs slightly from the HomeKit items; noted in a comment.
  • One pre-existing adjacent bug spotted but left untouched to keep the diff focused: HA scene target temperatures skip unit normalization (EntityMapper generateSceneActions), so on Fahrenheit servers the scene value is in °F while everything else is Celsius. Happy to send that as a separate PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_018RGy77ui93iHqVnNLtrVWF

Thermostats, heater-coolers, and Home Assistant climate entities now step
and display their setpoints in the increment the device advertises,
typically 0.5 °C, instead of hardcoded whole degrees. A 21.5° setpoint
set from Apple Home finally reads 21.5°, not 22°, and the ± steppers can
reach it.

How it works:
- ServiceData carries step/min/max metadata for target temperature and
  both thresholds, read from HMCharacteristicMetadata on HomeKit (same
  pattern as the existing rotation-speed and colour-temperature ranges)
  and from target_temp_step/min_temp/max_temp on Home Assistant, with
  Fahrenheit-server values normalized to internal Celsius (the step is a
  delta, so it scales by 5/9 without the offset).
- TemperatureFormatter.uiStep rounds the advertised step up to the
  nearest 0.5 °C (HomeKit's spec default of 0.1 °C is unusable one click
  at a time); missing metadata keeps whole-degree stepping.
- Celsius stepping snaps to the step grid, so an off-grid 21.3 steps to
  21.5 or 21.0. Fahrenheit stepping is untouched and keeps the one
  displayed-degree-per-click behaviour from nickustinov#137; its regression tests
  are byte-identical.
- Setpoint labels use a new formatSetpoint that shows one decimal only
  when the value is fractional; clamps use the device's min/max with the
  previous constants as fallback. The heat/cool gap stays 1 °C (ecobee
  rejects writes with a smaller spread).
- ACMenuItem now steps through TemperatureFormatter.step like the other
  climate items, fixing its latent Fahrenheit bug where each click moved
  the raw Celsius value by 1 (1.8 °F per click, the nickustinov#137 class of bug).
- Auto-mode range labels widen to 32 pt to fit half-degree strings, with
  the mode buttons at 34 pt to keep the row inside its 260 pt budget.

Tests: 1,023 passing (987 before), 36 added, including mutation-verified
regression tests for the AC Fahrenheit fix, metadata codable round trips,
grid stepping and formatting tables, HA unit scaling, and real
label-text assertions through the menu item views.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RGy77ui93iHqVnNLtrVWF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature request] add support for 0.5 degree setting for heating

1 participant