Skip to content
Closed
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
96 changes: 96 additions & 0 deletions skills/krisclarkdev/dakboard/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
---
metadata.clawdbot:
name: dakboard
description: Manage DAKboard screens, devices, and push custom display data.
author: Kristopher Clark
homepage: https://github.com/krisclarkdev/dakboard-skill
requires.env: ["DAKBOARD_API_KEY"]
files: ["scripts/*"]
---

# DAKboard Skill

This skill provides a command-line interface to interact with the DAKboard API. It allows for comprehensive management of devices and screens, and enables pushing custom data for dynamic displays.

## Setup

Before using this skill, you must set your DAKboard API key as an environment variable.

```bash
export DAKBOARD_API_KEY="your_api_key_here"
```

The primary tool for this skill is the Python script located at `scripts/dakboard.py`.

## Available Commands

### 1. List Devices
Retrieves a list of all DAKboard devices (e.g., Raspberry Pis) linked to your account. This is useful for finding the `device_id` needed for other commands.

**Usage:**
```bash
python3 scripts/dakboard.py devices
```

### 2. List Screens
Retrieves a list of all available screen layouts (e.g., "Big Monthly", "Two Column"). This is used to find the `screen_id` needed to change a device's display.

**Usage:**
```bash
python3 scripts/dakboard.py screens
```

### 3. Update Device Screen
Changes the screen layout currently being displayed on a specific device.

**Usage:**
```bash
# Usage: update-device <device_id> <screen_id>
python3 scripts/dakboard.py update-device "dev_0c3e1405a961" "scr_709367acf3d4"
```

### 4. Send Message
Pushes a text message to a "Custom Message" block on a screen. You can send to all devices or target a specific one by its serial number.

**Usage:**
```bash
# Send to all devices
python3 scripts/dakboard.py message "Dinner is ready!"

# Send to a specific device
python3 scripts/dakboard.py message "Meeting in 5 mins" --serial-num "6BC2849B"
```

### 5. Push Metric
Pushes a single, named data point to a "DAKboard Metrics" block. This is ideal for displaying real-time data like sensor readings or statistics.

**Usage:**
```bash
# Usage: metric <key> <value>
python3 scripts/dakboard.py metric "indoor_temp" "72.5"
```

### 6. Push Fetch Data
Pushes a complete JSON object to a "Fetch" block on a screen. This is for displaying more complex, structured data.

**Usage:**
```bash
# Usage: fetch '<json_string>'
python3 scripts/dakboard.py fetch '{"tasks": ["Buy milk", "Walk the dog"], "priority": "high"}'
```

## Security & Privacy

### External Endpoints
| URL | Data Sent | Purpose |
| :--- | :--- | :--- |
| `https://dakboard.com/api/` | API Key, Device IDs, Screen IDs, Custom Messages, Metrics Data | Used to interact with the DAKboard API to list and update devices, and push data to custom blocks. |

### Data Handling
Only data provided as arguments to the skill commands (such as messages or metrics to be displayed on the DAKboard) and your `DAKBOARD_API_KEY` are sent to `dakboard.com`. No local files are read or written.

### Model Invocation Note
This skill is designed to be autonomously invoked by the OpenClaw agent when requested by the user. You can opt-out of autonomous invocation by disabling this skill.

### Trust Statement
By using this skill, data sent is limited to the arguments provided and sent directly to `dakboard.com`. Only install this skill if you trust DAKboard with the information you choose to display.
115 changes: 115 additions & 0 deletions skills/krisclarkdev/dakboard/scripts/dakboard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#!/usr/bin/env python3
# SECURITY MANIFEST:
# Environment variables accessed: DAKBOARD_API_KEY (only)
# External endpoints called: https://dakboard.com/api/ (only)
# Local files read: none
# Local files written: none

import os
import sys
import json
import urllib.request
import urllib.error
import urllib.parse
import argparse

