Skip to content

utils: zbus proxy for OpenThread Border Router D-Bus interface#457

Closed
glennswest wants to merge 2 commits into
project-chip:mainfrom
glennswest:feat/zbus-openthread
Closed

utils: zbus proxy for OpenThread Border Router D-Bus interface#457
glennswest wants to merge 2 commits into
project-chip:mainfrom
glennswest:feat/zbus-openthread

Conversation

@glennswest
Copy link
Copy Markdown

Summary

OpenThread's posix border router (otbr-agent) exposes a per-Thread-interface D-Bus service at io.openthread.BorderRouter.<ifname> (typically wpan0) under /io/openthread/BorderRouter/<ifname>.

Linux rs-matter deployments that ship OTBR alongside Matter often want to talk to it from inside the same process — read the current Thread dataset, monitor SRP-server state, observe attach/detach — without shelling out to ot-ctl. This PR adds a hand-written zbus proxy for that interface.

What's in

  • rs-matter/src/utils/zbus_proxies/openthread.rs — module root.
  • rs-matter/src/utils/zbus_proxies/openthread/border_router.rs#[proxy]-generated trait for the BorderRouter interface, sourced from openthread's own introspect.xml.

Only the methods the controller currently needs are proxied. The file is structured so additional methods can land incrementally without disturbing existing users.

Feature gating

Gated behind the existing zbus feature — no new feature, no new dependency.

Test plan

  • cargo check -p rs-matter --no-default-features --features 'std,rustcrypto,log,os,zbus' --lib — clean.
  • cargo clippy on the same feature set — no warnings on the new module.
  • cargo xtask copyright check — clean.
  • cargo fmt -- --check — clean.

Use case

zman's matter controller (deployed on a Pi running OTBR) reads Thread network state (attach status, channel, PAN ID, dataset) directly via this proxy for its admin UI's Border Router status card, instead of shell-execing ot-ctl.

OpenThread's posix border router (otbr-agent) exposes a per-Thread-
interface D-Bus service at io.openthread.BorderRouter.<ifname> under
the well-known object path /io/openthread/BorderRouter/<ifname>.
Existing rs-matter Linux deployments that ship OTBR alongside the
Matter stack want to talk to it from inside the same process — get
the current Thread dataset, monitor SRP server state, watch for
attach/detach events — without shelling out to ot-ctl.

Adds rs-matter/src/utils/zbus_proxies/openthread/{,border_router.rs}:
a hand-written zbus proxy for the BorderRouter interface, sourced
from openthread's own introspect.xml at
<https://github.com/openthread/ot-br-posix>.

Only the methods the controller currently needs are proxied — the
file is structured so additional methods can land incrementally
without disturbing existing users. Gated behind the existing `zbus`
feature, no new dependency or feature added.

Test plan
---------
- cargo check -p rs-matter --no-default-features
                          --features='std,rustcrypto,log,os,zbus' --lib
  builds clean.
- cargo clippy on the same feature set produces no openthread-related
  warnings.

Use case: zman's matter controller surfaces Thread network state
(attach status, channel, PAN ID) in its admin UI by reading this
service from the OTBR co-located on the same Pi.
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new module for OpenThread Border Router D-Bus proxies. The reviewer identified a bug where the #[proxy] macro was missing the default_service parameter, which is necessary for correct D-Bus connectivity, and suggested using an enum for the device_role property to improve type safety and robustness.

Comment on lines +29 to +32
#[proxy(
interface = "io.openthread.BorderRouter",
default_path = "/io/openthread/BorderRouter/wpan0"
)]
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.

high

The #[proxy] macro uses the interface name as the default D-Bus service name if default_service is not specified. In this case, it would default to io.openthread.BorderRouter.

However, the OpenThread D-Bus service is named io.openthread.BorderRouter.<ifname>, which for the hardcoded wpan0 interface would be io.openthread.BorderRouter.wpan0. Without specifying the correct default_service, attempts to use this proxy with default settings will fail to connect.

Please add the default_service to the #[proxy] attribute.

#[proxy(
    interface = "io.openthread.BorderRouter",
    default_service = "io.openthread.BorderRouter.wpan0",
    default_path = "/io/openthread/BorderRouter/wpan0"
)]

Comment on lines +56 to +57
#[zbus(property)]
fn device_role(&self) -> zbus::Result<String>;
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.

medium

The device_role property returns a string, but the set of possible values is finite. To improve type safety and make the API more robust, consider defining an enum for the device role. zbus can automatically handle the conversion from a D-Bus string.

You could define an enum like this, for example, above the BorderRouter trait:

use serde::{Deserialize, Serialize};
use zbus::zvariant;

#[derive(Debug, Clone, PartialEq, Eq, zvariant::Type, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
#[zvariant(signature = "s")]
pub enum DeviceRole {
    Disabled,
    Detached,
    Child,
    Router,
    Leader,
}

