Skip to content
Merged
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
58 changes: 58 additions & 0 deletions PRIVACY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Privacy Policy

**Effective date:** 2026-04-21

This Privacy Policy describes how the **iotcli** open-source project ("we", "us", or "our") handles information.

## 1. Overview

iotcli is a local command-line tool for controlling IoT devices. It does not operate any central service, does not collect telemetry, and does not transmit your personal data to project maintainers.

## 2. Information Stored Locally

All data created by iotcli is stored locally on your machine in `~/.iotcli/`:

| File / Directory | Contents |
|------------------|----------|
| `devices.yaml` | Device names, protocols, IP addresses, and non-sensitive settings |
| `credentials/*.enc` | Fernet-encrypted secrets (tokens, local keys, passwords) |
| `.key` | Local-only Fernet encryption key (permissions `0600`) |
| `skills/` | Generated AI-agent skill files based on your device configuration |

No project maintainer, server, or third party (other than the device vendor you are intentionally communicating with) has access to these files.

## 3. Third-Party Services

When you use iotcli to communicate with a device, data is transmitted directly between your machine and the device or its vendor cloud (e.g., Tuya cloud, LG ThinQ). This communication is:

- **Initiated by you** via explicit CLI commands.
- **Governed by the device vendor's privacy policy**, not this one.

We do not act as an intermediary, proxy, or data processor for these communications.

## 4. No Telemetry or Analytics

iotcli does not:

- Collect usage statistics or crash reports.
- Use tracking cookies or fingerprinting.
- Send data to external analytics services.
- Display advertisements.

## 5. Data Security

- Credentials are encrypted at rest using Fernet (symmetric AES-128 in CBC mode with HMAC).
- The encryption key is stored with owner-only read permissions (`0600`).
- We recommend you keep your operating system and Python environment up to date.

## 6. Open Source

The full source code is available at https://github.com/iotviaai/iotcli. You can inspect exactly what the tool does and how it handles data.

## 7. Changes to This Policy

We may update this policy to reflect changes in the software. The latest version will always be available in the repository.

## 8. Contact

For privacy questions, open an issue at https://github.com/iotviaai/iotcli/issues.
192 changes: 136 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,56 @@
# iotcli

**Give your AI agent hands — control any smart home device from the terminal.**

One CLI to rule them all. Discover, configure, and control IoT devices across protocols. Built for AI agents, loved by humans.

```text
$ iotcli control status lg-ac
╭─── lg-ac — online ───╮
│ power: POWER_OFF │
│ mode: HEAT │
│ fan_speed: HIGH │
│ current_temp: 20.5 │
│ target_temp: 25 │
╰──────────────────────╯
```
<p align="center">
<b>Give your AI agent hands.</b><br>
One CLI to rule them all. Discover, configure, and control IoT devices across protocols.<br>
Built for AI agents, loved by humans.
</p>

<p align="center">
<a href="https://pypi.org/project/iotcli/"><img src="https://img.shields.io/pypi/v/iotcli?style=flat-square&color=blue" alt="PyPI version"></a>
<a href="https://pypi.org/project/iotcli/"><img src="https://img.shields.io/pypi/pyversions/iotcli?style=flat-square" alt="Python versions"></a>
<a href="https://github.com/iotviaai/iotcli/actions"><img src="https://img.shields.io/github/actions/workflow/status/iotviaai/iotcli/ci.yml?style=flat-square&logo=github" alt="CI"></a>
<a href="https://codecov.io/gh/iotviaai/iotcli"><img src="https://img.shields.io/codecov/c/github/iotviaai/iotcli?style=flat-square&logo=codecov" alt="Coverage"></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="License"></a>
<a href="PRIVACY.md"><img src="https://img.shields.io/badge/privacy-local%20only-success?style=flat-square" alt="Privacy"></a>
</p>

<p align="center">
<a href="#install">Install</a> &bull;
<a href="#quick-start">Quick Start</a> &bull;
<a href="#ai-agent-integration">AI Integration</a> &bull;
<a href="#supported-protocols">Protocols</a> &bull;
<a href="#architecture">Architecture</a>
</p>

---

## What is iotcli?

iotcli is a universal IoT device control CLI. It unifies smart-home devices behind a single, predictable interface — so you (or your AI agent) can control lights, AC units, feeders, and sensors without memorizing protocol quirks.

