Execute local and remote commands/scripts triggered by Allow2 quota state changes or on scheduled intervals.
-
Two Operating Modes:
- Mode 1 (Event-Driven): Execute scripts when Allow2 quota states change
- Mode 2 (Polling): Run scripts on intervals, parse JSON responses, report usage to Allow2
-
Flexible Execution:
- Run commands locally as current user
- Execute via SSH on remote devices
- Support for both password and SSH key authentication
-
Advanced Scripting:
- Mustache template parameters (
{{childName}},{{quotaRemaining}}, etc.) - Custom parameter definitions
- Multi-line script editor with syntax highlighting
- Mustache template parameters (
-
Complete Management UI:
- Script library with search and filtering
- Remote device credential storage (password/SSH key)
- Event trigger configuration
- Polling monitor setup with JSON condition evaluation
npm install @allow2/allow2automate-cmdOr install via the Allow2Automate plugin marketplace.
If you want to execute scripts on remote devices via SSH:
- Go to the Remote Devices tab
- Click Add Device
- Enter:
- Device name (e.g., "Router", "Mac Mini")
- Hostname/IP address
- Port (default: 22)
- Username
- Choose authentication method:
- Password: Enter password
- SSH Key: Paste private key (and optional passphrase)
- Click Test Connection to verify
- Click Save
- Go to the Scripts tab
- Click Create Script
- Fill in:
- Script Name: Descriptive name
- Description: What the script does
- Script Content: Your bash/shell commands (multi-line)
- Execute on Remote Device: Toggle if using SSH
- Remote Device: Select device (if SSH enabled)
- Timeout: Maximum execution time in milliseconds
- Add Custom Parameters (optional):
- Click "Add" to define custom parameters
- Use in script with
{{parameterName}}
- Click Test to verify execution
- Click Save
Automatically execute scripts when quota states change:
- Go to the Event Triggers tab
- Click Add Trigger
- Configure:
- Trigger Name: Descriptive name
- Child: Select child to monitor
- Activity Type: gaming, internet, screen, etc.
- Condition: Choose from:
- "Allowed → Blocked (time runs out)"
- "Blocked → Allowed (time granted)"
- "Permission Removed"
- "Permission Granted"
- "Drops Below Threshold"
- "Rises Above Threshold"
- Threshold: (if using threshold condition) Value in seconds
- Script to Execute: Select from your script library
- Enabled: Toggle on/off
- Click Save
Run scripts on intervals and report usage based on responses:
- Go to the Polling Monitors tab
- Click Add Monitor
- Configure:
- Monitor Name: Descriptive name
- Script to Run: Select script that returns JSON
- Run Every: Choose interval (1 min, 5 min, 10 min, etc.)
- JSON Response Condition:
- Field to Check: e.g., "active", "isConnected", "userOnline"
- Condition: Is True, Equals, Greater Than, etc.
- Value: (if using comparison operator)
- Report Usage to Allow2: Toggle on to report usage
- Child: Select child
- Activity Type: gaming, internet, etc.
- Usage Amount: How much quota to deduct per occurrence
- Enabled: Toggle on/off
- Click Save
Scenario: Automatically turn off gaming console when gaming time expires
Setup:
- Create script (local or SSH to smart home hub):
# Using WeMo
wemo switch "Gaming Console" off
# Using Home Assistant via SSH
curl -X POST http://homeassistant.local/api/services/switch/turn_off \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{"entity_id": "switch.gaming_console"}'- Create trigger:
- Child: "Bob"
- Activity: "gaming"
- Condition: "Allowed → Blocked (time runs out)"
- Script: "Turn Off Gaming Console"
Result: Console automatically powers off when Bob's gaming time depletes.
Scenario: Block internet access at router level when quota expires
Setup:
-
Add router as remote device:
- Name: "Home Router"
- Host: 192.168.1.1
- Username: admin
- Auth: SSH Key
-
Create script (SSH mode):
# Block device by MAC address
iptables -A FORWARD -m mac --mac-source {{childDeviceMAC}} -j DROP
# Or using OpenWrt
uci add firewall rule
uci set firewall.@rule[-1].src='lan'
uci set firewall.@rule[-1].mac='{{childDeviceMAC}}'
uci set firewall.@rule[-1].target='REJECT'
uci commit firewall
/etc/init.d/firewall reload- Create trigger:
- Child: "Alice"
- Activity: "internet"
- Condition: "Allowed → Blocked"
- Script: "Block Internet Access"
Parameters: Add custom parameter childDeviceMAC = "AA:BB:CC:DD:EE:FF"
Result: Alice's device is blocked at the router when internet quota expires.
Scenario: Monitor if child is actively playing games and report usage automatically
Setup:
- Create script that returns JSON:
#!/bin/bash
# Check if Minecraft is running
if pgrep -x "minecraft" > /dev/null; then
echo '{"active": true, "game": "minecraft", "pid": '$(pgrep minecraft)'}'
else
echo '{"active": false}'
fi- Create polling monitor:
- Script: "Check Gaming Process"
- Interval: 1 minute
- Condition Field:
active - Condition Operator: Is True
- Report Usage: Yes
- Child: "Bob"
- Activity: "gaming"
- Amount: 1 (minute)
Result: Every minute, the script checks if Minecraft is running. If yes, it reports 1 minute of gaming usage to Allow2.
Scenario: Lock Mac screen when bedtime quota expires
Setup:
-
Add Mac as remote device (SSH key recommended)
-
Create script (SSH to Mac):
# Lock screen immediately
pmset displaysleepnow
# Or force logout
sudo launchctl bootout user/$(id -u {{username}})- Create trigger:
- Child: "Sarah"
- Activity: "screen"
- Condition: "Allowed → Blocked"
- Script: "Lock Mac Screen"
Result: Sarah's Mac screen locks automatically when bedtime quota expires.
Scenario: Dim lights and lower TV volume at bedtime
Setup:
- Create script (SSH to Home Assistant server):
#!/bin/bash
# Dim bedroom lights to 20%
curl -X POST http://homeassistant.local:8123/api/services/light/turn_on \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{"entity_id": "light.bedroom", "brightness": 51}'
# Lower TV volume
curl -X POST http://homeassistant.local:8123/api/services/media_player/volume_set \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{"entity_id": "media_player.bedroom_tv", "volume_level": 0.2}'- Create trigger:
- Child: "Emma"
- Activity: "screen"
- Condition: "Drops Below Threshold"
- Threshold: 300 (5 minutes)
- Script: "Bedtime Warning"
Result: 5 minutes before Emma's screen time expires, lights dim and TV volume lowers as a warning.
Scenario: Shutdown Windows PC when quota expires
Setup:
-
Add PC as remote device (SSH via OpenSSH server on Windows)
-
Create script (SSH mode):
# Shutdown in 60 seconds with warning
shutdown /s /t 60 /c "Screen time quota expired. Shutting down in 60 seconds."- Create trigger:
- Child: "Tom"
- Activity: "screen"
- Condition: "Allowed → Blocked"
- Script: "Shutdown PC"
Result: PC shuts down with 60-second warning when Tom's screen time runs out.
Use mustache syntax {{parameterName}} in your scripts:
| Parameter | Description | Example Value |
|---|---|---|
{{childName}} |
Child's display name | "Bob" |
{{childId}} |
Child's unique ID | "child-123" |
{{activityType}} |
Activity type being monitored | "gaming" |
{{quotaRemaining}} |
Remaining quota in seconds | 1800 |
{{currentRemaining}} |
Current quota remaining | 900 |
{{previousRemaining}} |
Previous quota value | 1200 |
{{wasAllowed}} |
Previous allowed state | true |
{{isAllowed}} |
Current allowed state | false |
{{timestamp}} |
ISO 8601 timestamp | "2025-01-15T14:30:00Z" |
Define your own parameters for flexible scripts:
- In Script Editor, add custom parameter names
- Click "Add" to save each parameter
- Use in script:
{{myCustomParam}} - When trigger fires, values are automatically substituted
Example:
# Script with custom parameters
ssh {{serverHost}} "docker stop {{containerName}}"Custom parameters:
serverHost= "192.168.1.50"containerName= "minecraft-server"
Polling monitors require scripts to return valid JSON:
{
"active": true
}{
"active": true,
"status": "connected",
"value": 42,
"username": "alice"
}{
"system": {
"cpu": 85,
"memory": 70
},
"processes": {
"gaming": true,
"streaming": false
}
}Configure monitor to check specific field:
- Field:
processes.gaming(supports nested fields) - Operator: Is True
- Action: Report usage
ScriptManager (ScriptManager.js)
- Executes scripts locally via
child_process - SSH execution via
ssh2library - Mustache template parameter substitution
- Timeout and error handling
StateMonitor (StateMonitor.js)
- Polls Allow2 API every 30 seconds (configurable)
- Detects quota state transitions
- Triggers script execution on condition match
- Maintains state history for comparison
PollingMonitor (PollingMonitor.js)
- Runs scripts on configurable intervals
- Parses JSON responses from scripts
- Evaluates conditions against JSON fields
- Reports usage to Allow2 API when conditions met
Plugin Main (index.js)
- Manages plugin lifecycle (load, enable, disable, unload)
- IPC communication with renderer process
- Configuration persistence
- Coordinates all managers
StateMonitor (30s poll) → Allow2 API
↓ (detects state change)
Trigger Evaluation
↓ (condition met)
ScriptManager → Execute (local/SSH)
↓
Result → Notify UI
PollingMonitor (interval) → ScriptManager
↓
Execute Script (local/SSH)
↓
Parse JSON Response
↓ (condition met)
Report Usage → Allow2 API
↓
Notify UI
Renderer (UI) ↔ Main Process communication:
Script Operations:
saveScript- Save/update scriptdeleteScript- Remove scriptexecuteScript- Run script manuallytestSSH- Test SSH connection
Trigger Operations:
addTrigger- Create event triggerupdateTrigger- Modify triggerremoveTrigger- Delete trigger
Monitor Operations:
addMonitor- Create polling monitorupdateMonitor- Modify monitorremoveMonitor- Delete monitorgetMonitorStatus- Check status
Events (Main → Renderer):
scriptOutput- Real-time script outputscriptComplete- Script finishedscriptError- Script failedtriggerFired- Trigger executedmonitorRun- Monitor executedconditionMet- Condition satisfied
- SSH passwords and private keys are stored in the plugin's persisted state
- State is encrypted by Electron's built-in storage
- Consider using SSH keys instead of passwords for better security
- Scripts run with the same permissions as Allow2Automate application
- Local scripts execute as current user
- SSH scripts execute as configured remote user
- No privilege escalation without explicit
sudoin script
- Use SSH keys instead of passwords when possible
- Protect private keys with passphrases
- Limit SSH user permissions on remote devices
- Use dedicated automation users instead of root/admin
- Audit scripts regularly for security issues
- Parameter substitution uses Mustache templating (safe by default)
- No shell injection via parameters
- Script timeout prevents infinite execution
- JSON parsing validates structure before evaluation
Problem: Script doesn't run when triggered
Solutions:
- Check script syntax - test locally first
- Verify timeout is sufficient (increase if needed)
- Check main process logs for error messages
- Test script manually via "Execute Now" button
- Ensure script has correct permissions (chmod +x for shell scripts)
Problem: Cannot connect to remote device
Solutions:
- Use Test Connection button to verify settings
- Verify hostname/IP is reachable (
pingtest) - Check SSH server is running on remote device
- Verify port (default 22) is correct and not firewalled
- For password auth: Ensure password is correct
- For key auth:
- Verify key format (PEM format recommended)
- Check key permissions (should be 600)
- Ensure public key is in
~/.ssh/authorized_keyson remote
- Check username is correct for remote system
Problem: Event trigger doesn't execute
Solutions:
- Verify trigger is Enabled
- Check child has active quota for specified activity
- Review trigger condition matches expected state change
- Monitor polling interval (State monitor polls every 30s by default)
- Check main process logs for errors
- Verify script associated with trigger exists
Problem: Scheduled monitor doesn't execute
Solutions:
- Check monitor is Enabled
- Verify script returns valid JSON:
# Test script output ./your-script.sh | jq .
- Review condition field exists in JSON response
- Check condition operator and value are correct
- Verify interval is reasonable (not too short)
- Check script execution logs
Problem: Monitor reports JSON parsing error
Solutions:
- Validate JSON output:
./your-script.sh | python -m json.tool - Ensure script outputs only JSON (no debug messages)
- Check for trailing commas or syntax errors
- Verify field names match condition configuration
- Use proper quoting in bash:
echo '{"active": true}' # Correct echo {"active": true} # Wrong
Problem: Polling monitor doesn't report to Allow2
Solutions:
- Verify "Report Usage to Allow2" is enabled
- Check child and activity type are configured
- Ensure condition is being met (check logs)
- Verify Allow2 API connectivity
- Check usage amount is > 0
- Review Allow2 quota exists for specified activity
# Install dependencies
npm install
# Build for production
npm run build
# Watch mode (auto-rebuild on changes)
npm start
# Outputs to:
# - dist/index.js (CommonJS)
# - dist/index.es.js (ES modules)# Test individual components
node -e "require('./dist/index.js')"
# Test script execution
# (create test script and use UI to execute)allow2automate-cmd/
├── src/
│ ├── index.js # Main plugin entry
│ ├── ScriptManager.js # Script execution engine
│ ├── StateMonitor.js # Event-driven monitoring
│ ├── PollingMonitor.js # Scheduled execution
│ └── Components/
│ ├── TabContent.js # Main UI container
│ ├── ScriptEditor.js # Script creation/editing
│ ├── ScriptList.js # Script library
│ ├── RemoteDeviceManager.js # SSH credentials
│ ├── TriggerConfig.js # Event triggers
│ ├── PollingConfig.js # Polling monitors
│ └── Checkbox.js # Shared component
├── dist/ # Built output
├── package.json # Dependencies & metadata
├── rollup.config.js # Build configuration
└── README.md # This file
interface Script {
id: string; // Unique identifier
name: string; // Display name
description?: string; // Optional description
script: string; // Multi-line script content
useSSH: boolean; // Execute via SSH?
remoteDeviceId?: string; // Remote device ID (if SSH)
timeout: number; // Timeout in milliseconds
workingDir?: string; // Working directory (local only)
env?: Record<string, string>; // Environment variables
parameters: string[]; // Custom parameter names
updatedAt: number; // Last modified timestamp
}interface Trigger {
id: string; // Unique identifier
name: string; // Display name
childId: string; // Child to monitor
childName: string; // Child display name
activityType: string; // Activity type
condition: TriggerCondition; // Condition type
threshold?: number; // Threshold value (if applicable)
scriptId: string; // Script to execute
enabled: boolean; // Active status
lastTriggered?: number; // Last execution timestamp
updatedAt: number; // Last modified timestamp
}
type TriggerCondition =
| 'positive_to_zero' // Quota depletes
| 'zero_to_positive' // Quota granted
| 'allowed_to_blocked' // Permission removed
| 'blocked_to_allowed' // Permission granted
| 'below_threshold' // Drops below value
| 'above_threshold'; // Rises above valueinterface Monitor {
id: string; // Unique identifier
name: string; // Display name
scriptConfig: Script; // Script to execute
intervalMs: number; // Run interval (milliseconds)
conditionField: string; // JSON field to check
conditionOperator: Operator; // Comparison operator
conditionValue?: any; // Value to compare
reportUsage: boolean; // Report to Allow2?
childId?: string; // Child ID (if reporting)
childName?: string; // Child name (if reporting)
activityType?: string; // Activity type (if reporting)
usageAmount?: number; // Quota to deduct
enabled: boolean; // Active status
lastRun?: number; // Last execution timestamp
runCount: number; // Total runs
errorCount: number; // Total errors
updatedAt: number; // Last modified timestamp
}
type Operator =
| 'truthy' // Truthy check
| 'falsy' // Falsy check
| 'equals' // Equality
| 'not_equals' // Inequality
| 'greater_than' // Greater than
| 'less_than'; // Less thaninterface RemoteDevice {
id: string; // Unique identifier
name: string; // Display name
host: string; // Hostname or IP
port: number; // SSH port (default 22)
username: string; // SSH username
authMethod: 'password' | 'key'; // Auth method
password?: string; // Password (if password auth)
privateKey?: string; // Private key (if key auth)
passphrase?: string; // Key passphrase (optional)
updatedAt: number; // Last modified timestamp
}Q: Can I use this plugin to control Windows PCs? A: Yes! Install OpenSSH Server on Windows and use SSH mode to execute PowerShell or CMD commands remotely.
Q: What shells are supported for local execution? A: On Linux/Mac: bash, sh, zsh. On Windows: cmd.exe, PowerShell. The plugin uses the system's default shell.
Q: Can I have multiple triggers for the same child? A: Yes! You can create multiple triggers with different conditions and scripts.
Q: How accurate is the state monitoring? A: StateMonitor polls Allow2 every 30 seconds by default. State changes are detected within this window.
Q: Can polling monitors run faster than 1 minute? A: Yes, but be cautious of system load. You can configure intervals as low as needed, but 1 minute is recommended minimum.
Q: Are SSH credentials stored securely? A: Credentials are stored in Electron's encrypted storage, but SSH keys are more secure than passwords. Use keys when possible.
Q: Can I backup my scripts and configurations? A: Yes, the plugin state is stored in Allow2Automate's configuration. Back up the app data directory to preserve all settings.
Q: Does this work with Docker containers?
A: Yes! Use SSH to connect to Docker host and execute docker exec commands, or use local execution if running on same machine.
- Issues: GitHub Issues
- Documentation: Allow2 Automate Docs
- Discussion: Community Forum
Contributions welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
- Initial release
- Mode 1: Event-driven triggers
- Mode 2: Polling monitors with JSON parsing
- SSH and local execution support
- Complete UI with 4 management tabs
- Mustache parameter substitution
- Remote device credential management
MIT License
Copyright (c) 2025 Allow2 Pty Ltd
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Made with ❤️ by Allow2 - Helping families manage screen time and device usage responsibly.