Then, you can change the property in the trait to use this enum:

#[zbus(property)]
fn device_role(&self) -> zbus::Result<DeviceRole>;

This change will prevent bugs related to typos in role strings and make the code that uses this proxy cleaner.

@github-actions
Copy link
Copy Markdown

PR #457: Size comparison from da2d79e to 1614abf

Full report (8 builds for (core), dimmable-light, onoff-light, onoff-light-bt, speaker)
platform target config section da2d79e 1614abf change % change
(core) riscv32imac-unknown-none-elf infodefmt-optz-ltofat FLASH 436352 436368 16 0.0
RAM 70944 70944 0 0.0
thumbv6m-none-eabi infodefmt-optz-ltofat FLASH 354420 354464 44 0.0
RAM 66660 66660 0 0.0
thumbv7em-none-eabi infodefmt-optz-ltofat FLASH 332688 332688 0 0.0
RAM 66428 66428 0 0.0
x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 859667 859683 16 0.0
RAM 71258 71258 0 0.0
dimmable-light x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 1987104 1987880 776 0.0
RAM 60616 60616 0 0.0
onoff-light x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 1913920 1914312 392 0.0
RAM 59776 59776 0 0.0
onoff-light-bt x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 3355296 3355096 -200 -0.0
RAM 5776 5776 0 0.0
speaker x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 1950360 1951152 792 0.0
RAM 5472 5472 0 0.0

@ivmarkov
Copy link
Copy Markdown
Contributor

@glennswest there is no point in opening new PRs if the previous ones are not finished, as nothing would ever be merged. Please check my feedback in your previous PRs.

…derRouter.wpan0

Addresses Gemini review (project-chip#457): #[proxy] had default_path set but not
default_service, so the proxy defaulted the bus name to the interface name
(io.openthread.BorderRouter) instead of the actual openthread daemon service
name (io.openthread.BorderRouter.<ifname>). Hardcoding wpan0 matches the
hardcoded default_path; multi-interface support can come as a follow-up
(constructor that takes ifname + builds both names).

Not addressed: the medium-priority device_role enum suggestion. The set of
values is documented in the property's doc comment and the call sites match
on strings today; converting to a typed enum is a worthwhile follow-up but
not a correctness fix.
@github-actions
Copy link
Copy Markdown

PR #457: Size comparison from da2d79e to 251f789

Increases above 0.2%:

platform target config section da2d79e 251f789 change % change
(core) riscv32imac-unknown-none-elf infodefmt-optz-ltofat FLASH 436352 448786 12434 2.8
thumbv6m-none-eabi infodefmt-optz-ltofat FLASH 354420 362692 8272 2.3
thumbv7em-none-eabi infodefmt-optz-ltofat FLASH 332688 339884 7196 2.2
x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 859667 877915 18248 2.1
dimmable-light x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 1987104 2056536 69432 3.5
onoff-light x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 1913920 1983432 69512 3.6
onoff-light-bt x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 3355296 3507184 151888 4.5
speaker x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 1950360 2018392 68032 3.5
Full report (8 builds for (core), dimmable-light, onoff-light, onoff-light-bt, speaker)
platform target config section da2d79e 251f789 change % change
(core) riscv32imac-unknown-none-elf infodefmt-optz-ltofat FLASH 436352 448786 12434 2.8
RAM 70944 71080 136 0.2
thumbv6m-none-eabi infodefmt-optz-ltofat FLASH 354420 362692 8272 2.3
RAM 66660 66628 -32 -0.0
thumbv7em-none-eabi infodefmt-optz-ltofat FLASH 332688 339884 7196 2.2
RAM 66428 66404 -24 -0.0
x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 859667 877915 18248 2.1
RAM 71258 71386 128 0.2
dimmable-light x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 1987104 2056536 69432 3.5
RAM 60616 60368 -248 -0.4
onoff-light x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 1913920 1983432 69512 3.6
RAM 59776 59536 -240 -0.4
onoff-light-bt x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 3355296 3507184 151888 4.5
RAM 5776 5776 0 0.0
speaker x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 1950360 2018392 68032 3.5
RAM 5472 5472 0 0.0

@ivmarkov
Copy link
Copy Markdown
Contributor

ivmarkov commented Jun 1, 2026

Closing in favor of #472

@ivmarkov ivmarkov closed this Jun 1, 2026
ivmarkov added a commit that referenced this pull request Jun 1, 2026
Similar to #457 (and the justification for #457 is actually good!) but
also:
- The full dBus API, not just "what currently the controller needs"
- Not sure who is "the controller", but "a" controller might need other
APIs too
 - We try to expose the full API in all zbus proxies anyway

- Copyright headers in the rs-matter format, not shortened. Why are they
shortened?!?!

- A promise to assess the Gemini code review feedback
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.

2 participants