ruleway 0.1.1
ruleway: ^0.1.1 copied to clipboard
A lightweight embeddable rule engine for device automation and edge applications.
RuleWay #
RuleWay is a lightweight embeddable rule engine for Dart applications, device automation, and edge Linux devices. It is designed for workloads where rules are driven by device attributes, attribute changes, rule-level time windows, and explicit action handlers.
Features #
- Device attribute state and previous-state tracking.
device_exprandattr_changetriggers.- Multi-device rules through
targetsandtarget_index. durationandcooldownwindows.- Rule-level multi-segment
time_windowswith weekday repeat. time,date, and six-field second-levelcrontriggers.- Global actions and per-device
device_actionswith action parameters. - Action registration with built-in
emit_alarm. - Optional SQLite rule storage using compact JSON columns.
Usage #
import 'package:ruleway/ruleway.dart';
Future<void> main() async {
final engine = RuleEngine(
timeZone: 'Asia/Shanghai',
rules: [
Rule(
targets: ['AC01'],
triggers: [
Trigger(
type: TriggerType.deviceExpr,
targetIndex: 0,
attr: 'temperature',
op: '>',
value: 30,
),
],
actions: [
ActionConfig(type: 'emit_alarm', params: {'reason': 'too hot'}),
],
),
],
);
engine.onAlarm.listen((alarm) => print(alarm.toJson()));
await engine.postAttrUpdate('AC01', {'temperature': '31'});
engine.dispose();
}
postAttrUpdate() and tick() return futures because registered actions may be
asynchronous. Await them when deterministic action completion is required.
Event Context and Actions #
Attach request, session, or source metadata to an attribute update without storing it as device state:
engine.registerAction('notify', (action, context) async {
final requestId = context.context['request_id'];
await sendNotification(requestId, action.params);
});
await engine.postAttrUpdate(
'AC01',
{'temperature': 31},
context: {'request_id': 'req-42'},
);
For rules with duration > 0, RuleWay preserves the context from the event that
started the duration window. Calling tick() continues evaluating eligible
duration rules even when no new attribute event arrives. attr_change rules
still require a new edge event and are not completed by a timer tick.
Rule JSON #
{
"rules": [
{
"id": 1,
"enabled": true,
"duration": 0,
"cooldown": 5,
"targets": [{"id": "AC01"}, {"id": "Door01"}],
"time_windows": [
{"start": "08:00:00", "end": "09:00:00", "week_repeat": [0, 1, 2, 3, 4]},
{"start": "20:00:00", "end": "21:00:00", "week_repeat": [0, 1, 2, 3, 4]}
],
"triggers": [
{
"type": "attr_change",
"target_index": 0,
"attr": "has_person",
"from": 0,
"to": 1
}
],
"conditions": [
{
"type": "device_expr",
"target_index": 1,
"attr": "door_open",
"op": "==",
"value": true
}
],
"actions": [{"type": "emit_alarm", "reason": "person at open door"}],
"device_actions": [
{
"target_index": 1,
"actions": [
{"type": "set_property", "power": true},
{"type": "set_property", "brightness": 80}
]
}
]
}
]
}
week_repeat uses 0..6 for Monday..Sunday. Empty or missing means every day.
time_windows is optional; if omitted, the rule is always active.
All time_windows, time, date in_weekday, and cron calculations use the
timezone passed to RuleEngine(timeZone: ...); they never use the host machine's
local timezone.
device_actions is optional and runs after global actions.
SQLite Storage #
final storage = RuleStorage('/tmp/ruleway.db');
storage.init();
storage.importJsonFile('rules.json', reload: true);
final rules = storage.loadRules();
CLI #
dart run ruleway:ruleway_cli --timezone Asia/Shanghai rules.json
Input events as:
AC01.temperature=31
Door01.door_open=true