Open-source .NET SDK for MindSphere APIs.
This project was started on my own initiative. I am still a student and I am trying my best to develop useful solution for the absence of .NET SDK. Of course, any help is more than welcome. My goal is to develop SDK similar to MindSphere SDK for Node.js.
❗ Check out new features and changes in the version v1.1.1 -> CHANGELOG.md ❗
The SDK is hosted as a package on the nuget.org. It is possible to use following command.
dotnet add package MindSphereSdk.Core --version 1.1.1
An alternative is to install MindSphereSdk.Core via NuGet Package Manager.
To start using MindSphere APIs via this SDK an instance of the client class is needed. There are multiple clients available (e.g. AssetManagementClient). Those clients are provided by MindSphereApiSdk class. That has to be initialized with credentials and configuration.
// prerequisites
var creds = new AppCredentials(
"<client-id>",
"<client-secret>",
"<app-name>",
"<app-version>",
"<host-tenant>",
"<user-tenant>"
);
var config = new ClientConfiguration();
var sdk = new MindSphereApiSdk(creds, config);
// get client
var assetClient = sdk.GetAssetManagementClient();
// make request
var request = new ListAssetsRequest();
var assets = await assetClient.ListAssetsAsync(request);
There are two possible ways how to provide credentials to the SDK client. Application and user credentials.
Application credentials are issued in the Developer Cockpit. Find out more.
AppCredentials instance can be created using constructor with following parameters.
var credentials = new AppCredentials(
"<client-id>",
"<client-secret>",
"<app-name>",
"<app-version>",
"<host-tenant>",
"<user-tenant>"
);
It is also possible to import application credentials directly from a JSON file.
var credentials = AppCredentials.FromJsonFile("<file-path>");
The JSON file has to fit to the given structure.
{
"keyStoreClientId": "<client-id>",
"keyStoreClientSecret": "<client-secret>",
"appName": "<app-name>",
"appVersion": "<app-version>",
"hostTenant": "<host-tenant>",
"userTenant": "<user-tenant>"
}
UserCredentials constructor must be provided with access token, that can be extracted from the request headers. Find out more.
var creds = new UserCredentials("<access-token>");
var sdk = new MindSphereApiSdk(creds, config);
To change provided credentials in the runtime use the method UpdateCredentials. However it is not possible to change the credential type. New credentials must be the same type as the previous.
var creds = new UserCredentials("<access-token>");
var sdk = new MindSphereApiSdk(creds, config);
var newCreds = new UserCredentials("<new-access-token>");
sdk.UpdateCredentials(newCreds);
Additional options are passed to SDK client via ClientConfiguration that has following default values.
Property | Default value |
---|---|
Region | eu1 |
Domain | mindsphere.io |
Timeout | 100000 ms |
Proxy | empty |
var config = new ClientConfiguration(/* domain, region, timeout, proxy */);
❗ This SDK only provides C# code to make HTTP calls to the MindSphere APIs. It does not provide any additional logic. Always refer to the official docs to find out more about the REST API. E.g. Asset Mangement API specification. ❗
var assetClient = sdk.GetAssetManagementClient();
var request = new ListAssetsRequest()
{
Size = 5,
Page = 2,
/* and more ... */
};
var result = await assetClient.ListAssetsAsync(request);
var assetClient = sdk.GetAssetManagementClient();
var request = new AddAssetRequest()
{
Asset = new AssetAdd()
{
Name = "<asset-name>",
TypeId = "<asset-type-id>",
ParentId = "<parent-asset-id>",
/* and more ... */
}
};
var response = await assetClient.AddAssetAsync(request);
var assetClient = sdk.GetAssetManagementClient();
var request = new DownloadFileRequest()
{
Id = "<file-id>",
/* and more ... */
};
string fileContent = await assetClient.DownloadFileAsync(request);
// using MdspFile = MindSphereSdk.Core.AssetManagement.File;
var assetClient = sdk.GetAssetManagementClient();
MdspFile file;
using (var fs = new FileStream("test.txt", FileMode.Open))
{
var request = new UploadFileRequest()
{
File = fs,
Name = "test.txt",
/* and more ... */
};
file = await assetClient.UploadFileAsync(request);
}
To get time series data it is necessary to have corresponding class prepared. It is possible to use MindSphereName atribute or just to name the properties in the corresponding way so they could be deserialized.
var tsClient = sdk.GetIotTimeSeriesClient();
var request = new GetTimeSeriesRequest()
{
EntityId = "<asset-id>",
PropertySetName = "<aspect-name>"
From = DateTime.Now.AddDays(-1),
To = DateTime.Now,
Limit = 10,
/* and more ... */
};
var timeSeries = await tsClient.GetTimeSeriesAsync<TimeSeriesData>(request);
// for each aspect type
public class TimeSeriesData
{
// for timestamp
[MindSphereName("_time")]
public DateTime Time { get; set; }
// for aspect variable named "x"
[MindSphereName("x")]
public double X { get; set; }
// for aspect variable named "y"
[MindSphereName("y")]
public double Y { get; set; }
// for aspect variable named "z"
[MindSphereName("z")]
public double Z { get; set; }
}
To put new time series data into the MindSphere either predefined class or anonymous type can be used.
Name the properties same as the variables in the MindSphere or add MindSphereName atribute. Otherwise the deserialization would fail.
var tsClient = sdk.GetIotTimeSeriesClient();
DateTime nowUtc = DateTime.Now.ToUniversalTime();
// with class
List<TimeSeriesData> timeSeriesData = new()
{
new TimeSeriesData(nowUtc, 0.5, 0.7, 0.3),
new TimeSeriesData(nowUtc.AddMinutes(1), 0.8, 1.2, 0.7)
};
// with anonymous type
List<object> timeSeriesData = new()
{
new { _time = DateTime.Now, x = 0.5, y = 0.7, z = 0.3 }),
new { _time = DateTime.Now.AddMinutes(1), x = 0.8, y = 1.2, z = 0.7 })
};
PutTimeSeriesRequest request = new()
{
Data = timeSeriesData,
EntityId = "<asset-id>",
PropertySetName = "<aspect-name>"
};
await tsClient.PutTimeSeriesAsync(request);
// for each aspect type
public class TimeSeriesData
{
// for timestamp
[MindSphereName("_time")]
public DateTime Time { get; set; }
// for aspect variable named "x"
[MindSphereName("x")]
public double X { get; set; }
// for aspect variable named "y"
[MindSphereName("y")]
public double Y { get; set; }
// for aspect variable named "z"
[MindSphereName("z")]
public double Z { get; set; }
}
GetAggregateTimeSeriesAsync is generic method. It is necessary to set the type to a class derived from AggregateSet and to define expected MindSphere variables using properties of type AggregateVariable with corresponding names (or use MindSphereName attribute).
var iotAggregClient = sdk.GetIotTsAggregateClient();
var request = new GetAggregateTimeSeriesRequest()
{
AssetId = "<asset-id>",
AspectName = "<aspect-name>"
From = new DateTime(2021, 4, 25, 0, 0, 0),
To = new DateTime(2021, 4, 26, 0, 0, 0),
IntervalUnit = "minute",
IntervalValue = 2,
/* and more ... */
};
var tsAggregate = await iotAggregClient.GetAggregateTimeSeriesAsync<AggregateTsData>(request);
// for each apect type
public class AggregateTsData : AggregateSet
{
// for aspect variable named "x"
[MindSphereName("x")]
public AggregateVariable X { get; set; }
// for aspect variable named "y"
[MindSphereName("y")]
public AggregateVariable Y { get; set; }
// for aspect variable named "z"
[MindSphereName("z")]
public AggregateVariable Z { get; set; }
}
Since the event can have different properties based on their event type it is necessary to provide custom class to the SDK in which the custom properties will be specified.
In the code below the MyEventAdd class is custom class derived from the SDK's EventAddUpdate class that provides basic properties such as EntityId.
❗ Make sure that the custom property name matches with the name in the MindSphere or use MindSphereName attribute. ❗
var eventClient = sdk.GetEventManagementClient();
var request = new AddEventRequest()
{
Event = new MyEventAdd() // custom class derived from the SDK's Event class
{
EntityId = "<asset-id>",
Timestamp = DateTime.Now,
Description = "<description>", // event type specific property
Severity = 4 // event type specific property
},
/* and more ... */
};
// it is possible to replace Event with custom class derived from the Event
var response = await eventClient.AddEventAsync<Event>(request);
public class MyEventAddUpdate : EventAddUpdate
{
[MindSphereName("description")]
public string Description { get; set; }
[MindSphereName("severity")]
public int Severity { get; set; }
}
ListEventsAsync is also generic method that takes in a type derived from the Event class. This provides an option to fetch informations about events with custom properties.
❗ Make sure that the custom property name matches with the name in the MindSphere or use MindSphereName attribute. ❗
var eventClient = sdk.GetEventManagementClient();
var request = new ListEventsRequest();
var events = await eventClient.ListEventsAsync<MyEvent>(request);
public class MyEvent : Event
{
[MindSphereName("description")]
public string Description { get; set; }
[MindSphereName("severity")]
public int Severity { get; set; }
}
var eventClient = sdk.GetEventManagementClient();
var request = new UpdateEventTypeRequest()
{
EventTypeId = "<event-type-id>",
IfMatch = "0",
EventTypePatch = new EventTypePatch
{
Op = "replace",
Path = "/scope",
Value = "GLOBAL"
},
/* and more ...*/
};
var response = await eventClient.UpdateEventTypeAsync(request);
Aspect types | Method | Endpoint | SDK |
---|---|---|---|
GET | /aspecttypes | ListAspectTypesAsync | |
PUT | /aspecttypes/{id} | PutAspectTypeAsync | |
PATCH | /aspecttypes/{id} | PatchAspectTypeAsync | |
GET | /aspecttypes/{id} | GetAspectTypeAsync | |
DELETE | /aspecttypes/{id} | DeleteAspectTypeAsync |
Asset types | Method | Endpoint | SDK |
---|---|---|---|
GET | /assettypes | ListAssetTypesAsync | |
PUT | /assettypes/{id} | PutAssetTypeAsync | |
PATCH | /assettypes/{id} | PatchAssetTypeAsync | |
GET | /assettypes/{id} | GetAssetTypeAsync | |
DELETE | /assettypes/{id} | DeleteAssetTypeAsync | |
PUT | /assettypes/{id}/fileAssignments/{key} | AddAssetTypeFileAssignmentAsync | |
DELETE | /assettypes/{id}/fileAssignments/{key} | DeleteAssetTypeFileAssignmentAsync | |
PATCH | /assettypes/{id}/variables | PatchAssetTypeVariablesAsync |
Assets | Method | Endpoint | SDK |
---|---|---|---|
GET | /assets | ListAssetsAsync | |
POST | /assets | AddAssetAsync | |
GET | /assets/{id} | GetAssetAsync | |
PUT | /assets/{id} | PutAssetAsync | |
PATCH | /assets/{id} | PatchAssetAsync | |
DELETE | /assets/{id} | DeleteAssetAsync | |
POST | /assets/{id}/move | MoveAssetAsync | |
PUT | /assets/{id}/fileAssignments/{key} | SaveAssetFileAssignmentAsync | |
DELETE | /assets/{id}/fileAssignments/{key} | DeleteAssetFileAssignmentAsync | |
GET | /assets/root | GetRootAssetAsync |
Structures | Method | Endpoint | SDK |
---|---|---|---|
GET | /assets/{id}/variables | ListAssetVariablesAsync | |
GET | /assets/{id}/aspects | ListAssetAspectsAsync |
Locations | Method | Endpoint | SDK |
---|---|---|---|
PUT | /assets/{id}/location | PutAssetLocationAsync | |
DELETE | /assets/{id}/location | DeleteAssetLocationAsync |
Files | Method | Endpoint | SDK |
---|---|---|---|
POST | /files | UploadFileAsync | |
GET | /files | ListFilesAsync | |
GET | /files/{fileId}/file | DownloadFileAsync | |
GET | /files/{fileId} | GetFileAsync | |
PUT | /files/{fileId} | UpdateFileAsync | |
DELETE | /files/{fileId} | DeleteFileAsync |
Asset model locks | Method | Endpoint | SDK |
---|---|---|---|
GET | /model/lock | GetLockStateAsync | |
PUT | /model/lock | PutLockStateAsync |
Method | Endpoint | SDK |
---|---|---|
PUT | /timeseries | PutTimeSeriesMultipleAsync |
GET | /timeseries/{entityId}/{propertySetName} | GetTimeSeriesAsync |
PUT | /timeseries/{entityId}/{propertySetName} | PutTimeSeriesAsync |
DELETE | /timeseries/{entityId}/{propertySetName} | DeleteTimeSeriesAsync |
Method | Endpoint | SDK |
---|---|---|
GET | /aggregates | GetAggregateTimeSeriesAsync |
Events
Method | Endpoint | SDK |
---|---|---|
POST | /events | AddEventAsync |
GET | /events | ListEventsAsync |
GET | /events/{eventId} | GetEventAsync |
PUT | /events/{eventId} | UpdateEventAsync |
Event types
Method | Endpoint | SDK |
---|---|---|
POST | /eventTypes | AddEventTypeAsync |
GET | /eventTypes | ListEventTypesAsync |
PATCH | /eventTypes/{eventTypeId} | UpdateEventTypeAsync |
GET | /eventTypes/{eventTypeId} | GetEventTypeAsync |
DELETE | /eventTypes/{eventTypeId} | DeleteEventTypeAsync |
Jobs
Method | Endpoint | SDK |
---|---|---|
POST | /deleteEventsJobs | not implemented |
GET | /deleteEventsJobs/{jobId} | not implemented |
POST | /createEventsJobs | not implemented |
GET | /createEventsJobs/{jobId} | not implemented |