Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement requestThingDescription method #76

Merged
merged 5 commits into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 29 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,54 +36,49 @@ You can then use the package in your project by adding

## Usage

Below you can find a very basic example for reading a status from a Thing (using the
`coap.me` test server).
To do so, a Thing Description JSON string is first parsed and turned into a
`ThingDescription` object, which is then passed to a WoT runtime created by a
`Servient` with CoAP support.
Below you can find a basic example for incrementing and reading the value of a
counter Thing, which is part of the
[Thingweb Online Things](https://www.thingweb.io/services).

In the example, we first create a WoT runtime using a `Servient` with CoAP
support.
With the runtime, we then retrieve a TD (using the `requestThingDescription()`
method) and consume it (using the `consume()` method), creating a
`ConsumedThing` object,
Afterward, the actual interactions with the counter are performed by calling the
`invokeAction()` and `readProperty()` methods on the `ConsumedThing`.

```dart
import 'package:dart_wot/dart_wot.dart';

Future<void> main(List<String> args) async {
final CoapClientFactory coapClientFactory = CoapClientFactory();
final servient = Servient(
protocolClients: [coapClientFactory]
clientFactories: [
CoapClientFactory(),
],
);
final wot = await servient.start();

final thingDescriptionJson = '''
{
"@context": "http://www.w3.org/ns/td",
"title": "Test Thing",
"base": "coap://coap.me",
"security": ["nosec_sc"],
"securityDefinitions": {
"nosec_sc": {
"scheme": "nosec"
}
},
"properties": {
"status": {
"forms": [
{
"href": "/hello"
}
]
}
}
}
''';

final thingDescription = ThingDescription(thingDescriptionJson);
final url = Uri.parse('coap://plugfest.thingweb.io/counter');
print('Requesting TD from $url ...');
final thingDescription = await wot.requestThingDescription(url);

final consumedThing = await wot.consume(thingDescription);
final status = await consumedThing.readProperty("status");
print(
'Successfully retrieved and consumed TD with title '
'"${thingDescription.title}"!',
);

print('Incrementing counter ...');
await consumedThing.invokeAction('increment');

final status = await consumedThing.readProperty('count');
final value = await status.value();
print(value);
print('New counter value: $value');
}
```

A more complex example can be found in the `example` directory.
More complex examples can be found in the `example` directory.

## Additional information

Expand Down
122 changes: 12 additions & 110 deletions example/example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,126 +8,28 @@

import 'package:dart_wot/dart_wot.dart';

final Map<String, BasicCredentials> basicCredentials = {
'urn:test': BasicCredentials('rw', 'readwrite'),
};

Future<BasicCredentials?> basicCredentialsCallback(
Uri uri,
Form? form, [
BasicCredentials? invalidCredentials,
]) async {
final id = form?.thingDescription.identifier;

return basicCredentials[id];
}

Future<void> main(List<String> args) async {
final coapClientFactory = CoapClientFactory();
final httpClientFactory =
HttpClientFactory(basicCredentialsCallback: basicCredentialsCallback);
final mqttClientFactory = MqttClientFactory();

final servient = Servient(
clientFactories: [
coapClientFactory,
httpClientFactory,
mqttClientFactory,
CoapClientFactory(),
],
);

final wot = await servient.start();

const thingDescriptionJson = '''
{
"@context": "http://www.w3.org/ns/td",
"title": "Test Thing",
"id": "urn:test",
"base": "coap://coap.me",
"security": ["auto_sc"],
"securityDefinitions": {
"auto_sc": {
"scheme": "auto"
}
},
"properties": {
"status": {
"forms": [
{
"href": "/hello"
}
]
},
"status2": {
"observable": true,
"forms": [
{
"href": "mqtt://test.mosquitto.org:1884",
"mqv:filter": "test",
"op": ["readproperty", "observeproperty"],
"contentType": "text/plain"
}
]
}
},
"actions": {
"toggle": {
"forms": [
{
"href": "mqtt://test.mosquitto.org:1884",
"mqv:topic": "test",
"mqv:retain": true
}
]
}
}
}
''';
final url = Uri.parse('coap://plugfest.thingweb.io/counter');
print('Requesting TD from $url ...');
final thingDescription = await wot.requestThingDescription(url);

final thingDescription = ThingDescription(thingDescriptionJson);
final consumedThing = await wot.consume(thingDescription);
final status = await consumedThing.readProperty('status');
final value = await status.value();
print(value);
final subscription = await consumedThing.observeProperty(
'status2',
(data) async {
final value = await data.value();
print(value);
},
print(
'Successfully retrieved and consumed TD with title '
'"${thingDescription.title}"!',
);

await consumedThing.invokeAction('toggle', 'Hello World!');
await consumedThing.invokeAction('toggle', 'Hello World!');
await consumedThing.invokeAction('toggle', 'Hello World!');
await consumedThing.invokeAction('toggle', 'Hello World!');
await subscription.stop();
print('Incrementing counter ...');
await consumedThing.invokeAction('increment');

final thingUri = Uri.parse(
'https://raw.githubusercontent.com/w3c/wot-testing'
'/b07fa6124bca7796e6ca752a3640fac264d3bcbc/events/2021.03.Online/TDs'
'/Oracle/oracle-Festo_Shared.td.jsonld',
);

final thingDiscovery = wot.discover(thingUri);

await for (final thingDescription in thingDiscovery) {
final consumedDiscoveredThing = await wot.consume(thingDescription);
print(
'The title of the fetched TD is '
'${consumedDiscoveredThing.thingDescription.title}.',
);
}

await consumedThing.invokeAction('toggle', 'Bye World!');
await consumedThing.readAndPrintProperty('status2');
print('Done!');
}

extension ReadAndPrintExtension on ConsumedThing {
Future<void> readAndPrintProperty(String propertyName) async {
final output = await readProperty(propertyName);
final value = await output.value();
print(value);
}
final status = await consumedThing.readProperty('count');
final value = await status.value();
print('New counter value: $value');
}
105 changes: 105 additions & 0 deletions example/mqtt_example.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright 2023 Contributors to the Eclipse Foundation. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// SPDX-License-Identifier: BSD-3-Clause

// ignore_for_file: avoid_print

import 'package:dart_wot/dart_wot.dart';

const thingDescriptionJson = '''
{
"@context": "https://www.w3.org/2022/wot/td/v1.1",
"title": "Test Thing",
"id": "urn:test",
"base": "coap://coap.me",
"security": ["auto_sc"],
"securityDefinitions": {
"auto_sc": {
"scheme": "auto"
}
},
"properties": {
"status": {
"observable": true,
"forms": [
{
"href": "mqtt://test.mosquitto.org:1884",
"mqv:filter": "test",
"op": ["readproperty", "observeproperty"],
"contentType": "text/plain"
}
]
}
},
"actions": {
"toggle": {
"input": {
"type": "string"
},
"forms": [
{
"href": "mqtt://test.mosquitto.org:1884",
"mqv:topic": "test",
"mqv:retain": true
}
]
}
}
}
''';

final Map<String, BasicCredentials> basicCredentials = {
'urn:test': BasicCredentials('rw', 'readwrite'),
};

Future<BasicCredentials?> basicCredentialsCallback(
Uri uri,
Form? form, [
BasicCredentials? invalidCredentials,
]) async {
final id = form?.thingDescription.identifier;

return basicCredentials[id];
}

Future<void> main(List<String> args) async {
final servient = Servient(
clientFactories: [
MqttClientFactory(basicCredentialsCallback: basicCredentialsCallback),
],
);

final wot = await servient.start();

final thingDescription = ThingDescription(thingDescriptionJson);
final consumedThing = await wot.consume(thingDescription);
await consumedThing.readAndPrintProperty('status');

final subscription = await consumedThing.observeProperty(
'status',
(data) async {
final value = await data.value();
print(value);
},
);

await consumedThing.invokeAction('toggle', 'Hello World!');
await consumedThing.invokeAction('toggle', 'Hello World!');
await consumedThing.invokeAction('toggle', 'Hello World!');
await consumedThing.invokeAction('toggle', 'Hello World!');
await subscription.stop();

await consumedThing.invokeAction('toggle', 'Bye World!');
await consumedThing.readAndPrintProperty('status');
print('Done!');
}

extension ReadAndPrintExtension on ConsumedThing {
Future<void> readAndPrintProperty(String propertyName) async {
final output = await readProperty(propertyName);
final value = await output.value();
print(value);
}
}
8 changes: 8 additions & 0 deletions lib/src/binding_coap/coap_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -538,4 +538,12 @@ final class CoapClient implements ProtocolClient {
yield content;
}
}

@override
Future<Content> requestThingDescription(Uri url) async => _sendRequest(
url,
coap.RequestMethod.get,
form: null,
accept: coap.CoapMediaType.applicationTdJson,
);
}
14 changes: 14 additions & 0 deletions lib/src/binding_http/http_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -332,4 +332,18 @@ final class HttpClient implements ProtocolClient {

yield encodedLinks;
}

@override
Future<Content> requestThingDescription(Uri url) async {
final request = Request(HttpRequestMethod.get.methodName, url);
const tdContentType = 'application/td+json';
request.headers['Accept'] = tdContentType;

final response = await _client.send(request);

return Content(
response.headers['Content-Type'] ?? tdContentType,
response.stream,
);
}
}
6 changes: 6 additions & 0 deletions lib/src/binding_mqtt/mqtt_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -251,4 +251,10 @@ final class MqttClient implements ProtocolClient {
// TODO: implement discoverWithCoreLinkFormat
throw UnimplementedError();
}

@override
Future<Content> requestThingDescription(Uri url) {
// TODO: implement requestThingDescription
throw UnimplementedError();
}
}
3 changes: 3 additions & 0 deletions lib/src/core/protocol_interfaces/protocol_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,7 @@ abstract interface class ProtocolClient {
void Function(Exception error)? error,
required void Function() complete,
});

/// Requests a Thing Description as [Content] from a [url].
Future<Content> requestThingDescription(Uri url);
}
Loading