Overview
The Notifications 2.0 API allows applications or microservices to receive and process notifications generated by the use of the Cumulocity IoT REST or MQTT APIs (device management, measurements, alarms, events and other platform APIs) in a reliable manner.
The Cumulocity Messaging Service is an optional component of the Cumulocity IoT platform that may need to be enabled before Notifications 2.0 can be used. Changes to the configuration of the load balancer or ingress controller may also be required to allow access to the Websocket endpoint used by Notifications 2.0.
For the shared public cloud instances of the Cumulocity IoT platform, the Messaging Service is enabled by default on release 10.13 and above. For dedicated and self-hosted instances, the Messaging Service and Notifications 2.0 are available for release 10.11 and above, but will need to be explicitly enabled.
Please contact product support to inquire about using the Messaging Service and Notifications 2.0 capabilities in your Cumulocity IoT environment. See the Messaging Service - Installation & operations guide for further technical details of the configuration required, but note that these tasks can only be performed by a Cumulocity IoT platform operator, not by a normal user.
ROLES & PERMISSIONS:
- To view and manage Notifications 2.0 subscriptions (read, create, delete): ADMIN permission for the permission type “Notification 2”.
Notifications 2.0 improves upon the Real-time notification API by providing stronger delivery semantics and ordering guarantees. It is also intended to be simpler to use than the “Bayeaux” protocol used in the Real-time notification API. New capabilities are added to Notifications 2.0 in each release of Cumulocity IoT. However, it does not yet support all of the notifications available from the Real-time notification API so it is not yet a complete replacement for the older API. See the rest of this section and the detailed API documentation for full details of the notifications supported by this release of the Notifications 2.0 API.
Topics and subscriptions
Internally, Notifications 2.0 uses a publish-subscribe pattern, allowing use-cases to organize their desired selections of measurement, event, alarm, operation and/or inventory messages into topics according to functional interest. Creating a Notification 2.0 subscription in Cumulocity IoT creates a publisher (internally), that forwards Cumulocity IoT messages that it matches (based on message qualities such as its source, type or even content, for example) to a specific topic. Each subscription can only forward messages to one topic, but multiple subscriptions can forward messages to the same topic.
The diagram ‘Notification 2.0 topics and subscriptions’ below shows three subscriptions that have been created in Cumulocity IoT and are forwarding notification messages into two topics in the Messaging Service.
The ‘temperature’ topic is receiving measurements from the leftmost and centrally depicted subscriptions. Both of these have a “mo” (managed object) context and they both include messages from the measurement API only. A subscription with a managed object context can only forward messages from a specific managed object (such as a device). The leftmost subscription is forwarding measurements from a device with source ID ‘12345’ and the centrally depicted subscription does the same but from device ‘67890’.
The ‘alarms’ topic is receiving alarms from the rightmost subscription. It has a tenant context and includes messages from the alarm API. It forwards all alarms within the tenant that creates the subscription, regardless of how they are generated (you can create finer grained topics by using filters). The forwarded alarms include those raised by Cumulocity IoT itself, and those published via REST or MQTT from other components in, or attached to, the platform, including any alarms raised by the depicted devices.
Consumers and tokens
A topic’s notification messages can be received by WebSocket based consumers that present a valid authorizing token for that specific topic when connecting to the Notification 2.0 WebSocket endpoint. This token is in the form of a string conforming to the JWT (JSON Web Token) standard. Tokens can be obtained from the Cumulocity IoT token REST API by authenticated users.
Consumers receive the topic messages reliably, with at-least-once semantics, in order and must acknowledge each message in turn. Notification order is preserved from the point of view of any given device sending in REST and MQTT API requests. The protocol is text-based and described in detail in the Consumer protocol section. In typical usage, multiple consumers of a given topic operate independently in parallel, each receiving and acknowledging separate copies of those messages.
The diagram below shows three consumers that have been created in the Messaging Service by four consumer clients.
The rightmost client identifies its consumer as “alarmmonitor” and that consumer receives messages from the “alarms” subscription topic.
The second to right client identifies its consumer as “tempaudit” and that consumer receives messages from the “temperature” subscription topic.
The leftmost two consumer clients are two shares of a (logically single) shared consumer, and so share the same copy of topic messages. They both identify the same “tempmonitor” consumer; each receives a non-overlapping subset of the messages in the “temperature” topic. Collectively, they receive all of the messages in the topic.
Consumer lifecycle
When a subscription is created, Cumulocity IoT starts to create and forward notifications within the subscription’s scope to the Messaging Service subsystem. The Message Service does not necessarily retain these messages; it only retains a subscription topic’s messages if the topic is persistent, and it has at least one consumer for the topic that is or has been connected.
The set of retained topic messages that have not been received and acknowledged by a given consumer are known as the consumer’s backlog.
Only consumers of persistent subscription topics have backlogs that are maintained across client reconnections.
Consumers of non-persistent subscription topics get a new backlog pointing only to the next (latest) topic message when they connect/reconnect and any previous backlog is discarded, removing any message references. They can only cause backlog size problems if they remain connected but continually consume at a rate lower than the rate at which new messages arrive.
Persistent subscription topic messages are only stored once in the Messaging Service, but all associated consumers' backlogs, whether the consumer is connected or not, reference each message until they have received and acknowledged it. Therefore, all consumer backlogs should be considered to have a storage cost from when they first connect until they explicitly express no further interest in the topic by unsubscribing. The Messaging Service discards a given message only when there are no backlogs that reference it anymore.
The following diagram shows the lifecycle of a consumer backlog in relation to its associated client connection(s). The backlog is created when the consumer first connects to the Messaging Service and is only destroyed when it is explicitly unsubscribed. The consumer’s backlog is maintained, even if its client connection is interrupted. This is needed for reliable messaging, allowing the consumer to not miss messages during connection outages, but comes at the cost of explicit lifecycle management.
To unsubscribe its consumer, pass its token as the token parameter to the /notification2/unsubscribe REST endpoint.
Creating subscriptions
The JSON fields sent in a create subscription request
determine which Cumulocity IoT messages are forwarded to a topic, and the forwarded message content.
This request must be made by an authenticated Cumulocity IoT user with the ROLE_NOTIFICATION_2_ADMIN
role.
The context field broadly determines the type of Cumulocity IoT message a subscription might match and forward. There are two valid context values: “mo” (managed object) and “tenant”. Some subscription fields have constraints that vary according to the value of the context field. Where this is the case, it is pointed out in that field’s documentation in the create subscription documentation.
The source field can only be used if the context is “mo”. It must have a nested id field containing the value of a managed object’s global identifier (sometimes referred to as a “device ID” or “source ID”). This is used to target inclusion of messages from the given managed object.
subscription is the first of two fields that identify which topic this subscription will forward messages to. Multiple subscriptions contribute to a single topic if they have the same values for both the subscription and nonPersistent fields.
The nonPersistent field determines if the topic is persistent or non-persistent. Subscriptions with the same subscription field value, but different nonPersistent values forward to two different topics. It is, therefore, the second of the two fields that identifies the subscription topic.
The filter field can provide a subscription filter, allowing more finely grained selection of the included messages, based on the message type and API (is it a measurement or alarm, for example).
The fragmentsToCopy field allows the forwarded messages to be transformed so that they contain only a specific subset of the fragments present in the original Cumulocity IoT messages they are generated from. This can be useful, for example, for security, bandwidth saving or functionality scoping reasons.
The following summarizes the subscription fields.
Field Name | Value | Required/Default | Description |
---|---|---|---|
context | "mo" or "tenant" | Required | Only values "mo" and "tenant" are supported |
source | A managed object global identifier | Required if context is "mo" | Has a mandatory child id field |
subscription | String | Required | Determines messaging-service topic used |
nonPersistent | Boolean | false | Determines if a persistent or non-persistent topic is used |
filter | JSON object | All messages | Includes messages based on message values |
fragmentsToCopy | JSON list of strings | All fragments | Determines which fragments are included in forwarded messages |
Notifications for new managed objects creations can never be forwarded by subscriptions with an “mo” context as the managed object is only given an ID when it is created and that ID is needed as a field to create the subscription. Therefore, to receive notifications informing of new managed object creations, create a subscription with “tenant” context to listen for them. An application can use this to discover new managed objects. It can then choose to create subscriptions with “mo” context for those managed objects.
Subscriptions with “tenant” context can also use the alarms API and/or the events API to forward all alarms and/or events, respectively, which occur within the tenant, applying filters as desired.
The following summarizes the context and API support.
Context | ManagedObject Create | ManagedObject Update & Delete | Alarms | Events | Measurements | Operations |
---|---|---|---|---|---|---|
mo | ✗ | ✓ | ✓ | ✓ | ✓ | ✓ |
tenant | ✓ | ✗ | ✓ | ✓ | ✗ | ✗ |
Subscription filters
Subscription filters provide fine-grained selection of the Cumulocity IoT messages a subscription will forward. Filters can be provided as the subscriptionFilter field in a subscription’s JSON object at creation time. It is a JSON object with apis and typeFilter fields. Filters specified by subscriptions with “tenant” context must provide a typeFilter value. Filters specified by those with “mo” context can provide either or both filter fields.
The apis field is a JSON array that specifies which Cumulocity IoT API messages to include. Use an array containing just the wildcard value, “*”, to include messages from all APIs. To include messages from a subset of the APIs, use an array containing any single or multiple selection from “alarms”, “alarmsWithChildren”, “events”, “eventsWithChildren”, “measurements”, “managedobjects” and “operations”.
For example, to include messages from all APIs:
{
"context": "mo",
"subscription": "subscription01",
"source": {
"id": 2468
},
"filter": {
"apis": ["*"]
}
}
To include only messages from the measurements and alarms APIs:
{
"context": "mo",
"subscription": "subscription02",
"source": {
"id": 2468
},
"filter": {
"apis": ["measurements", "alarms"]
}
}
The “alarmsWithChildren” and “eventsWithChildren” apis values allow subscriptions with managed object context to filter in, respectively, alarms or events for all recursively descendant child managed objects of the source.id managed object in addition to those from the source.id managed object itself.
The typeFilter string field is matched against the original message’s type field. It can be a single value, or a
limited (supporting only or
) OData expression.
For example, to include messages with type “temperature” and messages with type “pressure”:
{
"context": "mo",
"subscription": "subscription03",
"source": {
"id": 2468
},
"filter": {
"type": "'temperature' or 'pressure'"
}
}
To include messages of type “temperature” only:
{
"context": "mo",
"subscription": "subscription04",
"source": {
"id": 2468
},
"filter": {
"type": "'temperature'"
}
}
Persistent and non-persistent subscriptions
A subscription can be persistent or non-persistent, implying that the Messaging Service topic for it is either persistent or non-persistent, respectively. These have different qualities which can be useful to satisfy the varying needs of differing use-cases.
Persistent subscriptions are the default. They are used for reliable messaging, ensuring that consumers never miss a message if their connection is interrupted. They use replicated secondary storage to maintain backlogs (within the constraints of any configured storage limits) and to maintain the consumers' positions in their topics. When a consumer of a persistent subscription topic has their connection interrupted, whether that is due to network issues or deliberate actions by the consumer, upon reconnection they will continue to receive notifications from the topic position they were at before the outage (specifically, from the message after the last one they acknowledged successfully before the outage).
Non-persistent subscriptions are only buffered in memory. A client consumer’s position is not persisted across interruptions of the client connection. When a consumer of a non-persistent subscription has their connection interrupted, upon reconnection they will start receiving notifications from the most recent message of the subscription, missing all other notifications that occurred during the connection outage. This is the case for all such temporarily disconnected consumers, even if other consumers of the same non-persistent subscription are still receiving older messages that occurred while it was not connected.
If you create both a persistent and a non-persistent subscription with the same subscription field, they are separate, independent subscriptions, backed by separate topics.
When creating a token for a non-persistent subscription topic, to access notifications from the correct topic,
the token’s nonPersistent field must be set to true
.
As is the case for the subscription, this field defaults to false
, meaning the token will be for a persistent
subscription topic by default.
Deleting subscriptions
Deleting a subscription prevents it from adding further notification messages to its associated topic. Many subscriptions can contribute notifications to a given topic so this does not control the topic lifecycle. Deleting all the subscriptions associated with a topic ensures no more notifications are added to it. This does not delete the topic either.
Even though the topic no longer accumulates new messages, there may still be consumers draining the last of the messages from it. When all messages are consumed by all consumers, the topic is empty and consumes negligible space in the Messaging Service.
Subscriptions can be deleted by sending a delete subscription request, using the subscription’s id as the URL filename. For example, to delete the subscription with ID 8765:
DELETE /notification2/subscriptions/8765 HTTP/1.1
Host: <HOST>
Authentication: Basic: <AUTHENTICATION>
When a tenant is deleted from Cumulocity IoT, all its subscriptions are deleted. However, topics and consumers may still be active in the Messaging Service until all messages are consumed.
Creating Tokens
The JSON fields sent in a create token request
determine which subscription topic a consumer can receive messages from, the token’s duration,
the shared nature of the consumer, and an identifier for the consumer.
This request must be made by an authenticated Cumulocity IoT user with the ROLE_NOTIFICATION_2_ADMIN
role.
The subscription field aligns with the same field in the subscription object, so broadly specifies the subscription topic the consumer will receive notifications from. The nonPersistent field is also a factor in identifying the topic.
The nonPersistent field aligns with the same field in the subscription object so identifies whether the subscription topic is persistent or non-persistent and therefore is a factor in identifying the topic only. There is no such thing as a peristent or non-peristent consumer and tokens cannot affect the nature they experience of topics using this field.
The subscriber field provides a unique (within the scope of this topic) identity for the consumer, allowing it to be recognized across connection interruptions, so allowing message delivery to be resumed after such interruptions and thus be reliable.
The shared field determines if multiple clients can connect in parallel as the consumer identified in the subscriber field, acting collectively to share the notification message processing load.
The expiresInMinutes field is the period that this token remains valid for, defaulting to 1440 minutes (1 day).
The following summarizes the token fields.
Field Name | Value | Required/Default | Description |
---|---|---|---|
subscription | string | Required | Identifies topic |
nonPersistent | Boolean | false | Must be true to identify a non-persistent topic |
subscriber | String | Required | Identifies the consumer bearing this token |
shared | Boolean | false | True if multiple clients can act collectively as this consumer |
expiresInMinutes | Integer | 1440 | The token duration |
Token expiration
The period a token remains valid for (its lifetime) is determined at creation time by the optional expiresInMinutes field, which defaults to 1440 minutes (one day). This can be used to limit exposure to potential security issues related to them being leaked to parties that are not authorized to access the system. Consequently, tokens may need to be re-created or refreshed periodically.
Tokens only allow a consumer to connect to the Messaging Service. If a token expires while its consumer is connected, the consumer is not automatically logged out or disconnected.
If a consumer disconnects, and their token has expired, the token must be recreated or refreshed in order that the consumer can reconnect.
Tokens can be refreshed by calling the token create request with the same parameters as originally (a different expiresInMinutes value can be given if a different duration is desired). The token string is a JWT (JSON Web Token) token so if the parameters used are not easily kept available, they can be extracted from the token string as outlined in the Java code below:
// The token string's sections are delimited by dots
String[] tokenParts = token.split("\\.");
// Base64 decode the second section
byte[] decoded = Base64.getDecoder().decode(tokenParts[1]);
// Create a string from the decoded bytes to get a JSON string of the token fields
String tokenJson = new String(decoded);
// ... optionally create an Object from the string or bytes using a JSON parser
If you are using the Cumulocity IoT Java SDK TokenApi class, it has a public refresh method which does the above for you, internally on the client side.
Shared consumer tokens
Shared consumer tokens allow parallelization of the consumer client workload. This is useful if the notifications would otherwise arrive at a higher rate than a single consuming client application can process them. It has no impact on the rate of notification throughput within, and thus their egress from, Cumulocity IoT core.
When you create a token, you can add the optional Boolean body parameter shared
to the request.
If it is set to true
, the created token is shared.
If it is not present or false
, the token is exclusive (not shared).
If a consumer’s token is not shared, the consumer is an exclusive consumer. Only one consumer client can connect using an exclusive token. An attempt to connect further consumers with the same exclusive token results in an error. An exclusive consumer receives a copy of all notifications from the subscription topic its token is for.
If a consumer’s token is shared, the consumer is a shared consumer. Additional consumer clients can connect using the same token. If only one shared consumer client is connected, it receives a copy of all notifications from the subscription topic. As additional consumer clients connect using the same token, the consumer notification load is rebalanced so that each consumer client receives a non-overlapping subset (share) of the notifications from the subscription topic. The set of consumer clients sharing a token can be thought of as a single logical consumer. Collectively, the set receives all notifications for the subscription topic.
The notification load is spread across the shared consumer clients according to the id of the source that generated the notification, typically a device id. All notifications for a given id are delivered to the same consumer. Each consumer may receive notifications for many different ids. This means that there is no benefit using shared tokens unless the notifications feeding the subscription topic are coming from multiple sources. Note that the load spreading algorithm may result in an asymmetric balance of notification load across the shares when there are few source ids in the subscription topic. The load should generally become more evenly distributed as the number of sources increases.
To keep the messages from a given set of source ids ‘sticky’ to a specific consumer client in the share in the face of connection interruptions,
the consumer clients can provide an optional consumer
parameter in their connection URL string, in addition to their usual token
parameter.
For example: two consumers identifying themselves as instance1 and instance2 connect using URL paths
notification2/consumer?token=xyz&consumer=instance1
and notification2/consumer?token=xyz&consumer=instance2
.
Subscriptions are always unaware of the nature and number of their consumers: any number of shared and exclusive tokens can be created for the same subscription topic and they all operate independently, each receiving their own copy of the notifications. This means you can have multiple shared tokens for the same subscription topic and their load is only divided within the scope of each shared token.
Building consuming applications and microservices
A consumer client requires only a valid URL and JWT string to connect. It can be implemented using any WebSocket library or programming language as the protocol is text-based and relatively simple. The implementation can be a microservice running internally to, or an application running externally from, Cumulocity IoT.
Java developers do not need to code to the protocol specification directly. The API and the protocol have been implemented in the Cumulocity Clients Java API . Examples using that can be found in the cumulocity-examples repository.
Consumer protocol
The Cumulocity IoT Notifications 2.0 API uses a secure WebSocket to consume notifications generated by the Cumulocity IoT API.
The new endpoint is accessible using the external Cumulocity IoT fully qualified domain name of your Cumulocity IoT environment and the standard SSL port 443 using a secure WebSocket connection. It is also available on the unsecured port 80 and to microservices using “cumulocity:8111” but in most cases a secure connection is preferred.
The URI scheme therefore is “wss” and consumers use URLs starting with “wss://” followed by the fully qualified domain name of the Cumulocity IoT environment or tenant, followed by a fixed URL path and a query string.
The fixed URL path is /notification2/consumer/ and there are only two query string arguments:
-
token
(required). Its value must be a valid token in the form of a JWT token string as returned by a create token request to the Tokens methods of the Notifications 2.0 API. Including the token as a query string parameter avoids having to set an HTTP header which can be an issue for some WebSocket clients or proxies. -
consumer
(optional). Its value is a non blank unique name for the consumer.
In summary, the URLs used by consumers follow the following patterns:
wss://your.cumulocity.environment.fullqualifieddomainname/notification2/consumer/?token=yourJwtTokenRequestedFromNotification2TokenService
or
wss://your.cumulocity.environment.fullqualifieddomainname/notification2/consumer/?token=yourJwtTokenRequestedFromNotification2TokenService&consumer=aUniqueNameForThisConsumer
WebSocket timeouts
There is a timeout of 5 minutes set on idle WebSocket connections after which the connection will be closed by the server side. Therefore the consumer must be prepared to handle closed connections which is required for fault tolerant operation in any case. All consuming microservices or applications should handle the WebSocket being closed and re-connect as necessary.
Notification acknowledgements
The WebSocket service sends a sequence of UTF-8 encoded textual notification messages to the consumer. Each notification message includes headers and a data payload. Headers occur first in the message and are separated by new line characters. The data payload is last in the message, separated from the last header by 2 new line characters, showing one blank line between the last header and the payload. The first header in the message is always the acknowledgement header.
When the client has finished processing a notification message, it must send that message’s acknowledgement header back to the server on the same connection the message was received on. Sending the acknowledgement tells the server that the consumer has successfully received and processed that message, allowing the server to forget the message. Each acknowledgement is unique to a particular notification and consumer. Note that batch and cumulative acknowledgements are not supported.
If too many of a consumer’s notifications (1000 by default) remain unacknowledged, the flow of notification messages to that consumer will stop until some of its unacknowledged messages are acknowledged. It is therefore best practise to process and acknowledge them quickly, to minimise the potential for a connection interruption causing a need to redeliver them. An acknowledgement should not be sent until its notification has been successfully processed. Otherwise, for example, if the client crashed during or before such processing, the message may be lost as acknowledged messages are not (usually) resent by the server.
The hello-world-notification-microservice example in the cumulocity-examples repository shows an example of how to send the acknowledgement back to the server in a self-contained WebSocket text message. It should be sent without quotation marks, as it is not a JSON message, and without any trailing new-line characters.
Notification message header and content
A notification (transmitted service to client) consists of a header and a body (similar to an HTTP request).
The header is one or more (in practice at least 3) lines of text, separated by a \n
(newline) character.
The end of the header is demarcated by a double new line \n\n
.
The notification body follows the header. This also consists of UTF text - for example a JSON document. If the notification is binary data or includes binary data then it will be Base64 encoded.
The header lines for a notification are as follows (separated by \n
newlines):
-
Required message identifier for message acknowledgement. This opaque value is an encoded binary 64 bit value. After the consumer has finished processing a notification, it must send this header back to the server to acknowledge the notification.
-
The notification description on the second header line. This is a string describing what type of notification this is and its source. Measurements (“measurements”), events (“events”) and alarms (“alarms”) are examples of notifications, as are inventory creates, updates and deletes (“managedObjects”). There is a direct correspondence with realtime notifications which features similar notification descriptions. These are not enumerated here and are expected to increase in number in the future. For REST API notifications, they follow a three-part format, separated by “/”. For more details on notification descriptions see Notification description header .
-
An action string is the third header. Examples are CREATE, UPDATE and DELETE. More actions may be added in the future. Together with the notification type they describe the logical event that generated the notification, such as a CREATE of an alarm or measurement.
Depending on the second header there may be further headers to follow but currently notifications only use the above three. In order to be future proof and forward compatible, we encourage consumer code to cope with more headers by parsing them out but ignoring them.
See the hello-world-notification-microservice example in the cumulocity-examples repository on how to do this.
After the headers, the notification body follows as UTF-8 text. This is typically a JSON document.
Notification description header
The second header line is the notification description string in the form of a /
-separated path. For API notifications descriptions have three parts: tenantId, type and sourceId.
- tenantId - this the identifier for the tenant under which the notification was generated.
- type - the platform type of notification generated. For example, event, measurement, alarm or managed object.
- sourceId - the identifier of the “source” object that generated or is the subject of the notification. Source is a very loose term here, much as in “event sourcing” but generally indicates which managed object the notification is about.
Some examples are provided in Traces and backwards compatibility to real-time notifications is provided for.
Dealing with notification duplication
When a WebSocket connection is lost, whether that is due to deliberate connection closure or connection failure, messages that were received but not successfully acknowledged before the connection loss are sent to the consumer again when it reconnects. This can result in duplicate messages being received by the consumer. Those duplicates can sometimes include acknowledged messages as these may be on-the-wire from the consumer to the server when the connection is lost.
Notification messages do not contain any specific unique identifiers to aid in de-duplication. Therefore, any de-duplication of messages must be done by the consumer based upon the notification description header and payload. Note that the acknowledgement header is not guaranteed to be unique across consumer reconnections, which is when duplicates are most likely.
Some events are easy to de-duplicate, such as inventory events where a unique source object is first created and then deleted. It will often be possible to use the notification message headers to determine these cases. However, inventory updates or logically sequenced events such as alarms and measurements will typically require application-specific understanding of the payload fields. Ideally the payload would include a field specifically for that purpose. If it is not possible to include such a field in the payload, then other existing fields will have to be used, maybe on a best effort basis.
A specific field in the payload would typically be a UUID or increasing sequence number. The latter may aid the efficiency of de-duplication by using ordering, though the sequence numbers will be unrelated across publishers, typically IoT devices, when the messages are originated from more than one publisher. Any messages received from the same publisher with a lower sequence number than the last one processed from that publisher can be quickly discarded, assuming the sequence has not rolled over. It is also easier for a human to understand that the ordering is correct and all messages are present.
Traces
The following is a sample of a trace of messages. The dashed lines are not a part of the message, they are a visual aid to separate the messages in the trace.
------------------------
CJ1eEAAgADAB
/tenant-a170/managedobjects/111
CREATE
{"additionParents":{"references":[],"self":"http://cumulocity.default.svc.cluster.local/inventory/managedObjects/111/additionParents"},"owner":"admin","childDevices":{"references":[],"self":"http://cumulocity.default.svc.cluster.local/inventory/managedObjects/111/childDevices"},"childAssets":{"references":[],"self":"http://cumulocity.default.svc.cluster.local/inventory/managedObjects/111/childAssets"},"creationTime":"2021-09-03T12:28:58.692Z","lastUpdated":"2021-09-03T12:28:58.692Z","childAdditions":{"references":[],"self":"http://cumulocity.default.svc.cluster.local/inventory/managedObjects/111/childAdditions"},"name":"a switch","assetParents":{"references":[],"self":"http://cumulocity.default.svc.cluster.local/inventory/managedObjects/111/assetParents"},"deviceParents":{"references":[],"self":"http://cumulocity.default.svc.cluster.local/inventory/managedObjects/111/deviceParents"},"self":"http://cumulocity.default.svc.cluster.local/inventory/managedObjects/111","id":"111","com_cumulocity_model_BinarySwitch":{"state":"ON"}}
------------------------
CKReEAAgADAB
/tenant-a170/measurements/111
CREATE
{"self":"http://cumulocity.default.svc.cluster.local/measurement/measurements/117","time":"2021-09-03T12:29:01.664Z","id":"117","source":{"self":"http://cumulocity.default.svc.cluster.local/inventory/managedObjects/111","id":"111"},"type":"c8y_TenantUsageStatisticsMeasurement","resourcesUsage":{"memory":0,"cpu":0,"usedBy":[]}}
------------------------
CI9eEAAgADAB
/tenant-a170/events/111
CREATE
{"creationTime":"2021-09-03T12:29:01.932Z","source":{"name":"a switch","self":"http://cumulocity.default.svc.cluster.local/inventory/managedObjects/111","id":"111"},"type":"com_cumulocity_model_DoorSensorEvent","self":"http://cumulocity.default.svc.cluster.local/event/events/118","time":"2021-09-03T12:29:01.664Z","id":"118","text":"Door sensor was triggered"}
------------------------
CAAQACAAMAE=
/tenant-a170/eventsWithChildren/111
CREATE
{"creationTime":"2021-09-03T12:29:01.932Z","source":{"name":"a switch","self":"http://cumulocity.default.svc.cluster.local/inventory/managedObjects/111","id":"111"},"type":"com_cumulocity_model_DoorSensorEvent","self":"http://cumulocity.default.svc.cluster.local/event/events/118","time":"2021-09-03T12:29:01.664Z","id":"118","text":"Door sensor was triggered"}
------------------------
CLJeEAAgADAB
/tenant-a170/managedobjects/111
UPDATE
{"additionParents":{"references":[],"self":"http://cumulocity.default.svc.cluster.local/inventory/managedObjects/111/additionParents"},"owner":"admin","childDevices":{"references":[],"self":"http://cumulocity.default.svc.cluster.local/inventory/managedObjects/111/childDevices"},"childAssets":{"references":[],"self":"http://cumulocity.default.svc.cluster.local/inventory/managedObjects/111/childAssets"},"creationTime":"2021-09-03T12:28:58.692Z","lastUpdated":"2021-09-03T12:28:58.692Z","childAdditions":{"references":[],"self":"http://cumulocity.default.svc.cluster.local/inventory/managedObjects/111/childAdditions"},"name":"a switch","assetParents":{"references":[],"self":"http://cumulocity.default.svc.cluster.local/inventory/managedObjects/111/assetParents"},"deviceParents":{"references":[],"self":"http://cumulocity.default.svc.cluster.local/inventory/managedObjects/111/deviceParents"},"self":"http://cumulocity.default.svc.cluster.local/inventory/managedObjects/111","id":"111","c8y_ActiveAlarmsStatus":{"major":1},"com_cumulocity_model_BinarySwitch":{"state":"ON"}}
------------------------
CMFeEAAgADAB
/tenant-a170/alarms/111
CREATE
{"severity":"MAJOR","creationTime":"2021-09-03T12:29:02.092Z","count":1,"history":{"auditRecords":[],"self":"http://cumulocity.default.svc.cluster.local/audit/auditRecords"},"source":{"name":"a switch","self":"http://cumulocity.default.svc.cluster.local/inventory/managedObjects/111","id":"111"},"type":"com_cumulocity_events_TamperEvent","self":"http://cumulocity.default.svc.cluster.local/alarm/alarms/119","time":"2021-09-03T12:29:01.664Z","id":"119","text":"Tamper sensor triggered","status":"ACTIVE","com_mycorp_MyProp":{"key1":"value1"}}
------------------------
CLxdEBQgADAB
/tenant-a170/alarms/111
CREATE
{"severity":"MAJOR","creationTime":"2021-09-03T12:29:02.092Z","count":1,"history":{"auditRecords":[],"self":"http://cumulocity.default.svc.cluster.local/audit/auditRecords"},"source":{"name":"a switch","self":"http://cumulocity.default.svc.cluster.local/inventory/managedObjects/111","id":"111"},"type":"com_cumulocity_events_TamperEvent","self":"http://cumulocity.default.svc.cluster.local/alarm/alarms/119","time":"2021-09-03T12:29:01.664Z","id":"119","text":"Tamper sensor triggered","status":"ACTIVE","com_mycorp_MyProp":{"key1":"value1"}}
------------------------
CMJdEAAgADAB
/tenant-a170/alarmsWithChildren/111
CREATE
{"severity":"MAJOR","creationTime":"2021-09-03T12:29:02.092Z","count":1,"history":{"auditRecords":[],"self":"http://cumulocity.default.svc.cluster.local/audit/auditRecords"},"source":{"name":"a switch","self":"http://cumulocity.default.svc.cluster.local/inventory/managedObjects/111","id":"111"},"type":"com_cumulocity_events_TamperEvent","self":"http://cumulocity.default.svc.cluster.local/alarm/alarms/119","time":"2021-09-03T12:29:01.664Z","id":"119","text":"Tamper sensor triggered","status":"ACTIVE","com_mycorp_MyProp":{"key1":"value1"}}