cth35 / tydom_python

Example of Python Code to manage Tydom (Delta Dore) devices - Need Tydom Gateway
Apache License 2.0
31 stars 5 forks source link

Answer = 401 (Unauthorized) #13

Open zian31 opened 1 year ago

zian31 commented 1 year ago

Hi. I've tried to use the "largotef" fork (without the MQTT part) of this program with :

  1. Raspberry with latest Raspberry Pi OS with desktop Release date: May 3rd 2023 System: 32-bit Kernel version: 6.1 Debian version: 11 (bullseye)

  2. Tydom 1.0 I achieved to configure Tydom 1.0 password with an android Tydom APK V3

  3. Python version is 3.9.6

My test is : python main.py

The result is always 401 (Unauthorized) :

send: b'GET /mediation/client?mac={my_mac}&appli=1 HTTP/1.1\r\nAccept-Encoding: identity\r\nConnection: Upgrade\r\nUpgrade: websocket\r\nHost: {my_ip}:443\r\nAccept: /\r\nSec-WebSocket-Key: {websocket_key}==\r\nSec-WebSocket-Version: 13\r\n\r\n' reply: 'HTTP/1.1 401 Unauthorized\r\n' header: Connection: close etc...

I've seen on domotics forums (jeedom...) that people have now exactly the same result 401 (Unauthorized) using this program.

But if I use a simple HTML5 websocket code on Chrome (on the Raspberry or on a PC Windows), the websocket connection is working well (but the sending message is not working : disconnection just after) :

index.html :

<!DOCTYPE HTML>
<html>
<head>
<script type = "text/javascript">
function WebSocketTest() {
 if ("WebSocket" in window) {
 alert("WebSocket is supported by your Browser!");
 // Open a web socket
 var ws = new WebSocket("wss://{my_mac}:{my_pass}@IP:443/mediation/client?mac={my_mac}&appli=1");
   ws.onopen = function() {
   alert("WebSocket oppened");
   // Web Socket is connected, send data using send()
   ws.send("GET /info HTTP/1.1\r\nContent-Length: 0\r\nContent-Type: application/json; charset=UTF-8\r\nTransac-Id: 0\r\n\r\n");
   alert("Message is sent...");
   };
   ws.onmessage = function (evt) { 
   var received_msg = evt.data;
   alert("Message is received...");
   };
   ws.onclose = function() { 
   // websocket is closed.
   alert("Connection is closed..."); 
   };
   } else { alert("WebSocket NOT supported by your Browser!"); }
}
</script>
</head>
<body>
<div id = "sse">
<a href = "javascript:WebSocketTest()">Run WebSocket</a>
</div>     
</body>
</html>

So here are my 2 questions :

A. Do you think the 401 answer with your software is due to a new security protection with Debian 11 (bullseye) or python 3.9.6 ? B. With my little index.html, do you know the right data format in ws.send(data) to send a simple tydom "GET /info" ?

Thanks !

cth35 commented 1 year ago

Hello, for response B, as I can remember, for remote request, you need to add a prefix (cmd_prefix = "\x02") just before the method (GET in your case). So something like this : ws.send("\x02GET /info ...") I didn't test my prog since a while so I cannot respond to question A. May be they have changed something on server side ?

SigmaPic commented 1 year ago

Look at my fork : https://github.com/SigmaPic/python_tydom I recently fixed something. I don't remember if it was linked to error 401. Either try my fork or apply the same fix to this code.

zian31 commented 1 year ago

Thanks for your answers. I can now answer :

for B, it's not possible to use simple Javascript for Websockets if ssl is used => Node.JS is needed. Prefix x02 is needed if IP is not local.

for A, SigmaPic, I've tried yesterday your code https://github.com/SigmaPic/python_tydom and it's working well so it was not a security protection with Debian 11 (bullseye) or python 3.9.6 Your program is the only one working well, all others python tydom on GitHub are answering with 401 at connection. I've tried to understand where is the difference, but didn't find it for the moment. In my case, it's perfect, and I've added PUT functions with your cth35 code.

So Thanks a lot for your 2 jobs ! My Tydom is now under control.

MichelRabozee commented 1 year ago

Hi, thank you SigmaPic, cth35 and zian31 !

@zian31: you said you add the PUT functions to SigmaPic code, is it possible to share your modifications ?

Thank you very much !

zian31 commented 1 year ago