**Key principles:**
- **One command shape** for every device — `on`, `off`, `status`, `set`
- **Protocol-agnostic** — Tuya, miIO, MQTT, HTTP, LG ThinQ, and more
- **AI-native** — JSON mode, skill files, and an MCP server for Claude Desktop / Cursor / Zed
- **Privacy-first** — everything local, credentials encrypted, no telemetry

## Install

```bash
# Global install (recommended — no venv needed, no package conflicts)
pipx install git+https://github.com/joeVenner/CLI-IoT.git
# Global install via pipx (recommended — no venv needed)
pipx install git+https://github.com/iotviaai/iotcli.git

# Or with MCP support for Claude Desktop / Cursor
pipx install git+https://github.com/iotviaai/iotcli.git[mcp]

# Or from a local clone
git clone https://github.com/joeVenner/CLI-IoT.git
cd CLI-IoT
pipx install .
# From source
git clone https://github.com/iotviaai/iotcli.git
cd iotcli
pip install -e ".[dev]"
```

After install, `iotcli` is available system-wide. No activation, no venv.
> Requires Python 3.10+

## Quick Start

Expand All @@ -36,9 +59,9 @@ After install, `iotcli` is available system-wide. No activation, no venv.
iotcli setup

# Discover devices on your network
iotcli discover
iotcli discover --network 192.168.1.0/24

# Add a device non-interactively
# Add a device
iotcli add --name living-room-light --protocol miio \
--ip 192.168.1.100 --token <32chars>

Expand All @@ -53,24 +76,48 @@ iotcli --json control status living-room-light
iotcli --json status-all
```

## Demo

```text
$ iotcli list
╭─────────── Devices ───────────╮
│ Name Protocol IP │
│ living-room-light miio 192.168.1.100 │
│ feeder tuya 192.168.1.4 │
│ lg-ac lgac (cloud) │
╰───────────────────────────────╯

$ iotcli control status lg-ac
╭─── lg-ac — online ───╮
│ power: POWER_OFF │
│ mode: HEAT │
│ fan_speed: HIGH │
│ current_temp: 20.5 │
│ target_temp: 25 │
╰──────────────────────╯

$ iotcli --json control on feeder
{"success": true, "device": "feeder", "action": "on"}
```

## Supported Protocols

| Protocol | Devices | Connection |
| -------- | -------------------------------- | ------------- |
| `miio` | Xiaomi / Yeelight | Local (UDP) |
| `tuya` | Tuya-based (lights, plugs, etc.) | Local (TCP) |
| `mqtt` | Zigbee / Aqara via MQTT broker | Local (TCP) |
| `http` | ESPHome / Tasmota | Local (HTTP) |
| `lgac` | LG Air Conditioner (ThinQ) | Cloud (HTTPS) |
| Protocol | Devices | Connection |
|----------|---------|------------|
| `miio` | Xiaomi / Yeelight | Local (UDP) |
| `tuya` | Tuya-based (lights, plugs, etc.) | Local (TCP) |
| `mqtt` | Zigbee / Aqara via MQTT broker | Local (TCP) |
| `http` | ESPHome / Tasmota | Local (HTTP) |
| `lgac` | LG Air Conditioner (ThinQ) | Cloud (HTTPS) |

### Tuya Device Profiles
### Tuya Profiles

| Profile | Devices | Special Actions |
| ------------ | --------------------- | -------------------------------------- |
| `generic` | Any Tuya device | power on/off |
| `light` | Smart bulbs | brightness, color_temperature, color |
| `switch` | Smart plugs | power, countdown |
| `petfeeder` | ROJECO / Tuya feeders | portions, quick_feed, slow_feed, light |
| Profile | Devices | Special Actions |
|---------|---------|-----------------|
| `generic` | Any Tuya device | power on/off |
| `light` | Smart bulbs | brightness, color_temperature, color |
| `switch` | Smart plugs | power, countdown |
| `petfeeder` | ROJECO / Tuya feeders | portions, quick_feed, slow_feed, light |

```bash
iotcli add --name feeder --protocol tuya --profile petfeeder \
Expand All @@ -79,32 +126,35 @@ iotcli add --name feeder --protocol tuya --profile petfeeder \

## AI Agent Integration

