dbus_remote_proxy
A Dart package that provides proxy classes for interacting with D-Bus remote objects through WebSocket connections, especially useful in Flutter web applications where direct D-Bus communication is not available.
This package allows you to abstract D-Bus method calls and signal listening over a WebSocket connection to a backend that bridges between D-Bus and WebSocket (e.g., a custom service running on localhost).
Features
- Cross-Platform Support: Works seamlessly in both web (via WebSocket) and non-web (direct D-Bus) environments.
- D-Bus Abstraction: Simplifies interaction with D-Bus services using a proxy class.
- WebSocket Integration: Enables Flutter web apps to communicate with a WebSocket server for D-Bus access.
- Session and System Bus Support: Handles both session and system D-Bus buses.
- Error and Timeout Handling: Includes built-in error management and a 5-second timeout for WebSocket requests.
Installation
Add dbus_remote_proxy
to your pubspec.yaml
file:
dependencies:
dbus_remote_proxy: ^1.0.0
Then, run the following command to fetch the package:
flutter pub get
Prerequisites
- Flutter SDK: Ensure you have the Flutter SDK installed (supports Flutter apps).
- D-Bus Dependency: The
dbus
package is required for direct D-Bus calls. Add it to yourpubspec.yaml
:dependencies: dbus: ^0.7.8
- WebSocket Dependency: The
web_socket_channel
package is required for WebSocket support. Add it to yourpubspec.yaml
:dependencies: web_socket_channel: ^2.4.0
- WebSocket Server (for Web): For Flutter web, a WebSocket server is required to bridge D-Bus communication. See the WebSocket Server Setup section.
Usage
1. DBusRemoteObjectProxy
This class allows you to call D-Bus methods with WebSocket support on web platforms and native D-Bus on main platforms.
import 'package:dbus/dbus.dart';
import 'package:dbus_remote_proxy/dbus_remote_proxy.dart';
void main() async {
final proxy = DBusRemoteObjectProxy(
type: 'system', // or 'session'
name: 'org.example.Service',
path: DBusObjectPath('/org/example/Object'),
);
try {
final response = await proxy.callMethod(
'org.example.Interface',
'SomeMethod',
[DBusString('param')],
replySignature: DBusSignature('s'),
);
print('Response: ${response.returnValues}');
} catch (e) {
print('Error: $e');
} finally {
await proxy.close();
}
}
-
Key Methods:
callMethod
: Calls a D-Bus method with optionalreplySignature
. On web, it sends a JSON request via WebSocket with a 5-second timeout.close
: Closes the WebSocket connection and cleans up resources.
-
Parameters for
callMethod
:interface
: The D-Bus interface (optional).name
: The method name.values
: Iterable ofDBusValue
parameters.replySignature
: Expected return signature (optional).noReplyExpected
,noAutoStart
,allowInteractiveAuthorization
: Passed to native D-Bus calls (ignored on web).
2. DbusRemoteObjectSignalStreamProxy
This class listens to D-Bus signals via WebSocket or native D-Bus.
import 'package:dbus/dbus.dart';
import 'package:dbus_remote_proxy/dbus_remote_proxy.dart';
import 'package:dbus_remote_proxy/dbus_remote_object_signal_stream_proxy.dart';
void main() async {
final proxy = DBusRemoteObjectProxy(
type: 'system',
name: 'org.example.Service',
path: DBusObjectPath('/org/example/Object'),
);
final signalProxy = DbusRemoteObjectSignalStreamProxy(
object: proxy,
interface: 'org.example.Interface',
name: 'SignalName',
);
try {
final subscription = signalProxy.listen(
(signal) {
print('Received signal: ${signal.name}, values: ${signal.values}');
},
onError: (error, stackTrace) {
print('Error: $error');
},
onDone: () {
print('Stream closed');
},
);
// Cancel subscription when no longer needed
// subscription.cancel();
} catch (e) {
print('Error: $e');
} finally {
await signalProxy.close();
}
}
- Key Methods:
listen
: Returns aStreamSubscription<DBusSignal>
to handle incoming signals.close
: Closes the WebSocket connection and cleans up resources.
3. Clean Up
Close the proxy to release resources:
await proxy.close();
WebSocket Support (Flutter Web)
For Flutter web applications, DBusRemoteObjectProxy
uses WebSocket to communicate with a server that handles D-Bus calls. This is enabled automatically when kIsWeb
is true
.
WebSocket Server Setup
You need a WebSocket server to act as an intermediary between the Flutter web app and D-Bus. Below is an example Dart WebSocket server compatible with this package:
import 'dart:convert';
import 'package:dbus/dbus.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf_web_socket/shelf_web_socket.dart';
void main() async {
final sessionClient = DBusClient.session();
final systemClient = DBusClient.system();
final handler = webSocketHandler((webSocket) {
webSocket.stream.listen((message) async {
try {
final params = jsonDecode(message);
final type = params['serviceType'] as String? ?? 'system';
final name = params['serviceName'] as String;
final path = params['path'] as String;
final interface = params['interface'] as String?;
final member = params['member'] as String;
final values = (params['values'] as List)
.map((v) => fromNativeValue(v))
.toList();
final replySignature = params['replySignature'] != null
? DBusSignature(params['replySignature'])
: null;
final client = type == 'system' ? systemClient : sessionClient;
final object = DBusRemoteObject(client, name: name, path: DBusObjectPath(path));
final result = await object.callMethod(
interface,
member,
values,
replySignature: replySignature,
);
//TODO: if you have face an error while convert the objects, then please double check here
final returnValues = result.returnValues.map((v) => v.toNative()).toList();
webSocket.sink.add(jsonEncode({
'status': 'success',
'returnValues': returnValues,
}));
} catch (e) {
webSocket.sink.add(jsonEncode({
'status': 'error',
'message': e.toString(),
}));
}
});
});
final server = await Handler(handler, address: '0.0.0.0', port: 3030).startServer();
print('WebSocket server running on ${server.address.host}:${server.port}');
}
Run the server with:
dart run server.dart
WebSocket Configuration
- The WebSocket IP and port are defined via environment variables:
static const String _ip = String.fromEnvironment('WEBSOCKET_IP', defaultValue: '127.0.0.1'); static const String _port = String.fromEnvironment('WEBSOCKET_PORT', defaultValue: '3030');
- Run your Flutter app with these variables:
flutter run -d chrome --dart-define=WEBSOCKET_IP=[127.0.0.1 | REMOTE_WEBSOCKET_IP] --dart-define=WEBSOCKET_PORT=3030
Notes
- Timeout Handling: WebSocket requests have a default 5-second timeout. Adjust by modifying the
timeout
duration incallMethod
. - Error Handling: Wrap
callMethod
calls in atry-catch
block to handle exceptions. - Resource Cleanup: Call
close
in thedispose
method to prevent resource leaks. - WebSocket IP/Port: Default values are
'127.0.0.1'
and'3030'
, configurable via--dart-define
.
Contributing
Contributions are welcome! Please submit a pull request or open an issue on the GitHub repository for bug reports, feature requests, or suggestions.
License
This package is licensed under the MIT License. See the LICENSE file for details.
Support
If you encounter any issues while using DBusRemoteObjectProxy, please contact me at dangchithao@gmail.com.