Alarm de-duplication in Cumulocity IoT

Overview

Alarms in Cumulocity IoT are used to start processes or get the attention on specific devices / assets. In some cases alarms can be raised multiple times for the same issue. For that reason it is important to understand the alarm de-duplication concept of Cumulocity IoT. I am going to explain that in this article using the go-c8y-cli to create and update alarms at Cumulocity IoT.

Lets start with a clean state (no alarms):

Create initial first alarm

Create an alarm of
type my_BatteryLow
in severity MAJOR
and alarm text Battery low, 20% .
The time will be the current time (now).

The go-c8y-cli provides features to set time attributes in a relative manner. Relative time/dates | Cumulocity IoT CLI

Command:

c8y alarms create --device 676101 --type my_BatteryLow --time "-0s" --text "Battery low, 20%" --severity MAJOR

Alarm JSON object:

{
  "count": 1,
  "creationTime": "2023-06-21T12:58:07.103Z",
  "history": {
    "auditRecords": [],
    "self": "https://XXX.eu-latest.cumulocity.com/audit/auditRecords"
  },
  "id": "2408102",
  "lastUpdated": "2023-06-21T12:58:07.103Z",
  "self": "https://XXX.eu-latest.cumulocity.com/alarm/alarms/2408102",
  "severity": "MAJOR",
  "source": {
    "id": "676101",
    "name": "Temperature #1",
    "self": "https://XXX.eu-latest.cumulocity.com/inventory/managedObjects/676101"
  },
  "status": "ACTIVE",
  "text": "Battery low, 20%",
  "time": "2023-06-21T14:58:05.442+02:00",
  "type": "my_BatteryLow"
}

Following attributes are important:

count = Initially set to 1
creationTime = Time when alarm was created in the database. (Set by Cumulocity IoT)
lastUpdated = Time when alarm was last updated in the database. (Set by Cumulocity IoT)
time = Time which is set by the calling component usually apama, device etc.(first occurence) time. This time is also shown in the UI.
severity =The severity of the alarm: CRITICAL, MAJOR, MINOR or WARNING. Must be upper-case.
status=The status of the alarm: ACTIVE, ACKNOWLEDGED or CLEARED. If status was not appeared, new alarm will have status ACTIVE. Must be upper-case.
text=Text description of the alarm.

Create another alarm of same type with different text

Create same alarm but the alarm has different text where the percentage is a variable part “Battery low, 19%”.

 c8y alarms create --device 676101 --type my_BatteryLow --time "-0s" --text "Battery low, 19%" --severity MAJOR
{
  "count": 2,
  "creationTime": "2023-06-21T12:58:07.103Z",
  "firstOccurrenceTime": "2023-06-21T14:58:05.442+02:00",
  "history": {
    "auditRecords": [],
    "self": "https://XXX.eu-latest.cumulocity.com/audit/auditRecords"
  },
  "id": "2408102",
  "lastUpdated": "2023-06-21T13:02:02.988Z",
  "self": "https://XXX.eu-latest.cumulocity.com/alarm/alarms/2408102",
  "severity": "MAJOR",
  "source": {
    "id": "676101",
    "name": "Temperature #1",
    "self": "https://XXX.eu-latest.cumulocity.com/inventory/managedObjects/676101"
  },
  "status": "ACTIVE",
  "text": "Battery low, 20%",
  "time": "2023-06-21T15:02:01.011+02:00",
  "type": "my_BatteryLow"
}

text= As you can see, it gets not overwritten! The first occurence of the alarm is the leading alarm. This is important to consider if a variable part is in the alarm text! If this feature is needed the existing alarm must be updated (see next step) instead of creating a new alarm.
count = The number of times this alarm has been sent. The attributes type and source in that case “my_BatteryLow” and source.id 676101 is the key.
creationTime = Time when alarm was created in the database. As you can see, it hasn’t change, because it is still the same alarm.
lastUpdated = Time when alarm was last updated in the database. Has changed because the alarm was updated.
time = Time which is set by the calling component usually device. The time value is updated by the new time. This means the time of the previous alarm gets overwritten and only the first occurence gets stored, see next.
firstOccurrenceTime = The first time that this alarm occurred (i.e., where “count” was 1).

Update Text

To update the text of the alarm an update of an existing alarm is needed. Which is in some cases difficult to achieve, for example if the id of the existing alarm isn’t known. This is usually the case if the alarm is created by the device.

Command:

c8y alarms update --id 2408102 --text "Battery low, 19%"

Alarm object:

{
  "count": 2,
  "creationTime": "2023-06-21T12:58:07.103Z",
  "firstOccurrenceTime": "2023-06-21T14:58:05.442+02:00",
  "history": {
    "auditRecords": [],
    "self": "https://XXX.eu-latest.cumulocity.com/audit/auditRecords"
  },
  "id": "2408102",
  "lastUpdated": "2023-06-21T13:12:46.050Z",
  "self": "https://XXX.eu-latest.cumulocity.com/alarm/alarms/2408102",
  "severity": "MAJOR",
  "source": {
    "id": "676101",
    "name": "Temperature #1",
    "self": "https://XXX.eu-latest.cumulocity.com/inventory/managedObjects/676101"
  },
  "status": "ACTIVE",
  "text": "Battery low, 19%",
  "time": "2023-06-21T15:02:01.011+02:00",
  "type": "my_BatteryLow"
}

Be aware that the older text isn’t stored on the alarm. The old alarm text can be retrieved by the audit log.

 c8y auditrecords list --type Alarm --user alexander.pester@softwareag.com --dateFrom "-10s" --dateTo "0s"

Element of audit log

    {
      "activity": "Alarm updated",
      "changes": [
        {
          "attribute": "text",
          "newValue": "Battery low, 19%",
          "previousValue": "Battery low, 20%",
          "type": "java.lang.String"
        }
      ],
      "com_cumulocity_model_event_AuditSourceDevice": {
        "id": "676101"
      },
      "creationTime": "2023-06-21T13:12:46.063Z",
      "id": "2388101",
      "self": "https://XXX.eu-latest.cumulocity.com/audit/auditRecords/2388101",
      "severity": "MAJOR",
      "source": {
        "id": "2408102",
        "self": "https://XXX.eu-latest.cumulocity.com/inventory/managedObjects/2408102"
      },
      "text": "Device name: 'Temperature #1', alarm text: 'Battery low, 20%'",
      "time": "2023-06-21T14:58:05.442+02:00",
      "type": "Alarm",
      "user": "alexander.pester@softwareag.com"
    },

Create another alarm of same type with different text and severity

If we have an device which creates alarms with same type but a different severity we have a similar issue. Imagine the device increases the severity if battery level falls under 15%.

Command:

c8y alarms create --device 676101 --type my_BatteryLow --time "-0s" --text "Battery low, 14%" --severity CRITICAL

Alarm object:

{
  "count": 3,
  "creationTime": "2023-06-21T12:58:07.103Z",
  "firstOccurrenceTime": "2023-06-21T14:58:05.442+02:00",
  "history": {
    "auditRecords": [],
    "self": "https://XXX.eu-latest.cumulocity.com/audit/auditRecords"
  },
  "id": "2408102",
  "lastUpdated": "2023-06-21T13:22:24.515Z",
  "self": "https://XXX.eu-latest.cumulocity.com/alarm/alarms/2408102",
  "severity": "MAJOR",
  "source": {
    "id": "676101",
    "name": "Temperature #1",
    "self": "https://XXX.eu-latest.cumulocity.com/inventory/managedObjects/676101"
  },
  "status": "ACTIVE",
  "text": "Battery low, 19%",
  "time": "2023-06-21T15:22:24.310+02:00",
  "type": "my_BatteryLow"
}

severity= As you can see, it gets not overwritten! The first occurrence of the alarm is the leading alarm. This is important to consider if you want to change the severity of the alarm! If this feature is needed the alarm must be updated see next step.

Update severity

Command:

 c8y alarms update --id 2408102 --text "Battery low, 14%" --severity CRITICAL

Alarm object:

