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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions