Closed cyr-ius closed 8 months ago
@brenard , cette issue pour continuer nos échanges
Concernant Asyncio, avez-vous vu ma PR #18 ?
Concernant Asyncio, avez-vous vu ma PR #18 ?
Oui je l'ai vu depuis longtemps sauf que je n'ai plus de livebox pour tester. Par contre @brenard c'est proposer de faire des tests , si tu es aussi OK , on peut tester à trois.
Je préfère utiliser aiohttp au lieu de htpx , que je trouve bcp plus performant. Du coup , je viens de pousser une branche nommé asyncio. J'ai refondu tout la partie des appels vers la box en intégrant une gestion d'erreur plus fine. Je me suis aussi assuré de toujours recevoir un dictionaire en retour des appels qui ont été émis avec succès.
Il faut tester , normalement j'ai préparer un fichier d'exemple , il suffit de mettre le mot de passe et tester. Au passage , j'ai aussi implémenter une possibilité d'appeler le livebox sur un autre port et d'activer le TLS mais sans garantie.
Si vous être preneur , tout est là https://github.com/cyr-ius/aiosysbus/tree/asyncio
je voudrais mocker les appels , je suis preneur de retour que peux faire le fichier example afin de construire un jeu de tests
Le fichier access.py a été remplacer par auth.py. Conserver pour comparer en cas de besoin
Premier test : échec :face_with_head_bandage:
2024-01-07 19:06:35,215 - asyncio - DEBUG - Using selector: EpollSelector
2024-01-07 19:06:35,252 - aiohttp.client - WARNING - Can not load response cookies: Illegal key '264377da/accept-language'
2024-01-07 19:06:35,282 - aiohttp.client - WARNING - Can not load response cookies: Illegal key '264377da/sessid'
2024-01-07 19:06:35,282 - aiosysbus.auth - DEBUG - Response headers: <CIMultiDictProxy('Set-Cookie': '264377da/sessid=8dHaacDun8QthmHx7k170IFf; path=/; SameSite=Strict; HttpOnly', 'Cache-Control': 'no-cache', 'Pragma': 'no-cache', 'TE': 'chunked', 'Transfer-Encoding': 'chunked', 'Content-Type': 'application/x-sah-ws-1-call+json', 'X-Content-Type-Options': 'nosniff')>
2024-01-07 19:06:35,283 - root - ERROR - Unexpected response , content-type incorrect (application/x-sah-ws-1-call+json)
Note: petit patch nécessaire pour avoir ce retour avec les headers de la réponse et un message d'erreur correctement formaté :
diff --git a/aiosysbus/auth.py b/aiosysbus/auth.py
index 7732fa0..2690131 100644
--- a/aiosysbus/auth.py
+++ b/aiosysbus/auth.py
@@ -174,10 +174,11 @@ class Auth:
"Error occurred while communicating with Livebox."
) from error
+ _LOGGER.debug("Response headers: %s", response.headers)
content_type = response.headers.get("Content-Type", "")
if content_type not in CONTENT_TYPES:
raise UnexpectedResponse(
- "Unexpected response , content-type incorrect (%s)", content_type
+ f"Unexpected response , content-type incorrect ({content_type})"
)
if (response.status // 100) in [4, 5]:
J ai corrigé, le retour. Après dans l ancien code je vérifiais pas le retour du content-type. De ce coté , si le contrôle de retour est trop exigeant on pourra le bypasser. En attendant j ai corrigé et ajouter le content type dans le tableau CONTENT-TYPE Pas hésiter à faire un PR, je les validerais sur la branche asyncio A tester
Sur mon poste, il faut ajouter le content-type "application/json; charset=UTF-8":
--- a/aiosysbus/auth.py
+++ b/aiosysbus/auth.py
@@ -24,6 +24,7 @@ CONTENT_TYPES = [
"application/x-sah-ws-1-call+json",
"application/x-sah-ws-1-call+json; charset=UTF-8",
"application/json",
+ "application/json; charset=UTF-8",
]
Une fois le content-type
ajouté, j'ai moi aussi le problème avec les cookies qui sont refusés.
2024-01-08 08:12:58,074 - aiohttp.client - WARNING - Can not load response cookies: Illegal key 'e2c29097/accept-language'
2024-01-08 08:12:58,074 - aiosysbus.auth - DEBUG - Challenge headers: <CIMultiDictProxy('Cache-Control': 'must-revalidate', 'Set-Cookie': 'e2c29097/accept-language=; path=/; SameSite=Strict', 'Etag': 'e2c29097', 'X-Frame-Options': 'SAMEORIGIN', 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', 'X-Content-Type-Options': 'nosniff', 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'must-revalidate', 'Content-Length': '1449')>
On dirait que aiohttp n'aime pas le "/" dans le nom du cookie.
OK, donc il y a plusieurs soucis (en plus du CONTENT_TYPES
):
Tout d'abord, aiohttp
n'accepte pas le "/" dans le nom du cookie donc ça pose problème. J'ai réussi à bypass avec le code suivant en haut de "example.py"
import sys
if "http" in sys.modules:
raise ImportError("Crawler must be imported before http module")
import http.cookies
http.cookies._is_legal_key = lambda _: True
Deuxième problème, par défaut, le CookieJar fournit par aiohttp n'accepte pas les adresses IP et il faut passer unsafe=True
dans le constructeur:
self._session = session
if not self._session:
jar = CookieJar(unsafe=True)
self._session = ClientSession(cookie_jar=jar)
Troisième problème - si on cherche à générer un deuxième token (ça semble être toujours le cas actuellement) la livebox renvoie un Set-Cookie pour vider/recréer le cookie sessid
que aiohttp ne comprends pas.
Il vaut mieux vider le CookieJar c'est que la réponse de la livebox vide et recrée dans la même header le cookie sessid
:
2024-01-08 10:14:03,757 - aiosysbus.auth - DEBUG - RESPONSE HEADERS: <CIMultiDictProxy('Set-Cookie': 'e2c29097/sessid=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/; SameSite=Strict', 'Set-Cookie': 'e2c29097/sessid=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/; SameSite=Strict', 'Set-Cookie': 'e2c29097/sessid=Ah+xeWOLvJxMo6e+mN2SvSVy; path=/; SameSite=Strict; HttpOnly', 'Cache-Control': 'no-cache', 'Pragma': 'no-cache', 'TE': 'chunked', 'Transfer-Encoding': 'chunked', 'Content-Type': 'application/x-sah-ws-1-call+json', 'X-Content-Type-Options': 'nosniff')>
# Get challenge from API
self.session.cookie_jar.clear_domain(str(self.base_url))
await self._async_get_challenge(self.base_url)
Ok , vous avez fait un super taff. C est là qu on voit que maintenir le code sans livebox c est pas simple Vous êtes tombé dans le truc que je redoutais. le cookie du challenge de départ qui fonctionne tout seul dans le mode requests et touch'y sur aiohttp . Merci @epenet pour avoir persévéré à vouloir garder aiohttp d autre aurait changer de package
Du coup, j ai mergé dans le code dans l ordre proposer par @brenard 40,43,45,46 . Aucun soucis , pas de conflit. A le lecture je vois pas de coquille. Maintenant je vous laisse tester la bête
La petite classe IgnoreIllegalKeyFilter pour intercepté le warning de aiohttp, c le MustHave. Celle là, je l avais pas. Ca rigole plus.🤘
@epenet ,@brenard , pouvez vous me dire si cela fonctionne ? J'ai mergé votre code.
@epenet ,@brenard , pouvez vous me dire si cela fonctionne ? J'ai mergé votre code.
Je viens de tester et ça fonctionne comme attendu.
Reste que j'ai commenté quelques trucs dans ton fichier de tests qui marchait pas, soit parce que la Livebox retournait une erreur, soit parce que je voyais pas quelle requête ou quel test tu cherchais à faire. J'ai systématiquement mis l'erreur que j'avais en commentaire avec mon analyse. Si tu peut faire le tour sur celles-ci et m'en dire plus, peut-être qu'on pourra en corriger quelques unes.
PS : si je comprends bien, la finalité initiale que tu visais, c'est d'utilisé ce mode async dans ton extension Home Assistant pour pouvoir se passer du bridge (ou du moins d'y faire des appels asynchrones sans "adapteur") ? Tu as besoin d'aide sur ce point ?
Effectivement c'est l'objectif. Dans mon fichier d'exemple , j'ai toujours eu quelques requêtes inopérantes car je ne maitriaisais pas les paramètres attendus. Faire du reverse fait que j'ai pas toujours su déterminer comment appeler certains API. J'ai regarder le fichier d'exemple et effectivement je penses que pour celles ou tu as noté des erreurs , l'api attend des paramètres que nous passons pas. Pour l'exception de la ligne 76. Mon exemple sortait simplement quelques éléments de la variable "hosts" de la ligne 66 et j'iterais sur celle-ci . Mais il est bien possible qu'il faille utilisé l'exemple de la ligne 63 pour que l'itération de ligne 87 fonctionne.
Du coup , je vais pousser notre nouvelle version Aiosysbus dans pypi pour effectivement refondre l'addon HA car depuis on a fait un bond côté HA sur la façon de coder l'addon et cela sera certainement plus efficient.
Du coup, comme on a forké aiosysbus avec une branch asyncio. Je vais forké le dépot cyr-ius/hass-livebox-component avec une branch asyncio.
Recoder l'ensemble me pose pas de soucis, après comme d'habitude il faudra tester le nouveau module et proposer des PR si cela ne fonctionne pas. Donc aucun pbl , pour coder à plusieurs.
Je pensais effectivement me débarrser complètement du bridge. Garder un coordinateur qui récupère ce qu'il a besoin et ensuite injecter cela dans les différentes entités en s'appuyant sur la classe SensorDescription pour être plus efficace. Tout en essayant de garder la retro compatibilité pour éviter ques ls gens ne voyent apparaitrent des trucs nouveaux ou casser dans HA
Merci pour votre contribution.
https://github.com/cyr-ius/aiosysbus
De là , je fais une nouvelle branche que j'appel asyncio , je prépare l'api en mode asynchrone et un fichier example complet.
Ensuite , il te suffirait de lancer un python3.11 dans un venv , de faire un pip -r requirements.txt et de jouer le fichier example.py Si il ne plante pas. On est bon , on a une API opérationnelle. On pourrait ainsi l'utiliser dans l'addon Livebox de Home Assistant
Originally posted by @cyr-ius in https://github.com/cyr-ius/hass-livebox-component/issues/89#issuecomment-1879671536