{
  "count": 3,
  "creationTime": "2023-06-21T12:58:07.103Z",
  "firstOccurrenceTime": "2023-06-21T14:58:05.442+02:00",
  "history": {
    "auditRecords": [],
    "self": "https://XXX.eu-latest.cumulocity.com/audit/auditRecords"
  },
  "id": "2408102",
  "lastUpdated": "2023-06-21T13:25:37.109Z",
  "self": "https://XXX.eu-latest.cumulocity.com/alarm/alarms/2408102",
  "severity": "CRITICAL",
  "source": {
    "id": "676101",
    "name": "Temperature #1",
    "self": "https://XXX.eu-latest.cumulocity.com/inventory/managedObjects/676101"
  },
  "status": "ACTIVE",
  "text": "Battery low, 14%",
  "time": "2023-06-21T15:22:24.310+02:00",
  "type": "my_BatteryLow"
}

Again, be aware that changes of text and severity are historical tracked in the audit log! For the change the audit record looks like that:

{
  "auditRecords": [
    {
      "activity": "Alarm updated",
      "changes": [
        {
          "attribute": "severity",
          "newValue": "CRITICAL",
          "previousValue": "MAJOR",
          "type": "com.cumulocity.model.event.CumulocitySeverities"
        },
        {
          "attribute": "text",
          "newValue": "Battery low, 14%",
          "previousValue": "Battery low, 19%",
          "type": "java.lang.String"
        }
      ],
      "com_cumulocity_model_event_AuditSourceDevice": {
        "id": "676101"
      },
      "creationTime": "2023-06-21T13:25:37.129Z",
      "id": "2408105",
      "self": "https://XXX.eu-latest.cumulocity.com/audit/auditRecords/2408105",
      "severity": "MAJOR",
      "source": {
        "id": "2408102",
        "self": "https://XXX.eu-latest.cumulocity.com/inventory/managedObjects/2408102"
      },
      "text": "Device name: 'Temperature #1', alarm text: 'Battery low, 19%'",
      "time": "2023-06-21T14:58:05.442+02:00",
      "type": "Alarm",
      "user": "alexander.pester@softwareag.com"
    },

Update status

The same applies to status changes. Status can only be updated. Sending another alarm with another status will also not have any effect on the current alarm group.

Command:

c8y alarms update --id 2408102 --status ACKNOWLEDGED

Alarm object:

{
  "count": 3,
  "creationTime": "2023-06-21T12:58:07.103Z",
  "firstOccurrenceTime": "2023-06-21T14:58:05.442+02:00",
  "history": {
    "auditRecords": [],
    "self": "https://XXX.eu-latest.cumulocity.com/audit/auditRecords"
  },
  "id": "2408102",
  "lastUpdated": "2023-06-21T14:05:58.488Z",
  "self": "https://XXX.eu-latest.cumulocity.com/alarm/alarms/2408102",
  "severity": "CRITICAL",
  "source": {
    "id": "676101",
    "name": "Temperature #1",
    "self": "https://XXX.eu-latest.cumulocity.com/inventory/managedObjects/676101"
  },
  "status": "ACKNOWLEDGED",
  "text": "Battery low, 14%",
  "time": "2023-06-21T15:22:24.310+02:00",
  "type": "my_BatteryLow"
}

What you should take away!

Alarm de-duplication means only one alarm object exists and the count gets incremented. Alarms are grouped by type and source. If the Alarm gets cleared, the next occuring Alarm will be the new Alarm, starting from count 1. Fragment time is updated and contains the time of the last alarm which is send via POST. The first alarm time of the group will be stored at firstOccurrenceTime. Fragments status, severity and text will not be updated if more alarms are send via POST. This is really important to understand. If those fragments must be changed, this is only possible via PUT operation. The values which are changed are stored in the audit log not on the alarm.

By the way, the online documentation already describes this very important feature:

Alarm de-duplication

If an ACTIVE or ACKNOWLEDGED alarm with the same source and type exists, no new alarm is created. Instead, the existing alarm is updated by incrementing the count property; the time property is also updated. Any other changes are ignored, and the alarm history is not updated. Alarms with status CLEARED are not de-duplicated. The first occurrence of the alarm is recorded in the firstOccurrenceTime property.

3 Likes