"""Ezviz cloud MQTT client for push messages.""" import os import logging logging.basicConfig( format='[%(asctime)s] %(levelname)s %(module)s/%(funcName)s - %(message)s', level=logging.DEBUG if os.environ.get('DEBUG') else logging.INFO) import base64 import json import threading import time import paho.mqtt.client as mqtt import requests from pyezviz.constants import DEFAULT_TIMEOUT, FEATURE_CODE from pyezviz.exceptions import HTTPError, InvalidURL, PyEzvizError API_ENDPOINT_SERVER_INFO = "/v3/configurations/system/info" API_ENDPOINT_REGISTER_MQTT = "/v1/getClientId" API_ENDPOINT_START_MQTT = "/api/push/start" API_ENDPOINT_STOP_MQTT = "/api/push/stop" MQTT_APP_KEY = "4c6b3cc2-b5eb-4813-a592-612c1374c1fe" APP_SECRET = "17454517-cc1c-42b3-a845-99b4a15dd3e6" def on_subscribe(client, userdata, mid, granted_qos): """On MQTT message subscribe.""" # pylint: disable=unused-argument logging.info("Subscribed: " + str(mid) + " " + str(granted_qos)) def on_connect(client, userdata, flags, return_code): """On MQTT connect.""" # pylint: disable=unused-argument if return_code == 0: logging.info("connected OK Returned code=%s", return_code) else: logging.info("Bad connection Returned code=%s", return_code) client.reconnect() #def on_message(client, userdata, msg): # """On MQTT message receive.""" # # pylint: disable=unused-argument # mqtt_message = json.loads(msg.payload) # mqtt_message["ext"] = mqtt_message["ext"].split(",") # # # Print payload message # decoded_message = {mqtt_message['ext'][2]:{'id':mqtt_message['id'], 'alert':mqtt_message['alert'], 'time':mqtt_message['ext'][1], 'alert type':mqtt_message['ext'][4], 'image':mqtt_message['ext'][16]}} # print(decoded_message) class MQTTClient(threading.Thread): """Open MQTT connection to ezviz cloud.""" def __init__( self, token, callback, timeout=DEFAULT_TIMEOUT, ): """Initialize the client object.""" threading.Thread.__init__(self) self._session = None self._token = token or { "session_id": None, "rf_session_id": None, "username": None, "api_url": "apiieu.ezvizlife.com", } self._callback = callback self._timeout = timeout self._stop_event = threading.Event() self._mqtt_data = { "mqtt_clientid": None, "ticket": None, "push_url": token["service_urls"]["pushAddr"], } def _mqtt(self): """Receive MQTT messages from ezviz server""" ezviz_mqtt_client = mqtt.Client( client_id=self._mqtt_data["mqtt_clientid"], protocol=4, transport="tcp" ) ezviz_mqtt_client.on_connect = on_connect ezviz_mqtt_client.on_subscribe = on_subscribe ezviz_mqtt_client.on_message = self._callback ezviz_mqtt_client.username_pw_set(MQTT_APP_KEY, APP_SECRET) ezviz_mqtt_client.connect(self._mqtt_data["push_url"], 1882, 60) ezviz_mqtt_client.subscribe( f"{MQTT_APP_KEY}/ticket/{self._mqtt_data['ticket']}", qos=2 ) ezviz_mqtt_client.loop_start() return ezviz_mqtt_client def _register_ezviz_push(self): """Register for push messages.""" auth_seq = base64.b64encode(f"{MQTT_APP_KEY}:{APP_SECRET}".encode("ascii")) auth_seq = "Basic " + auth_seq.decode() payload = { "appKey": MQTT_APP_KEY, "clientType": "5", "mac": FEATURE_CODE, "token": "123456", "version": "v1.3.0", } try: req = self._session.post( f"https://{self._mqtt_data['push_url']}{API_ENDPOINT_REGISTER_MQTT}", allow_redirects=False, headers={"Authorization": auth_seq}, data=payload, timeout=self._timeout, ) req.raise_for_status() except requests.ConnectionError as err: raise InvalidURL("A Invalid URL or Proxy error occured") from err except requests.HTTPError as err: raise HTTPError from err try: json_result = req.json() except ValueError as err: raise PyEzvizError( "Impossible to decode response: " + str(err) + "\nResponse was: " + str(req.text) ) from err self._mqtt_data["mqtt_clientid"] = json_result["data"]["clientId"] def run(self): """Method representing the thread's activity which should not be used directly.""" if self._session is None: self._session = requests.session() self._session.headers.update( {"User-Agent": "okhttp/3.12.1"} ) # Android generic user agent. self._register_ezviz_push() self._start_ezviz_push() self._mqtt() while not self._stop_event.is_set(): time.sleep(1) def start(self): """Start mqtt. Start mqtt thread """ super().start() def stop(self): """Stop push notifications.""" payload = { "appKey": MQTT_APP_KEY, "clientId": self._mqtt_data["mqtt_clientid"], "clientType": 5, "sessionId": self._token["session_id"], "username": self._token["username"], } try: req = self._session.post( f"https://{self._mqtt_data['push_url']}{API_ENDPOINT_STOP_MQTT}", data=payload, timeout=self._timeout, ) req.raise_for_status() except requests.ConnectionError as err: raise InvalidURL("A Invalid URL or Proxy error occured") from err except requests.HTTPError as err: raise HTTPError from err self._stop_event.set() def _start_ezviz_push(self): """Send start for push messages to ezviz api.""" payload = { "appKey": MQTT_APP_KEY, "clientId": self._mqtt_data["mqtt_clientid"], "clientType": 5, "sessionId": self._token["session_id"], "username": self._token["username"], "token": "123456", } try: req = self._session.post( f"https://{self._mqtt_data['push_url']}{API_ENDPOINT_START_MQTT}", allow_redirects=False, data=payload, timeout=self._timeout, ) req.raise_for_status() except requests.ConnectionError as err: raise InvalidURL("A Invalid URL or Proxy error occured") from err except requests.HTTPError as err: raise HTTPError from err try: json_result = req.json() except ValueError as err: raise PyEzvizError( "Impossible to decode response: " + str(err) + "\nResponse was: " + str(req.text) ) from err self._mqtt_data["ticket"] = json_result["ticket"]