Introduction
Cumulocity Notification 2.0 is the latest version of the notification API. It is an improved version of Cumulocity real-time notification API with stronger delivery semantics and ordering guarantees. To be able to receive the real-time notifications, a consumer application or microservice needs to first create a subscription and obtain a token from the messaging service hosted by Cumulocity IoT.
In this article, you will learn some relevant definitions and how to use the Cumulocity Notification 2.0 API.
Pre-requisite
Before you start, please confirm:
- your user account has ADMIN permission for the permission type âNotification 2â
- Cumulocity Messaging Service needs to be enabled (only need to check when the cloud instance older than 10.13, dedicated and self-hosted instances older than 10.11)
- Shared token only supported from 10.16
Glossary
Subscription
The subscription defines the scope (context and source ID) and type (API) of notifications that will be forwarded to the specific topics. The subscripition is created by sending a request to the Notification 2.0 subscription API /notification2/subscriptions
with the JSON format definition in the request body.
A subscription is identified by a subscription name, which must be declared in the request body. The request body also requires the value of âcontextâ to be specified. This determines whether the subscription is for a single device or for the entire tenant. If the value of the context is âtenantâ, which means the subscription is for the whole tenant, then the device ID is not required. If the value of the context is mo
, which represents managedObject
, then the device ID must be given in the source. Optionally, you can filter the notifications for specific APIs or types using subscriptionFilter. For more information please check the OpenAPI Specification of Cumulocity.
Consumer client
Consumer client can be an application or microservice that you build. With a valid token, the consumer client will receive messages that match the scope specified in the subscription. When you establish the WebSocket connection, you can name the consumer clients in the request header of consumer protocol as an optional parameter to distinguish them. E.g. notification2/consumer?token=xyz&consumer=instance1
. If a consumer client uses the shared token, it is a client of shared consumer.
Consumer
When a token is created using the Token API, a consumer is created in the Cumulocity messaging service at the same time. Subscriber field in the request body identifies the consumer. When unsubscribing a subscriber, the appropriate token is required in the request parameters. If the token is the type of shared, one token can be used by multiple clients of one shared consumer.
Token
The token is used to authorize the consumer to ensure that the holder is allowed to connect and receive the subscription notifications. It has a dedicated Rest API to create the token:
/notification2/token
.
The subscriber name and subscription name must be specified in the body of the request. The subscription name should be the same as the name used to create the subscription.
You can also set the expiration time and type of token in the request, as well as whether the token is signed and whether it is persistent.
Starting from Cumulocity IoT 10.16, C8Y Notification 2.0 supports the creation of shared tokens. Shared tokens allow one important pattern: The parallelization of the consumer client workload for a notification subscription. When a shared token used by multiple consumers, every notification will be sent to one of the consumers based on a implemented shared algorithm according to the ID of the source. All notifications for a given ID will be delivered to the same consumer. This can be helpful when the consumer canât process notifications faster than they arrive for multiple IDs.
Connectivity
A secure WebSocket connection is used to consume notifications by a consumer client. The C8Y tenant domain is the endpoint for the WebSocket connection, but the URI schema https://
or http://
should be replaced by wss://
or ws://
. The URL path for creating connection is /notification2/consumer/
. There is one required argument, the token collected from the platform. You can also add the customer clientâs name here as an argument. To keep the WebSocket connection alive for a long time, you should let the client send a ping message to the server regularly. In case of connection interruption the client should implement a reconnect function.
Get started with Notification 2.0
Here I will show you the basic steps to create the Notification 2.0 connection using a Python script. Of course, you can follow these steps with any other programming language. First, letâs see how to collect notifications using an exclusive consumer.
Create a subscription
The first step is to create a subscription using subscription API of Notification 2.0:
POST /notification2/subscriptions
In the request payload, you need to define the scope of notifications that you would like to receive. It is achieved by the context and subscription filter in the payload. You can subscribe to a specific device using context = mo
, or create a subscription across the entire tenant scope with context = tenant
. When the context is mo
, the device ID is required in the request body. And the subscription filter is available for âalarmsâ, âalarmsWithChildrenâ, âeventsâ, âeventsWithChildrenâ, âmanagedobjectsâ, âmeasurementsâ and âoperationsâ when context is mo
, while tenant
context supports âalarmsâ, âeventsâ and âinventoryâ subscription filter.
You can appoint the type of data to subscribe to. typeFilter
further narrows the scope of a subscription. You can specify one type or use operator or
to specify multiple types.
Here is an example of subscription payload with tenant context and filters:
subscription_json = {
"context": "tenant",
"subscription": "SubscriptionA",
"subscriptionFilter": {
"apis": [
"alarms",
"events",
]
"typeFilter": "'c8y_BootEvent' or 'c8y_UnavailabilityAlarm'"
}
Here is an example subscription payload for mo context:
subscription_json = {
'context': 'mo',
'subscription': 'SubscriptionB',
'subscriptionFilter': {
'apis': [
'measurements'
],
'typeFilter': 'c8y_Measurement'
},
'source': {
"id": "12345"
}
When you create the subscription successfully, you will get a response with the id of the subscription. Itâs useful for the following steps.
Create a token
To be able to receive the notifications, a token should be first obtained from the token API of Notification 2.0:
POST /notification2/token
The payload of this request requires subscription name and subscriber name. The subscription name must match the name of the subscription created earlier.
Here is an example of token request payload:
token_json = {
'subscription': 'SubscriptionA',
'subscriber': 'Subscriber1'
}
If you create the token successfully, you will receive a response containing a token string.
Establish the connection
Now you should have all you need to establish the WebSocket connection. First donât forget to check the URI schema to ws://
or wss://
. I used Python library websocket-client to create a long-lived connection.
ws_client = websocket.WebSocketApp(
C8Y_BASEURL_WEBSOCKET + '/notification2/consumer/?token=' + token_response['token'],
on_open = open_handler,
on_message = message_handler,
on_error = error_handler,
on_close = close_handler
)
ws_client.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE}, ping_interval=30)
As you can see in the example above, a WebSocket client has been created using the consumer API of Notification 2.0 with the token. If there are new data generated by REST or MQTT APIs and they meet the subscription scope, the web socket client will get the notifications. The message_handler function will send acknowledgement and get notifications.
Stop the connection
If you only close the WebSocket connection, the subscription will still exist. Then the notifications come after the disconnection will buffered on the platform. So to prevent unnecessary usage of the platform resources, donât forget to delete the subscription and unsubscribe the subscriber when you stop the connection.
First, delete the subscription. It will stop the buffering of the new notifications. The ID here is the subscription ID you reserved when you created the subscription.
DELETE /notification2/subscriptions/{id}
Then, unsubscribe the subscriber. It will delete the buffered notifications. Note, that the token does not have to match the token youâve used to initially connect. You can just create a new token with the same payload for that purpose if desired.
POST /notification2/unsubscribe/?token={{the-token}}
Notification 2.0 with shared token
Here you will need two Python scripts to subscribe to Notification 2.0 to simulate two consumers. The shared token for now only supports the mo
context.
Notifications are delivered to all consumers based on the device ID. Notifications with the same device ID are sent to the same consumer. This means that the more devices there are, the more evenly the notifications will be distributed.
Then you might ask if you need to create separate subscriptions and tokens for each device. The answer is yes, you need to send separate requests to create subscriptions for each device but with the same subscription name and same subscription settings (context and subscription filter). Then you only need to create one subscriber for all the subscriptions and obtain the token. When this token is shareable, multiple consumers will receive the notifications from different devices.
Create the subscriptions and the first consumer
In this case, you need to prepare some more devices and create subscription for each device with the same subscription name.
Compared to creating a exclusive consumer, creating the first consumer with a shared token requires an additional declaration of the shareable property of the token. For this you need to add a key-value pair 'shared': True
in the request payload for creating the token. You can follow the instruction above and add the following parts.
token_json = {
'subscription': 'SubscriptionA',
'subscriber': 'Subscriber1',
'shared': True
}
In order to identify the consumer and keep the notifications with their consumers when the connection is broken, it is recommended to define the consumer name when you connect the WebSocket.
wss: {{cumulocity-domainname}}/notification2/consumer/?token={{shared-token}}&consumer={{uniqueconsumername}}
You need to keep the shared token for other consumers.
Create the second consumer
As the subscriptions and token have been created during the creation of the first consumer, this consumer (or more other consumers) only need to establish the WebSocket connection with its name and the same shared token. As the first consumer covers the subscription deleting and subscriber unsubscribing, itâs not required to perform them here.
Test the consumers
Now, when you create some data that covered by the subscriptions using REST or MQTT APIs, both of the consumers will receive notifications from different devices.
Summary
Now you should know how to use the Notification 2.0 API. You can check the full scripts for subscribing to Notifications 2.0 with and without a shared token here:
Useful links | Relevant resources
https://cumulocity.com/api/core/10.16.0/#tag/Notification-2.0-API
This article is part of the TECHniques newsletter blog - technical tips and tricks for the Software AG community. Subscribe to receive our quarterly updates or read the latest issue.