Every command supports `--json` for structured output. Feed your agent the generated skill files and it knows how to control your home.
iotcli was built from the ground up for AI agents. Every command supports `--json` for structured, parseable output.

### Skill Files

Generate per-device skill files so your agent knows exactly what each device can do:

```bash
# Generate skill files for all configured devices
iotcli skills generate

# Files created in ~/.iotcli/skills/:
# iotcli.skill.yaml — global tool spec with all devices
# system_prompt.md — ready-to-use agent system prompt
# <device>/SKILL.md — per-device capability doc
# iotcli.tools.json — OpenAI/Anthropic tool schema
```

Files created in `~/.iotcli/skills/`:
- `<device>/SKILL.md` — per-device capability doc (OpenClaw-compatible)
- `iotcli.tools.json` — OpenAI/Anthropic tool schema
- `iotcli.skill.yaml` — legacy global skill spec
- `system_prompt.md` — ready-to-use agent system prompt

### MCP Server (Claude Desktop / Cursor / Zed)

iotcli ships an MCP server so Claude can control your devices directly in conversation — no copy-pasting commands.
Connect iotcli directly to your AI assistant via the Model Context Protocol:

```bash
# Install with MCP support
pip install iotcli[mcp]

# Start the server (Claude Desktop launches this for you)
# Start the server
iotcli serve mcp
```

Add to your Claude Desktop config (`claude_desktop_config.json`):
Add to `claude_desktop_config.json`:

```json
{
Expand All @@ -117,9 +167,9 @@ Add to your Claude Desktop config (`claude_desktop_config.json`):
}
```

Once connected, Claude can list devices, check status, turn them on/off, and set properties — all through structured tool calls with per-device enums and value validation.
Once connected, Claude can list devices, check status, turn them on/off, and set properties — all through structured tool calls with per-device enums and value validation. No copy-pasting commands.

### Agent Workflow (CLI)
### Agent Workflow

```bash
# 1. Discover what's available
Expand All @@ -133,6 +183,36 @@ iotcli --json control on "living-room-light"
iotcli --json control set "living-room-light" "brightness=80"
```

## Architecture

```text
src/iotcli/
├── core/ # Device model, protocol registry, controller
├── protocols/ # Self-registering protocol handlers
├── config/ # YAML config + Fernet credential vault
├── discovery/ # Async multi-protocol network scanner
├── tui/ # Rich + InquirerPy interactive wizard
├── cli/ # Click commands (discover, control, device, config, skills)
├── skills/ # Jinja2-based AI agent skill generator
└── mcp/ # MCP server for Claude Desktop / Cursor / Zed
```

**Design decisions:**
- **Protocol registry** (`core/registry.py`): Decorator-based self-registration. No hardcoded mappings.
- **Device model** (`core/device.py`): Dataclass with `slugify()` for normalized names.
- **Credentials**: Fernet-encrypted vault, never in `devices.yaml`. Key stored at `0600`.
- **Extensible**: Add a protocol by implementing `BaseProtocol` — the CLI, wizard, and skill generator pick it up automatically.

## Security

- Credentials are **never** stored in config files
- Secrets encrypted with Fernet at `~/.iotcli/credentials/`
- Encryption key has `0600` permissions (owner-only)
- No telemetry, no analytics, no cloud dependency
- Fully open source — inspect exactly what runs on your machine

See [PRIVACY.md](PRIVACY.md) for details.

## Extending — Add a New Protocol

```python
Expand All @@ -158,12 +238,12 @@ class MyProtocol(BaseProtocol):

Add one import in `protocols/__init__.py` — the CLI, wizard, and skill generator pick it up automatically.

## Security

- Credentials are **never** stored in config files
- Secrets encrypted with Fernet at `~/.iotcli/credentials/`
- Encryption key has `0600` permissions (owner-only)

## License

MIT

---

<p align="center">
Made with passion for AI agents and smart homes.
</p>
1 change: 0 additions & 1 deletion src/iotcli/cli/commands/control.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ def control(ctx, action, device_name, value):
out = Output(ctx.obj["json_output"])
cfg: ConfigManager = ctx.obj["config"]

device = cfg.get_device_or_none(device_name)
device = cfg.get_device_or_none(device_name)
if not device:
out.error(f"Device not found: {device_name}")
Expand Down
Loading