gmao/app_new/lib_ext/api_client.py

171 lines
5.8 KiB
Python
Raw Permalink Normal View History

2026-08-14 18:02:25 +02:00
"""
Client API Yeastar P520
Authentification et accès à l'API REST du P520
"""
import requests
import hashlib
import base64
import time
import uuid
from typing import Optional
class YeastarClient:
"""Client pour l'API Yeastar P-Series (P520)"""
def __init__(self, base_url: str, username: str, password: str):
self.base_url = base_url.rstrip('/')
self.username = username
self.password = password
self.session = requests.Session()
self.session.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json;charset=UTF-8',
'Origin': self.base_url,
'Referer': f'{self.base_url}/login'
})
self._logged_in = False
self._device_mark = None
def _generate_device_mark(self) -> str:
"""Génère un identifiant de device unique"""
timestamp = int(time.time() * 1000)
random_str = uuid.uuid4().hex[:28]
return f"{timestamp}-{random_str}"
def _encode_password(self, password: str) -> str:
"""Encode le mot de passe: MD5 → Base64"""
md5_hash = hashlib.md5(password.encode()).hexdigest()
return base64.b64encode(md5_hash.encode()).decode()
def login(self, language: str = "fr") -> dict:
"""Authentification auprès du P520"""
self._device_mark = self._generate_device_mark()
# Préparer les paramètres de l'URL
params = {
"username": self.username,
"language": language,
"supportcrx": "true",
"linkus_devicemark": self._device_mark,
"login_link_type": "all"
}
# Préparer le body
body = {
"username": self.username,
"password": self._encode_password(self.password),
"language": language,
"supportcrx": True,
"linkus_devicemark": self._device_mark,
"login_link_type": "all"
}
# Requête de login
url = f"{self.base_url}/api/v1.0/login"
resp = self.session.post(url, params=params, json=body, verify=False, timeout=10)
resp.raise_for_status()
data = resp.json()
if data.get('errcode') == 0:
self._logged_in = True
return {"success": True, "data": data}
else:
return {"success": False, "error": data.get('errmsg', 'Unknown error')}
def get_extensions(self) -> list:
"""Récupère la liste des extensions"""
if not self._logged_in:
raise RuntimeError("Non connecté. Appeler login() d'abord.")
url = f"{self.base_url}/api/v1.0/extension/searchwebclientext"
resp = self.session.get(url, verify=False, timeout=10)
resp.raise_for_status()
data = resp.json()
if data.get('errcode') == 0:
return data.get('ext_list', [])
return []
def get_personal_info(self) -> dict:
"""Récupère les infos de l'extension connectée"""
if not self._logged_in:
raise RuntimeError("Non connecté. Appeler login() d'abord.")
url = f"{self.base_url}/api/v1.0/extension/getpersonal"
resp = self.session.get(url, verify=False, timeout=10)
resp.raise_for_status()
data = resp.json()
if data.get('errcode') == 0:
return data.get('extension', {})
return {}
def get_pbx_info(self) -> dict:
"""Récupère les informations du PBX"""
if not self._logged_in:
raise RuntimeError("Non connecté. Appeler login() d'abord.")
url = f"{self.base_url}/api/v1.0/pbx/getinformation"
resp = self.session.get(url, verify=False, timeout=10)
resp.raise_for_status()
data = resp.json()
if data.get('errcode') == 0:
return data.get('information', {})
return {}
def set_presence(self, status: str, extension_id: int = None) -> bool:
"""Change le statut de présence d'une extension
Args:
status: available, away, business_trip, do_not_disturb, launch, off_work
extension_id: ID de l'extension (défaut: extension connectée)
Returns:
True si succès
"""
if not self._logged_in:
raise RuntimeError("Non connecté. Appeler login() d'abord.")
valid_statuses = ['available', 'away', 'business_trip', 'do_not_disturb', 'launch', 'off_work']
if status not in valid_statuses:
raise ValueError(f"Statut invalide. Valeurs: {valid_statuses}")
# Si pas d'ID fourni, utiliser l'extension connectée
if extension_id is None:
personal = self.get_personal_info()
extension_id = personal.get('id')
if not extension_id:
raise RuntimeError("Impossible de déterminer l'ID de l'extension")
url = f"{self.base_url}/api/v1.0/extension/update"
body = {
"id": extension_id,
"presence_status": status
}
resp = self.session.post(url, json=body, verify=False, timeout=10)
resp.raise_for_status()
data = resp.json()
return data.get('errcode') == 0
def logout(self):
"""Déconnexion"""
if self._logged_in:
url = f"{self.base_url}/api/v1.0/logout"
try:
self.session.post(url, verify=False, timeout=5)
except:
pass
self.session.close()
self._logged_in = False
def __enter__(self):
self.login()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.logout()