Cumulocity Web Development Tutorial - Part 3: Query data from Cumulocity
Tags: iot, tutorial, beginners, cumulocity
Published: 2023-01-18 10:06:25

---
title: Cumulocity Web Development Tutorial - Part 3: Query data from Cumulocity
published: true
date: 2023-01-05 15:02:53 UTC
tags: iot, tutorial, beginners, cumulocity
canonical_url: https://tech.forums.softwareag.com/t/cumulocity-web-development-tutorial-part-3-query-data-from-cumulocity/266388
---

 

## Part 3 has finally arrived!

![about-time](https://aws1.discourse-cdn.com/techcommunity/original/3X/9/d/9d35dd09e50ea6782f19d33b34510ab511c7e0af.gif)

In the [last part](https://tech.forums.softwareag.com/t/cumulocity-web-development-tutorial-part-2-create-your-first-component/265762) you have implemented your first component, which displays device data and temperature measurements. For now, you have mocked the data. In the third part of the web development tutorial series, you will start to query data directly from Cumulocity IoT. You will create a simulated device, which continuously sends temperature measurements. Your Cumulocity component will subscribe to these temperature measurements and will display the latest temperature measurement received by the platform.

But before, let’s have a brief look at how your current application can be build and deployed to Cumulocity.

## Build and deploy a Cumulocity application

The [@c8y/cli](https://www.npmjs.com/package/@c8y/cli) tools, which you have installed in the [first part](https://tech.forums.softwareag.com/t/cumulocity-web-development-tutorial-part-1-start-your-journey/259613) of this tutorial, have two commands. These commands let you build and deploy your application. To build your application, you need to run this instruction at the root of your project:

```
npx c8ycli build

```

This command compiles your application and creates a new directory `dist` in your project, which contains all relevant files. Once you have build your application, you are ready to deploy it. You can deploy your application by running this command at the root level of your application:

```
npx c8ycli deploy

```

The command will start a wizard, which guides you through the deployment process. You need to specify the Cumulocity tenant to which the application should be deployed to along with your username and password.

```
prompt: Instance URL: (http://demos.cumulocity.com) https://{{your-tenant}}.cumulocity.com
prompt: Username: (admin) {{your-username}}
prompt: Password: ********
Fetching application list...
Creating application...
Uploading application zip with 4093951 bytes...
Activating binary version id 8001643...
Application my-c8y-application deployed.

```

The command will take the content from the `dist` directory and create a zip archive out of it. This zip archive will be uploaded to the defined Cumulocity tenant. Once uploaded, the application will be activated and becomes available in the Cumulocity tenant.

> **Important** : the account you use for deploying the application needs to have `Admin` permissions for `Application management`.

[![image](https://aws1.discourse-cdn.com/techcommunity/optimized/3X/b/b/bba53a9915f9b41cbee121c5422347ef58eb6553_2_690x321.png)](https://aws1.discourse-cdn.com/techcommunity/original/3X/b/b/bba53a9915f9b41cbee121c5422347ef58eb6553.png "image")

## Create a simulated temperature sensor

Now that you have learned how to build and deploy an application, you will learn how to query data from Cumulocity IoT using its different APIs. In the previous part, you have displayed device data and its temperature measurement. For now, this data has been mocked within the project and the corresponding component. To learn how to actually query data directly from Cumulocity IoT, you will create a simulated device in Cumulocity, which mimics a real device. This simulated device publishes temperature measurements in an endless loop. The mocked data in the application will be replaced with actual data coming from the simulated temperature sensor.

Let’s create the simulator. In the [official documentation](https://cumulocity.com/guides/users-guide/device-management/#simulator), you will find all the necessary steps to create a new simulator. Read the documentation carefully.

> **Important** : You might see an error, if you try to access the `Simulators` section in the Device Management application. In case you experience this error, make sure your user or assigned roles have the `Admin` permission on the `Simulator` API set.

Instead of creating a simulator from scratch, you will simply use one of the predefined simulators. In the `Simulators` section of the Device Management application, click on `Add simulator`. Select `Temperature measurements` in the `Presets` list. Provide a meaningful name for your simulator, such as `Temperature sensor`. Keep the number of instance equal to 1. Finally, click on `Create` to generate your new simulator.

[![image](https://aws1.discourse-cdn.com/techcommunity/optimized/3X/f/8/f89c853ce403fa79a178f85ddb36d3e8fca70857_2_690x430.png)](https://aws1.discourse-cdn.com/techcommunity/original/3X/f/8/f89c853ce403fa79a178f85ddb36d3e8fca70857.png "image")

Your newly created simulated temperature sensor consists of 60 instructions. It creates temperature measurements, which have following structure:

```
{
	"c8y_Temperature": {
    	"T": { 
        	"value": 25,
            "unit": "°C" }
        },
    "time":"{{timestamp}}", 
    "source": {
    	"id":"{{deviceId}}" }, 
    "type": "c8y_Temperature"
}

```

In the overview of the simulators click on the toggle button of your simulator to start it. Once you started your simulator, a new device instance is created in your Cumulocity tenant. Check the `All devices` section in your Device Management application and you will see a new device, which is your simulated temperature sensor:

[![image](https://aws1.discourse-cdn.com/techcommunity/optimized/3X/b/5/b5d69b8cd131bf8f982fe14746a2f4e774ef10fd_2_690x338.png)](https://aws1.discourse-cdn.com/techcommunity/original/3X/b/5/b5d69b8cd131bf8f982fe14746a2f4e774ef10fd.png "image")

This will be the device you will query the data for in your Cumulocity web application. If you click on your device and have a look at the `Measurements` section, you can see actual temperature measurements being generated by the simulator.

## Query data using `@c8y/client`

The Cumulocity Web SDK comes with the [@c8y/client](https://www.npmjs.com/package/@c8y/client) package. Additional documentation is available [here](http://resources.cumulocity.com/documentation/websdk/client/). The `@c8y/client` is isomorphic and therefore can be used standalone in other web projects independent of their technology. It consists of several services to send requests to Cumulocity’s APIs, e.g. the Inventory API for Managed Objects or the Event API for events. It’s using JavaScript [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises) for asynchronous communication. In this section, you will learn how to use the `@c8y/client` to query data from the Inventory and how to subscribe for real-time measurements.

### Get data from the Inventory

You can continue with the project from the [2nd part](https://github.com/SoftwareAG/cumulocity-web-development-tutorial/tree/main/part-02/my-c8y-application) of this tutorial series. First, you will replace the mocked device data in the `DeviceInfoService` and query the actual device data from the Inventory. Inject the [InventoryService](http://resources.cumulocity.com/documentation/websdk/client/classes/inventoryservice.html) into your `DeviceInfoService`.

```
import { InventoryService } from '@c8y/client';

@Injectable()
export class DeviceInfoService {
  ...
  constructor(private inventoryService: InventoryService) {}
  ...
}

```

Update the `getDeviceDetails()` to request device details from the Inventory instead of mocking the data. Add a parameter for the device id to the function. This device id is used as input for the `InventoryService` function [detail()](http://resources.cumulocity.com/documentation/websdk/client/classes/inventoryservice.html#detail), which returns the corresponding ManagedObject for the provided id.

```
async getDeviceDetails(deviceId: string): Promise<DeviceDetails> {
  try {
    const response = await this.inventoryService.detail(deviceId);
    const deviceManagedObject = response.data;

    return { name: deviceManagedObject.name, type: deviceManagedObject.type };
  } catch (error) {
    console.error('Error occurred while loading the device description: ', error);

    return undefined;
  }
}

```

Surround the request to Cumulocity’s API with a try/catch to create a log statement and return `undefined` in case of any error, e.g. if the ManagedObject couldn’t be found in the Inventory. Use the `name` and `type` field of the ManagedObject to return an object, which implements the DeviceDetails interface.

Now you need to update the `getDeviceDetails()` call in the corresponding `DeviceInfoComponent`. First create a new constant in the component to specify the device id of your simulated temperature sensor. You can find the id of your device on its detail page in Cumulocity:

[![image](https://aws1.discourse-cdn.com/techcommunity/optimized/3X/1/8/1834b7c1c23ebfcda0a8b19b2470e634b2a20a39_2_607x500.png)](https://aws1.discourse-cdn.com/techcommunity/original/3X/1/8/1834b7c1c23ebfcda0a8b19b2470e634b2a20a39.png "image")

Use the constant as the input for the call of the `getDeviceDetails()` function.

```
@Component({
  selector: 'c8y-device-info',
  templateUrl: 'device-info.component.html',
  providers: [DeviceInfoService],
})
export class DeviceInfoComponent implements OnInit {
  private readonly DEVICE_ID = '{{deviceId}}';
  ...
  private async initDeviceDetails() {
    this.deviceDetails = await this.deviceInfoService.getDeviceDetails(this.DEVICE_ID);
  }
  ...
}

```

Replace `{{deviceId}}` with your device id. If you start your application locally, you will see that the component queries device data from your Cumulocity tenant:

[![image](https://aws1.discourse-cdn.com/techcommunity/original/3X/c/8/c803c80ea91c74332b3cf0b7d3307fb55e51264c.png)](https://aws1.discourse-cdn.com/techcommunity/original/3X/c/8/c803c80ea91c74332b3cf0b7d3307fb55e51264c.png "image")

### Get latest measurement

Now, you want to update how measurements are loaded from Cumulocity. For this, you need to fetch the latest measurement available for your device and in parallel subscribe for real-time notifications to receive updates for temperature measurements.

To query the latest measurement, you need to use the [MeasurementService](http://resources.cumulocity.com/documentation/websdk/client/classes/measurementservice.html) and its [list()](http://resources.cumulocity.com/documentation/websdk/client/classes/measurementservice.html#list) function. The `list()` function takes a filter object as a parameter. Getting the latest measurement is a bit tricky as you need to specify the `revert` query parameter, which only works if you set the `dateFrom` and `dateTo` query parameters. In the `DeviceInfoService` create a new private function to query the latest measurement:

**device-info.service.ts**

```
import { has, get } from 'lodash';

@Injectable()
export class DeviceInfoService {
  private readonly TEMPERATURE_FRAGMENT = 'c8y_Temperature';

  private readonly TEMPERATURE_SERIES = 'T';
  ...
  constructor(
    private inventoryService: InventoryService,
    private measurementService: MeasurementService
  ) {}
  
  subscribeForTemperatureMeasurements(deviceId: string): void {
    this.loadLatestMeasurement(deviceId, this.TEMPERATURE_FRAGMENT, this.TEMPERATURE_SERIES);
  }

  private async loadLatestMeasurement(
    deviceId: string,
    measurementFragment: string,
    measurementSeries: string
  ) {
    const filter = {
      source: deviceId,
      dateFrom: '1970-01-01',
      dateTo: new Date().toISOString(),
      valueFragmentType: measurementFragment,
      valueFragmentSeries: measurementSeries,
      pageSize: 1,
      revert: true,
    };

    try {
      const response = await this.measurementService.list(filter);

      if (
        !response.data ||
        response.data.length != 1 ||
        !has(response.data[0], `${measurementFragment}.${measurementSeries}`)
      ) {
        return;
      }

      const temperatureValue: number = get(
        response.data[0],
        `${measurementFragment}.${measurementSeries}.value`
      );
      const temperatureUnit: string = get(
        response.data[0],
        `${measurementFragment}.${measurementSeries}.unit`
      );

      this.temperatureMeasurement$.next({ value: temperatureValue, unit: temperatureUnit });
    } catch (error) {
      console.error('Error occurred while loading the latest measurement: ', error);
    }
  }
}

```

The important part of this function is the filter object, which takes `measurementFragment` and `measurementSeries` to set the `valueFragmentType` and `valueFragmentSeries` as query parameters. Furthermore, it defines the `dateFrom` and `dateTo`, which is a requirement for the `revert` query parameter to work. The `revert` parameter is set to `true`. As only the latest measurement should be loaded, the `pageSize` is set to 1. The device id will be set as `source` query parameter. The filter object is input for the `this.measurementService.list(filter)` call.

The response received will be processed to extract the value and the unit for the measurement. You will use some helper functions from [lodash](https://lodash.com/) to check if the measurement fragment and series are present. This check is done using the [has](https://lodash.com/docs/4.17.15#has) function of lodash. If the fragment and series are available you will use the [get](https://lodash.com/docs/4.17.15#get) function to read the value and unit of the temperature measurement. Use the `temperatureMeasurement$: Subject<TemperatureMeasuerement>` to publish the latest measurement to the component.

Update the `subscribeForTemperatureMeasurements` function to take the `deviceId` as parameter. Remove the old code with the mocked data and instead call the `loadLatestMeasurement` function. As input provide the `deviceId`, fragment and series. The fragment and series of the temperature measurement are defined as constants in the `DeviceInfoService`:

**device-info.service.ts**

```
...
private readonly TEMPERATURE_FRAGMENT = 'c8y_Temperature';

private readonly TEMPERATURE_SERIES = 'T';
...

```

> **Important** If you use your own device or custom simulator, which sends different measurements, you might need to adjust the fragment and series above based on the structure of your measurement, you want to subscribe to.

You can remove the `getRandomInt(min, max)` function in the `DeviceInfoService`. This function isn’t needed anymore.

In the `DeviceInfoComponent` update the call of the `subscribeForTemperatureMeasurements` by providing the device id as a parameter:

**device-info.component.ts**

```
this.deviceInfoService.subscribeForTemperatureMeasurements(this.DEVICE_ID);

```

### Subscribe for real-time notifications

Last but not least, you want to subscribe for real-time notifications to continuously receive the latest measurements. The generic approach is to use the [Realtime](http://resources.cumulocity.com/documentation/websdk/client/classes/realtime.html) service of the `@c8y/client`. The `Realtime` service allows you to subscribe for any real-time notifications, e.g. [alarms](http://resources.cumulocity.com/documentation/websdk/client/classes/realtime.html) or [measurements](http://resources.cumulocity.com/documentation/websdk/client/classes/realtime.html). You need to to use the [subscribe()] function to register for real-time notifications. Based on the subscribed channel, you will receive the according notifications in the callback, which you have defined. In this case you want to register for measurement updates and therefore you subscribe to the channel `/measurements/{{deviceId}}`. The placeholder `{{deviceId}}` needs to be replaced with the id of the device, you want to the receive measurements for. In case you want to get the measurements for all devices within your tenant, you can use the asterisk character `*`, instead of specifying a device id.

Update the `DeviceInfoService`. Inject the `Realtime` service and add a new function to subscribe for measurement updates.

**device-info.service.ts**

```
import { IMeasurement, InventoryService, MeasurementService, Realtime } from '@c8y/client';

export class DeviceInfoService {
  ...
  constructor(
    private inventoryService: InventoryService,
    private measurementService: MeasurementService,
    private realtime: Realtime
  ) {}
  ...
  private subscribeForMeasurements(
    deviceId: string,
    measurementFragment: string,
    measurementSeries: string
  ) {
    this.realtime.subscribe(`/measurements/${deviceId}`, (measurementNotification) => {
      const measurement: IMeasurement = measurementNotification.data.data;
      if (!measurement || !has(measurement, `${measurementFragment}.${measurementSeries}`)) {
        return;
      }

      const temperatureValue: number = get(
        measurement,
        `${measurementFragment}.${measurementSeries}.value`
      );
      const temperatureUnit: string = get(
        measurement,
        `${measurementFragment}.${measurementSeries}.unit`
      );
      this.temperatureMeasurement$.next({ value: temperatureValue, unit: temperatureUnit });
    });
  }

```

The `subscribeForMeasurements()` function takes the id of the device, the fragment and the series of the measurement as a parameter. The id of the device is used for the `subscribe()` function of the `Realtime` service to only receive notifications for the this particular device. Once a notification has been received it will be converted to a measurement. Based on the fragment and series, which have bee provided as parameters, the corresponding value and unit of the measurement are extracted and written to the `temperatureMeasurement$` Subject.

Finally, you need to update the `subscribeForTemperatureMeasurements()` in the `DeviceInfoService` to call the `subscribeForMeasurements()` function.

```
subscribeForTemperatureMeasurements(deviceId: string): void {
  this.loadLatestMeasurement(deviceId, this.TEMPERATURE_FRAGMENT, this.TEMPERATURE_SERIES);

  this.subscribeForMeasurements(deviceId, this.TEMPERATURE_FRAGMENT, this.TEMPERATURE_SERIES);
}

```

With these enhancements, you can now receive and display actual measurements of your device from Cumulocity IoT in your application.

![thumbs_up](https://aws1.discourse-cdn.com/techcommunity/original/3X/c/a/ca4e1e635940ff29924b3dc295b32ce9caaa5aef.gif)

## Conclusion and what’s next

You can find the code of the updated application of part 03 in the [corresponding github repository](https://github.com/SoftwareAG/cumulocity-web-development-tutorial/tree/main/part-03/my-c8y-application). Make sure to replace `{{deviceId}}` in the `device-info.component.ts` to specify your device id. Furthermore, if you want to run the full sample locally, make sure to specify your Cumulocity instance in the `package.json` for the start script.

In the next part of the this tutorial series you will learn how to clone and extend existing applications, such as the Cockpit or Device Management application. You will convert the device-info plugin into a widget, to actually use it on a Cumulocity dashboard.

[Read full topic](https://tech.forums.softwareag.com/t/cumulocity-web-development-tutorial-part-3-query-data-from-cumulocity/266388)