RouterOSClient
RouterOSClient
is a Dart/Flutter package that provides an easy-to-use interface for connecting and communicating with Mikrotik's RouterOS devices via a socket connection. This package supports both standard and secure (SSL/TLS) connections, enabling you to send commands and receive data from RouterOS devices in real-time.
Features
- Socket Connection: Connect to RouterOS devices using either standard TCP or secure SSL/TLS sockets.
- Command Execution: Send commands to RouterOS and receive structured replies.
- Tag Support: Execute multiple commands simultaneously with tag-based response routing.
- Concurrent Operations: Run multiple commands at once without requiring additional socket connections.
- Stream Data: Stream long-running commands to receive continuous updates.
- Error Handling: Comprehensive error handling with custom exceptions for various failure scenarios.
- Verbose Logging: Optional logging for debugging and monitoring communication.
Installation
Add the following to your pubspec.yaml
file:
dependencies:
router_os_client: ^2.0.0
Then run:
flutter pub get
Usage
1. Create an Instance of RouterOSClient
import 'package:router_os_client/router_os_client.dart';
void main() async {
RouterOSClient client = RouterOSClient(
address: '192.168.88.1', // Replace with your RouterOS IP address
user: 'admin', // Replace with your RouterOS username
password: 'password', // Replace with your RouterOS password
useSsl: false, // Set to true if you are using SSL/TLS
verbose: true, // Set to true for detailed logging
);
bool isConnected = await client.login();
if (isConnected) {
print('Connected to RouterOS');
} else {
print('Failed to connect to RouterOS');
}
}
2. Send a Command
To send a command to the RouterOS device and get a response:
void fetchInterfaces() async {
List<Map<String, String>> interfaces = await client.talk(['/interface/print']);
for (var interface in interfaces) {
print('Interface Name: ${interface['name']}');
}
}
3. Using Tags for Concurrent Operations
Tags allow you to execute multiple commands simultaneously and identify their responses:
void concurrentOperations() async {
// Execute multiple commands simultaneously
var commands = [
TaggedCommand(command: '/interface/print', tag: 'interfaces'),
TaggedCommand(command: '/ip/address/print', tag: 'addresses'),
TaggedCommand(command: '/system/resource/print', tag: 'resources'),
];
await for (var response in client.talkMultiple(commands)) {
print('Response from ${response.tag}: ${response.data.length} items');
if (response.isError) {
print('Error in ${response.tag}: ${response.errorMessage}');
}
}
}
4. Single Tagged Command
Send a single command with a tag for better control:
void singleTaggedCommand() async {
var response = await client.talkTagged('/interface/print', null, 'interface-list');
print('Tag: ${response.tag}');
print('Completed: ${response.isDone}');
print('Interfaces found: ${response.data.length}');
}
5. Cancel Operations by Tag
Cancel specific operations using their tags:
void cancelOperation() async {
// Start a long-running operation
String monitorTag = 'interface-monitor';
// Cancel it after some time
await Future.delayed(Duration(seconds: 10));
await client.cancelTagged(monitorTag);
}
6. Stream Data from RouterOS
For long-running commands like /tool/torch
, you can stream the data:
void streamTorchData() async {
await for (var data in client.streamData('/tool/torch interface=ether1')) {
print('Torch Data: $data');
}
}
7. Close the Connection
After you are done communicating with the RouterOS device, close the connection:
client.close();
Error Handling
RouterOSClient
provides several custom exceptions to handle errors gracefully:
LoginError
: Thrown when there is an error during the login process.WordTooLong
: Thrown when a command word exceeds the maximum length.CreateSocketError
: Thrown when the socket connection fails.RouterOSTrapError
: Thrown when RouterOS returns a trap error in response to a command.
Example:
try {
await client.login();
} catch (LoginError e) {
print('Login failed: ${e.message}');
} catch (CreateSocketError e) {
print('Socket creation failed: ${e.message}');
}
API Reference
New Classes for Tag Support
TaggedResponse
Represents a response from a tagged command:
class TaggedResponse {
final List<Map<String, String>> data; // Parsed response data
final String? tag; // Command tag
final bool isDone; // Whether command completed
final bool isError; // Whether response is an error
final String? errorMessage; // Error message if applicable
}
TaggedCommand
Represents a command for batch operations:
class TaggedCommand {
final dynamic command; // Command to execute
final Map<String, String>? params; // Command parameters
final String? tag; // Command tag
}
Enhanced Methods
talk()
- Now with optional tag support
// Traditional usage
var result = await client.talk('/interface/print');
// With parameters
var result = await client.talk('/interface/print', {'?type': 'ether'});
// With tag for concurrent operations
var result = await client.talk('/interface/print', {'?type': 'ether'}, 'my-tag');
talkTagged()
- New tagged command method
Future<TaggedResponse> talkTagged(
dynamic command,
[Map<String, String>? params, String? tag]
)
talkMultiple()
- Execute multiple commands simultaneously
Stream<TaggedResponse> talkMultiple(List<TaggedCommand> commands)
cancelTagged()
- Cancel commands by tag
Future<void> cancelTagged(String tag)
Examples
Here are comprehensive examples showcasing both traditional and new tag-based functionality:
Basic Connection and Commands
import 'package:router_os_client/router_os_client.dart';
void main() async {
RouterOSClient client = RouterOSClient(
address: '192.168.88.1',
user: 'admin',
password: 'password',
useSsl: false,
verbose: true,
);
try {
if (await client.login()) {
print('Connected to RouterOS');
// Fetch and print interface list
List<Map<String, String>> interfaces = await client.talk(['/interface/print']);
interfaces.forEach((interface) {
print('Interface: ${interface['name']}');
});
// Stream torch data
await for (var data in client.streamData('/tool/torch interface=ether1')) {
print('Torch Data: $data');
}
} else {
print('Failed to connect to RouterOS');
}
} catch (e) {
print('Error: $e');
} finally {
client.close();
}
}
Concurrent Operations with Tags
void demonstrateTaggedOperations() async {
RouterOSClient client = RouterOSClient(
address: '192.168.88.1',
user: 'admin',
password: 'password',
);
await client.login();
// Execute multiple commands simultaneously
var commands = [
TaggedCommand(
command: '/interface/print',
params: {'.proplist': 'name,type,running'},
tag: 'interfaces',
),
TaggedCommand(
command: '/ip/address/print',
params: {'.proplist': 'address,interface'},
tag: 'addresses',
),
TaggedCommand(
command: '/system/resource/print',
params: {'.proplist': 'cpu-load,free-memory'},
tag: 'resources',
),
];
var results = <String, List<Map<String, String>>>{};
await for (var response in client.talkMultiple(commands)) {
results[response.tag!] = response.data;
if (response.isDone) {
print('${response.tag} completed with ${response.data.length} items');
}
if (response.isError) {
print('${response.tag} failed: ${response.errorMessage}');
}
}
client.close();
}
Long-Running Operations with Cancellation
void monitorWithCancellation() async {
RouterOSClient client = RouterOSClient(
address: '192.168.88.1',
user: 'admin',
password: 'password',
);
await client.login();
String monitorTag = 'interface-monitor';
// Start monitoring interfaces
var monitoring = client.streamData('/interface/listen', null, monitorTag);
// Process changes for 30 seconds
var subscription = monitoring.listen((data) {
print('Interface change detected: $data');
});
// Cancel after 30 seconds
Future.delayed(Duration(seconds: 30), () async {
await client.cancelTagged(monitorTag);
subscription.cancel();
client.close();
});
}
Example: Using talk
with Parameters
The talk
method can now accept a Map<String, String>
for sending commands with parameters to the RouterOS device.
Example:
await client.talk('/queue/simple/add', {
'.id': '*1',
'target': '192.168.88.1/32',
'priority': '1',
'max-limit': '10M/10M',
'dynamic': 'false',
'disabled': 'false',
});
This allows you to send more complex commands with key-value pairs for configuring the RouterOS device.
License
This project is licensed under the MIT License. See the LICENSE file for details.
Contributing
Contributions are welcome! Please feel free to submit a pull request or file an issue on the GitHub repository.
Contact
For any issues or feature requests, please contact @Shafiq or open an issue on GitHub.