Skip to content

add_raw_event recalculates active_period as dict and drops notification_period / ramp_up_period / recovery_period in Sever.py #187

@alexmcm1999

Description

@alexmcm1999

Hi,

While integrating OpenLEADR with a certified OpenADR 2.0b VEN , I found what appears to be a bug in server.py add_raw_event() that causes optional ActivePeriod fields (such as notification_period) to be silently dropped from the generated XML.

When creating an event using server.add_event() and specifying:

notification_period=timedelta(minutes=10)

the ActivePeriod object is correctly constructed in add_event():

active_period = utils.get_active_period_from_intervals(intervals, False)
active_period.notification_period = notification_period

However, inside add_raw_event(), the following logic recalculates the active_period:

active_period = utils.get_active_period_from_intervals(
    [utils.get_active_period_from_intervals(utils.getmember(signal, 'intervals'), False)
     for signal in utils.getmember(event, 'event_signals')]
)

Since get_active_period_from_intervals() (From utils.py) defaults to as_dict=True, this returns:

{
'dtstart': ...,
'duration': ...
}

This replaces the existing ActivePeriod dataclas and remove notification_period, ramp_up_period, recovery_period, tolerance. As result, the generated XML does not include:

<ei:x-eiNotification>
    <duration>PT10M</duration>
</ei:x-eiNotification>

This caused strict OpenADR VEN implementations to reject the event with:
invalid activePeriod
Document missing event notification node

I also noticed that add_raw_event() checks:

utils.getmember(event, 'event_descriptor.active_period', None)

However, active_period is a direct attribute of the Event object, not part of event_descriptor.

Changing this to:

utils.getmember(event, 'active_period', None)

ensures the existing ActivePeriod object is respected and not unnecessarily recalculated.

Proposed Fix:

Force get_active_period_from_intervals() to return an ActivePeriod object instead of a dict:

if not utils.getmember(event, 'active_period', None):
    active_period = utils.get_active_period_from_intervals(
        [utils.get_active_period_from_intervals(
            utils.getmember(signal, 'intervals'), False)
         for signal in utils.getmember(event, 'event_signals')],
        False  # ← force dataclass instead of dict
    )
    utils.setmember(event, 'active_period', active_period)

This preserves the dataclass structure and allows:

  • notification_period
  • ramp_up_period
  • recovery_period

to be serialized correctly in the XML template.

The OpenADR 2.0b specification states that:

-ei:x-eiNotification is OPTIONAL in profile B

-REQUIRED in profile A

However, some certified VEN implementations expect this node to be present. Currently, even when users explicitly define notification_period, it does not appear in the final XML due to the re-conversion to dict.

After the change, the generated XML correctly includes:

<ei:x-eiNotification>
    <duration>PT10M</duration>
</ei:x-eiNotification>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions