MrWiwi

tydom2mqtt

Dec 11th, 2019
857
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 28.91 KB | None | 0 0
  1. #!/usr/bin/env python
  2. import asyncio
  3. import websockets
  4. import http.client
  5. from requests.auth import HTTPDigestAuth
  6. import sys
  7. import logging
  8. from http.client import HTTPResponse
  9. from io import BytesIO
  10. import urllib3
  11. import json
  12. import os
  13. import base64
  14. import time
  15. from http.server import BaseHTTPRequestHandler
  16. import ssl
  17. from datetime import datetime
  18. from gmqtt import Client as MQTTClient
  19.  
  20.  
  21. # Globals
  22. ####################################### MQTT
  23. tydom_topic = "homeassistant/+/tydom/#"
  24.  
  25. cover_config_topic = "homeassistant/cover/tydom/{id}/config"
  26. cover_config = "homeassistant/cover/tydom/{id}/config"
  27. cover_position_topic = "homeassistant/cover/tydom/{id}/current_position"
  28. cover_set_postion_topic = "homeassistant/cover/tydom/{id}/set_position"
  29. cover_attributes_topic = "homeassistant/cover/tydom/{id}/attributes"
  30.  
  31.  
  32. alarm_topic = "homeassistant/alarm_control_panel/tydom/#"
  33. alarm_config = "homeassistant/alarm_control_panel/tydom/{id}/config"
  34. alarm_state_topic = "homeassistant/alarm_control_panel/tydom/{id}/state"
  35. alarm_command_topic = "homeassistant/alarm_control_panel/tydom/{id}/set"
  36. alarm_sos_topic = "homeassistant/binary_sensor/tydom/{id}/sos"
  37. alarm_attributes_topic = "homeassistant/alarm_control_panel/tydom/{id}/attributes"
  38.  
  39. refresh_topic = "homeassistant/tydom/please_update"
  40.  
  41. mac = ""
  42. login = mac
  43. password = ""
  44. host = "" #"mediation.tydom.com" #"192.168.0.20" # Local ip address or mediation.tydom.com for remote connexion
  45.  
  46. mqtt_host = ''
  47. mqtt_user = ''
  48. mqtt_pass = ''
  49. mqtt_port = 8883
  50. mqtt_ssl = True
  51. #INIT Servers
  52. hassio = None
  53. tydom = None
  54.  
  55. # Set Host, ssl context and prefix for remote or local connection
  56. if host == "mediation.tydom.com":
  57. remote_mode = True
  58. ssl_context = None
  59. cmd_prefix = "\x02"
  60. else:
  61. remote_mode = False
  62. ssl_context = ssl._create_unverified_context()
  63. cmd_prefix = ""
  64.  
  65. deviceAlarmKeywords = ['alarmMode','alarmState','alarmSOS','zone1State','zone2State','zone3State','zone4State','zone5State','zone6State','zone7State','zone8State','gsmLevel','inactiveProduct','zone1State','liveCheckRunning','networkDefect','unitAutoProtect','unitBatteryDefect','unackedEvent','alarmTechnical','systAutoProtect','sysBatteryDefect','zsystSupervisionDefect','systOpenIssue','systTechnicalDefect','videoLinkDefect']
  66. # Device dict for parsing
  67. device_dict = dict()
  68.  
  69.  
  70. #MQTT
  71. STOP = asyncio.Event()
  72. def on_connect(client, flags, rc, properties):
  73. print("##################################")
  74.  
  75. print("Subscribing to : ", tydom_topic)
  76. # client.subscribe('homeassistant/#', qos=0)
  77. client.subscribe(tydom_topic, qos=0)
  78.  
  79. async def on_message(client, topic, payload, qos, properties):
  80. # print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
  81. # print('MQTT incoming : ', topic, payload)
  82. if (topic == "homeassistant/requests/tydom/update") or (payload == "please"):
  83. await get_data(tydom)
  84. if ('set_position' in str(topic)):
  85. print('MQTT set_position incoming : ', topic, payload)
  86. get_id = (topic.split("/"))[3] #extract id from mqtt
  87. # print(tydom, str(get_id), 'position', json.loads(payload))
  88. await put_devices_data(tydom, str(get_id), 'position', str(json.loads(payload)))
  89. else:
  90. return 0
  91.  
  92. async def on_disconnect(client, packet, exc=None):
  93. print('MQTT Disconnected')
  94. print("##################################")
  95. await main_task()
  96.  
  97. def on_subscribe(client, mid, qos):
  98. print("MQTT is connected and suscribed ! =)")
  99.  
  100.  
  101.  
  102. def ask_exit(*args):
  103. STOP.set()
  104.  
  105. async def mqttconnection(broker_host, user, password):
  106.  
  107. global hassio
  108. if (hassio == None):
  109. print('Attempting MQTT connection...')
  110. client = MQTTClient("client-id")
  111.  
  112. client.on_connect = on_connect
  113. client.on_message = on_message
  114. client.on_disconnect = on_disconnect
  115. client.on_subscribe = on_subscribe
  116.  
  117. client.set_auth_credentials(user, password)
  118. await client.connect(broker_host, port=mqtt_port, ssl=mqtt_ssl)
  119. hassio = client
  120.  
  121. # client.publish('TEST/TIME', str(time.time()), qos=1)
  122.  
  123. # await STOP.wait()
  124. # await client.disconnect()
  125.  
  126.  
  127. #######" END MQTT"
  128.  
  129. class Cover:
  130. def __init__(self, id, name, current_position=None, set_position=None, attributes=None):
  131.  
  132. self.id = id
  133. self.name = name
  134. self.current_position = current_position
  135. self.set_position = set_position
  136. self.attributes = attributes
  137.  
  138. def id(self):
  139. return self.id
  140.  
  141. def name(self):
  142. return self.name
  143.  
  144. def current_position(self):
  145. return self.current_position
  146.  
  147. def set_position(self):
  148. return self.set_position
  149.  
  150. def attributes(self):
  151. return self.attributes
  152.  
  153. # cover_config_topic = "homeassistant/cover/tydom/{id}/config"
  154. # cover_position_topic = "homeassistant/cover/tydom/{id}/current_position"
  155. # cover_set_postion_topic = "homeassistant/cover/tydom/{id}/set_position"
  156. # cover_attributes_topic = "homeassistant/cover/tydom/{id}/attributes"
  157.  
  158. def setup(self):
  159. self.device = {}
  160. self.device['manufacturer'] = 'Delta Dore'
  161. self.device['model'] = 'Volet'
  162. self.device['name'] = self.name
  163. self.device['identifiers'] = id=self.id
  164. self.config_topic = cover_config_topic.format(id=self.id)
  165. self.config = {}
  166. self.config['name'] = self.name
  167. self.config['unique_id'] = self.id
  168. # self.config['attributes'] = self.attributes
  169. self.config['command_topic'] = cover_set_postion_topic.format(id=self.id)
  170. self.config['set_position_topic'] = cover_set_postion_topic.format(id=self.id)
  171. self.config['position_topic'] = cover_position_topic.format(id=self.id)
  172. self.config['payload_open'] = 100
  173. self.config['payload_close'] = 0
  174. self.config['retain'] = 'true'
  175. self.config['device'] = self.device
  176.  
  177. # print(self.config)
  178. hassio.publish(self.config_topic, json.dumps(self.config), qos=0)
  179.  
  180. def update(self):
  181. self.setup()
  182. self.position_topic = cover_position_topic.format(id=self.id, current_position=self.current_position)
  183. hassio.publish(self.position_topic, self.current_position, qos=0, retain=True)
  184.  
  185. # self.attributes_topic = cover_attributes_topic.format(id=self.id, attributes=self.attributes)
  186. # hassio.publish(self.attributes_topic, self.attributes, qos=0)
  187.  
  188. class Alarm:
  189. def __init__(self, id, name, current_state=None, attributes=None):
  190. self.id = id
  191. self.name = name
  192. self.current_state = current_state
  193. self.attributes = attributes
  194.  
  195. # def id(self):
  196. # return id
  197.  
  198. # def name(self):
  199. # return name
  200.  
  201. # def current_state(self):
  202. # return current_state
  203.  
  204. # def attributes(self):
  205. # return attributes
  206.  
  207. def setup(self):
  208. self.device = {}
  209. self.device['manufacturer'] = 'Delta Dore'
  210. self.device['model'] = 'Tyxal'
  211. self.device['name'] = self.name
  212. self.device['identifiers'] = id=self.id
  213. self.config_alarm = alarm_config.format(id=self.id)
  214. self.config = {}
  215. self.config['name'] = self.name
  216. self.config['unique_id'] = self.id
  217. self.config['device'] = self.device
  218. # self.config['attributes'] = self.attributes
  219. self.config['command_topic'] = alarm_command_topic.format(id=self.id)
  220. self.config['state_topic'] = alarm_state_topic.format(id=self.id)
  221.  
  222.  
  223. # print(self.config)
  224. hassio.publish(self.config_alarm, json.dumps(self.config), qos=0)
  225.  
  226. def update(self):
  227. self.setup()
  228. self.state_topic = alarm_state_topic.format(id=self.id, state=self.current_state)
  229. hassio.publish(self.state_topic, self.current_state, qos=0, retain=True)
  230.  
  231. # self.attributes_topic = alarm_attributes_topic.format(id=self.id, attributes=self.attributes)
  232. # hassio.publish(self.attributes_topic, self.attributes, qos=0)
  233.  
  234.  
  235. # alarm_topic = "homeassistant/alarm_control_panel/tydom/#"
  236. # alarm_config = "homeassistant/alarm_control_panel/tydom/{id}/config"
  237. # alarm_state_topic = "homeassistant/alarm_control_panel/tydom/{id}/state"
  238. # alarm_sos_topic = "homeassistant/binary_sensor/tydom/{id}/sos"
  239. # alarm_attributes_topic = "homeassistant/alarm_control_panel/tydom/{id}/attributes"
  240.  
  241. class BytesIOSocket:
  242. def __init__(self, content):
  243. self.handle = BytesIO(content)
  244.  
  245. def makefile(self, mode):
  246. return self.handle
  247.  
  248. class HTTPRequest(BaseHTTPRequestHandler):
  249. def __init__(self, request_text):
  250. #self.rfile = StringIO(request_text)
  251. self.raw_requestline = request_text
  252. self.error_code = self.error_message = None
  253. self.parse_request()
  254.  
  255. def send_error(self, code, message):
  256. self.error_code = code
  257. self.error_message = message
  258.  
  259. def response_from_bytes(data):
  260. sock = BytesIOSocket(data)
  261. response = HTTPResponse(sock)
  262. response.begin()
  263. return urllib3.HTTPResponse.from_httplib(response)
  264.  
  265. def put_response_from_bytes(data):
  266. request = HTTPRequest(data)
  267. return request
  268.  
  269. # Get pretty name for a device id
  270. def get_name_from_id(id):
  271. name = ""
  272. if len(device_dict) != 0:
  273. name = device_dict[id]
  274. return(name)
  275.  
  276. # Basic response parsing. Typically GET responses
  277. async def parse_response(incoming):
  278. data = incoming
  279. msg_type = None
  280. first = str(data[:20])
  281.  
  282. # Detect type of incoming data
  283. if (data != ''):
  284. if ("id" in first):
  285. print('Incoming message type : data detected')
  286. msg_type = 'msg_data'
  287. elif ("date" in first):
  288. print('Incoming message type : config detected')
  289. msg_type = 'msg_config'
  290. elif ("doctype" in first):
  291. print('Incoming message type : html detected (probable pong)')
  292. msg_type = 'msg_html'
  293. print(data)
  294. else:
  295. print('Incoming message type : no type detected')
  296. print(first)
  297.  
  298.  
  299. if not (msg_type == None):
  300. try:
  301. parsed = json.loads(data)
  302. # print(parsed)
  303. if (msg_type == 'msg_config'):
  304. for i in parsed["endpoints"]:
  305. # Get list of shutter
  306. if i["last_usage"] == 'shutter':
  307. # print('{} {}'.format(i["id_endpoint"],i["name"]))
  308. device_dict[i["id_endpoint"]] = i["name"]
  309.  
  310. # TODO get other device type
  311. if i["last_usage"] == 'alarm':
  312. # print('{} {}'.format(i["id_endpoint"], i["name"]))
  313. device_dict[i["id_endpoint"]] = "Tyxal Alarm"
  314. print('Configuration updated')
  315. elif (msg_type == 'msg_data'):
  316. for i in parsed:
  317. attr = {}
  318. if i["endpoints"][0]["error"] == 0:
  319. for elem in i["endpoints"][0]["data"]:
  320. # Get full name of this id
  321. endpoint_id = i["endpoints"][0]["id"]
  322. # Element name
  323. elementName = elem["name"]
  324. # Element value
  325. elementValue = elem["value"]
  326.  
  327. # Get last known position (for shutter)
  328. if elementName == 'position':
  329. name_of_id = get_name_from_id(endpoint_id)
  330. if len(name_of_id) != 0:
  331. print_id = name_of_id
  332. else:
  333. print_id = endpoint_id
  334. # print('{} : {}'.format(print_id, elementValue))
  335. new_cover = "cover_tydom_"+str(endpoint_id)
  336. print("Cover created / updated : "+new_cover)
  337. new_cover = Cover(id=endpoint_id,name=print_id, current_position=elementValue, attributes=i)
  338. new_cover.update()
  339.  
  340. # Get last known position (for alarm)
  341. if elementName in deviceAlarmKeywords:
  342. alarm_data = '{} : {}'.format(elementName, elementValue)
  343. # print(alarm_data)
  344. # alarmMode : ON or ZONE or OFF
  345. # alarmState : ON = Triggered
  346. # alarmSOS : true = SOS triggered
  347. state = None
  348. sos = False
  349.  
  350. if alarm_data == "alarmMode : ON":
  351. state = "armed_away"
  352. elif alarm_data == "alarmMode : ZONE":
  353. state = "armed_home"
  354. elif alarm_data == "alarmMode : OFF":
  355. state = "disarmed"
  356. elif alarm_data == "alarmState : ON":
  357. state = "triggered"
  358. elif alarm_data == "alarmSOS : true":
  359. sos = True
  360. else:
  361. attr[elementName] = [elementValue]
  362. # attr[alarm_data]
  363. # print(attr)
  364. #device_dict[i["id_endpoint"]] = i["name"]
  365. if (sos == True):
  366. print("SOS !")
  367. if not (state == None):
  368. # print(state)
  369. alarm = "alarm_tydom_"+str(endpoint_id)
  370. print("Alarm created / updated : "+alarm)
  371. alarm = Alarm(id=endpoint_id,name="Tyxal Alarm", current_state=state, attributes=attr)
  372. alarm.update()
  373. elif (msg_type == 'msg_html'):
  374. print("pong")
  375. else:
  376. # Default json dump
  377. print()
  378. print(json.dumps(parsed, sort_keys=True, indent=4, separators=(',', ': ')))
  379. except Exception as e:
  380. print('Cannot parse response !')
  381. # print('Response :')
  382. # print(data)
  383. if (e != 'Expecting value: line 1 column 1 (char 0)'):
  384. print(e)
  385.  
  386.  
  387. # PUT response DIRTY parsing
  388. def parse_put_response(bytes_str):
  389. # TODO : Find a cooler way to parse nicely the PUT HTTP response
  390. resp = bytes_str[len(cmd_prefix):].decode("utf-8")
  391. fields = resp.split("\r\n")
  392. fields = fields[6:] # ignore the PUT / HTTP/1.1
  393. end_parsing = False
  394. i = 0
  395. output = str()
  396. while not end_parsing:
  397. field = fields[i]
  398. if len(field) == 0 or field == '0':
  399. end_parsing = True
  400. else:
  401. output += field
  402. i = i + 2
  403. parsed = json.loads(output)
  404. return json.dumps(parsed)
  405. # print(json.dumps(parsed, sort_keys=True, indent=4, separators=(',', ': ')))
  406.  
  407. # Generate 16 bytes random key for Sec-WebSocket-Keyand convert it to base64
  408. def generate_random_key():
  409. return base64.b64encode(os.urandom(16))
  410.  
  411. # Build the headers of Digest Authentication
  412. def build_digest_headers(nonce):
  413. digestAuth = HTTPDigestAuth(login, password)
  414. chal = dict()
  415. chal["nonce"] = nonce[2].split('=', 1)[1].split('"')[1]
  416. chal["realm"] = "ServiceMedia" if remote_mode is True else "protected area"
  417. chal["qop"] = "auth"
  418. digestAuth._thread_local.chal = chal
  419. digestAuth._thread_local.last_nonce = nonce
  420. digestAuth._thread_local.nonce_count = 1
  421. return digestAuth.build_digest_header('GET', "https://{}:443/mediation/client?mac={}&appli=1".format(host, mac))
  422.  
  423. # Send Generic GET message
  424. async def send_message(websocket, msg):
  425. str = cmd_prefix + "GET " + msg +" HTTP/1.1\r\nContent-Length: 0\r\nContent-Type: application/json; charset=UTF-8\r\nTransac-Id: 0\r\n\r\n"
  426. a_bytes = bytes(str, "ascii")
  427. await websocket.send(a_bytes)
  428. return 0
  429. # return await websocket.recv() #disable if handler
  430.  
  431. # Send Generic POST message
  432. async def send_post_message(websocket, msg):
  433. str = cmd_prefix + "POST " + msg +" HTTP/1.1\r\nContent-Length: 0\r\nContent-Type: application/json; charset=UTF-8\r\nTransac-Id: 0\r\n\r\n"
  434. a_bytes = bytes(str, "ascii")
  435. await websocket.send(a_bytes)
  436. return 0
  437. # return await websocket.recv()
  438.  
  439.  
  440. ###############################################################
  441. # Commands #
  442. ###############################################################
  443.  
  444. # Get some information on Tydom
  445. async def get_info(websocket):
  446. msg_type = '/info'
  447. parse_response(await send_message(websocket, msg_type), msg_type)
  448.  
  449. # Refresh (all)
  450. async def post_refresh(websocket):
  451. print("Refresh....")
  452. msg_type = '/refresh/all'
  453. await send_post_message(websocket, msg_type)
  454.  
  455. # Get the moments (programs)
  456. async def get_moments(websocket):
  457. msg_type = '/moments/file'
  458. await send_message(websocket, msg_type)
  459.  
  460. # Get the scenarios
  461. async def get_scenarios(websocket):
  462. msg_type = '/scenarios/file'
  463. await send_message(websocket, msg_type)
  464.  
  465. # Get a ping (pong should be returned)
  466. async def get_ping(websocket):
  467. msg_type = 'ping'
  468. await send_message(websocket, msg_type)
  469.  
  470. # Get all devices metadata
  471. async def get_devices_meta(websocket):
  472. msg_type = '/devices/meta'
  473. parse_response(await send_message(websocket, msg_type), msg_type)
  474.  
  475. # Get all devices data
  476. async def get_devices_data(websocket):
  477. msg_type = '/devices/data'
  478. await send_message(websocket, msg_type)
  479.  
  480. # List the device to get the endpoint id
  481. async def get_configs_file(websocket):
  482. msg_type = '/configs/file'
  483. await send_message(websocket, msg_type)
  484.  
  485.  
  486. async def get_data(websocket):
  487. await get_configs_file(websocket)
  488. await asyncio.sleep(2)
  489. await get_devices_data(websocket)
  490.  
  491. # Give order (name + value) to endpoint
  492. async def put_devices_data(websocket, endpoint_id, name, value):
  493. # For shutter, value is the percentage of closing
  494. body="[{\"name\":\"" + name + "\",\"value\":\""+ value + "\"}]"
  495. # endpoint_id is the endpoint = the device (shutter in this case) to open.
  496. str_request = cmd_prefix + "PUT /devices/{}/endpoints/{}/data HTTP/1.1\r\nContent-Length: ".format(str(endpoint_id),str(endpoint_id))+str(len(body))+"\r\nContent-Type: application/json; charset=UTF-8\r\nTransac-Id: 0\r\n\r\n"+body+"\r\n\r\n"
  497. a_bytes = bytes(str_request, "ascii")
  498. await websocket.send(a_bytes)
  499. # name = await websocket.recv()
  500. # parse_response(name)
  501. # name = await websocket.recv()
  502. # try:
  503. # parse_response(name)
  504. # except:
  505. # parse_put_response(name)
  506.  
  507. # Run scenario
  508. async def put_scenarios(websocket, scenario_id):
  509. body=""
  510. # scenario_id is the id of scenario got from the get_scenarios command
  511. str_request = cmd_prefix + "PUT /scenarios/{} HTTP/1.1\r\nContent-Length: ".format(str(scenario_id))+str(len(body))+"\r\nContent-Type: application/json; charset=UTF-8\r\nTransac-Id: 0\r\n\r\n"+body+"\r\n\r\n"
  512. a_bytes = bytes(str_request, "ascii")
  513. await websocket.send(a_bytes)
  514. name = await websocket.recv()
  515. parse_response(name)
  516.  
  517. # Give order to endpoint
  518. async def get_device_data(websocket, id):
  519. # 10 here is the endpoint = the device (shutter in this case) to open.
  520. str_request = cmd_prefix + "GET /devices/{}/endpoints/{}/data HTTP/1.1\r\nContent-Length: 0\r\nContent-Type: application/json; charset=UTF-8\r\nTransac-Id: 0\r\n\r\n".format(str(id),str(id))
  521. a_bytes = bytes(str_request, "ascii")
  522. await websocket.send(a_bytes)
  523. # name = await websocket.recv()
  524. # parse_response(name)
  525.  
  526. ######## Messages Logic
  527.  
  528. async def consumer(websocket):
  529. # Receiver
  530. while True:
  531. bytes_str = await websocket.recv()
  532. first = str(bytes_str[:20]) # Scanning 1st characters
  533.  
  534. if ("PUT" in first):
  535. print('PUT message detected !')
  536.  
  537. # print('RAW INCOMING :')
  538. # print(bytes_str)
  539. # print('END RAW')
  540.  
  541. try:
  542. incoming = parse_put_response(bytes_str)
  543. # print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
  544. await parse_response(incoming)
  545. print('PUT message processed !')
  546. print("##################################")
  547. except:
  548. print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
  549. print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
  550. print(incoming)
  551. print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
  552. print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
  553.  
  554. elif ("POST" in first):
  555.  
  556. # print('RAW INCOMING :')
  557. # print(bytes_str)
  558. # print('END RAW')
  559.  
  560. try:
  561. # print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
  562. incoming = parse_put_response(bytes_str)
  563. await parse_response(incoming)
  564. print('POST message processed !')
  565. print("##################################")
  566. except:
  567. print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
  568. print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
  569. print('RAW INCOMING :')
  570. print(bytes_str)
  571. print('END RAW')
  572.  
  573. print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
  574. print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
  575.  
  576. elif ("HTTP/1.1" in first): #(bytes_str != 0) and
  577. response = response_from_bytes(bytes_str[len(cmd_prefix):])
  578. incoming = response.data.decode("utf-8")
  579. # print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
  580. # print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
  581. # print(incoming)
  582. # print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
  583. # print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
  584. hassio.publish('homeassistant/sensor/tydom/last_update', str(datetime.fromtimestamp(time.time())), qos=1)
  585. try:
  586. # print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
  587. await parse_response(incoming)
  588. print('GET response message processed !')
  589. print("##################################")
  590. except:
  591. print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
  592. print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
  593. print(incoming)
  594. print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
  595. print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
  596. # await parse_put_response(incoming)
  597. else:
  598. print(bytes_str)
  599.  
  600. async def producer(websocket):
  601. # while True:
  602. await asyncio.sleep(48)
  603. # await get_ping(websocket)
  604. await get_data(tydom)
  605. print("Websocket refreshed at ", str(datetime.fromtimestamp(time.time())))
  606.  
  607.  
  608. async def consumer_handler(websocket):
  609. while True:
  610. try:
  611. await consumer(websocket)
  612. except Exception as e:
  613. print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
  614. print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
  615. print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
  616. print('Consumer task has crashed !')
  617. print(e)
  618. print('Restarting..............')
  619. await main_task()
  620.  
  621. async def producer_handler(websocket):
  622. while True:
  623. await producer(websocket)
  624.  
  625. ######## HANDLER
  626. async def handler(websocket):
  627. try:
  628. # print("Starting handlers...")
  629. consumer_task = asyncio.ensure_future(
  630. consumer_handler(websocket))
  631. producer_task = asyncio.ensure_future(
  632. producer_handler(websocket))
  633. done, pending = await asyncio.wait(
  634. [consumer_task, producer_task],
  635. return_when=asyncio.FIRST_COMPLETED,
  636. )
  637. for task in pending:
  638. task.cancel()
  639.  
  640. except Exception as e:
  641. print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
  642. print(e)
  643. print('Handler crashed.')
  644.  
  645.  
  646. async def websocket_connection():
  647. global tydom
  648. # logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
  649. httpHeaders = {"Connection": "Upgrade",
  650. "Upgrade": "websocket",
  651. "Host": host + ":443",
  652. "Accept": "*/*",
  653. "Sec-WebSocket-Key": generate_random_key(),
  654. "Sec-WebSocket-Version": "13"
  655. }
  656. # http.client.HTTPSConnection.debuglevel = 1
  657. # http.client.HTTPConnection.debuglevel = 1
  658. # Create HTTPS connection on tydom server
  659. conn = http.client.HTTPSConnection(host, 443, context=ssl_context)
  660. # Get first handshake
  661. conn.request("GET", "/mediation/client?mac={}&appli=1".format(mac), None, httpHeaders)
  662. res = conn.getresponse()
  663. # Get authentication
  664. nonce = res.headers["WWW-Authenticate"].split(',', 3)
  665. # read response
  666. res.read()
  667. # Close HTTPS Connection
  668. conn.close()
  669. # Build websocket headers
  670. websocketHeaders = {'Authorization': build_digest_headers(nonce)}
  671. if ssl_context is not None:
  672. websocket_ssl_context = ssl_context
  673. else:
  674. websocket_ssl_context = True # Verify certificate
  675.  
  676. print('"Attempting websocket connection..."')
  677. ########## CONNECTION
  678. # websocket = await websockets.client.connect('wss://{}:443/mediation/client?mac={}&appli=1'.format(host, mac),
  679. # extra_headers=websocketHeaders, ssl=websocket_ssl_context)
  680.  
  681. async with websockets.client.connect('wss://{}:443/mediation/client?mac={}&appli=1'.format(host, mac),
  682. extra_headers=websocketHeaders, ssl=websocket_ssl_context) as websocket:
  683.  
  684.  
  685. tydom = websocket
  686. print("Tydom Websocket is Connected !", tydom)
  687.  
  688. await mqttconnection(mqtt_host, mqtt_user, mqtt_pass)
  689. print("##################################")
  690. print('Requesting 1st data...')
  691. await get_data(tydom)
  692. while True:
  693. # await consumer(tydom) #Only receiving from socket in real time
  694. await handler(tydom) #If you want to send periodically something, disable consumer
  695.  
  696. # Main async task
  697. async def main_task():
  698. print(str(datetime.fromtimestamp(time.time())))
  699. try:
  700. if (tydom == None) or not tydom.open:
  701. print("##################################")
  702. start = time.time()
  703. await websocket_connection()
  704. print('Connection total time :')
  705. end = time.time()
  706. print(end - start)
  707.  
  708. else:
  709. print('Websocket is still opened ! requesting data...')
  710. await get_data(tydom)
  711. except Exception as e:
  712. print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
  713. print('Connection total time :')
  714. end = time.time()
  715. print(end - start)
  716. print(str(datetime.fromtimestamp(time.time())))
  717. print(e)
  718. print('Something bad happened, reconnecting...')
  719. # await asyncio.sleep(8)
  720. await main_task()
  721.  
  722.  
  723. if __name__ == '__main__':
  724. loop = asyncio.get_event_loop()
  725.  
  726. loop.run_until_complete(main_task())
  727. loop.run_forever()
  728.  
  729.  
  730.  
  731. # Get informations (not very useful)
  732. # await get_info(websocket)
  733.  
  734. # Get all moments stored on Tydom
  735. # await get_moments(websocket)
  736.  
  737. # Get scenarios ids
  738. # print("Get scenarii")
  739. # await get_scenarios(websocket)
  740.  
  741. # Run scenario with scn id returned in previous command
  742. # await put_scenarios(websocket, 15)
  743. # print("Get names of all devices")
  744. # await get_configs_file(websocket)
  745. # # await get_devices_meta(websocket)
  746.  
  747. # print("Get data of all devices")
  748. # await get_devices_data(websocket)
  749.  
  750. # Get data of a specific device
  751. #await get_device_data(websocket, 9)
  752.  
  753. # Set a shutter position to 10%
  754. #await put_devices_data(websocket, 9, "position", "10.0")
  755. # TODO : Wait hardcoded for now to put response from websocket server
  756. #time.sleep(45)
Add Comment
Please, Sign In to add comment