first commit

This commit is contained in:
Ladebeze66 2025-04-27 19:45:45 +02:00
commit 5be2ecd2a1
38 changed files with 12963 additions and 0 deletions

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"commentTranslate.targetLanguage": "fr"
}

4208
devices.json Normal file

File diff suppressed because it is too large Load Diff

22
scan_wifi.py Normal file
View File

@ -0,0 +1,22 @@
import tinytuya
# Créer un objet Cloud avec vos identifiants
cloud = tinytuya.Cloud(
apiRegion="eu", # 'eu', 'us', 'cn', selon ton projet
apiKey="fdjdjurkm34yyjp77c7d",
apiSecret="3e71807fb0c6406792bdf4f07f98e577"
)
# Scanner les appareils
print("🔍 Recherche de vos appareils connectés...")
devices = cloud.getdevices()
# Afficher tous les appareils trouvés
for device in devices:
print("="*40)
print(f"Nom : {device['name']}")
print(f"ID : {device['id']}")
print(f"IP : {device.get('ip', 'N/A')}")
print(f"LocalKey : {device.get('local_key', 'N/A')}")
print("="*40)

746
snapshot.json Normal file
View File

@ -0,0 +1,746 @@
{
"timestamp": 1745608535.9669468,
"devices": [
{
"id": "bf3254180de3bdd7bfnbei",
"ip": "192.168.1.179",
"uuid": "5ff3703402383ea3",
"active": 2,
"encrypt": true,
"productKey": "uqehhcrmk5depvtl",
"token": true,
"wf_cfg": true,
"sl": 0,
"clientLink": 3,
"name": "prise caf\u00e9",
"key": "5S2Z#]<iwep.PTnB",
"mac": "4c:a9:19:35:c0:2a",
"ability": 0,
"dev_type": "default",
"err": "",
"type": "default",
"dps": {
"dps": {
"1": false,
"9": 0,
"17": 1,
"18": 0,
"19": 0,
"20": 2356,
"21": 1,
"22": 558,
"23": 28500,
"24": 15392,
"25": 2710,
"26": 0,
"38": "memory",
"39": "relay",
"40": false,
"41": "",
"42": "",
"43": "",
"51": false
}
},
"origin": "broadcast",
"ver": "3.5"
},
{
"id": "bff082745aedf9fc1aqy3e",
"ip": "192.168.1.198",
"uuid": "197acbff0fa71bc7",
"active": 2,
"encrypt": true,
"productKey": "4pqpkp1rskkjtxay",
"token": true,
"wf_cfg": true,
"sl": 0,
"clientLink": 3,
"name": "Volet dino",
"key": "VXg'ZP9IOI|:N)r:",
"mac": "38:a5:c9:1e:da:ad",
"ability": 0,
"dev_type": "default",
"err": "",
"type": "default",
"dps": {
"dps": {
"1": "stop",
"8": "forward",
"10": 60
}
},
"origin": "broadcast",
"ver": "3.5"
},
{
"id": "bfc74a089fe0a9a50d2eeg",
"ip": "192.168.1.126",
"uuid": "022f41ec2cea7aad",
"active": 2,
"encrypt": true,
"productKey": "4pqpkp1rskkjtxay",
"token": true,
"wf_cfg": true,
"sl": 0,
"clientLink": 3,
"name": "volet salon",
"key": "~s0wD/yuq#p}]:]n",
"mac": "b8:06:0d:69:a2:2d",
"ability": 0,
"dev_type": "default",
"err": "",
"type": "default",
"dps": {
"dps": {
"1": "stop",
"8": "back",
"10": 60
}
},
"origin": "broadcast",
"ver": "3.5"
},
{
"id": "bf5b5aa8312d543135xxak",
"ip": "192.168.1.61",
"uuid": "454132f7cf43daaf",
"active": 2,
"encrypt": true,
"productKey": "4pqpkp1rskkjtxay",
"token": true,
"wf_cfg": true,
"sl": 0,
"clientLink": 3,
"name": "volet delia",
"key": "Hj1MO4C)5TL)qndh",
"mac": "d8:d6:68:c0:ef:8c",
"ability": 0,
"dev_type": "default",
"err": "",
"type": "default",
"dps": {
"dps": {
"1": "stop",
"8": "forward",
"10": 60
}
},
"origin": "broadcast",
"ver": "3.5"
},
{
"id": "820180048cce4e2aecc7",
"ip": "192.168.1.160",
"active": 2,
"ability": 0,
"mode": 0,
"encrypt": true,
"productKey": "tsbguim4trl6fa7g",
"name": "bureau dino",
"key": "_rgNtA^X&Vp)A|^l",
"mac": "8c:ce:4e:2a:ec:c7",
"token": "",
"wf_cfg": "",
"dev_type": "default",
"err": "",
"type": "default",
"dps": {
"devId": "820180048cce4e2aecc7",
"dps": {
"1": true,
"2": true,
"3": true,
"7": false,
"9": 0,
"10": 0,
"11": 0,
"15": 0,
"18": 788,
"19": 1809,
"20": 2293,
"101": false,
"102": false
}
},
"origin": "broadcast",
"ver": "3.3"
},
{
"id": "bf81367b1f5c267af7yyht",
"ip": "192.168.1.118",
"uuid": "89a27ed28f171442",
"active": 2,
"encrypt": true,
"productKey": "4pqpkp1rskkjtxay",
"token": true,
"wf_cfg": true,
"sl": 0,
"clientLink": 3,
"name": "volet cuisine",
"key": "9l{9^IYi}CT63k.b",
"mac": "d8:d6:68:c0:f1:c5",
"ability": 0,
"dev_type": "default",
"err": "",
"type": "default",
"dps": {
"dps": {
"1": "stop",
"8": "forward",
"10": 60
}
},
"origin": "broadcast",
"ver": "3.5"
},
{
"id": "bf0e197f469e9f2670fb27",
"ip": "192.168.1.20",
"uuid": "7b42b4575e8aa241",
"active": 2,
"encrypt": true,
"productKey": "4pqpkp1rskkjtxay",
"token": true,
"wf_cfg": true,
"sl": 0,
"clientLink": 3,
"name": "volet jean luc",
"key": "j$doVJIPumHd2PUf",
"mac": "b8:06:0d:69:a8:8b",
"ability": 0,
"dev_type": "default",
"err": "",
"type": "default",
"dps": {
"dps": {
"1": "stop",
"8": "back",
"10": 60
}
},
"origin": "broadcast",
"ver": "3.5"
},
{
"id": "bf5516c52504a7a8dcof7r",
"ip": "192.168.1.54",
"uuid": "ea51f80ebfc154f7",
"active": 2,
"encrypt": true,
"productKey": "key8u54q9dtru5jw",
"token": true,
"wf_cfg": true,
"clientLink": 3,
"name": "light 2",
"key": "|iyOE'];IEiaQ7}l",
"mac": "d8:d6:68:88:56:9d",
"ability": 0,
"dev_type": "default",
"err": "",
"type": "default",
"dps": {
"dps": {
"20": false,
"21": "white",
"22": 100,
"23": 200,
"24": "003c03e803e8",
"25": "07464602000003e803e800000000464602007803e803e80000000046460200f003e803e800000000464602003d03e803e80000000046460200ae03e803e800000000464602011303e803e800000000",
"26": 0
}
},
"origin": "broadcast",
"ver": "3.5"
},
{
"id": "bfdc2f8e4effa0d826gnxg",
"ip": "192.168.1.138",
"uuid": "ac759c73d50d90cd",
"active": 2,
"encrypt": true,
"productKey": "key8u54q9dtru5jw",
"token": true,
"wf_cfg": true,
"clientLink": 3,
"name": "Light 1",
"key": "a_8Bhm|P1L;{wCHL",
"mac": "d8:d6:68:88:60:28",
"ability": 0,
"dev_type": "default",
"err": "",
"type": "default",
"dps": {
"dps": {
"20": false,
"21": "white",
"22": 100,
"23": 200,
"24": "003c03e803e8",
"25": "07464602000003e803e800000000464602007803e803e80000000046460200f003e803e800000000464602003d03e803e80000000046460200ae03e803e800000000464602011303e803e800000000",
"26": 0
}
},
"origin": "broadcast",
"ver": "3.5"
},
{
"id": "bf4054de281cb6ada8actg",
"ip": "192.168.1.149",
"active": 2,
"encrypt": true,
"productKey": "d7bwplp3edtnkxxh",
"token": true,
"wf_cfg": true,
"name": "Sir\u00e8ne intelligente",
"key": "HokvwP_PM{r#SIHb",
"mac": "4c:a9:19:01:08:14",
"ability": 0,
"dev_type": "default",
"err": "",
"type": "default",
"dps": {
"dps": {
"105": 180,
"106": true,
"116": "Disarm_",
"117": "0",
"118": "0",
"120": "3",
"121": true,
"127": false
}
},
"origin": "broadcast",
"ver": "3.4"
},
{
"id": "bfd5eb950aecda95d3mgpq",
"ip": "192.168.1.53",
"uuid": "71be6e7cdc78dcde",
"active": 2,
"encrypt": true,
"productKey": "keynremrcjf97twe",
"token": true,
"wf_cfg": true,
"clientLink": 3,
"name": "bambulight",
"key": "R=_N.r9`clUY!JKA",
"mac": "f8:17:2d:75:a4:f4",
"ability": 0,
"dev_type": "default",
"err": "",
"type": "default",
"dps": {
"dps": {
"20": false,
"21": "white",
"22": 1000,
"25": "000e0d0000000000000000c803e8",
"26": 0,
"34": false
}
},
"origin": "broadcast",
"ver": "3.5"
},
{
"id": "bf2765002c5c1bcf1e4vcl",
"ip": "192.168.1.28",
"active": 2,
"encrypt": true,
"productKey": "key5ckdnys3pqgec",
"token": true,
"wf_cfg": true,
"name": "salon",
"key": "d_gEbC>@_d:0F{bV",
"mac": "4c:a9:19:68:7f:bb",
"ability": 0,
"dev_type": "default",
"err": "",
"type": "default",
"dps": {
"dps": {
"1": false,
"2": true,
"7": 0,
"8": 0,
"14": "memory",
"16": true,
"17": "",
"18": "",
"19": ""
}
},
"origin": "broadcast",
"ver": "3.4"
},
{
"id": "bfca435612874e22ecfqa0",
"ip": "192.168.1.115",
"active": 2,
"encrypt": true,
"productKey": "gaobbrxqiblcng2p",
"token": true,
"wf_cfg": true,
"name": "Light tv",
"key": "Nei5]1k?GzT)c]ZM",
"mac": "c4:82:e1:de:b1:4a",
"ability": 0,
"dev_type": "default",
"err": "",
"type": "default",
"dps": {
"dps": {
"101": "film_mode",
"102": false,
"103": 1,
"104": 100,
"105": 0,
"108": 64,
"111": "0000ff",
"112": 100,
"113": 5,
"114": 100,
"115": false,
"116": 12,
"117": 7,
"118": 200,
"119": 200,
"120": 0,
"122": "ffffff",
"125": "BRG",
"126": true,
"127": "RD200 _HDMI2.0_240109 V08.08.08.3840*2160P 60Hz36bitYCbCr420.CS 27. 27.296M.HDR.2200.2250. 148. 072.1.1921.1079.0.hdcp22.led 38."
}
},
"origin": "broadcast",
"ver": "3.4"
},
{
"id": "bf06f9007abca55618iiwv",
"ip": "192.168.1.68",
"active": 2,
"encrypt": true,
"productKey": "keyjnuy4s3kre7m7",
"token": true,
"wf_cfg": true,
"name": "SW2",
"key": "fkkV`gX-@(}H.0U=",
"mac": "b8:06:0d:28:c1:93",
"ability": 0,
"dev_type": "default",
"err": "",
"type": "default",
"dps": {
"dps": {
"1": false,
"9": 0,
"20": 1,
"21": 0,
"22": 0,
"23": 2333,
"24": 1,
"29": 0,
"38": "memory",
"42": "",
"43": "",
"44": "AAAC"
}
},
"origin": "broadcast",
"ver": "3.4"
},
{
"id": "bfc369d2ca89602058xajc",
"ip": "192.168.1.13",
"active": 2,
"encrypt": true,
"productKey": "keyjup78v54myhan",
"token": true,
"wf_cfg": true,
"name": "ecran gauche",
"key": "+/l==gS<gkhbF(}0",
"mac": "d8:d6:68:89:38:d4",
"ability": 0,
"dev_type": "default",
"err": "",
"type": "default",
"dps": {
"dps": {
"1": true,
"9": 0,
"17": 18,
"18": 272,
"19": 361,
"20": 2306,
"21": 1,
"22": 563,
"23": 27351,
"24": 14750,
"25": 2820,
"26": 0,
"38": "memory",
"39": false,
"41": false,
"42": "",
"43": "",
"44": ""
}
},
"origin": "broadcast",
"ver": "3.4"
},
{
"id": "bfadb5f27d0fe9a778ixwc",
"ip": "192.168.1.70",
"uuid": "3c52e36d800e6c9c",
"active": 2,
"encrypt": true,
"productKey": "keynremrcjf97twe",
"token": true,
"wf_cfg": true,
"clientLink": 3,
"name": "ringlight",
"key": "dOo5*OR3ixjP?OxO",
"mac": "f8:17:2d:6f:72:55",
"ability": 0,
"dev_type": "default",
"err": "",
"type": "default",
"dps": {
"dps": {
"20": true,
"21": "white",
"22": 1000,
"25": "000e0d0000000000000000c803e8",
"26": 0,
"34": false
}
},
"origin": "broadcast",
"ver": "3.5"
},
{
"id": "bf9043f5954449c435mcec",
"ip": "192.168.1.92",
"active": 2,
"encrypt": true,
"productKey": "keyjup78v54myhan",
"token": true,
"wf_cfg": true,
"name": "imprimante 3d",
"key": "t0o#ih7L!$;+6R(2",
"mac": "b8:06:0d:fb:a8:1b",
"ability": 0,
"dev_type": "default",
"err": "",
"type": "default",
"dps": {
"dps": {
"1": true,
"9": 0,
"17": 49,
"18": 307,
"19": 352,
"20": 2341,
"21": 1,
"22": 578,
"23": 28444,
"24": 15476,
"25": 2690,
"26": 0,
"38": "memory",
"39": true,
"41": false,
"42": "",
"43": "",
"44": ""
}
},
"origin": "broadcast",
"ver": "3.4"
},
{
"id": "bf10d8832c8447c512jedz",
"ip": "192.168.1.8",
"uuid": "60c8aec31f22286a",
"active": 2,
"encrypt": true,
"productKey": "keynremrcjf97twe",
"token": true,
"wf_cfg": true,
"clientLink": 3,
"name": "windowled",
"key": "e#Z$#dvgmRo}nS8^",
"mac": "f8:17:2d:cd:24:f7",
"ability": 0,
"dev_type": "default",
"err": "",
"type": "default",
"dps": {
"dps": {
"20": false,
"21": "colour",
"24": "00b403e803e8",
"25": "1a464602000a038403e800000000464602000003e803e800000000",
"26": 0,
"34": false,
"42": "010000016432000003e8007803e800f003e8003c03e800b403e8012c03e8"
}
},
"origin": "broadcast",
"ver": "3.5"
},
{
"id": "bfe57aa0e61a9b529bzrff",
"ip": "192.168.1.128",
"active": 2,
"encrypt": true,
"productKey": "rtbhfbuii82scjrp",
"token": true,
"wf_cfg": true,
"name": "PC",
"key": "|fI8f~QH~5>>m0x?",
"mac": "b8:06:0d:3e:79:77",
"ability": 0,
"dev_type": "default",
"err": "",
"type": "default",
"dps": {
"dps": {
"1": true,
"7": false,
"8": true,
"38": "off",
"40": false,
"101": "0",
"102": "off"
}
},
"origin": "broadcast",
"ver": "3.4"
},
{
"id": "bff3ca58c60d8b18d4x3dn",
"ip": "192.168.1.122",
"uuid": "f95b88cfd4065f4d",
"active": 2,
"encrypt": true,
"productKey": "keynremrcjf97twe",
"token": true,
"wf_cfg": true,
"clientLink": 3,
"name": "chinalight",
"key": "Nt6*~S4<2x9;?Q+-",
"mac": "f8:17:2d:6f:66:89",
"ability": 0,
"dev_type": "default",
"err": "",
"type": "default",
"dps": {
"dps": {
"20": false,
"21": "white",
"22": 1000,
"25": "000e0d0000000000000000c803e8",
"26": 0,
"34": false
}
},
"origin": "broadcast",
"ver": "3.5"
},
{
"id": "bf81674a39ad0f61f4tzjt",
"ip": "192.168.1.99",
"active": 2,
"encrypt": true,
"productKey": "keyjup78v54myhan",
"token": true,
"wf_cfg": true,
"name": "Bureau",
"key": "~@d`/>iCON6aPp~@",
"mac": "d8:d6:68:87:07:6a",
"ability": 0,
"dev_type": "default",
"err": "",
"type": "default",
"dps": {
"dps": {
"1": true,
"9": 0,
"17": 27,
"18": 417,
"19": 546,
"20": 2294,
"21": 1,
"22": 557,
"23": 26736,
"24": 14371,
"25": 2900,
"26": 0,
"38": "memory",
"39": false,
"41": false,
"42": "",
"43": "",
"44": ""
}
},
"origin": "broadcast",
"ver": "3.4"
},
{
"id": "bf584a5db6d2208dcf2bol",
"ip": "192.168.1.80",
"active": 2,
"encrypt": true,
"productKey": "biw8urjw2dvss3ur",
"token": true,
"name": "Cam-Dino",
"key": "mAF-Jj#O.#0@Lk?[",
"mac": "c5:92:42:1a:13:79",
"ability": 0,
"wf_cfg": "",
"dev_type": "default",
"err": "",
"type": "default",
"dps": {
"dps": {
"101": true,
"103": false,
"104": true,
"105": false,
"106": "1",
"108": "0",
"109": "61038592|48714752|12323840",
"110": 1,
"117": 0,
"120": false,
"134": false,
"139": false,
"140": "0",
"150": true,
"151": "2",
"159": false,
"160": 10,
"161": false,
"168": false,
"169": "",
"170": false,
"174": false,
"175": "0",
"176": "0",
"188": "0",
"194": 20,
"195": 50,
"237": false,
"239": false
}
},
"origin": "broadcast",
"ver": "3.3"
}
]
}