@MichelRabozee yes, of course, you have to add this code to tydom.py :

async def _send_message_put(self, method, msg, body):
    txt = self.cmd_prefix + method +' ' + msg +" HTTP/1.1\r\nContent-Length: "+ str(len(body)) +"\r\nContent-Type: application/json; charset=UTF-8\r\nTransac-Id: 0\r\n\r\n"+body+"\r\n\r\n"
    a_bytes = bytes(txt, "ascii")
    if not 'pwd' in msg:
        print('>>>>>> Send Request:', method, msg)
    else:
        print('>>>>>> Send Request:', method, 'secret msg')

    await self.connection.send(a_bytes)

    # get response from response queue
    return await self._response_queue.get()

And :

# PUT tydom device data
async def put_device_data(self, id_dev, id_ep, name, value):
    async with self.lock:
        msg_type = '/devices/{}/endpoints/{}/data'.format(id_dev, id_ep)
        req = 'PUT'
        body_type = "[{\"name\":\"" + name + "\",\"value\":\""+ value + "\"}]"
        return await self._send_message_put(method=req, msg=msg_type, body=body_type)

And then (example of UP, wait 2s, STOP) :

    data = await tydom.put_device_data(ID,ID,"positionCmd", "UP")
    print(data)
    await asyncio.sleep(2)
    data = await tydom.put_device_data(ID,ID,"positionCmd", "STOP")
    print(data)

You will find ID with :

    data = await tydom.get_devices_meta()

With that I can open and close my shutters... You also can know the positions with :

    data = await tydom.get_device_data(ID,ID)

{'name': 'position', 'validity': 'upToDate', 'value': 0} => Volet en haut (butée atteinte) {'name': 'position', 'validity': 'upToDate', 'value': 100} => Volet en bas (butée atteinte) {'name': 'position', 'validity': 'upToDate', 'value': None} => Volet en dehors des butées haute ou basse

MichelRabozee commented 1 year ago

@zian31 , thank you very much !

MichelRabozee commented 1 year ago

Hello, I was happy too soon, even with SigmaPic's code, I end up with error 401 :-(

./tydom-test.py 
Connecting to tydom 192.168.1.30
Traceback (most recent call last):
  File "/Users/mrabozee/sources/Tydom/./tydom-test.py", line 21, in <module>
    asyncio.get_event_loop().run_until_complete(demo())
  File "/usr/local/Cellar/python@3.9/3.9.14/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/base_events.py", line 647, in run_until_complete
    return future.result()
  File "/Users/mrabozee/sources/Tydom/./tydom-test.py", line 13, in demo
    await tydom.connect(keep_alive_delay=None)
  File "/Users/mrabozee/sources/Tydom/tydom.py", line 272, in connect
    self.connection = await websockets.client.connect('wss://{}:443/mediation/client?mac={}&appli=1'.format(self.host, self.mac),
  File "/usr/local/lib/python3.9/site-packages/websockets/client.py", line 542, in __await_impl__
    await protocol.handshake(
  File "/usr/local/lib/python3.9/site-packages/websockets/client.py", line 296, in handshake
    raise InvalidStatusCode(status_code)
websockets.exceptions.InvalidStatusCode: server rejected WebSocket connection: HTTP 401

Here is the test code I use (xxxxx is my password):

#!/usr/bin/env python3
from tydom import Tydom
import asyncio

def  callback(uri, data):
    print(uri, data)

async def demo():
    # create tydom instance
    tydom = Tydom("001A25068836", "xxxxx", host='192.168.1.30', request_handler=callback)

    # connect to tydom
    await tydom.connect(keep_alive_delay=None)

    # get data
    while True:
        data = await tydom.get_info()
        print(data)
        await asyncio.sleep(5)

asyncio.get_event_loop().run_until_complete(demo())

What do I do wrong ?

zian31 commented 1 year ago

Maybe a bad password : With App V4, deltadore is auto-generating a password for the Tydom 1.0, it's possible to find it, but not obvious. With App V3, you can reset and redefine the password of Tydom 1.0 So I used App V3 on android.

MichelRabozee commented 1 year ago

Thank you, I use an iPhone, but I have an Android only for these kind of tricks \o/ I installed V3 ( https://tydom.fr.aptoide.com/app?store_name=aptoide-web&app_id=58618221 ) and reset the password of the box itself !!!