Matter attribute update: is there a way to know when the report has been sent?

Hi everyone,
I’m updating a Matter attribute:

uint16_t mv;
if (get_battery_voltage(&mv) == BATTERY_OK)
{
    Clusters::PowerSource::Attributes::BatVoltage::Set(
        kPositionEndpoint,
        static_cast<uint32_t>(mv),
        MarkAttributeDirty::kYes);
}

I’d like to know if there is any callback or event that confirms when the attribute report has actually been sent by the Matter stack.
I don’t need confirmation from the controller; I only need to know that the report transmission has completed so I can safely power down the device.
Is there any recommended way to detect this?
Thanks!

Parents
  • Hi,

    If I read your request, what your asking for is basically some kind of acknowlegde sent back after a report has been sent by the stack and the change has taken place on a device. 

    If so, have you had a look at https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/protocols/matter/getting_started/matter_api.html#interacting_with_matter_data_model if this fits your needs?

    Kind regards,
    Andreas

  • Hi,
    Thanks for the suggestion!

    I had already looked at that part of the documentation, but unfortunately it does not solve the specific requirement I have in my application.

    In my case the device is battery powered and the flow is roughly:

    - Wake up
    - Acquire from sensor
    - Update a Matter attribute (with Set())
    - Ensure the update has actually been sent by the Matter stack (currenty ensured by sleep)
    - Enter power-off (witouth any retention)

    Currently the attribute is updated using:

    Clusters::PowerSource::Attributes::BatVoltage::Set(kPositionEndpoint, static_cast<uint32_t>(mv), MarkAttributeDirty::kYes);

    The problem is that this only marks the attribute as dirty in the data model and lets the Matter reporting engine send the report asynchronously if a subscription exists.

    The section you referenced mainly describes ZCL callbacks used to interact with the Matter data model. However, those callbacks operate at the data model layer, not at the reporting / transport layer.

    Because of that, they do not provide a callback or notification when the stack has finished sending the message.

    Since the reporting engine works asynchronously, the device might power off before the report has actually been transmitted.

    At the moment the only workaround is something like:

    k_sleep(K_SECONDS(10));

    before entering power-off, which is obviously not ideal for a low-power device.
    What I am looking for instead is something like: a callback when the reporting engine finishes sending a report, or confirmation that the report has been handed off to the transport layer so the device can safely power down without relying on a fixed delay.

    If there is an API or callback in the Matter stack that indicates report transmission completion, that would solve the problem cleanly. Otherwise I may need to redesign the flow (for example using events + an application-level acknowledgement).

    Any other suggestions on how to reliably detect when a report has actually been transmitted would be greatly appreciated.

    Kind regards.

Reply
  • Hi,
    Thanks for the suggestion!

    I had already looked at that part of the documentation, but unfortunately it does not solve the specific requirement I have in my application.

    In my case the device is battery powered and the flow is roughly:

    - Wake up
    - Acquire from sensor
    - Update a Matter attribute (with Set())
    - Ensure the update has actually been sent by the Matter stack (currenty ensured by sleep)
    - Enter power-off (witouth any retention)

    Currently the attribute is updated using:

    Clusters::PowerSource::Attributes::BatVoltage::Set(kPositionEndpoint, static_cast<uint32_t>(mv), MarkAttributeDirty::kYes);

    The problem is that this only marks the attribute as dirty in the data model and lets the Matter reporting engine send the report asynchronously if a subscription exists.

    The section you referenced mainly describes ZCL callbacks used to interact with the Matter data model. However, those callbacks operate at the data model layer, not at the reporting / transport layer.

    Because of that, they do not provide a callback or notification when the stack has finished sending the message.

    Since the reporting engine works asynchronously, the device might power off before the report has actually been transmitted.

    At the moment the only workaround is something like:

    k_sleep(K_SECONDS(10));

    before entering power-off, which is obviously not ideal for a low-power device.
    What I am looking for instead is something like: a callback when the reporting engine finishes sending a report, or confirmation that the report has been handed off to the transport layer so the device can safely power down without relying on a fixed delay.

    If there is an API or callback in the Matter stack that indicates report transmission completion, that would solve the problem cleanly. Otherwise I may need to redesign the flow (for example using events + an application-level acknowledgement).

    Any other suggestions on how to reliably detect when a report has actually been transmitted would be greatly appreciated.

    Kind regards.

Children
  • Noted, thanks for elaborating. I will have to look a bit more around then. I'm aiming to give you a follow up to this within a day or two.

    In advance, thanks for the patience

    Kind regards,
    Andreas

  • Hi, 

    This is just a heads up that I've not forgotten you and that I've asked for some input internally if there exists a callback function that handles this or not.

    Kind regards,
    Andreas

  • Hi again, 

    Yeah there seems to be no direct equivalent for a callback for an application level "report sent".

    The best I have is the previously mentioned doc page and for for you to evaluate some internal hooks that might be usable or to use the IDC State Observer which I think should be 

    Internal hook Where it fires Meaning
    ReadHandler::Observer::OnSubscriptionReportSent() (ReadHandler.cpp:361) Right after SendMessage() returns OK Packet handed to transport layer
    Engine::OnReportConfirm() (Engine.cpp:1153) After the controller sends back a StatusResponse (subscription) or immediately (read) Controller acknowledged receipt

    Neither is accessible to application code: OnSubscriptionReportSent is consumed by ReportSchedulerImpl (single observer slot), and OnReportConfirm is purely internal engine state.

    W.r.t the IDC State Observer, here's a summary of how the IDC state observer should work (if I understood it correct when reading through it ). See https://docs.nordicsemi.com/bundle/ncs-3.2.4/page/nrf/protocols/matter/getting_started/low_power_configuration.html  

    A pseudo-code example is below:

    #include <app/icd/server/ICDManager.h>
    #include <app/icd/server/ICDStateObserver.h>
    
    class PowerSourceICDObserver : public chip::app::ICDStateObserver
    {
    public:
        void OnEnterActiveMode() override {}
        void OnTransitionToIdle() override {}   // ~ICD_ACTIVE_TIME_JITTER_MS before idle
        void OnICDModeChange()   override {}
    
        void OnEnterIdleMode() override
        {
            // All pending reports have been confirmed by the stack.
            // Safe to power down.
            TriggerPowerDown();
        }
    };
    
    // During application init:
    static PowerSourceICDObserver sICDObserver;
    chip::app::ICDManager::GetInstance().RegisterObserver(&sICDObserver);

    1. Reporting engine schedules a run → ICDNotifier::NotifySubscriptionReport() keeps the device in Active mode
    2. Engine::BuildAndSendSingleReportData() sends the ReportData message
    3. For subscriptions, the stack transitions ReadHandler to AwaitingReportResponse and waits for the controller's StatusResponse — only then does OnReportConfirm() decrement mNumReportsInFlight
    4. Once all in-flight reports are confirmed and no dirty attributes remain, the Active mode timer expires
    5. OnTransitionToIdle() fires (last chance to extend) → if nothing extends → OnEnterIdleMode() fires

    OnEnterIdleMode() is your safe power-down point.

    I've not used either, but they look to be something that could fit your use case. However it is up to you to evaluate this.

    Kind regards,
    Andreas

Related