6
tinytuya.json Normal file
View File

@ -0,0 +1,6 @@
{
"apiKey": "fdjdjurkm34yyjp77c7d",
"apiSecret": "3e71807fb0c6406792bdf4f07f98e577",
"apiRegion": "eu",
"apiDeviceID": "scan"
}

83
tuya-control/README.md Normal file
View File

@ -0,0 +1,83 @@
# Tuya Control pour StreamDeck
Ce projet permet de contrôler vos appareils Tuya (prises, lumières, volets, etc.) via des scripts Python, conçus pour être utilisés avec un StreamDeck.
## Structure du projet
```
tuya-control/
├── common/ # Fonctions utilitaires communes
│ ├── utils.py # Fonctions partagées
│ └── config.py # Configuration générale
├── devices/ # Scripts spécifiques par type d'appareil
│ ├── lights/ # Contrôle des lumières
│ ├── shutters/ # Contrôle des volets
│ ├── plugs/ # Contrôle des prises
│ ├── cameras/ # Contrôle des caméras
│ └── others/ # Autres types d'appareils
├── scripts/ # Scripts utilitaires
│ ├── scan_devices.py # Scanner les appareils sur le réseau
│ └── status_devices.py # Afficher le statut des appareils
├── devices.json # Base de données des appareils
└── README.md # Ce fichier
```
## Configuration du StreamDeck
Pour utiliser ces scripts avec votre StreamDeck:
1. Configurez une touche sur votre StreamDeck pour exécuter un script Python
2. Pointez vers le script correspondant à l'appareil que vous souhaitez contrôler
(ex: `tuya-control/devices/lights/light_1_toggle.py`)
3. Définissez une icône appropriée pour l'état ON/OFF
Chaque script gère automatiquement la détection de l'état actuel et bascule entre allumé/éteint ou ouvert/fermé/arrêté selon le type d'appareil.
## Exemples d'utilisation
### Contrôler une prise
```bash
python tuya-control/devices/plugs/prise_cafe_toggle.py
```
### Contrôler un volet
```bash
python tuya-control/devices/shutters/volet_salon_toggle.py
```
### Contrôler une lumière
```bash
python tuya-control/devices/lights/light_1_toggle.py
```
### Scanner les appareils sur le réseau
```bash
python tuya-control/scripts/scan_devices.py
```
### Afficher le statut des appareils
```bash
python tuya-control/scripts/status_devices.py
```
## Personnalisation
Pour ajouter ou modifier des appareils, il suffit de copier un script existant et d'adapter:
- L'ID de l'appareil
- Le nom de l'appareil
- Le type de commande (DPS) spécifique à l'appareil
## Dépendances
Ce projet nécessite:
- Python 3.6+
- La bibliothèque tinytuya
Pour installer les dépendances:
```bash
pip install tinytuya
```
## Remarques
Pour que les scripts fonctionnent, le fichier `devices.json` doit contenir les informations de connexion pour tous vos appareils, incluant les clés locales.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,24 @@
"""
Configuration pour l'accès à l'API Tuya.
Ce fichier centralise les informations de connexion pour tous les scripts.
"""
# Informations de l'API Tuya
API_REGION = "eu"
API_KEY = "fdjdjurkm34yyjp77c7d"
API_SECRET = "3e71807fb0c6406792bdf4f07f98e577"
# Mode de connexion - 'scan', 'local', ou 'both'
CONNECTION_MODE = "local" # Privilégier la connexion locale pour plus de rapidité
# Délai d'attente en secondes pour les opérations réseau
TIMEOUT = 3.0
# Gestion des voyants sur les appareils
DISABLE_STATUS_LED = False # True = désactiver les LEDs des appareils (si supporté)
# Configuration du mode verbose
VERBOSE_MODE = True # Afficher plus d'informations pendant l'exécution
# Préfixe pour l'intégration avec StreamDeck
STREAMDECK_PREFIX = "TUYA_" # Préfixe pour les variables d'environnement

View File

@ -0,0 +1,257 @@
"""
Utilitaires partagés pour les scripts de contrôle des appareils Tuya.
Ce module fournit des fonctions communes utilisées par tous les scripts.
"""
import os
import sys
import json
import time
import logging
import tinytuya
from . import config
# Configuration du logging
logging.basicConfig(
level=logging.INFO if config.VERBOSE_MODE else logging.WARNING,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[logging.StreamHandler()]
)
logger = logging.getLogger("TuyaControl")
def load_device_info(device_id):
"""
Charge les informations d'un appareil depuis le fichier devices.json
Args:
device_id (str): ID de l'appareil Tuya
Returns:
dict: Informations de l'appareil ou None si non trouvé
"""
try:
# Chemin vers le répertoire parent du projet
base_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
devices_file = os.path.join(base_dir, 'devices.json')
with open(devices_file, 'r') as f:
devices = json.load(f)
for device in devices:
if device['id'] == device_id:
return device
logger.error(f"Appareil avec ID {device_id} non trouvé dans devices.json")
return None
except Exception as e:
logger.error(f"Erreur lors du chargement des informations de l'appareil: {e}")
return None
def connect_device(device_id, device_ip=None, device_key=None, device_version=3.3):
"""
Se connecte à un appareil Tuya
Args:
device_id (str): ID de l'appareil
device_ip (str, optional): IP de l'appareil (facultatif si mode scan)
device_key (str, optional): Clé de l'appareil (facultatif si chargé depuis devices.json)
device_version (float, optional): Version du protocole
Returns:
tinytuya.Device: Instance de l'appareil connecté ou None en cas d'erreur
"""
try:
# Si la clé n'est pas fournie, charger depuis devices.json
if device_key is None:
device_info = load_device_info(device_id)
if not device_info:
logger.error(f"Impossible de trouver les informations pour l'appareil {device_id}")
return None
device_key = device_info.get('key')
# Créer l'objet appareil
device = tinytuya.Device(
dev_id=device_id,
address=device_ip, # None si on veut que la bibliothèque recherche l'appareil
local_key=device_key,
version=device_version
)
# Configurer le timeout
device.set_socketTimeout(config.TIMEOUT)
# Tester la connexion
status = device.status()
if 'Error' in status:
logger.error(f"Erreur de connexion à l'appareil {device_id}: {status['Error']}")
return None
logger.info(f"Connexion réussie à l'appareil {device_id}")
return device
except Exception as e:
logger.error(f"Erreur lors de la connexion à l'appareil {device_id}: {e}")
return None
def toggle_device(device, switch_id="1", current_state=None):
"""
Bascule l'état d'un appareil (allumé/éteint)
Args:
device (tinytuya.Device): Instance de l'appareil
switch_id (str): ID du commutateur (généralement "1" ou "20" pour les lumières)
current_state (bool, optional): État actuel si connu, sinon sera détecté
Returns:
bool: Nouvel état (True=On, False=Off) ou None en cas d'erreur
"""
try:
# Si l'état actuel n'est pas fourni, l'obtenir
if current_state is None:
status = device.status()
if 'dps' in status and switch_id in status['dps']:
current_state = status['dps'][switch_id]
else:
logger.error(f"Impossible de déterminer l'état actuel du commutateur {switch_id}")
return None
# Basculer l'état (inverse de l'état actuel)
new_state = not current_state
result = device.set_value(switch_id, new_state)
logger.info(f"Appareil basculé de {current_state} à {new_state}")
return new_state
except Exception as e:
logger.error(f"Erreur lors du basculement de l'appareil: {e}")
return None
def control_shutter(device, action="stop"):
"""
Contrôle un volet roulant ou store
Args:
device (tinytuya.Device): Instance de l'appareil
action (str): Action à effectuer ("stop", "up"/"forward", "down"/"back")
Returns:
bool: True si réussi, False sinon
"""
try:
# Convertir les actions en commandes spécifiques au dispositif
command = action
if action == "up":
command = "forward"
elif action == "down":
command = "back"
# Les volets utilisent généralement le point de données "1" pour leurs commandes
result = device.set_value("1", command)
logger.info(f"Volet contrôlé avec commande: {command}")
return True
except Exception as e:
logger.error(f"Erreur lors du contrôle du volet: {e}")
return False
def get_device_status_text(device_id, status=None):
"""
Obtient un texte descriptif de l'état de l'appareil
Args:
device_id (str): ID de l'appareil
status (dict, optional): État actuel si déjà obtenu
Returns:
str: Description textuelle de l'état
"""
try:
device_info = load_device_info(device_id)
if not device_info:
return "Appareil inconnu"
# Si le statut n'est pas fourni, essayer de se connecter et obtenir l'état
if status is None:
device = connect_device(device_id)
if device:
status = device.status()
else:
return "Non connecté"
# Obtenir le nom de l'appareil
device_name = device_info.get('name', 'Appareil')
# Analyser l'état selon le type d'appareil
if 'dps' in status:
dps = status['dps']
# Pour les prises et la plupart des appareils on/off
if '1' in dps and isinstance(dps['1'], bool):
state = "ALLUMÉ" if dps['1'] else "ÉTEINT"
return f"{device_name}: {state}"
# Pour les lumières (généralement dps 20)
elif '20' in dps and isinstance(dps['20'], bool):
state = "ALLUMÉE" if dps['20'] else "ÉTEINTE"
return f"{device_name}: {state}"
# Pour les volets
elif '1' in dps and isinstance(dps['1'], str):
states = {
"stop": "ARRÊTÉ",
"forward": "EN OUVERTURE",
"back": "EN FERMETURE"
}
state = states.get(dps['1'], dps['1'].upper())
return f"{device_name}: {state}"
return f"{device_name}: État inconnu"
except Exception as e:
logger.error(f"Erreur lors de l'obtention de l'état de l'appareil: {e}")
return "Erreur"
def scan_for_devices():
"""
Recherche les appareils Tuya disponibles sur le réseau local
Returns:
list: Liste des appareils trouvés
"""
try:
logger.info("Recherche des appareils Tuya sur le réseau...")
devices = tinytuya.scan()
if devices is None:
devices = []
logger.info(f"Trouvé {len(devices)} appareils")
return devices
except Exception as e:
logger.error(f"Erreur lors de la recherche d'appareils: {e}")
return []
# Fonction pour l'intégration avec StreamDeck
def report_for_streamdeck(result, device_name="Appareil", new_state=None):
"""
Génère un rapport formaté pour l'intégration avec StreamDeck
Args:
result (bool): Résultat de l'opération
device_name (str): Nom de l'appareil
new_state: Nouvel état (True=On, False=Off, str pour d'autres états)
Returns:
dict: Rapport formaté
"""
# Format attendu par StreamDeck
report = {
"success": result is not None and result is not False,
"device": device_name,
"state": str(new_state) if new_state is not None else "unknown",
"timestamp": time.time()
}
# Afficher le rapport au format JSON pour StreamDeck
print(json.dumps(report))
return report

View File

@ -0,0 +1,37 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Script pour basculer l'état de la caméra (activé/désactivé)
À utiliser avec StreamDeck
"""
import sys
import os
# Ajouter le répertoire parent au path pour les imports
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from common.utils import connect_device, toggle_device, report_for_streamdeck
# ID et nom de l'appareil
DEVICE_ID = "bf584a5db6d2208dcf2bol"
DEVICE_NAME = "Cam-Dino"
# Pour cette caméra, le commutateur principal est sur le DPS 101
SWITCH_ID = "101"
def main():
"""Fonction principale pour basculer l'état de la caméra"""
# Connexion à l'appareil
device = connect_device(DEVICE_ID)
if not device:
report_for_streamdeck(False, DEVICE_NAME)
return
# Basculer l'état et obtenir le nouvel état
new_state = toggle_device(device, SWITCH_ID)
# Rapport pour StreamDeck
report_for_streamdeck(new_state is not None, DEVICE_NAME, new_state)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,161 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Script pour basculer l'état de plusieurs lumières d'ambiance simultanément:
- Saber du bureau dino
- Ringlight
- Chinalight
- Windowled
À utiliser avec StreamDeck
"""
import sys
import os
import time
import logging
import tinytuya
# Ajouter le répertoire parent au path pour les imports
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from common.utils import report_for_streamdeck
from common import config
# Obtenir le logger
logger = logging.getLogger("TuyaControl")
# ID et paramètres des appareils
DEVICES = [
{
"id": "820180048cce4e2aecc7",
"name": "Saber (Bureau Dino)",
"switch": "7", # Code pour la prise USB (saber)
"ip": "192.168.1.160",
"key": "_rgNtA^X&Vp)A|^l",
"version": 3.3
},
{
"id": "bfadb5f27d0fe9a778ixwc",
"name": "Ringlight",
"switch": "20",
"ip": "192.168.1.70",
"key": "dOo5*OR3ixjP?OxO",
"version": 3.5
},
{
"id": "bff3ca58c60d8b18d4x3dn",
"name": "Chinalight",
"switch": "20",
"ip": "192.168.1.122",
"key": "Nt6*~S4<2x9;?Q+-",
"version": 3.5
},
{
"id": "bf10d8832c8447c512jedz",
"name": "Windowled",
"switch": "20",
"ip": "192.168.1.8",
"key": "e#Z$#dvgmRo}nS8^",
"version": 3.5
}
]
def connect_device_direct(device_info):
"""
Se connecte directement à un appareil avec son IP sans passer par la découverte
"""
try:
# Créer l'objet appareil
device = tinytuya.Device(
dev_id=device_info["id"],
address=device_info["ip"],
local_key=device_info["key"],
version=device_info["version"]
)
# Configurer le timeout
device.set_socketTimeout(config.TIMEOUT)
# Vérifier la connexion (optionnel)
status = device.status()
if 'Error' in status:
logger.error(f"Erreur de connexion à {device_info['name']} ({device_info['ip']}): {status['Error']}")
return None
logger.info(f"Connexion réussie à {device_info['name']} ({device_info['ip']})")
return device
except Exception as e:
logger.error(f"Erreur lors de la connexion à {device_info['name']} ({device_info['ip']}): {e}")
return None
def main():
"""Fonction principale pour basculer l'état des lumières d'ambiance simultanément"""
# Connexion aux appareils
connected_devices = []
for device_info in DEVICES:
try:
# Ajouter un petit délai entre chaque connexion pour éviter les collisions de socket
time.sleep(0.5)
# Connexion directe avec l'IP
device = connect_device_direct(device_info)
if device:
connected_devices.append({
"device": device,
"name": device_info["name"],
"switch": device_info["switch"]
})
except Exception as e:
logger.error(f"Erreur de connexion à {device_info['name']}: {e}")
if not connected_devices:
report_for_streamdeck(False, "Ambiance")
return
try:
# Déterminer l'état actuel pour décider de la nouvelle action
lights_on = False
for device_data in connected_devices:
try:
status = device_data["device"].status()
if status.get('dps', {}).get(device_data["switch"], False):
lights_on = True
break
except Exception as e:
logger.error(f"Erreur lors de la récupération du statut de {device_data['name']}: {e}")
# Définir le nouvel état (inverse de l'état actuel)
new_state = not lights_on
# Appliquer l'action sur tous les appareils
success = True
for device_data in connected_devices:
try:
result = device_data["device"].set_value(device_data["switch"], new_state)
if result is None or "Error" in str(result):
success = False
logger.error(f"Erreur lors du changement d'état de {device_data['name']}")
# Ajouter un petit délai entre chaque commande
time.sleep(0.2)
except Exception as e:
success = False
logger.error(f"Erreur lors du changement d'état de {device_data['name']}: {e}")
# Rapport pour StreamDeck
report_for_streamdeck(success, "Ambiance", new_state)
finally:
# Fermer explicitement toutes les connexions
for device_data in connected_devices:
try:
device_data["device"].set_socketRetryLimit(0)
device_data["device"].set_socketTimeout(1)
if hasattr(device_data["device"], "_connection") and device_data["device"]._connection:
device_data["device"]._connection.close()
except:
pass
if __name__ == "__main__":
main()

View File

@ -0,0 +1,92 @@
import tinytuya
import json
# Informations Cloud Tuya
API_REGION = "eu"
API_KEY = "fdjdjurkm34yyjp77c7d"
API_SECRET = "3e71807fb0c6406792bdf4f07f98e577"
# Informations de l'appareil
DEVICE_ID = "bfd5eb950aecda95d3mgpq" # ID de bambulight
DEVICE_NAME = "bambulight"
SWITCH_CODE = "switch_led" # Utiliser le code correct pour l'interrupteur
def main():
cloud = tinytuya.Cloud(
apiRegion=API_REGION,
apiKey=API_KEY,
apiSecret=API_SECRET
)
# Activer le mode debug dans tinytuya
tinytuya.set_debug(True)
# Lire l'état actuel
print(f"🔍 Récupération du statut pour l'appareil {DEVICE_ID}...")
status = cloud.getstatus(DEVICE_ID)
# Vérifier si on a bien une réponse
if not status:
print(f"❌ Aucune réponse de l'API Cloud pour {DEVICE_NAME}")
return
# Essayer d'afficher la réponse complète
try:
print(f"📊 Réponse brute: {status}")
print(f"📊 Type de réponse: {type(status).__name__}")
if isinstance(status, dict):
print(f"📊 Clés disponibles: {list(status.keys())}")
except Exception as e:
print(f"⚠️ Erreur lors de l'affichage de la réponse: {e}")
is_on = None
# Vérifier le format de la réponse
if isinstance(status, dict) and "result" in status:
result = status["result"]
if isinstance(result, list):
for item in result:
if isinstance(item, dict) and item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
elif isinstance(result, dict) and "status" in result:
for item in result["status"]:
if item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
elif isinstance(status, list):
for item in status:
if isinstance(item, dict) and item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
if is_on is None:
print(f"❌ Impossible de trouver le statut de l'interrupteur '{SWITCH_CODE}' pour {DEVICE_NAME}")
return
print(f" État actuel de {DEVICE_NAME}: {'ALLUMÉ' if is_on else 'ÉTEINT'}")
# Basculer l'état
new_value = not is_on
print(f" Nouvel état à appliquer: {'ALLUMÉ' if new_value else 'ÉTEINT'}")
# Envoyer la commande
commands = {
"commands": [
{"code": SWITCH_CODE, "value": new_value}
]
}
print(f"📤 Envoi de la commande: {json.dumps(commands, indent=2)}")
response = cloud.sendcommand(DEVICE_ID, commands)
print(f"📥 Réponse reçue: {json.dumps(response, indent=2) if response else 'Aucune réponse'}")
# Vérifier si response n'est pas None avant d'appeler get()
if response is not None and response.get("success"):
print(f"{DEVICE_NAME} est maintenant {'allumé' if new_value else 'éteint'}")
else:
error_msg = response.get("msg", "Erreur inconnue") if response else "Pas de réponse"
print(f"❌ Échec pour {DEVICE_NAME}: {error_msg}")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,92 @@
import tinytuya
import json
# Informations Cloud Tuya
API_REGION = "eu"
API_KEY = "fdjdjurkm34yyjp77c7d"
API_SECRET = "3e71807fb0c6406792bdf4f07f98e577"
# Informations de l'appareil
DEVICE_ID = "820180048cce4e2aecc7" # ID du bureau dino
DEVICE_NAME = "Saber (Bureau Dino)"
SWITCH_CODE = "switch_usb1" # Code pour la prise USB (saber)
def main():
cloud = tinytuya.Cloud(
apiRegion=API_REGION,
apiKey=API_KEY,
apiSecret=API_SECRET
)
# Activer le mode debug dans tinytuya
tinytuya.set_debug(True)
# Lire l'état actuel
print(f"🔍 Récupération du statut pour l'appareil {DEVICE_ID}...")
status = cloud.getstatus(DEVICE_ID)
# Vérifier si on a bien une réponse
if not status:
print(f"❌ Aucune réponse de l'API Cloud pour {DEVICE_NAME}")
return
# Essayer d'afficher la réponse complète
try:
print(f"📊 Réponse brute: {status}")
print(f"📊 Type de réponse: {type(status).__name__}")
if isinstance(status, dict):
print(f"📊 Clés disponibles: {list(status.keys())}")
except Exception as e:
print(f"⚠️ Erreur lors de l'affichage de la réponse: {e}")
is_on = None
# Vérifier le format de la réponse
if isinstance(status, dict) and "result" in status:
result = status["result"]
if isinstance(result, list):
for item in result:
if isinstance(item, dict) and item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
elif isinstance(result, dict) and "status" in result:
for item in result["status"]:
if item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
elif isinstance(status, list):
for item in status:
if isinstance(item, dict) and item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
if is_on is None:
print(f"❌ Impossible de trouver le statut de l'interrupteur '{SWITCH_CODE}' pour {DEVICE_NAME}")
return
print(f" État actuel de {DEVICE_NAME}: {'ALLUMÉ' if is_on else 'ÉTEINT'}")
# Basculer l'état
new_value = not is_on
print(f" Nouvel état à appliquer: {'ALLUMÉ' if new_value else 'ÉTEINT'}")
# Envoyer la commande
commands = {
"commands": [
{"code": SWITCH_CODE, "value": new_value}
]
}
print(f"📤 Envoi de la commande: {json.dumps(commands, indent=2)}")
response = cloud.sendcommand(DEVICE_ID, commands)
print(f"📥 Réponse reçue: {json.dumps(response, indent=2) if response else 'Aucune réponse'}")
# Vérifier si response n'est pas None avant d'appeler get()
if response is not None and response.get("success"):
print(f"{DEVICE_NAME} est maintenant {'allumé' if new_value else 'éteint'}")
else:
error_msg = response.get("msg", "Erreur inconnue") if response else "Pas de réponse"
print(f"❌ Échec pour {DEVICE_NAME}: {error_msg}")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,92 @@
import tinytuya
import json
# Informations Cloud Tuya
API_REGION = "eu"
API_KEY = "fdjdjurkm34yyjp77c7d"
API_SECRET = "3e71807fb0c6406792bdf4f07f98e577"
# Informations de l'appareil
DEVICE_ID = "bff3ca58c60d8b18d4x3dn" # ID de chinalight
DEVICE_NAME = "chinalight"
SWITCH_CODE = "switch_led" # Utiliser le code correct pour l'interrupteur
def main():
cloud = tinytuya.Cloud(
apiRegion=API_REGION,
apiKey=API_KEY,
apiSecret=API_SECRET
)
# Activer le mode debug dans tinytuya
tinytuya.set_debug(True)
# Lire l'état actuel
print(f"🔍 Récupération du statut pour l'appareil {DEVICE_ID}...")
status = cloud.getstatus(DEVICE_ID)
# Vérifier si on a bien une réponse
if not status:
print(f"❌ Aucune réponse de l'API Cloud pour {DEVICE_NAME}")
return
# Essayer d'afficher la réponse complète
try:
print(f"📊 Réponse brute: {status}")
print(f"📊 Type de réponse: {type(status).__name__}")
if isinstance(status, dict):
print(f"📊 Clés disponibles: {list(status.keys())}")
except Exception as e:
print(f"⚠️ Erreur lors de l'affichage de la réponse: {e}")
is_on = None
# Vérifier le format de la réponse
if isinstance(status, dict) and "result" in status:
result = status["result"]
if isinstance(result, list):
for item in result:
if isinstance(item, dict) and item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
elif isinstance(result, dict) and "status" in result:
for item in result["status"]:
if item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
elif isinstance(status, list):
for item in status:
if isinstance(item, dict) and item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
if is_on is None:
print(f"❌ Impossible de trouver le statut de l'interrupteur '{SWITCH_CODE}' pour {DEVICE_NAME}")
return
print(f" État actuel de {DEVICE_NAME}: {'ALLUMÉ' if is_on else 'ÉTEINT'}")
# Basculer l'état
new_value = not is_on
print(f" Nouvel état à appliquer: {'ALLUMÉ' if new_value else 'ÉTEINT'}")
# Envoyer la commande
commands = {
"commands": [
{"code": SWITCH_CODE, "value": new_value}
]
}
print(f"📤 Envoi de la commande: {json.dumps(commands, indent=2)}")
response = cloud.sendcommand(DEVICE_ID, commands)
print(f"📥 Réponse reçue: {json.dumps(response, indent=2) if response else 'Aucune réponse'}")
# Vérifier si response n'est pas None avant d'appeler get()
if response is not None and response.get("success"):
print(f"{DEVICE_NAME} est maintenant {'allumé' if new_value else 'éteint'}")
else:
error_msg = response.get("msg", "Erreur inconnue") if response else "Pas de réponse"
print(f"❌ Échec pour {DEVICE_NAME}: {error_msg}")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,108 @@
import tinytuya
import json
# Informations Cloud Tuya
API_REGION = "eu"
API_KEY = "fdjdjurkm34yyjp77c7d"
API_SECRET = "3e71807fb0c6406792bdf4f07f98e577"
# Informations de l'appareil
DEVICE_ID = "bfdc2f8e4effa0d826gnxg"
DEVICE_NAME = "Light 1"
SWITCH_CODE = "switch_led" # ou "switch" ou "switch_led", dépend du device (à vérifier !)
def main():
cloud = tinytuya.Cloud(
apiRegion=API_REGION,
apiKey=API_KEY,
apiSecret=API_SECRET
)
# Activer le mode debug dans tinytuya
tinytuya.set_debug(True)
# Lire l'état actuel
print(f"🔍 Récupération du statut pour l'appareil {DEVICE_ID}...")
status = cloud.getstatus(DEVICE_ID)
# Vérifier si on a bien une réponse
if not status:
print(f"❌ Aucune réponse de l'API Cloud pour {DEVICE_NAME}")
return
# Essayer d'afficher la réponse complète
try:
print(f"📊 Réponse brute: {status}")
print(f"📊 Type de réponse: {type(status).__name__}")
if isinstance(status, dict):
print(f"📊 Clés disponibles: {list(status.keys())}")
except Exception as e:
print(f"⚠️ Erreur lors de l'affichage de la réponse: {e}")
is_on = None
# Vérifier le format de la réponse
if isinstance(status, dict) and "result" in status:
result = status["result"]
if isinstance(result, list):
# Format: {"result": [{"code": "switch_led", "value": true}, ...]}
for item in result:
if isinstance(item, dict) and item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
elif isinstance(result, dict) and "status" in result:
# Format: {"result": {"status": [{"code": "switch_led", "value": true}, ...]}}
for item in result["status"]:
if item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
elif isinstance(status, list):
# Format alternatif: liste directe de statuts
for item in status:
if isinstance(item, dict) and item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
if is_on is None:
print(f"❌ Impossible de trouver le statut de l'interrupteur '{SWITCH_CODE}' pour {DEVICE_NAME}")
# Afficher les codes disponibles
codes = []
if isinstance(status, dict) and "result" in status:
result = status["result"]
if isinstance(result, list):
codes = [item.get("code") for item in result if isinstance(item, dict) and "code" in item]
elif isinstance(result, dict) and "status" in result:
codes = [item.get("code") for item in result["status"] if isinstance(item, dict) and "code" in item]
elif isinstance(status, list):
codes = [item.get("code") for item in status if isinstance(item, dict) and "code" in item]
print(f"💡 Codes disponibles: {codes}")
return
print(f" État actuel de {DEVICE_NAME}: {'ALLUMÉ' if is_on else 'ÉTEINT'}")
# Basculer l'état
new_value = not is_on
print(f" Nouvel état à appliquer: {'ALLUMÉ' if new_value else 'ÉTEINT'}")
# Envoyer la commande
commands = {
"commands": [
{"code": SWITCH_CODE, "value": new_value}
]
}
print(f"📤 Envoi de la commande: {json.dumps(commands, indent=2)}")
response = cloud.sendcommand(DEVICE_ID, commands)
print(f"📥 Réponse reçue: {json.dumps(response, indent=2) if response else 'Aucune réponse'}")
# Vérifier si response n'est pas None avant d'appeler get()
if response is not None and response.get("success"):
print(f"{DEVICE_NAME} est maintenant {'allumé' if new_value else 'éteint'}")
else:
error_msg = response.get("msg", "Erreur inconnue") if response else "Pas de réponse"
print(f"❌ Échec pour {DEVICE_NAME}: {error_msg}")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,95 @@
import tinytuya
import json
# Informations Cloud Tuya
API_REGION = "eu"
API_KEY = "fdjdjurkm34yyjp77c7d"
API_SECRET = "3e71807fb0c6406792bdf4f07f98e577"
# Informations de l'appareil
DEVICE_ID = "bf5516c52504a7a8dcof7r"
DEVICE_NAME = "Light 2"
SWITCH_CODE = "switch_led" # ou "switch" ou "switch_led", dépend du device (à vérifier !)
def main():
cloud = tinytuya.Cloud(
apiRegion=API_REGION,
apiKey=API_KEY,
apiSecret=API_SECRET
)
# Activer le mode debug dans tinytuya
tinytuya.set_debug(True)
# Lire l'état actuel
print(f"🔍 Récupération du statut pour l'appareil {DEVICE_ID}...")
status = cloud.getstatus(DEVICE_ID)
# Vérifier si on a bien une réponse
if not status:
print(f"❌ Aucune réponse de l'API Cloud pour {DEVICE_NAME}")
return
# Essayer d'afficher la réponse complète
try:
print(f"📊 Réponse brute: {status}")
print(f"📊 Type de réponse: {type(status).__name__}")
if isinstance(status, dict):
print(f"📊 Clés disponibles: {list(status.keys())}")
except Exception as e:
print(f"⚠️ Erreur lors de l'affichage de la réponse: {e}")
is_on = None
# Vérifier le format de la réponse
if isinstance(status, dict) and "result" in status:
result = status["result"]
if isinstance(result, list):
# Format: {"result": [{"code": "switch_led", "value": true}, ...]}
for item in result:
if isinstance(item, dict) and item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
elif isinstance(result, dict) and "status" in result:
# Format: {"result": {"status": [{"code": "switch_led", "value": true}, ...]}}
for item in result["status"]:
if item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
elif isinstance(status, list):
# Format alternatif: liste directe de statuts
for item in status:
if isinstance(item, dict) and item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
if is_on is None:
print(f"❌ Impossible de trouver le statut de l'interrupteur '{SWITCH_CODE}' pour {DEVICE_NAME}")
return
print(f" État actuel de {DEVICE_NAME}: {'ALLUMÉ' if is_on else 'ÉTEINT'}")
# Basculer l'état
new_value = not is_on
print(f" Nouvel état à appliquer: {'ALLUMÉ' if new_value else 'ÉTEINT'}")
# Envoyer la commande
commands = {
"commands": [
{"code": SWITCH_CODE, "value": new_value}
]
}
print(f"📤 Envoi de la commande: {json.dumps(commands, indent=2)}")
response = cloud.sendcommand(DEVICE_ID, commands)
print(f"📥 Réponse reçue: {json.dumps(response, indent=2) if response else 'Aucune réponse'}")
# Vérifier si response n'est pas None avant d'appeler get()
if response is not None and response.get("success"):
print(f"{DEVICE_NAME} est maintenant {'allumé' if new_value else 'éteint'}")
else:
error_msg = response.get("msg", "Erreur inconnue") if response else "Pas de réponse"
print(f"❌ Échec pour {DEVICE_NAME}: {error_msg}")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,106 @@
import tinytuya
import json
# Informations Cloud Tuya
API_REGION = "eu"
API_KEY = "fdjdjurkm34yyjp77c7d"
API_SECRET = "3e71807fb0c6406792bdf4f07f98e577"
# Informations des appareils
DEVICES = [
{
"id": "bfdc2f8e4effa0d826gnxg", # ID de la lumière 1
"name": "Light 1",
"switch_code": "switch_led"
},
{
"id": "bf5516c52504a7a8dcof7r", # ID de la lumière 2
"name": "Light 2",
"switch_code": "switch_led"
}
]
def main():
cloud = tinytuya.Cloud(
apiRegion=API_REGION,
apiKey=API_KEY,
apiSecret=API_SECRET
)
# Activer le mode debug dans tinytuya
tinytuya.set_debug(True)
for device in DEVICES:
device_id = device["id"]
device_name = device["name"]
switch_code = device["switch_code"]
# Lire l'état actuel
print(f"🔍 Récupération du statut pour l'appareil {device_id}...")
status = cloud.getstatus(device_id)
# Vérifier si on a bien une réponse
if not status:
print(f"❌ Aucune réponse de l'API Cloud pour {device_name}")
continue
# Essayer d'afficher la réponse complète
try:
print(f"📊 Réponse brute: {status}")
print(f"📊 Type de réponse: {type(status).__name__}")
if isinstance(status, dict):
print(f"📊 Clés disponibles: {list(status.keys())}")
except Exception as e:
print(f"⚠️ Erreur lors de l'affichage de la réponse: {e}")
is_on = None
# Vérifier le format de la réponse
if isinstance(status, dict) and "result" in status:
result = status["result"]
if isinstance(result, list):
for item in result:
if isinstance(item, dict) and item.get("code") == switch_code:
is_on = item.get("value")
break
elif isinstance(result, dict) and "status" in result:
for item in result["status"]:
if item.get("code") == switch_code:
is_on = item.get("value")
break
elif isinstance(status, list):
for item in status:
if isinstance(item, dict) and item.get("code") == switch_code:
is_on = item.get("value")
break
if is_on is None:
print(f"❌ Impossible de trouver le statut de l'interrupteur '{switch_code}' pour {device_name}")
continue
print(f" État actuel de {device_name}: {'ALLUMÉ' if is_on else 'ÉTEINT'}")
# Basculer l'état
new_value = not is_on
print(f" Nouvel état à appliquer: {'ALLUMÉ' if new_value else 'ÉTEINT'}")
# Envoyer la commande
commands = {
"commands": [
{"code": switch_code, "value": new_value}
]
}
print(f"📤 Envoi de la commande: {json.dumps(commands, indent=2)}")
response = cloud.sendcommand(device_id, commands)
print(f"📥 Réponse reçue: {json.dumps(response, indent=2) if response else 'Aucune réponse'}")
# Vérifier si response n'est pas None avant d'appeler get()
if response is not None and response.get("success"):
print(f"{device_name} est maintenant {'allumé' if new_value else 'éteint'}")
else:
error_msg = response.get("msg", "Erreur inconnue") if response else "Pas de réponse"
print(f"❌ Échec pour {device_name}: {error_msg}")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,92 @@
import tinytuya
import json
# Informations Cloud Tuya
API_REGION = "eu"
API_KEY = "fdjdjurkm34yyjp77c7d"
API_SECRET = "3e71807fb0c6406792bdf4f07f98e577"
# Informations de l'appareil
DEVICE_ID = "bfadb5f27d0fe9a778ixwc" # ID de la ringlight
DEVICE_NAME = "ringlight"
SWITCH_CODE = "switch_led" # Utiliser le code correct pour l'interrupteur
def main():
cloud = tinytuya.Cloud(
apiRegion=API_REGION,
apiKey=API_KEY,
apiSecret=API_SECRET
)
# Activer le mode debug dans tinytuya
tinytuya.set_debug(True)
# Lire l'état actuel
print(f"🔍 Récupération du statut pour l'appareil {DEVICE_ID}...")
status = cloud.getstatus(DEVICE_ID)
# Vérifier si on a bien une réponse
if not status:
print(f"❌ Aucune réponse de l'API Cloud pour {DEVICE_NAME}")
return
# Essayer d'afficher la réponse complète
try:
print(f"📊 Réponse brute: {status}")
print(f"📊 Type de réponse: {type(status).__name__}")
if isinstance(status, dict):
print(f"📊 Clés disponibles: {list(status.keys())}")
except Exception as e:
print(f"⚠️ Erreur lors de l'affichage de la réponse: {e}")
is_on = None
# Vérifier le format de la réponse
if isinstance(status, dict) and "result" in status:
result = status["result"]
if isinstance(result, list):
for item in result:
if isinstance(item, dict) and item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
elif isinstance(result, dict) and "status" in result:
for item in result["status"]:
if item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
elif isinstance(status, list):
for item in status:
if isinstance(item, dict) and item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
if is_on is None:
print(f"❌ Impossible de trouver le statut de l'interrupteur '{SWITCH_CODE}' pour {DEVICE_NAME}")
return
print(f" État actuel de {DEVICE_NAME}: {'ALLUMÉ' if is_on else 'ÉTEINT'}")
# Basculer l'état
new_value = not is_on
print(f" Nouvel état à appliquer: {'ALLUMÉ' if new_value else 'ÉTEINT'}")
# Envoyer la commande
commands = {
"commands": [
{"code": SWITCH_CODE, "value": new_value}
]
}
print(f"📤 Envoi de la commande: {json.dumps(commands, indent=2)}")
response = cloud.sendcommand(DEVICE_ID, commands)
print(f"📥 Réponse reçue: {json.dumps(response, indent=2) if response else 'Aucune réponse'}")
# Vérifier si response n'est pas None avant d'appeler get()
if response is not None and response.get("success"):
print(f"{DEVICE_NAME} est maintenant {'allumé' if new_value else 'éteint'}")
else:
error_msg = response.get("msg", "Erreur inconnue") if response else "Pas de réponse"
print(f"❌ Échec pour {DEVICE_NAME}: {error_msg}")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,92 @@
import tinytuya
import json
# Informations Cloud Tuya
API_REGION = "eu"
API_KEY = "fdjdjurkm34yyjp77c7d"
API_SECRET = "3e71807fb0c6406792bdf4f07f98e577"
# Informations de l'appareil
DEVICE_ID = "bf2765002c5c1bcf1e4vcl" # ID du salon
DEVICE_NAME = "salon"
SWITCH_CODE = "switch_1" # Utiliser le code correct pour l'interrupteur 1
def main():
cloud = tinytuya.Cloud(
apiRegion=API_REGION,
apiKey=API_KEY,
apiSecret=API_SECRET
)
# Activer le mode debug dans tinytuya
tinytuya.set_debug(True)
# Lire l'état actuel
print(f"🔍 Récupération du statut pour l'appareil {DEVICE_ID}...")
status = cloud.getstatus(DEVICE_ID)
# Vérifier si on a bien une réponse
if not status:
print(f"❌ Aucune réponse de l'API Cloud pour {DEVICE_NAME}")
return
# Essayer d'afficher la réponse complète
try:
print(f"📊 Réponse brute: {status}")
print(f"📊 Type de réponse: {type(status).__name__}")
if isinstance(status, dict):
print(f"📊 Clés disponibles: {list(status.keys())}")
except Exception as e:
print(f"⚠️ Erreur lors de l'affichage de la réponse: {e}")
is_on = None
# Vérifier le format de la réponse
if isinstance(status, dict) and "result" in status:
result = status["result"]
if isinstance(result, list):
for item in result:
if isinstance(item, dict) and item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
elif isinstance(result, dict) and "status" in result:
for item in result["status"]:
if item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
elif isinstance(status, list):
for item in status:
if isinstance(item, dict) and item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
if is_on is None:
print(f"❌ Impossible de trouver le statut de l'interrupteur '{SWITCH_CODE}' pour {DEVICE_NAME}")
return
print(f" État actuel de {DEVICE_NAME}: {'ALLUMÉ' if is_on else 'ÉTEINT'}")
# Basculer l'état
new_value = not is_on
print(f" Nouvel état à appliquer: {'ALLUMÉ' if new_value else 'ÉTEINT'}")
# Envoyer la commande
commands = {
"commands": [
{"code": SWITCH_CODE, "value": new_value}
]
}
print(f"📤 Envoi de la commande: {json.dumps(commands, indent=2)}")
response = cloud.sendcommand(DEVICE_ID, commands)
print(f"📥 Réponse reçue: {json.dumps(response, indent=2) if response else 'Aucune réponse'}")
# Vérifier si response n'est pas None avant d'appeler get()
if response is not None and response.get("success"):
print(f"{DEVICE_NAME} est maintenant {'allumé' if new_value else 'éteint'}")
else:
error_msg = response.get("msg", "Erreur inconnue") if response else "Pas de réponse"
print(f"❌ Échec pour {DEVICE_NAME}: {error_msg}")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,92 @@
import tinytuya
import json
# Informations Cloud Tuya
API_REGION = "eu"
API_KEY = "fdjdjurkm34yyjp77c7d"
API_SECRET = "3e71807fb0c6406792bdf4f07f98e577"
# Informations de l'appareil
DEVICE_ID = "bf10d8832c8447c512jedz" # ID de windowled
DEVICE_NAME = "windowled"
SWITCH_CODE = "switch_led" # Utiliser le code correct pour l'interrupteur
def main():
cloud = tinytuya.Cloud(
apiRegion=API_REGION,
apiKey=API_KEY,
apiSecret=API_SECRET
)
# Activer le mode debug dans tinytuya
tinytuya.set_debug(True)
# Lire l'état actuel
print(f"🔍 Récupération du statut pour l'appareil {DEVICE_ID}...")
status = cloud.getstatus(DEVICE_ID)
# Vérifier si on a bien une réponse
if not status:
print(f"❌ Aucune réponse de l'API Cloud pour {DEVICE_NAME}")
return
# Essayer d'afficher la réponse complète
try:
print(f"📊 Réponse brute: {status}")
print(f"📊 Type de réponse: {type(status).__name__}")
if isinstance(status, dict):
print(f"📊 Clés disponibles: {list(status.keys())}")
except Exception as e:
print(f"⚠️ Erreur lors de l'affichage de la réponse: {e}")
is_on = None
# Vérifier le format de la réponse
if isinstance(status, dict) and "result" in status:
result = status["result"]
if isinstance(result, list):
for item in result:
if isinstance(item, dict) and item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
elif isinstance(result, dict) and "status" in result:
for item in result["status"]:
if item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
elif isinstance(status, list):
for item in status:
if isinstance(item, dict) and item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
if is_on is None:
print(f"❌ Impossible de trouver le statut de l'interrupteur '{SWITCH_CODE}' pour {DEVICE_NAME}")
return
print(f" État actuel de {DEVICE_NAME}: {'ALLUMÉ' if is_on else 'ÉTEINT'}")
# Basculer l'état
new_value = not is_on
print(f" Nouvel état à appliquer: {'ALLUMÉ' if new_value else 'ÉTEINT'}")
# Envoyer la commande
commands = {
"commands": [
{"code": SWITCH_CODE, "value": new_value}
]
}
print(f"📤 Envoi de la commande: {json.dumps(commands, indent=2)}")
response = cloud.sendcommand(DEVICE_ID, commands)
print(f"📥 Réponse reçue: {json.dumps(response, indent=2) if response else 'Aucune réponse'}")
# Vérifier si response n'est pas None avant d'appeler get()
if response is not None and response.get("success"):
print(f"{DEVICE_NAME} est maintenant {'allumé' if new_value else 'éteint'}")
else:
error_msg = response.get("msg", "Erreur inconnue") if response else "Pas de réponse"
print(f"❌ Échec pour {DEVICE_NAME}: {error_msg}")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,44 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Script pour basculer l'état du bureau dino (allumé/éteint) sans affecter le saber
À utiliser avec StreamDeck
"""
import sys
import os
# Ajouter le répertoire parent au path pour les imports
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from common.utils import connect_device, report_for_streamdeck
# ID et nom de l'appareil
DEVICE_ID = "820180048cce4e2aecc7"
DEVICE_NAME = "Bureau Dino"
# Le commutateur principal est sur le DPS 1
SWITCH_ID = "1"
def main():
"""Fonction principale pour basculer l'état du bureau dino sans affecter le saber"""
# Connexion à l'appareil
device = connect_device(DEVICE_ID)
if not device:
report_for_streamdeck(False, DEVICE_NAME)
return
# Obtenir l'état actuel
status = device.status()
# Pour ce type d'appareil, le DPS 1 contrôle l'alimentation principale
current_state = status.get('dps', {}).get(SWITCH_ID, False)
# Basculer l'état (inverse de l'état actuel)
new_state = not current_state
result = device.set_value(SWITCH_ID, new_state)
# Rapport pour StreamDeck
report_for_streamdeck(result is not None, DEVICE_NAME, new_state)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,41 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Script pour basculer l'état de la sirène intelligente (activée/désactivée)
À utiliser avec StreamDeck
"""
import sys
import os
# Ajouter le répertoire parent au path pour les imports
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from common.utils import connect_device, report_for_streamdeck
# ID et nom de l'appareil
DEVICE_ID = "bf4054de281cb6ada8actg"
DEVICE_NAME = "Sirène intelligente"
def main():
"""Fonction principale pour basculer l'état de la sirène"""
# Connexion à l'appareil
device = connect_device(DEVICE_ID)
if not device:
report_for_streamdeck(False, DEVICE_NAME)
return
# Obtenir l'état actuel
status = device.status()
# Pour ce type d'appareil, le DPS 127 indique l'état de l'alarme
current_state = status.get('dps', {}).get('127', False)
# Basculer l'état (inverse de l'état actuel)
new_state = not current_state
result = device.set_value('127', new_state)
# Rapport pour StreamDeck
report_for_streamdeck(result is not None, DEVICE_NAME, new_state)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,37 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Script pour basculer l'état de l'écran gauche (allumé/éteint)
À utiliser avec StreamDeck
"""
import sys
import os
# Ajouter le répertoire parent au path pour les imports
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from common.utils import connect_device, toggle_device, report_for_streamdeck
# ID et nom de l'appareil
DEVICE_ID = "bfc369d2ca89602058xajc"
DEVICE_NAME = "ecran gauche"
# Le commutateur principal est sur le DPS 1
SWITCH_ID = "1"
def main():
"""Fonction principale pour basculer l'état de l'écran"""
# Connexion à l'appareil
device = connect_device(DEVICE_ID)
if not device:
report_for_streamdeck(False, DEVICE_NAME)
return
# Basculer l'état et obtenir le nouvel état
new_state = toggle_device(device, SWITCH_ID)
# Rapport pour StreamDeck
report_for_streamdeck(new_state is not None, DEVICE_NAME, new_state)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,37 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Script pour basculer l'état du PC (allumé/éteint)
À utiliser avec StreamDeck
"""
import sys
import os
# Ajouter le répertoire parent au path pour les imports
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from common.utils import connect_device, toggle_device, report_for_streamdeck
# ID et nom de l'appareil
DEVICE_ID = "bfe57aa0e61a9b529bzrff"
DEVICE_NAME = "PC"
# Commutateur principal
SWITCH_ID = "1"
def main():
"""Fonction principale pour basculer l'état du PC"""
# Connexion à l'appareil
device = connect_device(DEVICE_ID)
if not device:
report_for_streamdeck(False, DEVICE_NAME)
return
# Basculer l'état et obtenir le nouvel état
new_state = toggle_device(device, SWITCH_ID)
# Rapport pour StreamDeck
report_for_streamdeck(new_state is not None, DEVICE_NAME, new_state)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,37 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Script pour basculer l'état du bureau dino (allumé/éteint)
À utiliser avec StreamDeck
"""
import sys
import os
# Ajouter le répertoire parent au path pour les imports
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from common.utils import connect_device, toggle_device, report_for_streamdeck
# ID et nom de l'appareil
DEVICE_ID = "820180048cce4e2aecc7"
DEVICE_NAME = "bureau dino"
# Le commutateur principal est sur le DPS 1
SWITCH_ID = "1"
def main():
"""Fonction principale pour basculer l'état du bureau"""
# Connexion à l'appareil
device = connect_device(DEVICE_ID)
if not device:
report_for_streamdeck(False, DEVICE_NAME)
return
# Basculer l'état et obtenir le nouvel état
new_state = toggle_device(device, SWITCH_ID)
# Rapport pour StreamDeck
report_for_streamdeck(new_state is not None, DEVICE_NAME, new_state)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,37 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Script pour basculer l'état de l'imprimante 3D (allumé/éteint)
À utiliser avec StreamDeck
"""
import sys
import os
# Ajouter le répertoire parent au path pour les imports
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from common.utils import connect_device, toggle_device, report_for_streamdeck
# ID et nom de l'appareil
DEVICE_ID = "bf9043f5954449c435mcec"
DEVICE_NAME = "imprimante 3d"
# Le commutateur principal est sur le DPS 1
SWITCH_ID = "1"
def main():
"""Fonction principale pour basculer l'état de l'imprimante"""
# Connexion à l'appareil
device = connect_device(DEVICE_ID)
if not device:
report_for_streamdeck(False, DEVICE_NAME)
return
# Basculer l'état et obtenir le nouvel état
new_state = toggle_device(device, SWITCH_ID)
# Rapport pour StreamDeck
report_for_streamdeck(new_state is not None, DEVICE_NAME, new_state)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,34 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Script pour basculer l'état de la prise café (allumé/éteint)
À utiliser avec StreamDeck
"""
import sys
import os
# Ajouter le répertoire parent au path pour les imports
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from common.utils import connect_device, toggle_device, report_for_streamdeck
# ID et nom de l'appareil
DEVICE_ID = "bf3254180de3bdd7bfnbei"
DEVICE_NAME = "prise café"
def main():
"""Fonction principale pour basculer l'état de la prise"""
# Connexion à l'appareil
device = connect_device(DEVICE_ID)
if not device:
report_for_streamdeck(False, DEVICE_NAME)
return
# Basculer l'état et obtenir le nouvel état
new_state = toggle_device(device)
# Rapport pour StreamDeck
report_for_streamdeck(new_state is not None, DEVICE_NAME, new_state)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,92 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Script pour basculer l'état de SW2 (allumé/éteint)
À utiliser avec StreamDeck
"""
import tinytuya
import json
# Informations Cloud Tuya
API_REGION = "eu"
API_KEY = "fdjdjurkm34yyjp77c7d"
API_SECRET = "3e71807fb0c6406792bdf4f07f98e577"
# Informations de l'appareil
DEVICE_ID = "bf06f9007abca55618iiwv" # ID de SW2
DEVICE_NAME = "SW2" # Nom de l'appareil
SWITCH_CODE = "switch_1" # Code pour l'interrupteur
def main():
cloud = tinytuya.Cloud(
apiRegion=API_REGION,
apiKey=API_KEY,
apiSecret=API_SECRET
)
# Activer le mode debug dans tinytuya
tinytuya.set_debug(True)
# Lire l'état actuel
print(f"🔍 Récupération du statut pour l'appareil {DEVICE_ID}...")
status = cloud.getstatus(DEVICE_ID)
if not status:
print(f"❌ Aucune réponse de l'API Cloud pour {DEVICE_NAME}")
return
try:
print(f"📊 Réponse brute: {status}")
except Exception as e:
print(f"⚠️ Erreur lors de l'affichage de la réponse: {e}")
is_on = None
# Analyse du statut
if isinstance(status, dict) and "result" in status:
result = status["result"]
if isinstance(result, list):
for item in result:
if isinstance(item, dict) and item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
elif isinstance(result, dict) and "status" in result:
for item in result["status"]:
if item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
elif isinstance(status, list):
for item in status:
if isinstance(item, dict) and item.get("code") == SWITCH_CODE:
is_on = item.get("value")
break
if is_on is None:
print(f"❌ Impossible de trouver le statut '{SWITCH_CODE}' pour {DEVICE_NAME}")
return
print(f" État actuel de {DEVICE_NAME}: {'ALLUMÉ' if is_on else 'ÉTEINT'}")
# Basculer l'état
new_value = not is_on
print(f" Nouvel état à appliquer: {'ALLUMÉ' if new_value else 'ÉTEINT'}")
# Envoyer la commande
commands = {
"commands": [
{"code": SWITCH_CODE, "value": new_value}
]
}
print(f"📤 Envoi de la commande: {json.dumps(commands, indent=2)}")
response = cloud.sendcommand(DEVICE_ID, commands)
print(f"📥 Réponse reçue: {json.dumps(response, indent=2) if response else 'Aucune réponse'}")
if response is not None and response.get("success"):
print(f"{DEVICE_NAME} est maintenant {'allumé' if new_value else 'éteint'}")
else:
error_msg = response.get("msg", "Erreur inconnue") if response else "Pas de réponse"
print(f"❌ Échec pour {DEVICE_NAME}: {error_msg}")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,48 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Script pour contrôler le volet de la cuisine (ouvrir/fermer/arrêter)
À utiliser avec StreamDeck
"""
import sys
import os
# Ajouter le répertoire parent au path pour les imports
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from common.utils import connect_device, control_shutter, report_for_streamdeck
# ID et nom de l'appareil
DEVICE_ID = "bf81367b1f5c267af7yyht"
DEVICE_NAME = "volet cuisine"
def main():
"""Fonction principale pour contrôler le volet"""
# Connexion à l'appareil
device = connect_device(DEVICE_ID)
if not device:
report_for_streamdeck(False, DEVICE_NAME)
return
# Obtenir l'état actuel
status = device.status()
# Si le volet est arrêté, l'ouvrir
# Si le volet est en mouvement, l'arrêter
current_state = status.get('dps', {}).get('1', 'stop')
if current_state == "stop":
# Si arrêté, on ouvre
action = "up" # Utilise "forward" en interne
else:
# Si en mouvement, on arrête
action = "stop"
# Exécuter l'action et obtenir le résultat
result = control_shutter(device, action)
# Rapport pour StreamDeck
report_for_streamdeck(result, DEVICE_NAME, action)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,48 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Script pour contrôler le volet Delia (ouvrir/fermer/arrêter)
À utiliser avec StreamDeck
"""
import sys
import os
# Ajouter le répertoire parent au path pour les imports
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from common.utils import connect_device, control_shutter, report_for_streamdeck
# ID et nom de l'appareil
DEVICE_ID = "bf5b5aa8312d543135xxak"
DEVICE_NAME = "volet delia"
def main():
"""Fonction principale pour contrôler le volet"""
# Connexion à l'appareil
device = connect_device(DEVICE_ID)
if not device:
report_for_streamdeck(False, DEVICE_NAME)
return
# Obtenir l'état actuel
status = device.status()
# Si le volet est arrêté, l'ouvrir
# Si le volet est en mouvement, l'arrêter
current_state = status.get('dps', {}).get('1', 'stop')
if current_state == "stop":
# Si arrêté, on ouvre
action = "up" # Utilise "forward" en interne
else:
# Si en mouvement, on arrête
action = "stop"
# Exécuter l'action et obtenir le résultat
result = control_shutter(device, action)
# Rapport pour StreamDeck
report_for_streamdeck(result, DEVICE_NAME, action)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,77 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Script pour fermer le volet Dino
À utiliser avec StreamDeck
"""
import tinytuya
import json
# Informations Cloud Tuya
API_REGION = "eu"
API_KEY = "fdjdjurkm34yyjp77c7d"
API_SECRET = "3e71807fb0c6406792bdf4f07f98e577"
# ID et nom de l'appareil
DEVICE_ID = "bff082745aedf9fc1aqy3e" # ID du volet Dino
DEVICE_NAME = "Volet dino" # Nom de l'appareil
# Code des commandes du volet
SWITCH_CODE = "control" # Pour les volets Tuya, le code est souvent "control"
def main():
"""Fonction principale pour fermer le volet via Cloud"""
# Connexion au Cloud
cloud = tinytuya.Cloud(
apiRegion=API_REGION,
apiKey=API_KEY,
apiSecret=API_SECRET
)
tinytuya.set_debug(True) # Pour debug si besoin
# Lire l'état actuel du volet
print(f"🔍 Récupération du statut pour l'appareil {DEVICE_ID}...")
status = cloud.getstatus(DEVICE_ID)
if not status:
print(f"❌ Aucune réponse de l'API Cloud pour {DEVICE_NAME}")
return
try:
print(f"📊 Réponse brute: {json.dumps(status, indent=2)}")
except Exception as e:
print(f"⚠️ Erreur lors de l'affichage de la réponse: {e}")
# Vérification de l'état actuel
current_state = None
if isinstance(status, dict) and "result" in status:
result = status["result"]
for item in result:
if item.get("code") == SWITCH_CODE:
current_state = item.get("value")
break
print(f" État actuel du volet: {current_state}")
# Envoyer la commande pour fermer le volet
commands = {
"commands": [
{"code": SWITCH_CODE, "value": "close"}, # Essayez "close" pour fermer
# Vous pouvez également essayer "fermer" si cela est supporté
]
}
print(f"📤 Envoi de la commande: {json.dumps(commands, indent=2)}")
response = cloud.sendcommand(DEVICE_ID, commands)
print(f"📥 Réponse reçue: {json.dumps(response, indent=2) if response else 'Aucune réponse'}")
if response is not None and response.get("success"):
print(f"{DEVICE_NAME} est maintenant fermé")
else:
error_msg = response.get("msg", "Erreur inconnue") if response else "Pas de réponse"
print(f"❌ Échec pour {DEVICE_NAME}: {error_msg}")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,66 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Script pour ouvrir le volet Dino
À utiliser avec StreamDeck
"""
import tinytuya
import json
# Informations Cloud Tuya
API_REGION = "eu"
API_KEY = "fdjdjurkm34yyjp77c7d"
API_SECRET = "3e71807fb0c6406792bdf4f07f98e577"
# ID et nom de l'appareil
DEVICE_ID = "bff082745aedf9fc1aqy3e" # ID du volet Dino
DEVICE_NAME = "Volet dino" # Nom de l'appareil
# Code des commandes du volet
SWITCH_CODE = "control" # Pour les volets Tuya, le code est souvent "control"
def main():
"""Fonction principale pour ouvrir le volet via Cloud"""
# Connexion au Cloud
cloud = tinytuya.Cloud(
apiRegion=API_REGION,
apiKey=API_KEY,
apiSecret=API_SECRET
)
tinytuya.set_debug(True) # Pour debug si besoin
# Lire l'état actuel du volet
print(f"🔍 Récupération du statut pour l'appareil {DEVICE_ID}...")
status = cloud.getstatus(DEVICE_ID)
if not status:
print(f"❌ Aucune réponse de l'API Cloud pour {DEVICE_NAME}")
return
try:
print(f"📊 Réponse brute: {json.dumps(status, indent=2)}")
except Exception as e:
print(f"⚠️ Erreur lors de l'affichage de la réponse: {e}")
# Envoyer la commande pour ouvrir le volet
commands = {
"commands": [
{"code": SWITCH_CODE, "value": "open"}, # Utiliser "open" pour ouvrir
# Vous pouvez également essayer "ouvrir" si cela est supporté
]
}
print(f"📤 Envoi de la commande: {json.dumps(commands, indent=2)}")
response = cloud.sendcommand(DEVICE_ID, commands)
print(f"📥 Réponse reçue: {json.dumps(response, indent=2) if response else 'Aucune réponse'}")
if response is not None and response.get("success"):
print(f"{DEVICE_NAME} est maintenant ouvert")
else:
error_msg = response.get("msg", "Erreur inconnue") if response else "Pas de réponse"
print(f"❌ Échec pour {DEVICE_NAME}: {error_msg}")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,48 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Script pour contrôler le volet Jean Luc (ouvrir/fermer/arrêter)
À utiliser avec StreamDeck
"""
import sys
import os
# Ajouter le répertoire parent au path pour les imports
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from common.utils import connect_device, control_shutter, report_for_streamdeck
# ID et nom de l'appareil
DEVICE_ID = "bf0e197f469e9f2670fb27"
DEVICE_NAME = "volet jean luc"
def main():
"""Fonction principale pour contrôler le volet"""
# Connexion à l'appareil
device = connect_device(DEVICE_ID)
if not device:
report_for_streamdeck(False, DEVICE_NAME)
return
# Obtenir l'état actuel
status = device.status()
# Si le volet est arrêté, l'ouvrir
# Si le volet est en mouvement, l'arrêter
current_state = status.get('dps', {}).get('1', 'stop')
if current_state == "stop":
# Si arrêté, on ouvre
action = "up" # Utilise "forward" en interne
else:
# Si en mouvement, on arrête
action = "stop"
# Exécuter l'action et obtenir le résultat
result = control_shutter(device, action)
# Rapport pour StreamDeck
report_for_streamdeck(result, DEVICE_NAME, action)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,48 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Script pour contrôler le volet du salon (ouvrir/fermer/arrêter)
À utiliser avec StreamDeck
"""
import sys
import os
# Ajouter le répertoire parent au path pour les imports
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from common.utils import connect_device, control_shutter, report_for_streamdeck
# ID et nom de l'appareil
DEVICE_ID = "bfc74a089fe0a9a50d2eeg"
DEVICE_NAME = "volet salon"
def main():
"""Fonction principale pour contrôler le volet du salon"""
# Connexion à l'appareil
device = connect_device(DEVICE_ID)
if not device:
report_for_streamdeck(False, DEVICE_NAME)
return
# Obtenir l'état actuel
status = device.status()
# Si le volet est arrêté, l'ouvrir
# Si le volet est en mouvement, l'arrêter
current_state = status.get('dps', {}).get('1', 'stop')
if current_state == "stop":
# Si arrêté, on ouvre
action = "up" # Utilise "forward" en interne
else:
# Si en mouvement, on arrête
action = "stop"
# Exécuter l'action et obtenir le résultat
result = control_shutter(device, action)
# Rapport pour StreamDeck
report_for_streamdeck(result, DEVICE_NAME, action)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,62 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Script pour scanner et afficher tous les appareils Tuya disponibles sur le réseau
Utile pour diagnostiquer les problèmes de connexion
"""
import sys
import os
import json
import time
# Ajouter le répertoire parent au path pour les imports
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from common.utils import scan_for_devices
def main():
"""Fonction principale pour scanner et afficher les appareils"""
print("Recherche des appareils Tuya sur le réseau...")
print("Veuillez patienter, cela peut prendre jusqu'à 30 secondes...")
# Réaliser un scan du réseau
devices = scan_for_devices()
if not devices:
print("Aucun appareil trouvé sur le réseau.")
return
print(f"\nAppareils trouvés: {len(devices)}\n")
# Afficher les informations détaillées pour chaque appareil
for device in devices:
print("="*50)
print(f"Nom : {device.get('name', 'Inconnu')}")
print(f"ID : {device.get('id', 'N/A')}")
print(f"IP : {device.get('ip', 'N/A')}")
print(f"Produit : {device.get('productKey', 'N/A')}")
print(f"Version : {device.get('ver', 'N/A')}")
# Afficher l'état actuel si disponible
if 'dps' in device:
print("\nÉtat actuel:")
try:
# Formater les DPS pour une meilleure lisibilité
dps_formatted = json.dumps(device['dps'], indent=2, ensure_ascii=False)
print(dps_formatted)
except:
print(device['dps'])
print("="*50 + "\n")
# Sauvegarder les résultats dans un fichier snapshot
timestamp = int(time.time())
snapshot_file = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), f"snapshot_{timestamp}.json")
with open(snapshot_file, 'w', encoding='utf-8') as f:
json.dump({"timestamp": time.time(), "devices": devices}, f, indent=4, ensure_ascii=False)
print(f"Résultats sauvegardés dans {snapshot_file}")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,72 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Script pour afficher le statut de tous les appareils Tuya connus
Utile pour avoir une vue d'ensemble rapide
"""
import sys
import os
import json
# Ajouter le répertoire parent au path pour les imports
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from common.utils import connect_device, get_device_status_text
def main():
"""Fonction principale pour afficher le statut des appareils"""
print("Récupération du statut des appareils Tuya...\n")
# Charger la liste des appareils depuis devices.json
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
devices_file = os.path.join(base_dir, 'devices.json')
try:
with open(devices_file, 'r', encoding='utf-8') as f:
devices = json.load(f)
except Exception as e:
print(f"Erreur lors du chargement du fichier devices.json: {e}")
return
# Catégoriser les appareils
categories = {
"lights": [], # Lumières
"shutters": [], # Volets
"plugs": [], # Prises
"cameras": [], # Caméras
"others": [] # Autres appareils
}
# Trier les appareils par catégorie
for device in devices:
device_id = device.get('id')
device_name = device.get('name', 'Appareil inconnu')
product_key = device.get('product_id', '')
# Classifier selon le type
if product_key in ['key8u54q9dtru5jw', 'keynremrcjf97twe']:
categories["lights"].append((device_id, device_name))
elif product_key in ['4pqpkp1rskkjtxay']:
categories["shutters"].append((device_id, device_name))
elif product_key in ['uqehhcrmk5depvtl']:
categories["plugs"].append((device_id, device_name))
elif product_key in ['biw8urjw2dvss3ur', 'pg7xibyidyvt5pia']:
categories["cameras"].append((device_id, device_name))
else:
categories["others"].append((device_id, device_name))
# Afficher le statut par catégorie
for category, cat_devices in categories.items():
if not cat_devices:
continue
print(f"\n=== {category.upper()} ===")
for device_id, device_name in cat_devices:
status_text = get_device_status_text(device_id)
print(f" - {status_text}")
print("\nTerminé!")
if __name__ == "__main__":
main()

5727
tuya-raw.json Normal file

File diff suppressed because it is too large Load Diff