feat: méthode WebSocket HA pour Lovelace + vue lumières créée

This commit is contained in:
Nox
2026-02-22 18:28:34 +00:00
parent 7d6605e33e
commit 917d1da7c0
668 changed files with 198094 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
{
"version": 1,
"registry": "https://clawhub.ai",
"slug": "fal-ai",
"installedVersion": "0.1.0",
"installedAt": 1771426182485
}
+33
View File
@@ -0,0 +1,33 @@
![](https://i.imgur.com/tP0xHSp.png)
# fal.ai API Skill
See [SKILL.md](./SKILL.md) for full documentation.
## Quick Start
```bash
# Set your API key
export FAL_KEY="your-api-key"
# Generate an image
python3 fal_api.py --prompt "A cute robot cat" --model flux-schnell
# List available models
python3 fal_api.py --list-models
```
## Configure Credentials
```bash
# Via environment
export FAL_KEY="your-api-key"
# Or via clawdbot config
clawdbot config set skill.fal_api.key YOUR_API_KEY
```
## Requirements
- Python 3.7+
- No external dependencies (uses stdlib)
+95
View File
@@ -0,0 +1,95 @@
---
name: fal-api
description: Generate images, videos, and audio via fal.ai API (FLUX, SDXL, Whisper, etc.)
version: 0.1.0
metadata:
{
"openclaw": { "requires": { "env": ["FAL_KEY"] }, "primaryEnv": "FAL_KEY" },
}
---
# fal.ai API Skill
Generate images, videos, and transcripts using fal.ai's API with support for FLUX, Stable Diffusion, Whisper, and more.
## Features
- Queue-based async generation (submit → poll → result)
- Support for 600+ AI models
- Image generation (FLUX, SDXL, Recraft)
- Video generation (MiniMax, WAN)
- Speech-to-text (Whisper)
- Stdlib-only dependencies (no `fal_client` required)
## Setup
1. Get your API key from https://fal.ai/dashboard/keys
2. Configure with:
```bash
export FAL_KEY="your-api-key"
```
Or via clawdbot config:
```bash
clawdbot config set skill.fal_api.key YOUR_API_KEY
```
## Usage
### Interactive Mode
```
You: Generate a cyberpunk cityscape with FLUX
Klawf: Creates the image and returns the URL
```
### Python Script
```python
from fal_api import FalAPI
api = FalAPI()
# Generate and wait
urls = api.generate_and_wait(
prompt="A serene Japanese garden",
model="flux-dev"
)
print(urls)
```
### Available Models
| Model | Endpoint | Type |
| ------------- | ------------------------------------- | ------------ |
| flux-schnell | `fal-ai/flux/schnell` | Image (fast) |
| flux-dev | `fal-ai/flux/dev` | Image |
| flux-pro | `fal-ai/flux-pro/v1.1-ultra` | Image (2K) |
| fast-sdxl | `fal-ai/fast-sdxl` | Image |
| recraft-v3 | `fal-ai/recraft-v3` | Image |
| sd35-large | `fal-ai/stable-diffusion-v35-large` | Image |
| minimax-video | `fal-ai/minimax-video/image-to-video` | Video |
| wan-video | `fal-ai/wan/v2.1/1.3b/text-to-video` | Video |
| whisper | `fal-ai/whisper` | Audio |
For the full list, run:
```bash
python3 fal_api.py --list-models
```
## Parameters
| Parameter | Type | Default | Description |
| ---------- | ---- | ---------------- | -------------------------------------------------- |
| prompt | str | required | Image/video description |
| model | str | "flux-dev" | Model name from table above |
| image_size | str | "landscape_16_9" | Preset: square, portrait_4_3, landscape_16_9, etc. |
| num_images | int | 1 | Number of images to generate |
| seed | int | None | Random seed for reproducibility |
## Credits
Built following the krea-api skill pattern. Uses fal.ai's queue-based API for reliable async generation.
+6
View File
@@ -0,0 +1,6 @@
{
"ownerId": "kn7bwx2qpadhr9wgbcqfr6q86h8098s2",
"slug": "fal-ai",
"version": "0.1.0",
"publishedAt": 1769875535293
}
+297
View File
@@ -0,0 +1,297 @@
#!/usr/bin/env python3
"""
fal.ai API - Image, Video, and Audio Generation Skill
Usage:
python fal_api.py --prompt "A beautiful sunset" --model flux-dev
Or use as a module:
from fal_api import FalAPI
api = FalAPI()
urls = api.generate_and_wait(prompt="...")
"""
import json
import os
import time
import urllib.request
import urllib.error
import argparse
from typing import Optional, List, Dict, Any
class FalAPI:
"""Client for fal.ai generative media API."""
QUEUE_URL = "https://queue.fal.run"
# Available models and their endpoints
MODELS = {
# Image generation
"flux-schnell": "fal-ai/flux/schnell",
"flux-dev": "fal-ai/flux/dev",
"flux-pro": "fal-ai/flux-pro/v1.1-ultra",
"fast-sdxl": "fal-ai/fast-sdxl",
"recraft-v3": "fal-ai/recraft-v3",
"sd35-large": "fal-ai/stable-diffusion-v35-large",
# Video generation
"minimax-video": "fal-ai/minimax-video/image-to-video",
"wan-video": "fal-ai/wan/v2.1/1.3b/text-to-video",
# Audio
"whisper": "fal-ai/whisper",
}
# Preset image sizes
IMAGE_SIZES = {
"square": "square",
"square_hd": "square_hd",
"portrait_4_3": "portrait_4_3",
"portrait_16_9": "portrait_16_9",
"landscape_4_3": "landscape_4_3",
"landscape_16_9": "landscape_16_9",
}
def __init__(self, api_key: str = None):
"""
Initialize the fal.ai API client.
Args:
api_key: Your FAL_KEY (or set via env/config)
"""
if not api_key:
api_key = os.environ.get("FAL_KEY") or self._get_config("key")
if not api_key:
raise ValueError("FAL_KEY required. Set via env or clawdbot config.")
self.api_key = api_key
self.headers = {
"Authorization": f"Key {self.api_key}",
"Content-Type": "application/json",
"Accept": "application/json",
"User-Agent": "Mozilla/5.0 (compatible; Klawf/1.0; +https://clawdhub.com/agmmnn/fal-api)"
}
def _get_config(self, key: str) -> Optional[str]:
"""Get config from clawdbot config if available."""
try:
import subprocess
result = subprocess.run(
["clawdbot", "config", "get", f"skill.fal_api.{key}"],
capture_output=True, text=True
)
return result.stdout.strip() if result.returncode == 0 else None
except Exception:
return None
def _request(self, method: str, url: str, data: dict = None) -> dict:
"""Make HTTP request to fal.ai API."""
req = urllib.request.Request(url, method=method)
for k, v in self.headers.items():
if method == "GET" and k.lower() == "content-type":
continue
req.add_header(k, v)
if data:
req.data = json.dumps(data).encode()
with urllib.request.urlopen(req, timeout=120) as response:
return json.loads(response.read().decode())
def submit(
self,
model: str,
payload: Dict[str, Any],
) -> dict:
"""
Submit a job to the queue.
Args:
model: Model name or full endpoint
payload: Request payload
Returns:
dict with request_id, status_url, response_url
"""
endpoint = self.MODELS.get(model, model)
url = f"{self.QUEUE_URL}/{endpoint}"
return self._request("POST", url, payload)
def get_status(self, model: str, request_id: str) -> dict:
"""Get the status of a queued request."""
endpoint = self.MODELS.get(model, model)
url = f"{self.QUEUE_URL}/{endpoint}/requests/{request_id}/status"
return self._request("GET", url)
def get_result(self, model: str, request_id: str) -> dict:
"""Get the result of a completed request."""
endpoint = self.MODELS.get(model, model)
url = f"{self.QUEUE_URL}/{endpoint}/requests/{request_id}"
return self._request("GET", url)
def wait_for_completion(
self,
model: str,
request_id: str,
poll_interval: float = 2.0,
timeout: float = 300.0
) -> dict:
"""Poll until job completes or times out."""
start = time.time()
while time.time() - start < timeout:
status = self.get_status(model, request_id)
state = status.get("status")
if state == "COMPLETED":
return self.get_result(model, request_id)
elif state == "FAILED":
raise Exception(f"Job failed: {status}")
time.sleep(poll_interval)
raise TimeoutError(f"Job {request_id} did not complete within {timeout}s")
def generate_image(
self,
prompt: str,
model: str = "flux-dev",
image_size: str = "landscape_16_9",
num_images: int = 1,
seed: Optional[int] = None,
**kwargs
) -> dict:
"""
Submit an image generation job.
Args:
prompt: Text description of the image
model: Model name (default: "flux-dev")
image_size: Size preset (default: "landscape_16_9")
num_images: Number of images (default: 1)
seed: Random seed for reproducibility
**kwargs: Additional model-specific parameters
Returns:
dict with request_id and status URLs
"""
payload = {
"prompt": prompt,
"image_size": self.IMAGE_SIZES.get(image_size, image_size),
"num_images": num_images,
**kwargs
}
if seed is not None:
payload["seed"] = seed
return self.submit(model, payload)
def generate_video(
self,
prompt: str,
image_url: str = None,
model: str = "minimax-video",
**kwargs
) -> dict:
"""
Submit a video generation job.
Args:
prompt: Text description
image_url: Source image URL (for image-to-video)
model: Video model name
**kwargs: Additional parameters
Returns:
dict with request_id and status URLs
"""
payload = {"prompt": prompt, **kwargs}
if image_url:
payload["image_url"] = image_url
return self.submit(model, payload)
def transcribe(
self,
audio_url: str,
model: str = "whisper",
**kwargs
) -> dict:
"""
Submit an audio transcription job.
Args:
audio_url: URL of audio file
model: Whisper model variant
**kwargs: Additional parameters
Returns:
dict with request_id and status URLs
"""
payload = {"audio_url": audio_url, **kwargs}
return self.submit(model, payload)
def generate_and_wait(
self,
prompt: str,
model: str = "flux-dev",
**kwargs
) -> List[str]:
"""Generate an image and wait for the result."""
job = self.generate_image(prompt, model, **kwargs)
request_id = job["request_id"]
print(f"Job submitted: {request_id}")
result = self.wait_for_completion(model, request_id)
# Extract URLs from result (format varies by model)
images = result.get("images", [])
if images:
return [img.get("url") for img in images if img.get("url")]
# Fallback for different response formats
if "image" in result:
return [result["image"].get("url")]
return []
def main():
parser = argparse.ArgumentParser(description="Generate media with fal.ai API")
parser.add_argument("--prompt", help="Text description")
parser.add_argument("--model", default="flux-dev", help="Model name (default: flux-dev)")
parser.add_argument("--size", default="landscape_16_9", help="Image size preset")
parser.add_argument("--num-images", type=int, default=1, help="Number of images")
parser.add_argument("--seed", type=int, help="Random seed")
parser.add_argument("--list-models", action="store_true", help="List available models")
parser.add_argument("--api-key", help="FAL_KEY (or set via environment)")
args = parser.parse_args()
if args.list_models:
print("Available models:")
for name, endpoint in FalAPI.MODELS.items():
print(f" {name:20}{endpoint}")
return
if not args.prompt:
parser.error("--prompt is required unless --list-models is set")
api = FalAPI(api_key=args.api_key)
print(f"Generating '{args.prompt[:50]}...' with {args.model}...")
urls = api.generate_and_wait(
prompt=args.prompt,
model=args.model,
image_size=args.size,
num_images=args.num_images,
seed=args.seed
)
print("\nGenerated images:")
for url in urls:
print(f" {url}")
if __name__ == "__main__":
main()