TokioAI integrates with Home Assistant to control IoT devices through natural language via Telegram or CLI.
User (Telegram/CLI)
|
TokioAI Agent (GCP / any host)
| REST API calls
v
Home Assistant (local network / Tailscale mesh)
|
Smart Devices (lights, switches, vacuum, Alexa, sensors)
TokioAI communicates with Home Assistant via its REST API. When deployed on a remote server (e.g., GCP), connectivity is provided through a Tailscale mesh VPN — no ports exposed to the internet.
Tuya smart devices are controlled locally via LocalTuya — a custom HA integration that communicates directly with devices on the LAN, bypassing the Tuya cloud entirely.
Benefits over Tuya Cloud:
- No cloud dependency — works even if Tuya servers are down
- No "sign invalid" token errors
- Faster response times (direct LAN communication)
- No internet required for device control
Requirements:
- Tuya device local keys (extracted via
tinytuyaortuya_sharing) - Devices must be on the same network as Home Assistant
- Device IPs (discovered via
tinytuya.deviceScan())
See LocalTuya setup details below.
- Home Assistant installed and running (Docker recommended)
- Long-Lived Access Token from HA (Profile > Security > Long-Lived Access Tokens)
- Network connectivity between TokioAI and HA (local network or Tailscale)
docker run -d \
--name homeassistant \
--restart=unless-stopped \
--network=host \
--privileged \
--stop-timeout 120 \
-e TZ=America/Buenos_Aires \
-v /path/to/ha-config:/config \
-v /run/dbus:/run/dbus:ro \
ghcr.io/home-assistant/home-assistant:stableImportant flags:
--stop-timeout 120— Gives HA enough time to flush its SQLite database on shutdown. Without this, configuration changes made through the UI may be lost on restart (default is only 10 seconds).--network=host— Required for device discovery (mDNS, SSDP).-v /path/to/ha-config:/config— Persistent config directory. All settings, automations, and database are stored here.
After first start, open http://<your-ip>:8123 and complete the onboarding wizard:
- Set your location, timezone, and unit system (metric recommended)
- Create your admin account
- Add device integrations (Tuya, Alexa Media Player, etc.)
- Go to your HA profile (bottom-left corner)
- Scroll to Long-Lived Access Tokens
- Click Create Token, name it
tokioai - Copy the token — you won't see it again
Add to your .env file:
HOME_ASSISTANT_URL=http://<ha-ip>:8123
HOME_ASSISTANT_TOKEN=<your-long-lived-token>If TokioAI runs on a different network (e.g., GCP), use the Tailscale IP:
HOME_ASSISTANT_URL=http://<tailscale-ip>:8123docker compose down && docker compose up -dTokioAI uses a strict device whitelist to prevent accidental control of unintended devices. Only explicitly listed entities can be queried or controlled.
Without a whitelist, the agent could attempt to interact with any HA entity — including internal system entities, configuration switches, or devices that shouldn't be automated. This caused instability in earlier versions.
Edit tokio_agent/engine/tools/builtin/iot_tools.py:
# PRIMARY_DEVICES: the real devices (what gets listed/reported)
PRIMARY_DEVICES = {
"light.smart_bulb": "Kitchen Lamp",
"light.smart_bulb_2": "Living Room",
"switch.my_smart_plug": "Smart Plug",
"media_player.alexa": "Alexa",
"vacuum.my_robot": "Robot Vacuum",
}
# ALLOWED_ENTITY_IDS: full set including useful sub-entities
ALLOWED_ENTITY_IDS = {
*PRIMARY_DEVICES.keys(),
"sensor.temperature", # read-only sensor
"sensor.my_robot_battery", # vacuum battery
"select.my_robot_mode", # vacuum mode
}To find your entity IDs:
- Go to HA > Settings > Devices & Services > Entities
- Or use the API:
curl -s http://<ha-ip>:8123/api/states -H "Authorization: Bearer <token>" | python3 -m json.tool
- Add the
entity_idtoPRIMARY_DEVICES(with a friendly name) orALLOWED_ENTITY_IDS - Rebuild and restart TokioAI
- Test: ask TokioAI to check the device status
| Type | Actions | Example |
|---|---|---|
| Lights | on, off, toggle, brightness, color | "Turn on the kitchen light in blue" |
| Switches | on, off, toggle | "Turn off the kitchen plug" |
| Vacuum | start, stop, pause, return_to_base, locate | "Start the vacuum" |
| Media Player | speak (TTS), play music, volume, status | "Tell Alexa to play jazz" |
| Sensors | read state | "What's the temperature?" |
Cause: Docker's default stop timeout (10s) is too short for HA to flush its SQLite WAL journal.
Fix: Always use --stop-timeout 120 when creating the container:
docker run -d --stop-timeout 120 ...If the container already exists, recreate it:
docker stop -t 60 homeassistant
docker rm homeassistant
docker run -d --stop-timeout 120 ... # full command aboveCause: TokioAI container can't reach HA on the network.
Fixes:
- If same host: use
http://host.docker.internal:8123or--network=host - If remote (via Tailscale): use the Tailscale IP (
100.x.x.x) - Verify HA is listening on all interfaces:
ss -tlnp | grep 8123should show0.0.0.0:8123
Change in .storage/core.config:
# Edit the file
sudo python3 -c "
import json
path = '/path/to/ha-config/.storage/core.config'
with open(path) as f:
data = json.load(f)
data['data']['unit_system_v2'] = 'metric'
with open(path, 'w') as f:
json.dump(data, f, indent=2)
"
# Restart HA
docker restart homeassistant- The device may be offline or not paired with HA
- Check HA UI > Settings > Devices for the device status
- Try power-cycling the device
LocalTuya replaces the Tuya Cloud integration for local-only device control.
Download hass-localtuya and extract to <ha-config>/custom_components/localtuya/.
# Using tuya_sharing (requires a valid Tuya Cloud account)
from tuya_sharing import Manager, LoginControl
m = Manager("", "", "", "", "")
m.terminal_id = "your_terminal_id"
m.token_info = {...} # from existing HA Tuya config entry
m.update_device_cache()
for dev in m.device_map.values():
print(f"{dev.name}: id={dev.id} key={dev.local_key}")Or use tinytuya wizard for an interactive approach.
import tinytuya
devices = tinytuya.deviceScan(verbose=False, maxretry=3)
for ip, dev in devices.items():
print(f"{ip}: id={dev['gwId']}, ver={dev['version']}")Go to Settings > Integrations > Add > LocalTuya. Choose "No Cloud" mode for fully local operation.
Add each device with:
- Device ID and Local Key (from step 2)
- IP address (from step 3)
- Protocol version (3.3 for most devices, 3.4 for newer plugs)
For each device, select the appropriate platform:
- Lights: DPS 20 (switch), 22 (brightness), 23 (color temp), 24 (color HSV)
- Switches: DPS 1 (on/off)
- Vacuum: DPS 5 (status), 1 (start), 2 (pause), 4 (mode), 9 (fan speed)
Once LocalTuya is confirmed working, remove the Tuya Cloud integration to avoid "sign invalid" errors and duplicate entities.
Alexa is controlled via alexa_media_player, a custom HA integration.
TokioAI uses a 3-method fallback for reliable music playback:
notify/alexa_mediawith TTS — Sends a voice command like "play jazz on Amazon Music" (most accurate)notify/alexa_mediawith ANNOUNCE — Sends as an announcement commandmedia_player/play_mediawith AMAZON_MUSIC — Direct media type fallback
This approach gives much better results than the generic play_media method alone, since it interprets the query the same way Alexa would interpret a voice command.
- The HA token is stored only in
.env(gitignored, never committed) - Communication between TokioAI and HA goes through Tailscale (WireGuard-encrypted) or local network
- The device whitelist prevents the agent from interacting with unauthorized entities
- No HA ports are exposed to the public internet
- LocalTuya communicates only on the local network — no data leaves to cloud