Skip to content

Add Denon rs232 integration#166923

Open
balloob wants to merge 12 commits intodevfrom
denon-rs232
Open

Add Denon rs232 integration#166923
balloob wants to merge 12 commits intodevfrom
denon-rs232

Conversation

@balloob
Copy link
Copy Markdown
Member

@balloob balloob commented Mar 31, 2026

Breaking change

Proposed change

This adds a new integration for Denon receivers controlled via RS232 serial protocol.

Waiting for #167023

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:

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new denon_rs232 integration to Home Assistant to control Denon receivers over an RS232 serial connection, including config flow, media player entities, translations, and initial test coverage.

Changes:

  • Introduces the denon_rs232 integration (config entry setup/unload + media_player platform).
  • Adds config flow for selecting a detected USB serial port or entering a port manually.
  • Adds tests for config flow and media_player behavior, plus generated/requirements/brand/codeowner updates.

Reviewed changes

Copilot reviewed 15 out of 17 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
homeassistant/components/denon_rs232/init.py Sets up/unloads the receiver and forwards the config entry to platforms.
homeassistant/components/denon_rs232/config_flow.py Implements UI flow for selecting/entering a serial port and validating connectivity.
homeassistant/components/denon_rs232/media_player.py Adds receiver media_player entities (main + zones) with volume/source/power controls.
homeassistant/components/denon_rs232/const.py Defines domain/logger and typed config entry alias.
homeassistant/components/denon_rs232/strings.json Provides translations for config flow and source attribute states.
homeassistant/components/denon_rs232/quality_scale.yaml Declares integration quality scale rule statuses.
homeassistant/components/denon_rs232/manifest.json Declares the new integration metadata and dependency.
tests/components/denon_rs232/conftest.py Provides fixtures and a receiver test double for integration tests.
tests/components/denon_rs232/test_config_flow.py Tests config flow success, error handling, and duplicate-port aborts.
tests/components/denon_rs232/test_media_player.py Tests entity creation, state updates, and service command mapping.
tests/components/denon_rs232/init.py Defines shared constants for tests.
requirements_all.txt Adds denon-rs232==3.0.0 to runtime requirements.
requirements_test_all.txt Adds denon-rs232==3.0.0 to test requirements.
homeassistant/generated/integrations.json Registers the new integration in generated metadata.
homeassistant/generated/config_flows.py Adds the integration to the generated config flow list.
homeassistant/brands/denon.json Adds denon_rs232 to Denon brand integrations list.
CODEOWNERS Adds codeowners for the new integration and its tests.

Copilot AI review requested due to automatic review settings April 1, 2026 17:45
@balloob balloob marked this pull request as ready for review April 1, 2026 17:46
Comment on lines +122 to +127
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, config_entry.entry_id)},
manufacturer="Denon",
model=model.name,
name=config_entry.title,
)
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.

If they are zones, can they be in different areas? Should we have a different device so you can easily assign it to a different area?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

You can add entities to other areas, do we really want to make a receiver show up as 3 different devices?

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.

There are integrations that do this, I believe cambridge audio, bluesound

@home-assistant home-assistant bot marked this pull request as draft April 1, 2026 17:52
@home-assistant
Copy link
Copy Markdown

home-assistant bot commented Apr 1, 2026

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.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 15 out of 17 changed files in this pull request and generated 1 comment.

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Copilot AI review requested due to automatic review settings April 2, 2026 14:46

from .const import DOMAIN, LOGGER

MODEL_OPTIONS = {key: model.name for key, model in MODELS.items()}
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.

Why would we include Other? (Do note again that we can use a selectselector here if we want to keep Other in).

In a way it would be cool if we can discover more models and have people report that to us so we can add them to the list

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The RS232 protocol does not support capability or model discovery. Users need to tell us the models in advance so we can populate the right source list.

Example: https://github.com/home-assistant-libs/denon-rs232/blob/main/src/denon_rs232/models.py#L198-L210

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.

Yea, I figured, but more like, there's not a real nice way for users to tell us their device upfront (and what its capable off). And I guess you can add this option to the reconfiguration flow in the future so the user can select another one.

There's no nice way to allow people to use it while finding out what device they use without disallowing them to set it up.

One option could be to allow to reconfigure it, log in the logs that users need to create an issue with the functionality it has, the model and that they need to reconfigure it once fixed. But that also depends on people reading logs

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

So with Other, at least we give them all possible source options. And then hopefully, some people will open github issues and help fix it for everyone.

Reconfigure flow has been left out of the initial PR, can be added in the future.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 15 out of 17 changed files in this pull request and generated 2 comments.

Copilot AI review requested due to automatic review settings April 2, 2026 15:12
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 15 out of 17 changed files in this pull request and generated 3 comments.

async def test_only_active_zones_are_created(
hass: HomeAssistant, initial_receiver_state: MockState
) -> None:
"""Test setup only creates entities for zones with queried power state."""
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.

Why do we do this?

Copy link
Copy Markdown
Member Author

@balloob balloob Apr 2, 2026

Choose a reason for hiding this comment

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

If power of a zone is None, the receiver does not have that zone.

Comment on lines +309 to +318
def test_input_source_translation_keys_cover_all_enum_members() -> None:
"""Test all input sources have a declared translation key."""
assert set(INPUT_SOURCE_DENON_TO_HA) == set(InputSource)

strings = load_json(STRINGS_PATH)
assert set(INPUT_SOURCE_DENON_TO_HA.values()) == set(
strings["entity"]["media_player"]["receiver"]["state_attributes"]["source"][
"state"
]
)
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.

We generally don't have tests for this

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This test is to:

  • ensure we map all values from the libraries enum
  • we provide a translation for each one

This will help make sure that future library upgrades are not just blindly applied if they need a translation

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 2, 2026 15:34
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 15 out of 17 changed files in this pull request and generated 3 comments.

Copilot AI review requested due to automatic review settings April 2, 2026 19:44
@balloob balloob marked this pull request as ready for review April 2, 2026 19:44
@home-assistant home-assistant bot requested a review from joostlek April 2, 2026 19:44
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 16 out of 18 changed files in this pull request and generated 2 comments.

Comment on lines +23 to +30
try:
await receiver.connect()
await receiver.query_state()
except (ConnectionError, OSError) as err:
LOGGER.error("Error connecting to Denon receiver at %s: %s", port, err)
if receiver.connected:
await receiver.disconnect()
raise ConfigEntryNotReady from err
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

Catch TimeoutError (and any other connect/query exceptions you already handle in the config flow) during config entry setup so the entry retries instead of failing and leaving an open connection.

Copilot uses AI. Check for mistakes.
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, config_entry.entry_id)},
manufacturer="Denon",
model_id=model.name,
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

Populate the device registry model field (and reserve model_id for a stable identifier) so the device shows a proper model name in the UI and can be filtered consistently.

Suggested change
model_id=model.name,
model=model.name,

Copilot uses AI. Check for mistakes.
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