def get_api_key():
key = os.environ.get("DAKBOARD_API_KEY")
if not key:
print("Error: DAKBOARD_API_KEY environment variable is not set.", file=sys.stderr)
sys.exit(1)
return key

def make_request(method, endpoint, data=None):
if endpoint.startswith("/v2/"):
base = "https://dakboard.com/api"
else:
base = "https://dakboard.com/api/2"

url = f"{base}/{endpoint.lstrip('/')}"
api_key = get_api_key()
body = None
headers = {"Accept": "application/json"}

is_form_encoded = (method == "PUT" and endpoint.startswith("/devices")) or \
(method == "POST" and endpoint.startswith("/v2/custom-message"))

# Always add api_key to the URL
if "?" in url:
url += f"&api_key={api_key}"
else:
url += f"?api_key={api_key}"

if is_form_encoded:
body = urllib.parse.urlencode(data or {}).encode("utf-8")
headers["Content-Type"] = "application/x-www-form-urlencoded"
elif data:
body = json.dumps(data).encode("utf-8")
headers["Content-Type"] = "application/json"

req = urllib.request.Request(url, data=body, headers=headers, method=method)

try:
with urllib.request.urlopen(req) as response:
res_body = response.read().decode("utf-8")
if not res_body.strip().startswith(("{", "[")):
return {"status": "success", "message": res_body}
return json.loads(res_body)
except urllib.error.HTTPError as e:
error_body = e.read().decode("utf-8")
print(f"HTTP Error {e.code}: {e.reason}", file=sys.stderr)
print(f"Response: {error_body}", file=sys.stderr)
sys.exit(1)
except Exception as e:
print(f"Error making request: {e}", file=sys.stderr)
sys.exit(1)

def cmd_get_devices(args):
print(json.dumps(make_request("GET", "/devices"), indent=2))

def cmd_get_screens(args):
print(json.dumps(make_request("GET", "/screens"), indent=2))

def cmd_update_device(args):
print(json.dumps(make_request("PUT", f"/devices/{args.device_id}", data={"screen_id": args.screen_id}), indent=2))

def cmd_send_message(args):
data = {"message": args.message}
if args.serial_num:
data['serial_numbers[]'] = args.serial_num
print(json.dumps(make_request("POST", "/v2/custom-message", data=data), indent=2))

def cmd_push_metric(args):
print(json.dumps(make_request("POST", "/metrics", data={args.key: args.value}), indent=2))

def cmd_push_fetch(args):
print(json.dumps(make_request("POST", "/fetch", data=json.loads(args.json_data)), indent=2))

def main():
parser = argparse.ArgumentParser(description="DAKboard API CLI Skill")
subparsers = parser.add_subparsers(dest="command", required=True)

subparsers.add_parser("devices", help="List all DAKboard devices.")
subparsers.add_parser("screens", help="List all available screen layouts.")
p_update = subparsers.add_parser("update-device", help="Update the screen layout on a device.")
p_update.add_argument("device_id", help="The ID of the device (e.g., dev_xxxxxxxx).")
p_update.add_argument("screen_id", help="The ID of the screen to assign (e.g., scr_xxxxxxxx).")
p_msg = subparsers.add_parser("message", help="Send a message to a Custom Message block.")
p_msg.add_argument("message", help="The text message to send.")
p_msg.add_argument("--serial-num", help="Optional: The serial number of the target device.")
p_metric = subparsers.add_parser("metric", help="Push a single data point to a Metrics block.")
p_metric.add_argument("key", help="The name of the metric.")
p_metric.add_argument("value", help="The value of the metric.")
p_fetch = subparsers.add_parser("fetch", help="Push a JSON object to a Fetch block.")
p_fetch.add_argument("json_data", help="The JSON object as a string.")

args = parser.parse_args()

cmd_map = {
"devices": cmd_get_devices, "screens": cmd_get_screens, "update-device": cmd_update_device,
"message": cmd_send_message, "metric": cmd_push_metric, "fetch": cmd_push_fetch,
}
cmd_map[args.command](args)

if __name__ == "__main__":
main()