Skip to content

Add Litter-Robot 5 night light#174508

Open
Legendberg wants to merge 4 commits into
home-assistant:devfrom
Legendberg:pr/litterrobot-lr5-night-light-color
Open

Add Litter-Robot 5 night light#174508
Legendberg wants to merge 4 commits into
home-assistant:devfrom
Legendberg:pr/litterrobot-lr5-night-light-color

Conversation

@Legendberg

@Legendberg Legendberg commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Breaking change

None.

Proposed change

Adds night light support for the Litter-Robot 5 (LR5 Pro):

  • A new light entity for the globe night light, exposing RGB color,
    brightness, and on/off. Color, brightness, and mode are written together
    because the device replaces the entire night light settings object on each
    command.
  • A new night_light_preset select offering the seven fully-saturated
    colors (red, green, blue, cyan, magenta, yellow, white) that the LED renders
    accurately at every brightness. Intermediate colors drift with brightness, so
    the light's RGB picker remains available for custom colors. Selecting a preset
    preserves the current mode and brightness.
  • The globe_brightness select's LOW/MEDIUM/HIGH levels are mapped to
    LR5-specific percentages (10/100/75). The LR5 globe LED renders brightness
    non-monotonically — a firmware quirk also present in the Whisker app, where
    e.g. 25% can appear brighter than 100% — so these values were chosen so the
    levels read as dim/medium/bright on the LR5. The Litter-Robot 4 keeps the
    standard 25/50/100 levels.

It also fixes LitterRobotSelectEntity.current_option to return None
(Unknown) instead of the literal string "None" when the current value matches
no option.

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New integration (thank you!)
  • New feature (which adds functionality to an existing integration)
  • Deprecation (breaking change to happen in the future)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

Checklist

  • I understand the code I am submitting and can explain how it works.
  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • I have followed the perfect PR recommendations
  • The code has been formatted using Ruff (ruff format homeassistant tests)
  • Tests have been added to verify that the new code works.
  • Any generated code has been carefully reviewed for correctness and compliance with project standards.

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • The manifest file has all fields filled out correctly.
    Updated and included derived files by running: python3 -m script.hassfest.
  • New or updated dependencies have been added to requirements_all.txt.
    Updated by running python3 -m script.gen_requirements_all.
  • For the updated dependencies a diff between library versions and ideally a link to the changelog/release notes is added to the PR description.

To help with the load of incoming pull requests:

@home-assistant

Copy link
Copy Markdown
Contributor

Hey there @natekspencer, @tkdrob, mind taking a look at this pull request as it has been labeled with an integration (litterrobot) you are listed as a code owner for? Thanks!

Code owner commands

Code owners of litterrobot can trigger bot actions by commenting:

  • @home-assistant close Closes the pull request.
  • @home-assistant mark-draft Mark the pull request as draft.
  • @home-assistant ready-for-review Remove the draft status from the pull request.
  • @home-assistant rename Awesome new title Renames the pull request.
  • @home-assistant reopen Reopen the pull request.
  • @home-assistant unassign litterrobot Removes the current integration label and assignees on the pull request, add the integration domain after the command.
  • @home-assistant update-branch Update the pull request branch with the base branch.
  • @home-assistant add-label needs-more-information Add a label (needs-more-information, problem in dependency, problem in custom component, problem in config, problem in device, feature-request) to the pull request.
  • @home-assistant remove-label needs-more-information Remove a label (needs-more-information, problem in dependency, problem in custom component, problem in config, problem in device, feature-request) on the pull request.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds night light control for the Litter-Robot 5 (LR5) to the platinum-quality litterrobot integration. It introduces a new light platform for the globe night light (RGB color, brightness, on/off) and a night_light_preset select offering seven fully-saturated preset colors, and it remaps the LR5 globe_brightness select to LR5-specific percentages to work around a documented firmware brightness quirk. It also fixes LitterRobotSelectEntity.current_option to return None instead of the literal string "None" when no option matches.

Changes:

  • New light.py platform (LitterRobotNightLight) exposing RGB/brightness/on-off, writing mode+brightness+color together because the device replaces the whole settings object per command.
  • New night_light_preset select and an LR5-specific globe_brightness mapping (low/medium/high10/100/75); LR4 keeps its BrightnessLevel enum.
  • Fix for current_option returning "None", plus translations, icons, PLATFORMS registration, test mock, and new/updated tests.

