mirror of https://github.com/Andre0512/pyhOn.git
Support cloud push with wss mqtt
This commit is contained in:
parent
a1347f7a46
commit
f108005a4d
|
@ -7,10 +7,12 @@ from types import TracebackType
|
|||
from typing import Dict, Optional, Any, List, no_type_check, Type
|
||||
|
||||
from aiohttp import ClientSession
|
||||
from awscrt import mqtt5
|
||||
from typing_extensions import Self
|
||||
|
||||
from pyhon import const, exceptions
|
||||
from pyhon.appliance import HonAppliance
|
||||
from pyhon.connection import mqtt
|
||||
from pyhon.connection.auth import HonAuth
|
||||
from pyhon.connection.handler.anonym import HonAnonymousConnectionHandler
|
||||
from pyhon.connection.handler.hon import HonConnectionHandler
|
||||
|
@ -18,6 +20,7 @@ from pyhon.connection.handler.hon import HonConnectionHandler
|
|||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
class HonAPI:
|
||||
def __init__(
|
||||
self,
|
||||
|
@ -37,6 +40,7 @@ class HonAPI:
|
|||
self._hon_handler: Optional[HonConnectionHandler] = None
|
||||
self._hon_anonymous_handler: Optional[HonAnonymousConnectionHandler] = None
|
||||
self._session: Optional[ClientSession] = session
|
||||
self._mqtt_client: mqtt5.Client | None = None
|
||||
|
||||
async def __aenter__(self) -> Self:
|
||||
return await self.create()
|
||||
|
@ -191,6 +195,13 @@ class HonAPI:
|
|||
maintenance: Dict[str, Any] = (await response.json()).get("payload", {})
|
||||
return maintenance
|
||||
|
||||
async def load_aws_token(self) -> str:
|
||||
url: str = f"{const.API_URL}/auth/v1/introspection"
|
||||
async with self._hon.get(url) as response:
|
||||
introspection: Dict[str, Any] = (await response.json()).get("payload", {})
|
||||
result: str = introspection.get("tokenSigned", "")
|
||||
return result
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
appliance: HonAppliance,
|
||||
|
@ -258,6 +269,10 @@ class HonAPI:
|
|||
result: Dict[str, Any] = await response.json()
|
||||
return result
|
||||
|
||||
async def subscribe_mqtt(self, appliances: list[HonAppliance]) -> None:
|
||||
if not self._mqtt_client:
|
||||
self._mqtt_client = await mqtt.start(self, appliances)
|
||||
|
||||
async def close(self) -> None:
|
||||
if self._hon_handler is not None:
|
||||
await self._hon_handler.close()
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
import json
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from awscrt import mqtt5
|
||||
from awsiot import mqtt5_client_builder # type: ignore[import-untyped]
|
||||
|
||||
from pyhon import const
|
||||
from pyhon.appliance import HonAppliance
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pyhon import HonAPI
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
appliances: list[HonAppliance] = []
|
||||
|
||||
|
||||
def on_lifecycle_stopped(lifecycle_stopped_data: mqtt5.LifecycleStoppedData) -> None:
|
||||
print("Lifecycle Stopped")
|
||||
print(lifecycle_stopped_data)
|
||||
|
||||
|
||||
def on_lifecycle_connection_success(
|
||||
lifecycle_connect_success_data: mqtt5.LifecycleConnectSuccessData,
|
||||
) -> None:
|
||||
print("Lifecycle Connection Success")
|
||||
print(lifecycle_connect_success_data.connack_packet)
|
||||
print(lifecycle_connect_success_data.negotiated_settings)
|
||||
|
||||
|
||||
def on_publish_received(data: mqtt5.PublishReceivedData) -> None:
|
||||
if not (data and data.publish_packet and data.publish_packet.payload):
|
||||
return
|
||||
payload = json.loads(data.publish_packet.payload.decode())
|
||||
topic = data.publish_packet.topic
|
||||
if topic and "appliancestatus" in topic:
|
||||
appliance = next(
|
||||
a for a in appliances if topic in a.info["topics"]["subscribe"]
|
||||
)
|
||||
for parameter in payload["parameters"]:
|
||||
appliance.attributes["parameters"][parameter["parName"]].update(parameter)
|
||||
print(parameter)
|
||||
else:
|
||||
print(topic, payload)
|
||||
|
||||
|
||||
async def create_mqtt_client(api: "HonAPI") -> mqtt5.Client:
|
||||
client: mqtt5.Client = mqtt5_client_builder.websockets_with_custom_authorizer(
|
||||
endpoint=const.AWS_ENDPOINT,
|
||||
auth_authorizer_name=const.AWS_AUTHORIZER,
|
||||
auth_authorizer_signature=await api.load_aws_token(),
|
||||
auth_token_key_name="token",
|
||||
auth_token_value=api.auth.id_token,
|
||||
client_id=const.MOBILE_ID,
|
||||
on_lifecycle_stopped=on_lifecycle_stopped,
|
||||
on_lifecycle_connection_success=on_lifecycle_connection_success,
|
||||
on_publish_received=on_publish_received,
|
||||
)
|
||||
client.start()
|
||||
return client
|
||||
|
||||
|
||||
def subscribe(client: mqtt5.Client, appliance: HonAppliance) -> None:
|
||||
for topic in appliance.info.get("topics", {}).get("subscribe", []):
|
||||
client.subscribe(mqtt5.SubscribePacket([mqtt5.Subscription(topic)])).result(10)
|
||||
_LOGGER.error("Subscribed to topic %s", topic)
|
||||
|
||||
|
||||
async def start(api: "HonAPI", app: list[HonAppliance]) -> mqtt5.Client:
|
||||
client = await create_mqtt_client(api)
|
||||
global appliances # pylint: disable=global-statement
|
||||
appliances = app
|
||||
for appliance in appliances:
|
||||
subscribe(client, appliance)
|
||||
return client
|
|
@ -1,6 +1,8 @@
|
|||
AUTH_API = "https://account2.hon-smarthome.com"
|
||||
API_URL = "https://api-iot.he.services"
|
||||
API_KEY = "GRCqFhC6Gk@ikWXm1RmnSmX1cm,MxY-configuration"
|
||||
AWS_ENDPOINT = "a30f6tqw0oh1x0-ats.iot.eu-west-1.amazonaws.com"
|
||||
AWS_AUTHORIZER = "candy-iot-authorizer"
|
||||
APP = "hon"
|
||||
CLIENT_ID = (
|
||||
"3MVG9QDx8IX8nP5T2Ha8ofvlmjLZl5L_gvfbT9."
|
||||
|
|
|
@ -120,6 +120,7 @@ class Hon:
|
|||
api = TestAPI(test_data)
|
||||
for appliance in await api.load_appliances():
|
||||
await self._create_appliance(appliance, api)
|
||||
await self.api.subscribe_mqtt(self.appliances)
|
||||
|
||||
async def close(self) -> None:
|
||||
await self.api.close()
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
aiohttp>=3.8.6
|
||||
yarl>=1.8
|
||||
typing-extensions>=4.8
|
||||
awsiotsdk>=1.21.0
|
|
@ -3,3 +3,4 @@ flake8>=6.0
|
|||
mypy>=0.991
|
||||
pylint>=2.15
|
||||
setuptools>=62.3
|
||||
types-awscrt
|
7
setup.py
7
setup.py
|
@ -21,7 +21,12 @@ setup(
|
|||
packages=find_packages(),
|
||||
include_package_data=True,
|
||||
python_requires=">=3.10",
|
||||
install_requires=["aiohttp>=3.8.6", "typing-extensions>=4.8", "yarl>=1.8"],
|
||||
install_requires=[
|
||||
"aiohttp>=3.8.6",
|
||||
"typing-extensions>=4.8",
|
||||
"yarl>=1.8",
|
||||
"awsiotsdk>=1.21.0",
|
||||
],
|
||||
classifiers=[
|
||||
"Development Status :: 4 - Beta",
|
||||
"Environment :: Console",
|
||||
|
|
Loading…
Reference in New Issue