Open KleemannT opened 4 years ago
Thx for the update and the insight in the dev.
I will try to update that !
I will have a deep look into this, but by using an unofficial API, we could have issue regarding breaking change and our integration won't work the time we analyse the changes and make changes to the code.
I am not sure this is the right move to do if we want a stable system (something I want regarding my home automations ;) )
Grm
And i have a question,what permits this new api that is not permitted by the official API ? The first versions of python librairies were reverse engeneering ports
py-smart-gardena is using the official documented Gardena-API from the Husqvarna website https://developer.husqvarnagroup.cloud/apis/GARDENA+smart+system+API which ist based on REST and websocket communication.
If you reverse engineer the JavaScript code of the Browser app (https://smart.gardena.com/) you will find a new API based on Server-Sent Events (SSE) and a GET-request with JSON data. Have a look at the "Gardena-Binding" source from openHAB, you find a partial implementation of it. The new API has totally new login, locations, devices functions and much more detailled information called "abilities" per device.
I've written some small python programs which get data from this new API, but since I'm not a Python Pro, I could not finish my work. You're welcome to get my "small code" and some hints (Postman scripts) if you rewrite parts of py-smart-gardena.
import json import pprint import sseclient import time import sys, traceback import logging
logging.basicConfig( format='%(asctime)s %(levelname)-8s %(message)s', level=logging.DEBUG, datefmt='%Y-%m-%d %H:%M:%S', filename="events-{}.txt".format(time.strftime("%Y%m%d-%H%M")))
def with_urllib3(url): """Get a streaming response for the given event feed using urllib3.""" import urllib3 http = urllib3.PoolManager(timeout=urllib3.Timeout(connect=120.0, read=3600.0)) return http.request( 'GET', url, headers={ 'accept': 'text/event-stream', 'referer': 'https://smart.gardena.com/', 'origin': 'https://smart.gardena.com/', 'sec-fetch-dest': 'empty', 'sec-fetch-mode': 'cors', 'sec-fetch-site': 'cross-site' }, preload_content=False)
def with_requests(url): """Get a streaming response for the given event feed using requests.""" import requests headers={ 'accept': 'text/event-stream', 'referer': 'https://smart.gardena.com/', 'origin': 'https://smart.gardena.com/', 'sec-fetch-dest': 'empty', 'sec-fetch-mode': 'cors', 'sec-fetch-site': 'cross-site' } return requests.get(url, stream=True, headers=headers, timeout=None)
base_url = "https://rt-api.sg.dss.husqvarnagroup.net/bff/v1/events/" location_id = "059...-...-...0c8" session = "ey...qJg" #very long session string url = base_url + location_id + "?session=" + session
response = with_urllib3(url)
response = with_requests(url)
fmt_date = time.strftime("%Y-%m-%d %H:%M:%S") print(f"Start: {fmt_date}")
client = sseclient.SSEClient(response)
try: for event in client.events(): pprint.pprint(json.loads(event.data))
except requests.exceptions.Timeout as e: print(e)
except KeyboardInterrupt: print ("Interrupted by KeyboardInterrupt")
smart_system.quit()
else: fmt_date = time.strftime("%Y-%m-%d %H:%M:%S") print(f"End: {fmt_date}")
Get login/auth token: POST https://smart.gardena.com/v1/auth/token BODY: {"data":{"type":"token","attributes":{"username":"","password":"","client_id":"smartgarden-jwt-client"}}}
ANSWER BODY: { "data": { "id": "eyJ...yYA #a very long session string ", "type": "token", "attributes": { "expires_in": 863999, "refresh_token": "a97...-...-...307b", "provider": "husqvarna", "user_id": "1b5...-...-...-...ba1", "scope": "iam:read iam:write", "client_id": "smartgarden-jwt-client" } } }