I verified that set_night_light_settings, night_light_color, and NightLightMode exist in the pinned pylitterbot==2025.4.0 (added in 2025.2.0), that LR5 is not a subclass of LR4 so no duplicate globe_brightness entity is produced, and that the mode-based is_on (vs. the library's is_night_light_on) is a deliberate choice that keeps AUTO mode consistent when toggling. Two design points warrant human attention: the new light overlaps with the existing globe_light/globe_brightness/night_light_preset selects (all controlling the same night-light setting), and the intentionally non-monotonic LR5 brightness mapping.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
homeassistant/components/litterrobot/light.py New LR5 night-light entity with RGB/brightness/on-off control.
homeassistant/components/litterrobot/select.py Adds night_light_preset + LR5 globe_brightness; splits LR4/LR5; fixes current_option.
homeassistant/components/litterrobot/__init__.py Registers the LIGHT platform.
homeassistant/components/litterrobot/strings.json Adds night light and preset translations.
homeassistant/components/litterrobot/icons.json Adds icons for the new entities.
tests/components/litterrobot/conftest.py Mocks set_night_light_settings.
tests/components/litterrobot/test_light.py New tests for the light platform.
tests/components/litterrobot/test_select.py Tests for the preset select; LR5 globe_brightness remains untested.
tests/components/litterrobot/snapshots/test_light.ambr Snapshot for the new light entity.

Comment on lines +139 to +155
LitterRobot5: (
RobotSelectEntityDescription[LitterRobot5, str](
key="globe_brightness",
translation_key="globe_brightness",
current_fn=lambda robot: next(
(
level
for level, pct in LR5_GLOBE_BRIGHTNESS.items()
if pct == robot.night_light_brightness
),
None,
),
options_fn=lambda _: list(LR5_GLOBE_BRIGHTNESS),
select_fn=lambda robot, opt: robot.set_night_light_brightness(
LR5_GLOBE_BRIGHTNESS[opt]
),
),
@Legendberg Legendberg marked this pull request as ready for review June 23, 2026 01:21
Copilot AI review requested due to automatic review settings July 2, 2026 00:15

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Comment on lines +139 to +155
LitterRobot5: (
RobotSelectEntityDescription[LitterRobot5, str](
key="globe_brightness",
translation_key="globe_brightness",
current_fn=lambda robot: next(
(
level
for level, pct in LR5_GLOBE_BRIGHTNESS.items()
if pct == robot.night_light_brightness
),
None,
),
options_fn=lambda _: list(LR5_GLOBE_BRIGHTNESS),
select_fn=lambda robot, opt: robot.set_night_light_brightness(
LR5_GLOBE_BRIGHTNESS[opt]
),
),
@Legendberg Legendberg force-pushed the pr/litterrobot-lr5-night-light-color branch from e641363 to f67a183 Compare July 2, 2026 00:59
Comment on lines +22 to +34
# Fully saturated colors (every channel 0 or 255) are the only ones the LED
# renders accurately at all brightness levels; intermediate colors drift. They
# are offered as named presets while the light's RGB picker stays available for
# custom colors.
NIGHT_LIGHT_PRESETS: dict[str, str] = {
"red": "#FF0000",
"green": "#00FF00",
"blue": "#0000FF",
"cyan": "#00FFFF",
"magenta": "#FF00FF",
"yellow": "#FFFF00",
"white": "#FFFFFF",
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really want to make presets if we have all the colors we want?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes — I'd argue we do, because on this hardware "all the colors we want" is exactly what we don't reliably have. The rendered color for a stored RGB value isn't necessarily the color the globe shows: it varies with the brightness level. This is reproducible in the stock Whisker app (which only offers a color wheel and brightness slider — no hex or numeric input): pick a color, change the brightness, and the globe can render a visibly different hue.

Concrete example, live on my LR5 Pro right now — night light set to #83FF5B / RGB (131, 255, 91), spring green, with the stored value verified identical in the app, the cloud API, pylitterbot, and HA. Varying only brightness:

Brightness Globe shows
10–30% green (faithful)
40–50% vivid purple (~170, 60, 220)
60–75% near-white with a slight purple tint
100% light blue (~140, 190, 255)

Same stored color, four distinct renderings, faithful only below ~⅓ brightness — and deterministic: the same values render the same way whether set via the brightness slider or the select. Since the data path carries the value unchanged end-to-end, this is the device's rendering, not the integration or library.

The seven presets are colors verified on hardware (all seven re-tested today) to render true at every brightness level. The light entity still provides the full color wheel and brightness slider, so users can absolutely find their own colors — the presets just give them a set that's proven to display correctly. Happy to attach photos of the globe showing the same stored color at 20% (green) vs 44% (purple).

Comment on lines +29 to +34
def _hex_to_rgb(hex_color: str) -> tuple[int, int, int]:
"""Convert a color hex string into an RGB tuple."""
value = hex_color.lstrip("#")
if len(value) == 3:
value = "".join(component * 2 for component in value)
return int(value[0:2], 16), int(value[2:4], 16), int(value[4:6], 16)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, hex to RGB should be in the library (if it's not a common helper already)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — opened natekspencer/pylitterbot#425 adding a night_light_rgb_color property (and RGB tuple support in set_night_light_settings). Once it's released I'll bump the requirement here and drop the local helper.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in cec15d4 — pylitterbot 2025.6.1 (which includes natekspencer/pylitterbot#425) is now required, the local _hex_to_rgb helper is gone, the light entity reads night_light_rgb_color and passes RGB tuples to set_night_light_settings, and the presets are plain RGB tuples. No color hex knowledge left in the integration.

Comment on lines +36 to +40
# The LR5 globe LED renders brightness non-monotonically (a firmware quirk also
# present in the Whisker app), so LOW/MEDIUM/HIGH map to the percentages that
# read as dim/medium/bright on the LR5 rather than the LR4 25/50/100 levels. The
# light entity exposes the full continuous range.
LR5_GLOBE_BRIGHTNESS: dict[str, int] = {"low": 10, "medium": 100, "high": 75}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the same as the light brightness?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It writes the same underlying device setting, yes — but it isn't a duplicate of the slider, it's a calibrated view of it, because the firmware doesn't render the numeric scale faithfully. Brightness is non-monotonic on the LR5: 75% displays visibly brighter than 100%. Anyone with the device can reproduce this with pylitterbot alone (set_night_light_brightness(100), look at the globe, then (75)) — so it's the device, not this integration. That's why low/medium/high map to eye-calibrated percentages (10/100/75) that actually read as dim/medium/bright on the hardware, while the light entity's slider keeps the full range. The LR4 keeps its standard 25/50/100 levels.

@home-assistant home-assistant Bot marked this pull request as draft July 2, 2026 14:03
@home-assistant

home-assistant Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Please take a look at the requested changes, and use the Ready for review button when you are done, thanks 👍

Learn more about our pull request process.

Copilot AI review requested due to automatic review settings July 5, 2026 00:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Comment on lines +140 to +168
LitterRobot5: (
RobotSelectEntityDescription[LitterRobot5, str](
key="globe_brightness",
translation_key="globe_brightness",
current_fn=lambda robot: next(
(
level
for level, pct in LR5_GLOBE_BRIGHTNESS.items()
if pct == robot.night_light_brightness
),
None,
),
options_fn=lambda _: list(LR5_GLOBE_BRIGHTNESS),
select_fn=lambda robot, opt: robot.set_night_light_brightness(
LR5_GLOBE_BRIGHTNESS[opt]
),
),
RobotSelectEntityDescription[LitterRobot5, str](
key="night_light_preset",
translation_key="night_light_preset",
current_fn=_active_night_light_preset,
options_fn=lambda _: list(NIGHT_LIGHT_PRESETS),
select_fn=lambda robot, opt: robot.set_night_light_settings(
# The API replaces the whole object, so keep mode + brightness.
mode=robot.night_light_mode or NightLightMode.ON,
brightness=robot.night_light_brightness,
color=NIGHT_LIGHT_PRESETS[opt],
),
),
Legendberg and others added 3 commits July 5, 2026 16:16
Adds an RGB light entity for the Litter-Robot 5 globe night light, plus a
globe brightness and night light preset select. Includes @OverRide markers
required by current mypy and tests for the LR5 globe brightness mapping.

Rebased on dev; regenerated the light snapshot for the new enum-keyed
capability/state attribute serialization.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bump pylitterbot to 2025.6.1 and drop the local hex parsing: the light
entity reads night_light_rgb_color and passes RGB tuples straight to
set_night_light_settings, and the night light presets are defined as
RGB tuples, removing all color hex knowledge from the integration.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Legendberg Legendberg force-pushed the pr/litterrobot-lr5-night-light-color branch from 17c2b63 to cec15d4 Compare July 5, 2026 16:44
@Legendberg Legendberg marked this pull request as ready for review July 5, 2026 17:19
Copilot AI review requested due to automatic review settings July 5, 2026 17:19
@home-assistant home-assistant Bot requested a review from joostlek July 5, 2026 17:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants