Plugin pour piloter les caméras Tapo

Hello,

Il existe une librairie pytapo qui permet de piloter les caméras TPlink Tapo.
Je l’ai installée et l’utilise via le plugin script, avec les petites connaissances, mais il serait cool d’avoir un plugin tout-en-un qui exploite à fond ces possibilités.
Par exemple j’ai découvert récemment qu’il y a une fonction qui permet directement de récupérer les fonctionnalités disponibles sur le modèle de la caméra en question, pratique !

Vous pouvez avoir un aperçu sur mon dernier post ici sur le sujet avec le script en question et la tuile générée.

Je pense que @bodbod pourrait donner son avis aussi puisque je me suis basé sur son code.

4 « J'aime »

Je viens de faire quelques tests.

A défaut de créer un deamon, on peut exécuter du python (donc ton script) dans le code PHP.

En passant en paramètre les login/pass/ip, on peut imager l’appeler avec les paramètres du plugin et device.

Je trouve pas ca super propre mais ca peut être un début.

1 « J'aime »

En effet, mais est-ce que notre code (c’est le tien avant d’être le lien :smiley: ) ne serait pas inutile dans ce cadre là ? Vu qu’il sert « juste » à faire une passerelle entre les fonctions pytapo et les appels par le plugin script ?

D’ailleurs je pense de plus en plus que c’est pas optimisé parce qu’à chaque fois que je sauvegarde mon équipement script ça lance une synchro de toutes les commandes info et c’est trrrrrès long (en dizaines de secondes). Je pense que c’est parce qu’à chaque commande je relance eune requête avec les credentials…

En jetant un oeil a pytapo, je vois qu’il y a une fonction getMost qui ramène tous les états d’un seul coup (json)
Plutôt pratique si on veut récupérer les états sans faire 50 requêtes par minute

Peux-tu la tester pour vérifier ce qu’elle renvoie ?

Ça renvoie ça (j’arrive pas à a savoir si c’est tronqué ou pas, c’est sacrément long :stuck_out_tongue: )

{'getDstRule': [{'system': {'dst': {'enabled': '1', 'synced': '1', 'has_rule': '1', 'dst_rule': 'DST-02:00,M3.5.0/02:00:00,M10.5.0/03:00:00'}}}], 'getClockStatus': [{'system': {'clock_status': {'seconds_from_1970': 1743615865, 'local_time': '2025-04-02 19:44:25'}}}], 'getTimezone': [{'system': {'basic': {'timing_mode': 'ntp', 'zone_id': 'Europe/Brussels', 'timezone': 'UTC+01:00'}}}], 'getAlertTypeList': [{'msg_alarm': {'alert_type': {'alert_type_list': ['Siren', 'Tone']}}}], 'getNightVisionCapability': [{'image_capability': {'supplement_lamp': {'supplement_lamp_type': ['infrared_lamp'], 'night_vision_mode_range': ['inf_night_vision']}}}], 'getDeviceInfo': [{'device_info': {'basic_info': {'device_type': 'SMART.IPCAMERA', 'device_info': 'C210 2.0 IPC', 'features': 3, 'barcode': '', 'device_model': 'C210', 'sw_version': '1.4.5 Build 250228 Rel.54710n', 'device_name': 'C210 2.0', 'hw_version': '2.0', 'device_alias': 'Tapo_C210_B66F', 'mobile_access': '0', 'mac': 'F0-09-0D-BE-B6-6F', 'dev_id': '8021F6A5B6F5C4B4A5AB5079368E2FFE230C3C96', 'hw_id': '5FAAA6EC6A1A74A8B50B0C3302FB26FD', 'oem_id': 'A324FA71283989909AAB5B8646626D4C', 'hw_desc': '00000000000000000000000000000000', 'manufacturer_name': 'TP-LINK', 'region': 'EU', 'ffs': False, 'is_cal': True, 'avatar': 'Baby room', 'has_set_location_info': 1, 'longitude': -11036, 'latitude': 446370}}}], 'getDetectionConfig': [{'motion_detection': {'motion_det': {'enabled': 'off', 'sensitivity': 'high', 'digital_sensitivity': '50', 'people_enabled': 'off', 'vehicle_enabled': 'off', 'non_vehicle_enabled': 'off'}}}], 'getPersonDetectionConfig': [{'people_detection': {'detection': {'enabled': 'off', 'sensitivity': '50'}}}], 'getVehicleDetectionConfig': [False], 'getBCDConfig': [{'sound_detection': {'bcd': {'enabled': 'off', 'sensitivity': 'low', 'digital_sensitivity': '50'}}}], 'getPetDetectionConfig': [False], 'getBarkDetectionConfig': [False], 'getMeowDetectionConfig': [False], 'getGlassDetectionConfig': [False], 'getTamperDetectionConfig': [{'tamper_detection': {'tamper_det': {'enabled': 'off', 'sensitivity': 'medium', 'digital_sensitivity': '50'}}}], 'getLensMaskConfig': [{'lens_mask': {'lens_mask_info': {'enabled': 'off'}}}], 'getLdc': [{'image': {'switch': {'schedule_end_time': '64800', 'schedule_start_time': '21600', 'clear_licence_plate_mode': 'off', 'switch_mode': 'common', 'rotate_type': 'off', 'flip_type': 'off', 'ldc': 'off', 'night_vision_mode': 'inf_night_vision', 'full_color_people_enhance': 'off', 'full_color_min_keep_time': '5', 'wtl_intensity_level': '5', 'wtl_force_time': '300', 'overexposure_people_suppression': 'off', 'best_view_distance': '0', 'image_scene_mode': 'normal', 'image_scene_mode_common': 'normal', 'image_scene_mode_shedday': 'normal', 'image_scene_mode_shednight': 'normal', 'image_scene_mode_autoday': 'normal', 'image_scene_mode_autonight': 'normal', 'wtl_manual_start_flag': 'off'}, 'common': {'chroma': '50', 'luma': '50', 'sharpness': '50', 'saturation': '50', 'contrast': '50', 'inf_delay': '5', 'wd_gain': '50', 'exp_gain': '0', 'smartir_level': '100', 'auto_exp_gain_max': '0', 'wb_R_gain': '50', 'wb_B_gain': '50', 'wb_G_gain': '50', 'wtl_sensitivity': '4', 'wtl_delay': '5', 'wtl_sensitivity_day2night': '1400', 'wtl_sensitivity_night2day': '9100', 'iris_level': '160', 'lock_red_gain': '0', 'lock_gr_gain': '0', 'lock_gb_gain': '0', 'lock_blue_gain': '0', 'lock_red_colton': '0', 'lock_green_colton': '0', 'lock_blue_colton': '0', 'focus_limited': '10', 'inf_start_time': '64800', 'inf_end_time': '21600', 'wtl_start_time': '64800', 'wtl_end_time': '21600', 'inf_sensitivity_day2night': '1400', 'inf_sensitivity_night2day': '9100', 'wide_dynamic': 'off', 'high_light_compensation': 'off', 'dehaze': 'off', 'eis': 'off', 'auto_exp_antiflicker': 'off', 'inf_type': 'auto', 'inf_sensitivity': '1', 'wtl_type': 'auto', 'backlight': 'off', 'area_compensation': 'default', 'smartir': 'off', 'exp_type': 'auto', 'exp_level': '0', 'shutter': '1/25', 'style': 'standard', 'focus_type': 'manual', 'wb_type': 'auto', 'lock_source': 'local', 'smartwtl': 'auto_wtl', 'smartwtl_level': '5', 'smartwtl_digital_level': '100', 'light_freq_mode': 'auto'}}}], 'getLastAlarmInfo': [{'msg_alarm': {'chn1_msg_alarm_info': {'enabled': 'off', 'light_alarm_enabled': 'on', 'alarm_mode': ['sound', 'light'], 'alarm_type': '0', 'light_type': '1', 'sound_alarm_enabled': 'on', 'alarm_volume': 'high', 'alarm_duration': '0'}}}], 'getLedStatus': [{'led': {'config': {'enabled': 'off'}}}], 'getTargetTrackConfig': [{'target_track': {'target_track_info': {'enabled': 'off', 'track_mode': 'pantilt', 'back_time': '30', 'track_time': '0'}}}], 'getPresetConfig': [{'preset': {'preset': {'id': ['1', '2', '3'], 'name': ['Point de vue 1', 'Test', 'Point de vue 3'], 'read_only': ['0', '0', '0'], 'position_pan': ['0.377973', '0.377973', '-0.084281'], 'position_tilt': ['-0.143216', '-0.143216', '-0.143216'], 'position_zoom': []}}}], 'getFirmwareUpdateStatus': [{'cloud_config': {'upgrade_status': {'state': 'normal', 'lastUpgradingSuccess': True}}}], 'getMediaEncrypt': [{'cet': {'media_encrypt': {'enabled': 'on'}}}], 'getConnectionType': [{'link_type': 'wifi', 'ssid': 'La-box-de-buch', 'rssiValue': -54, 'rssi': '3'}], 'getAlarmConfig': [False], 'getAlarmPlan': [False], 'getSirenTypeList': [False, False], 'getSirenConfig': [False], 'getAlertConfig': [{'msg_alarm': {'chn1_msg_alarm_info': {'enabled': 'off', 'light_alarm_enabled': 'on', 'alarm_mode': ['sound', 'light'], 'alarm_type': '0', 'light_type': '1', 'sound_alarm_enabled': 'on', 'alarm_volume': 'high', 'alarm_duration': '0'}, 'capability': {'usr_def_audio_alarm_support': '1', 'usr_def_audio_alarm_max_num': '15', 'alarm_volume_support': '1', 'alarm_duration_support': '1', 'alert_event_type_support': '1', 'usr_def_start_file_id': '8195', 'usr_def_audio_type': '0', 'usr_def_audio_max_duration': '15'}, 'usr_def_audio': []}}], 'getLightTypeList': [False], 'getSirenStatus': [False, False], 'getLightFrequencyInfo': [{'image': {'common': {'chroma': '50', 'luma': '50', 'sharpness': '50', 'saturation': '50', 'contrast': '50', 'inf_delay': '5', 'wd_gain': '50', 'exp_gain': '0', 'smartir_level': '100', 'auto_exp_gain_max': '0', 'wb_R_gain': '50', 'wb_B_gain': '50', 'wb_G_gain': '50', 'wtl_sensitivity': '4', 'wtl_delay': '5', 'wtl_sensitivity_day2night': '1400', 'wtl_sensitivity_night2day': '9100', 'iris_level': '160', 'lock_red_gain': '0', 'lock_gr_gain': '0', 'lock_gb_gain': '0', 'lock_blue_gain': '0', 'lock_red_colton': '0', 'lock_green_colton': '0', 'lock_blue_colton': '0', 'focus_limited': '10', 'inf_start_time': '64800', 'inf_end_time': '21600', 'wtl_start_time': '64800', 'wtl_end_time': '21600', 'inf_sensitivity_day2night': '1400', 'inf_sensitivity_night2day': '9100', 'wide_dynamic': 'off', 'high_light_compensation': 'off', 'dehaze': 'off', 'eis': 'off', 'auto_exp_antiflicker': 'off', 'inf_type': 'auto', 'inf_sensitivity': '1', 'wtl_type': 'auto', 'backlight': 'off', 'area_compensation': 'default', 'smartir': 'off', 'exp_type': 'auto', 'exp_level': '0', 'shutter': '1/25', 'style': 'standard', 'focus_type': 'manual', 'wb_type': 'auto', 'lock_source': 'local', 'smartwtl': 'auto_wtl', 'smartwtl_level': '5', 'smartwtl_digital_level': '100', 'light_freq_mode': 'auto'}}}], 'getLightFrequencyCapability': [False], 'getChildDeviceList': [False], 'getRotationStatus': [{'image': {'switch': {'schedule_end_time': '64800', 'schedule_start_time': '21600', 'clear_licence_plate_mode': 'off', 'switch_mode': 'common', 'rotate_type': 'off', 'flip_type': 'off', 'ldc': 'off', 'night_vision_mode': 'inf_night_vision', 'full_color_people_enhance': 'off', 'full_color_min_keep_time': '5', 'wtl_intensity_level': '5', 'wtl_force_time': '300', 'overexposure_people_suppression': 'off', 'best_view_distance': '0', 'image_scene_mode': 'normal', 'image_scene_mode_common': 'normal', 'image_scene_mode_shedday': 'normal', 'image_scene_mode_shednight': 'normal', 'image_scene_mode_autoday': 'normal', 'image_scene_mode_autonight': 'normal', 'wtl_manual_start_flag': 'off'}}}], 'getNightVisionModeConfig': [{'image': {'switch': {'schedule_end_time': '64800', 'schedule_start_time': '21600', 'clear_licence_plate_mode': 'off', 'switch_mode': 'common', 'rotate_type': 'off', 'flip_type': 'off', 'ldc': 'off', 'night_vision_mode': 'inf_night_vision', 'full_color_people_enhance': 'off', 'full_color_min_keep_time': '5', 'wtl_intensity_level': '5', 'wtl_force_time': '300', 'overexposure_people_suppression': 'off', 'best_view_distance': '0', 'image_scene_mode': 'normal', 'image_scene_mode_common': 'normal', 'image_scene_mode_shedday': 'normal', 'image_scene_mode_shednight': 'normal', 'image_scene_mode_autoday': 'normal', 'image_scene_mode_autonight': 'normal', 'wtl_manual_start_flag': 'off'}}}], 'getWhitelampStatus': [{'status': 0, 'rest_time': 0}], 'getWhitelampConfig': [{'image': {'switch': {'schedule_end_time': '64800', 'schedule_start_time': '21600', 'clear_licence_plate_mode': 'off', 'switch_mode': 'common', 'rotate_type': 'off', 'flip_type': 'off', 'ldc': 'off', 'night_vision_mode': 'inf_night_vision', 'full_color_people_enhance': 'off', 'full_color_min_keep_time': '5', 'wtl_intensity_level': '5', 'wtl_force_time': '300', 'overexposure_people_suppression': 'off', 'best_view_distance': '0', 'image_scene_mode': 'normal', 'image_scene_mode_common': 'normal', 'image_scene_mode_shedday': 'normal', 'image_scene_mode_shednight': 'normal', 'image_scene_mode_autoday': 'normal', 'image_scene_mode_autonight': 'normal', 'wtl_manual_start_flag': 'off'}}}], 'getMsgPushConfig': [{'msg_push': {'chn1_msg_push_info': {'notification_enabled': 'on', 'rich_notification_enabled': 'off'}}}], 'getSdCardStatus': [{'harddisk_manage': {'hd_info': [{'hd_info_1': {'disk_name': '1', 'rw_attr': 'r', 'status': 'offline', 'detect_status': 'offline', 'write_protect': '0', 'percent': '0', 'type': 'local', 'record_duration': '0', 'record_free_duration': '0', 'record_start_time': '0', 'loop_record_status': '0', 'total_space': '0B', 'total_space_accurate': '0B', 'free_space': '0B', 'free_space_accurate': '0B', 'video_total_space': '0B', 'video_total_space_accurate': '0B', 'video_free_space': '0B', 'video_free_space_accurate': '0B', 'picture_total_space': '0B', 'picture_total_space_accurate': '0B', 'picture_free_space': '0B', 'picture_free_space_accurate': '0B', 'crossline_total_space': '0B', 'crossline_total_space_accurate': '0B', 'crossline_free_space': '0B', 'crossline_free_space_accurate': '0B', 'msg_push_total_space': '0B', 'msg_push_total_space_accurate': '0B', 'msg_push_free_space': '0B', 'msg_push_free_space_accurate': '0B'}}]}}], 'getCircularRecordingConfig': [{'harddisk_manage': {'harddisk': {'loop': 'on'}}}], 'getRecordPlan': [{'record_plan': {'chn1_channel': {'enabled': 'on', 'monday': '["0000-2400:2"]', 'tuesday': '["0000-2400:2"]', 'wednesday': '["0000-2400:2"]', 'thursday': '["0000-2400:2"]', 'friday': '["0000-2400:2"]', 'saturday': '["0000-2400:2"]', 'sunday': '["0000-2400:2"]'}}}], 'getAudioConfig': [{'audio_config': {'speaker': {'volume': '100', 'mute': 'off', 'output_device_type': 'SpeakerOut'}, 'microphone': {'sampling_rate': '8', 'channels': '1', 'encode_type': 'G711alaw', 'mute': 'off', 'volume': '100', 'input_device_type': 'MicIn', 'noise_cancelling': 'on', 'bitrate': '64', 'echo_cancelling': 'off'}}}], 'getFirmwareAutoUpgradeConfig': [{'auto_upgrade': {'common': {'enabled': 'off', 'time': '03:00', 'random_range': '120'}}}], 'getVideoQualities': [{'video': {'main': {'quality': '3', 'bitrate': '1382', 'frame_rate': '65551', 'smart_codec': 'off', 'encode_type': 'H264', 'resolution': '1920*1080', 'bitrate_type': 'vbr', 'name': 'VideoEncoder_1', 'default_bitrate': '1382'}}}], 'getVideoCapability': [{'video_capability': {'main': {'encode_types': ['H264', 'H265'], 'frame_rates': ['65551', '65556', '65561', '65566'], 'bitrates': ['256', '512', '1024', '1382', '2048'], 'bitrate_types': ['cbr', 'vbr'], 'resolutions': ['2304*1296', '1920*1080', '1280*720'], 'qualitys': ['1', '3', '5'], 'minor_stream_support': '0', 'change_fps_support': '1'}}}]}

Mais effectivement si ça remonte toutes les fonctions « automatiquement » ça peut être pratique pour le plugin, surtout couplé à la fonction getModuleInfo qui liste ce qui est dispo ou pour pas sur l’équipement en question.

C’est bon signe

1 « J'aime »

Ah ouais après faut jouer une sacrée partition de json :stuck_out_tongue:
Il faudrait donc appeler cette fonction une fois, puis la « couper » dans Jeedom sans refaire une requête script ? Dans un virtuel du coup ?

non juste dans le plugin-script => si plusieurs commandes font la même requête, le plugin ne fait en réalité qu’une seule fois la requête à chaque « refresh » et utilise le résultat pour chacune des commandes

1 « J'aime »

Ok compris, mais du coup ça devient une requête Json? Ou toujours une requête script?
Je ne sais pas comment appeler un script python (qui ne contiendrait plus que getMost() finalement) pour ensuite extraire la donnée Json qui m’intéresse.
Un truc comme ça dans la requête script \blabla\data\pytapo.PY getMost["basic_info"]["module_info"][etc...] ça fonctionnerait ?

Mon idée est son utilisation dans un plugin.

A l’image d’un autre plugin (heatzy) que je commence a maîtriser, l’idée serait de faire cet appel toutes les minutes (cron) pour récupérer les retours d’état.

Sur ce que dit @Mips, le plugin script permet de prendre une seule donnée dans un json
Voir la doc https://doc.jeedom.com/fr_FR/plugins/programming/script/ avec l’exemple de type data > 2 > show_name

Ok en effet dans le cadre du plugin ça a du sens ! J’ai mélangé avec notre au discussion sur le cas d’usage script :stuck_out_tongue:

Pour jeter un oeil sur une ébauche de script, il faudrait déjà que je règle mon souci de lib pytapo/python/kasa

@rom.jou : tu as deja eu le souci sur un autre sujet. Peut être peux tu m’aider ?

Yep tu peux regarder ce message où j’ai résumé ce que j’ai fait. En gros si j’ai bien compris la dernière version de kasa (utilisé par pytapo) n’est pas compatible de la version de python 3.3.9 donc il faut soit installer une vieille version de kasa/pytapo soit passer par un venv pour installer un python plus récent.
Pour le futur plugin je ne sais pas quelle est la meilleure stratégie à adopter. @Michel_F qui m’a guidé dans la découverte de venv semblait dire que c’était une solution utilisée sur d’autres plugins.

Et sinon, voilà où j’en suis dans la réflexion sur ce plugin (spoiler alert: je ne sais rien faire de tout ça :smiley: ) :

  • l’installation du plugin devrait déclencher l’installation de pytapo et kasa, à la bonne version et/ou via un venv (cf message précédent).
  • la page d’accueil du plugin proposerait un bouton pour ajouter un équipement
  • la page de config d’un équipement consisterait initialement en des champs adresse IP, login, MDP et un bouton de synchronisation qui ferait alors appel à la fonction get BasicInfo et getModuleInfo qui permettent respectivement de récupérer le nom de l’équipement et de lister les fonctionnalités dispo sur cet équipement pour activer ou non les commandes associées.
  • les commandes de l’équipement seraient donc issues des fonctions set dispo dans pytapo, et de la fonction getMost qui tournerait régulièrement en fond (cron) pour être « découpée » en plusieurs commandes infos correspondant à chaque besoin.

Dans ma tête c’est clair, en vrai je sais pas si c’est faisable :slight_smile:

Bonjour,

My 2 cents :

  • 1er point : bidon. L’installation d’un venv c’est pour y installer des modules de manière isolée par rapport au système. Avec la bibliothèque de Mips et nebz, ça peut aller tout seul.
  • 2ème point : standard pour un plugin Jeedom
  • 3ème point : ça semble logique, mais je conseillerais de déclencher la synchronisation sur sauvegarde de l’équipement
  • 4ème point :
    • création des commandes : faisable si les fonctions de pytapo retournent quelque chose d’exploitable
    • cron : dans un démon python, on peut définir le temps de cycle avec le temps de réponse de l’équipement comme seuil bas (si l’équipement met 4 secondes à envoyer le json complet, on ne peut pas mettre un temps de cycle de 2 secondes)

A+
Michel

En fait il faut installer pyenv pour installer une version de python plus récente et créer un venv pour cette version de python pour isoler un environnement avec les versions des modules kasa et pytapo compatibles.
:disguised_face:

Je me devais de préciser ça.

Je comprends pas tout donc je suis d’accord :smiley:
Je donne des pistes après, je laisse faire les sachants :nerd_face: