Skip to content

Add DALI integration with dimmable LED driver support - #187347

Open
markus-becker-tridonic-com wants to merge 30 commits into
home-assistant:devfrom
tridonic-com:dali-pr1-dimmable-led-driver
Open

markus-becker-tridonic-com wants to merge 30 commits into
home-assistant:devfrom
tridonic-com:dali-pr1-dimmable-led-driver

Conversation

@markus-becker-tridonic-com

@markus-becker-tridonic-com markus-becker-tridonic-com commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Breaking change

Proposed change

This is the first pull request for a new DALI integration, deliberately kept
small per the "keep the first pull request
small"

guidance. It adds a hub integration that drives a DALI bus directly through a
USB interface
and, in this first PR, exposes dimmable LED control gear
(device type 6)
as light entities.

Scope of this PR:

  • USB-discovered and manual-serial config flow for the DALI-USB interface, with
    an optional step to power the DALI bus from the interface.
  • A 30-second polling coordinator that discovers control gear on the bus and
    reads each gear's actual level and min/max level.
  • light entities with brightness control, honouring the gear's dimming
    curve
    (logarithmic vs. linear) when converting between Home Assistant
    brightness and DALI arc-power levels.
  • Reconnect handling when the USB interface is unplugged and replugged.

To keep this first PR small it ships only the light platform; diagnostics,
repair flows, and the other entity platforms follow in the PRs below. The
integration targets the Silver quality scale in this PR.

About DALI and python-dali

DALI (Digital Addressable Lighting Interface) is the open IEC 62386 lighting
control standard. It is vendor-neutral, and the DALI Alliance certifies a large
catalogue of interoperable products from many manufacturers
(https://www.dali-alliance.org/products). This integration speaks the standard
itself rather than one vendor's cloud or gateway, so it is not limited to a
single brand of luminaire.

It builds on the python-dali library (IEC 62386 control gear, control
devices and drivers). A substantial amount of upstream work went into
python-dali to make this integration possible — an asyncio-native unified
driver send() API, DT6 LED and DT8 colour queries, DT1 self-contained
emergency support with memory bank 208, IEC 62386-306 general sensors, the DiiA
memory-bank decoders (Parts 251/252/253), and stricter typing. Those capabilities
are introduced to Home Assistant incrementally in the follow-up PRs below.

How this differs from the Lunatone and Sunricher DALI integrations

Home Assistant already has two DALI-adjacent integrations, both of which talk to
a networked DALI gateway over IP:

  • Lunatone — a REST client for the Lunatone DALI-2 IoT / IoT4 gateway and
    DALI-2 Displays (local_polling).
  • Sunricher DALI — a client for the Sunricher SR-GW-EDA network DALI gateway
    (local_push).

This DALI integration is different: it drives a DALI-USB interface plugged
directly into the Home Assistant host
and speaks the raw IEC 62386 protocol on
the bus through python-dali (dependencies: ["usb"], USB VID/PID discovery).
There is no network gateway or vendor cloud in between.

Testing

Tested against a Lunatone DALI-USB2 interface with multiple Tridonic
devices
on the DALI bus.

Follow-up PRs

The complete integration — the final state after all of the follow-up PRs below —
is already available for early testers as a HACS custom integration at
tridonic-com/hacs-dali.

To keep reviews manageable, functionality lands incrementally after this PR:

  • PR 2 — device identity (memory bank 0) and luminaire (memory bank 1)
    sensors, fade-time configuration entities, identify/rescan buttons; raises the
    integration to Gold, including the short-address assignment and
    address-collision repairs. https://github.com/tridonic-com/core/tree/dali-pr2-identity-memory-banks
    is currently rebased on PR1. The branches for the remaining PRs in https://github.com/tridonic-com/core/branches need rebasing.
  • PR 3 — DT8 colour control gear (colour temperature and xy colour).
  • PR 4 — control-gear and light-source diagnostics (memory banks 205/206),
    luminaire maintenance / rated life (memory bank 207) and active energy (memory
    bank 202).
  • PR 5 — DT1 self-contained emergency lighting (memory bank 208).
  • PR 6 — DALI-2 control/input devices: occupancy and pushbutton events.
  • PR 7 — DALI-2 Part 306 general sensors (temperature, humidity, CO₂,
    illuminance, …).

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 home-assistant Bot 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.

When adding new integrations, limit included platforms to a single platform. While we appreciate the effort, reviewing larger than necessary PRs slows down the review process. Please reduce this PR to a single platform. See the review process for more details.

@home-assistant

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.

@github-actions

Copy link
Copy Markdown
Contributor

Check requirements

Checked at commit 2764f76.

⚠️ Some checks require attention — see the details below.

Package Old New No Advisories Not Yanked Repo Public CI Upload Release Pipeline Security PR Link Async Safe
python-dali — 0.14 ✅ ✅ ✅ ✅ ✅ ☑️ ❌ ✅
📦 python-dali: 0.14
  • No Advisories: ✅ No active advisories reported by PyPI for version 0.14.
  • Not Yanked: ✅ Version 0.14 is a live (non-yanked) release.
  • Repo Public: ✅ https://github.com/sde1000/python-dali is publicly accessible.
  • CI Upload: ✅ Trusted Publisher attestation found (GitHub).
  • Release Pipeline: ✅ OIDC via Trusted Publisher attestation (GitHub); automated CI upload verified by PyPI.
  • Security: ☑️ Baseline scan found nothing obvious in setup.py, pyproject.toml, dali/__init__.py, dali/driver/usb.py, dali/driver/hid.py, dali/driver/serial.py. This is not a security review — only the cheap checks were run.
  • PR Link: ❌ PR description must link to the source repository at https://github.com/sde1000/python-dali. A PyPI page link is not sufficient. The description links to contributors.home-assistant.io, the tridonic-com/hacs-dali fork, the home-assistant.io docs PR, and the brands PR, but not to github.com/sde1000/python-dali itself.
  • Async Safe: ✅ New dependency reviewed in full. The async surfaces (dali/driver/hid.py, dali/driver/serial.py) use non-blocking I/O correctly: dali/driver/hid.py opens the device fd with os.O_NONBLOCK and integrates it via asyncio.get_running_loop().add_reader(...), and dali/driver/serial.py is built on asyncio.Protocol/transport.write(). No sync requests/socket/subprocess/time.sleep calls were found reachable from async def bodies.

Generated by Check requirements (AW) · copilot · auto · 72.7 AIC · ⌖ 6 AIC · ⊞ 13.4K · ◷

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.

🟡 Changes recommended

State reporting, serial identity handling, transport cleanup, and communication-error handling have unresolved correctness issues.

8 open issues
What changed in this PR

Adds a USB-connected DALI hub integration to Home Assistant, initially supporting dimmable LED control gear.

Changes:

  • Adds USB discovery, manual serial configuration, and optional bus power.
  • Implements brightness control, polling, reconnect handling, repairs, and diagnostics.
  • Adds integration tests, metadata, and dependencies.
File Description
tests/​components/​dali/​test_repairs.py Tests bus-power repair.
tests/​components/​dali/​test_light.py Tests light controls and brightness conversion.
tests/​components/​dali/​test_init.py Tests setup, unloading, and reconnection.
tests/​components/​dali/​test_diagnostics.py Tests diagnostics output.
tests/​components/​dali/​test_coordinator.py Tests polling and gear metadata.
tests/​components/​dali/​test_config_flow.py Tests discovery and configuration.
tests/​components/​dali/​snapshots/​test_diagnostics.ambr Records expected diagnostics.
tests/​components/​dali/​conftest.py Provides fake driver fixtures.
tests/​components/​dali/​__init__.py Initializes the test package.
requirements_all.txt Registers integration dependencies.
homeassistant/​generated/​usb.py Registers USB discovery matching.
homeassistant/​generated/​integrations.json Adds integration metadata.
homeassistant/​generated/​config_flows.py Registers the config flow.
homeassistant/​components/​dali/​strings.json Adds configuration and repair messages.
homeassistant/​components/​dali/​repairs.py Implements bus-power repair.
homeassistant/​components/​dali/​README.md Documents installation and operation.
homeassistant/​components/​dali/​quality_scale.yaml Records quality-scale status.
homeassistant/​components/​dali/​manifest.json Declares dependencies and discovery metadata.
homeassistant/​components/​dali/​light.py Implements dimmable lights.
homeassistant/​components/​dali/​helpers.py Defines the DALI mask constant.
homeassistant/​components/​dali/​diagnostics.py Serializes integration diagnostics.
homeassistant/​components/​dali/​coordinator.py Discovers gear and refreshes state.
homeassistant/​components/​dali/​const.py Defines shared constants and identifiers.
homeassistant/​components/​dali/​config_flow.py Implements USB and serial configuration.
homeassistant/​components/​dali/​__init__.py Manages integration lifecycle.
CODEOWNERS Assigns integration ownership.

Review effort: Balanced

Comment thread homeassistant/components/dali/__init__.py Outdated
Comment thread homeassistant/components/dali/config_flow.py
Comment thread homeassistant/components/dali/config_flow.py
Comment thread homeassistant/components/dali/coordinator.py Outdated
Comment thread homeassistant/components/dali/coordinator.py Outdated
Comment thread homeassistant/components/dali/light.py Outdated
Comment thread homeassistant/components/dali/light.py
Comment thread homeassistant/components/dali/light.py Outdated
@markus-becker-tridonic-com

markus-becker-tridonic-com commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor Author

FYI: python-dali is dual licensed: GPL-3.0 and LGPL-3.0. PyPi listing has "LGPL-3.0-or-later". Does this need to be changed on PyPi?
It has been changed on PyPi now.

I will address the coverage and bot comments.

Copilot AI balanced review requested due to automatic review settings October 19, 2026 15:24

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.

Comment thread homeassistant/components/dali/config_flow.py Outdated
Comment thread homeassistant/components/dali/coordinator.py
Comment thread homeassistant/components/dali/manifest.json Outdated
Comment thread homeassistant/components/dali/__init__.py Outdated
Comment thread homeassistant/components/dali/config_flow.py Outdated
Comment thread homeassistant/components/dali/coordinator.py Outdated
@github-actions

Copy link
Copy Markdown
Contributor

Check requirements

Checked at commit b39d456.

All requirements checks passed. ✅

Package Old New No Advisories Not Yanked Repo Public CI Upload Release Pipeline Security PR Link Async Safe
python-dali — 0.15 ✅ ✅ ✅ ✅ ✅ ☑️ ✅ ✅
📦 python-dali: 0.15
  • No Advisories: ✅ No active advisories reported by PyPI for version 0.15.
  • Not Yanked: ✅ Version 0.15 is a live (non-yanked) release.
  • Repo Public: ✅ https://github.com/sde1000/python-dali is publicly accessible.
  • CI Upload: ✅ Trusted Publisher attestation found (GitHub).
  • Release Pipeline: ✅ OIDC via Trusted Publisher attestation (GitHub); automated CI upload verified by PyPI.
  • Security: ☑️ Baseline scan found nothing obvious in setup.py, pyproject.toml, dali/__init__.py. Install is a plain setuptools.build_meta build with no custom hooks, and the inspected modules contain no network calls or dynamic code execution. This is not a security review — only the cheap checks were run.
  • PR Link: ✅ PR description links to the source repository at https://github.com/sde1000/python-dali.
  • Async Safe: ✅ No blocking calls found reachable from async def methods. The library is async-native (asyncio, serialx for serial transport); the HID driver (dali/driver/hid.py) uses os.read/os.write on a file descriptor opened with O_NONBLOCK and registered via asyncio.get_running_loop().add_reader, which is a standard non-blocking integration pattern rather than a blocking call.

Generated by Check requirements (AW) · copilot · auto · 95.2 AIC · ⌖ 8.3 AIC · ⊞ 13.4K · ◷

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.

🟡 Changes recommended

The pinned driver rejects the configuration probe argument, preventing both manual setup and USB discovery.

3 open issues
3 previously missed issues

In code that hasn't changed since last review

Medium severity Register transport cleanup before awaiting driver connection

homeassistant/​components/​dali/​__init__.py:39

Register transport cleanup before awaiting driver.connect(). The driver opens the serial transport before awaiting its handshake. Cancellation during that handshake bypasses the exception handler and never reaches the unload registration, leaving the port open. Registering the coordinator's cleanup first lets config-entry setup cleanup handle cancellation and unexpected exceptions too.

Medium severity Preserve scheduled bus refresh when publishing gear updates

homeassistant/​components/​dali/​coordinator.py:114

Preserve the scheduled bus-wide refresh when publishing a single-gear update. async_set_updated_data() cancels the pending poll and starts another 30-second interval. Commands issued less than 30 seconds apart to one light can therefore indefinitely prevent polling other gear and discovering new devices. Notify listeners without resetting the polling timer.

Medium severity Add cannot_connect translation for USB discovery aborts

homeassistant/​components/​dali/​strings.json:4

Add cannot_connect to the abort translations. async_step_usb() aborts with this reason when probing fails, but the existing config.error.cannot_connect entry only covers form errors. Without config.abort.cannot_connect, USB discovery failures lack their intended explanation.

5 resolved since last review

Review effort: Balanced

Comment thread homeassistant/components/dali/config_flow.py
Copilot AI balanced review requested due to automatic review settings October 20, 2026 07:48
@markus-becker-tridonic-com

Copy link
Copy Markdown
Contributor Author

Waiting for merge of sde1000/python-dali#165 and release of python-dali 0.16.

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.

Comment thread homeassistant/components/dali/README.md Outdated
Comment thread homeassistant/components/dali/config_flow.py Outdated
Comment thread homeassistant/components/dali/coordinator.py Outdated
Comment thread homeassistant/components/dali/coordinator.py Outdated
Comment thread homeassistant/components/dali/manifest.json
Comment thread homeassistant/components/dali/strings.json
Copilot AI balanced review requested due to automatic review settings October 20, 2026 08:24
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Serialise the periodic bus scan with single-gear command updates so a
  mid-scan read cannot publish a stale level over a just-sent command.
- Report full arc power as full brightness instead of the minimum-level value.
- Handle a selected serial port disappearing before the form is submitted.
- Skip the control-device instance scan on connect, as only control gear is used.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Connect with apply_settings=False so probing a USB-discovered interface
cannot depower a bus its integrated supply is already driving. This needs
the non-mutating probe added in python-dali 0.16; the requirement bump
follows once that release is published.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tests.common was moved to the pytest_homeassistant package on dev; follow
the move so the tests collect against current dev.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
0.16 adds connect(apply_settings=False), which the config flow uses to
probe a discovered interface without writing its settings. A guard test
asserts the pinned driver exposes the argument so an incompatible pin
fails the tests instead of raising TypeError during setup.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add the config.abort.cannot_connect translation used by USB discovery.
- Remove the unused _unique_id_from_port config-flow helper.
- Correct the README to describe the five-minute full-bus scan and the
  dimmable-LED (device type 6) only support.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
async_set_updated_data cancels and reschedules the periodic refresh, so
frequent commands to one light could indefinitely postpone scanning and
discovering the other gear. Publish the optimistic update via the data
attribute and listeners instead, leaving the scan deadline intact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The coordinator update succeeds even when a gear stops responding, so its
built-in logging never reports per-gear outages. Track the addresses seen
in both the periodic scan and single-gear update paths and log a gear
going unavailable and recovering once each, distinguishing a recovery
from a first discovery, to back the Silver log-when-unavailable rule.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The serial transport is opened during connect(), so registering its
cleanup only after a successful connect leaks it when connect is
cancelled. Register the cleanup on the entry before connecting so a
failed or cancelled connect, first refresh, or platform setup all close
the transport.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A failed setup (for example the interface unplugged on restart) never
assigns runtime_data, yet the device-removal API still calls the removal
callback. Guard the access so a stale device can be removed instead of
raising AttributeError.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The local candidate window and request-dependent tie-break could map a
reported brightness back to a different level than it came from, e.g. with
logarithmic dimming and min_level 96. Choose the lowest level whose
readback is closest to the request over the whole range, making the
mapping a stable right inverse of the readback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A dropped QueryDimmingCurve response defaulted linear_dimming to False,
flipping a linear gear to the logarithmic conversion and corrupting both
the reported brightness and later commands. Treat a missing reply as a
query miss so the existing known-gear retry re-reads it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A deleted light left its address in the platform tracking set, so gear
responding again at that address was not recreated until a reload. Clear
the address from the async_removed_from_registry hook so the gear is
rediscovered on the next scan.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A dropped QueryMinLevel reply reset a known minimum to 1, and a
framing-error QueryDimmingCurve reply (BackwardFrameError) was decoded
via as_integer, both skewing the brightness conversion. Keep the cached
minimum for known gear, reject framing-error dimming-curve replies so the
known-gear retry runs, and give the fake driver the error flag real
responses carry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two manual flows for the same serial port can both pass the initial
duplicate check and wait at the bus-power step; manual ports have no
unique ID for the flow manager to deduplicate, so the second could still
create a competing entry. Re-run the duplicate check before creating the
entry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The minimum-level shortcut mapped any level at or below min_level to
brightness 1, breaking the logarithmic readback and its round trip at a
raised minimum (for min_level 200, level 200 read back as 1 while its
curve value is 58). Apply the shortcut only to linear dimming.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Submitting the bus-power step unchanged defaulted to on, which would add a
second supply on an externally powered bus. Enabling the integrated supply
now requires an explicit opt-in.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The linear readback rescaled the curve by the configured minimum level, so
the same level reported different brightness depending on the minimum and a
command below the minimum drove the gear brighter than requested. The
minimum level only limits output (IEC 62386-207): the readback now depends
only on the level and dimming curve, and the command path clamps to the
minimum.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add error handling for missing DALI interface descriptor.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Cover the config-flow path where the interface descriptor cannot be read
(raises OSError -> cannot_connect) for both USB discovery and the manual
flow, and the coordinator's optimistic_level override that shows a
just-sent brightness before the next scan reconciles it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A dropped or out-of-range QueryMaxLevel response reset a known maximum to
None, lifting the command clamp so a full-power request could drive a gear
above its rated ceiling until the next successful read. Keep the cached
maximum for gear already seen, mirroring the minimum-level handling, and
default to no cap only for newly found gear.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
_level_or_mask and the MASK_LEVEL constant were carried into this first PR
but never called here; the actual-level read uses _response_value. Remove
the dead helper and its single-constant module to keep the initial platform
minimal; a later PR that needs the MASK handling reintroduces it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A light.turn_on without a brightness drove an already-dimmed gear to full
output. Fall back to the gear's current nonzero level instead, so repeated
turn-on calls leave a dimmed light where it was; an off or unknown light
still turns on to full.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The log-when-unavailable quality-scale rule expects availability changes at
info level. Lower the "stopped responding" message from warning to info so
it pairs with the "responding again" message.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Use a SerialPortSelector for the manual port step instead of a hand-built
enum. The selector is what marks DALI as a serial-port integration, so the
ESPHome device's serial-proxy setup wizard now offers DALI; the enum listed
the same ports but kept DALI out of that wizard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
hassfest derives SERIAL_PORT_SELECTOR_INTEGRATIONS from config flows that
use SerialPortSelector, so regenerate usb.py now that DALI does.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Follow two tree-wide core changes:
- Define DaliConfigEntry in coordinator.py and import it from there rather
  than re-exporting it through the package __init__.
- Return None from async_setup_entry/async_unload_entry and unload with
  async_unload_platforms_or_raise instead of returning a bool.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Device types are a static property of a control gear, but
_query_gear_once re-queried them on every single-gear refresh,
including the one triggered after each light command. A known gear now
reuses the device types from the last scan and only queries them for a
newly discovered gear, cutting the per-command bus traffic.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A single control gear rejecting a frame (a collision, arbitration loss,
or the bus sagging under the extra frames a multi-type gear needs) raised
TransmissionError, which the periodic scan caught at the loop level and
turned into a whole-update failure. The scan walks addresses in order, so
one noisy gear aborted discovery of every higher-addressed gear after it,
leaving them undiscovered while the lower addresses kept their last-known
state.

Treat a TransmissionError for one gear as a per-gear miss, the same as a
DALISequenceError, so the scan continues to the rest of the bus. A
genuinely bus-wide fault now surfaces as every gear missing, taking the
lights unavailable and recovering on the next good scan, instead of a
single UpdateFailed. OSError/TimeoutError still fail the update so a lost
interface is handled as before.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

2 participants