openhab / openhab1-addons

Add-ons for openHAB 1.x
Eclipse Public License 2.0
3.43k stars 1.7k forks source link

SamsungAC: ssl handshake error #2863

Closed ronny332 closed 7 years ago

ronny332 commented 9 years ago

Are there any known issue for the SamsungAC binding for newer models? In our house we have 3 Samsung devices working, 2 devices are from 2013 and the newest one was introduced in late 2014.

Both devices of 2013 are working (Samsung Flagship Jungfrau), but the newest one does not connect. Openhab is reporting

15:18:16.666 [DEBUG] [.b.s.internal.SamsungAcBinding:267  ] - java.lang.Exception: Cannot connect to 10.0.0.125:2878 : javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

A short curl test shows the same result. I can connect to both older devices, but not to the newest one.

A short wireshark debug recording is showing no "big" data frames anymore. Is it possible samsung has left the XML communication between the remote app and the device?

marko3dana commented 7 years ago

@pipka76 any news regarding Samsung API ? I'm in the same situation as you...

shmrymbd commented 7 years ago

@pipka76 could you share how to sniff samsung washer with wifi?

miklosandras commented 7 years ago

I have the same issue, my AC is: Samsung Better AR12KSPDBWKN/XEU

Here is the log from the openhab.log: 2017-05-19 00:42:20.138 [DEBUG] [.samsungac.internal.SamsungAcBinding] - Broken connection found for 'Livingroom', attempting to reconnect... 2017-05-19 00:42:20.139 [DEBUG] [ng.samsungac.internal.AirConditioner] - Disconnected so we'll try again 2017-05-19 00:42:20.140 [DEBUG] [ng.samsungac.internal.AirConditioner] - Disconnected from AC: 192.168.1.36 2017-05-19 00:42:22.487 [DEBUG] [ng.samsungac.internal.AirConditioner] - Nothing more to read from AC 2017-05-19 00:42:22.488 [DEBUG] [ng.samsungac.internal.AirConditioner] - Token has been acquired: '' 2017-05-19 00:42:22.489 [DEBUG] [ng.samsungac.internal.AirConditioner] - Sending request:'<Request Type="AuthToken"><User Token="" /></Request>' 2017-05-19 00:42:22.665 [DEBUG] [ng.samsungac.internal.AirConditioner] - Got response:'<html>' 2017-05-19 00:42:22.668 [DEBUG] [ng.samsungac.internal.AirConditioner] - Sending request:'<Request Type="DeviceState" DUID="C0972729EB73"></Request>' 2017-05-19 00:42:22.669 [DEBUG] [ng.samsungac.internal.AirConditioner] - Could not write line. Disconnecting. java.net.SocketException: Connection closed by remote host at sun.security.ssl.SSLSocketImpl.checkWrite(SSLSocketImpl.java:1555)[:1.8.0_65] at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:71)[:1.8.0_65] at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)[:1.8.0_65] at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291)[:1.8.0_65] at sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:295)[:1.8.0_65] at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:141)[:1.8.0_65] at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:229)[:1.8.0_65] at java.io.BufferedWriter.flush(BufferedWriter.java:254)[:1.8.0_65] at org.openhab.binding.samsungac.internal.AirConditioner.writeLine(AirConditioner.java:245)[205:org.openhab.binding.samsungac:1.10.0.201705140110] at org.openhab.binding.samsungac.internal.AirConditioner.getStatus(AirConditioner.java:346)[205:org.openhab.binding.samsungac:1.10.0.201705140110] at org.openhab.binding.samsungac.internal.AirConditioner.loginWithToken(AirConditioner.java:122)[205:org.openhab.binding.samsungac:1.10.0.201705140110] at org.openhab.binding.samsungac.internal.AirConditioner.login(AirConditioner.java:78)[205:org.openhab.binding.samsungac:1.10.0.201705140110] at org.openhab.binding.samsungac.internal.SamsungAcBinding.reconnectToAirConditioner(SamsungAcBinding.java:297)[205:org.openhab.binding.samsungac:1.10.0.201705140110] at org.openhab.binding.samsungac.internal.SamsungAcBinding.execute(SamsungAcBinding.java:289)[205:org.openhab.binding.samsungac:1.10.0.201705140110] at org.openhab.core.binding.AbstractActiveBinding$BindingActiveService.execute(AbstractActiveBinding.java:157)[208:org.openhab.core.compat1x:2.1.0.201705151816] at org.openhab.core.service.AbstractActiveService$RefreshThread.run(AbstractActiveService.java:169)[208:org.openhab.core.compat1x:2.1.0.201705151816] 2017-05-19 00:42:22.676 [DEBUG] [ng.samsungac.internal.AirConditioner] - Disconnected from AC: 192.168.1.36 2017-05-19 00:42:22.677 [DEBUG] [ng.samsungac.internal.AirConditioner] - Disconnecting... with exception: java.lang.Exception: Could not update status for air conditioner with IP: 192.168.1.36 2017-05-19 00:42:22.678 [DEBUG] [ng.samsungac.internal.AirConditioner] - Disconnected from AC: 192.168.1.36 2017-05-19 00:42:22.679 [DEBUG] [.samsungac.internal.SamsungAcBinding] - Caught exception: java.lang.Exception: Could not update status for air conditioner with IP: 192.168.1.36 : java.net.SocketException: Connection closed by remote host 2017-05-19 00:42:22.680 [DEBUG] [.samsungac.internal.SamsungAcBinding] - Reconnect failed for 'Livingroom', will retry in 60s

And this is the response from the command line: root@raspberrypi:/etc/openhab2/services# curl -XPOST -H "Content-Type: text/xml" -d "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Request Type=\"GetToken\" />" --cert cert.pem "https://192.168.1.36:8888/capability" --insecure {"errorCode":"0","errorDescription":"Token is not valid"} root@raspberrypi:/etc/openhab2/services#

Anyone can help? Thank you in advance. @marko3dana @pipka76 ?

skavan commented 7 years ago

289555329-Smart-Home-SDK-Development-Guide-v1-0-Beta.pdf See attached file...that I wandered into...Page 82 looks very interesting... Hopefully this will kick start work on developing a process that will work with their latest api and port 8888.

Seems to me, we need to deal with the certificate problem...but @pipka76 seems to have got past this hurdle. If he could dump the commands he has found that would also be a good leap forward.

I think , once the certificates are sorted, we need to do a: GET: https://192.168.1.XX:8888/devicetoken?UUID=ABCDEFHG-1234-0000-0000-000000000000 and hopefully, we would get back a valid devicetoken. (Of course, one must do this within 30 seconds of turning the unit on).

From there, we should be off to the races... As luck would have it, I am remote from my AC Units and can't test the above right now.... But hopefully someone out here can! If we could get hold of the simulator program referenced in the document, we would be really flying. Fingers Crossed.

miklosandras commented 7 years ago

@mapilarc Did you find out what is the password for this: "Enter key password for " or @netsuso would you please share it, if you find out that? Thank you in advance.

bioego commented 7 years ago

What about trying with the old certificate (full procedure on @RemcoteWinkel comment of March 5 2016)?

During my last try about 1 year ago using curl and that certificate I was getting a response from the AC albeit only to tell me about the invalid token, but I think that if the certificate wasn't valid anymore I wouldn't even get that, would I?

For the record this is what I did & obtain:

curl -k --cert cert.pem https://192.168.1.201:8888/capability {"errorCode":"0","errorDescription":"Token is not valid"}

Where cert.pem is the old certificate.

Unfortunately I can't test at the moment, but maybe some of you can try with @skavan procedure and see what happens. I'll try as soon as possible but might require me a few days still.

skavan commented 7 years ago

OK - to follow up on my post above...the Page 82 process i referred to was a red herring. The UUID is not the UUID of the device - but rather a UUID of the controller, that is presumably supplied by samsung cloud services. The interesting page is actually Page 83 - which claims that it only works with TV (as of 2014) -- but I think it may be the entry way into our new firmware AC's.

If I issue a POST /devicetoken/request (SSL on port 8888), I actually get an "OK" back. I presume this means that my certificate is working just fine.

I then power off and power on the AC. The docs say, the Controlled Device will then send Device Token - but I can't figure out where it is sending it/how to capture it. If I then send another request to the AC unit, it gives me a message something along the lines of "it is still processing the previous request". I think there is something in this. It will take someone more savvy than I to study page 83 and figure out how to capture the POST that is sent by the AC unit.

skavan commented 7 years ago

On the subject of certificates...all I did was take the Pastebin file referenced by @RemcoteWinkel in March 2016, and plug it into https://www.base64decode.org/ - then I took that resultant (decoded) PEM file and went to https://www.sslshopper.com/ssl-converter.html and chose

Lasering commented 7 years ago

In page 83 there is POST /devicetoken/request and POST /devicetoken/response. In the POST request the host header is supposed to have an IPv4 value, and the devicetoken response seems to be performed from the controlled device to the controller device. Which seems to suggest we need to have a webserver listening in our PC.

skavan commented 7 years ago

Yep...my thoughts exactly, unless it's some server side events thing. In any event, we need to somehow figure out what Port that thing lives on...

miklosandras commented 7 years ago

Do we have newer documentation? this is from 2014 october. Mine AC is quite new version (2016), and may the protocol changed. I hope that we can get it working. :S

skavan commented 7 years ago

this doc is a beta, from late 2014. I have scoured and found nothing newer. Post Samsungs acquisition of SmartThings all dev seems to have switched to the cloud based protocol...so I suspect this doc represents the best clues we are going to get...if there is a way of local control...page 83 is the key (pun not intended).

s.

mapilarc commented 7 years ago

Okey guys, I think I'm a step further but I'd need some support. Basically, I followed the dev instruction from the PDF. It says that we are doing device token request with a POST. The step works for me. That's actually a one liner in python:

resp = s.post("https://192.168.1.232:8888/devicetoken/request", data=payload, headers=headers, stream=True, verify=False, cert='ac/cert.pem')

Payload -> not sure if I need it at all Headers -> {'content-type': 'text/xml'}

Im getting HTTP 200.

Then the PDF says we should expect a response after activating the AC. What? Where? How? I've sniffed my network wireshark. The AC tries to response!!! I can see flying packages. They are sent to the host of the previous request but on port 8889!

So the next step. I've built a simple HTTPS server:

from http.server import HTTPServer, BaseHTTPRequestHandler
import ssl

class RequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        request_path = self.path

        print("\n----- Request Start ----->\n")
        print("Request path:", request_path)
        print("Request headers:", self.headers)
        print("<----- Request End -----\n")

        self.send_response(200)
        self.send_header("Set-Cookie", "foo=bar")
        self.end_headers()

    def do_POST(self):
        request_path = self.path

        print("\n----- Request Start ----->\n")
        print("Request path:", request_path)

        request_headers = self.headers
        content_length = request_headers.get('Content-Length')
        length = int(content_length) if content_length else 0
        #body = self.body

        print("Content Length:", length)
        print("Request headers:", request_headers)
        print("Request payload:", self.rfile.read(length))
        print("Body:", self)
        print("<----- Request End -----\n")

        self.send_response(200)
        self.end_headers()

    do_PUT = do_POST
    do_DELETE = do_GET

def main():
    port = 8889
    print('Listening on localhost:%s' % port)
    server = HTTPServer(('', port), RequestHandler)
    server.socket = ssl.wrap_socket(server.socket, certfile='ac/cert.pem', server_side=True)
    server.serve_forever()

if __name__ == "__main__":
    main()

And I've got it. I mean the AC is doing a POST requests:

Listening on localhost:8889

<----- Request Start ----->

Request path: /devicetoken/response
Content Length: 0
Request headers: Host: 192.168.1.240:8889
Accept: */*

X-API-Version : v1.0.0
Content-Type: application/json
Content-Length: 28

Request payload: b''
Body: <__main__.RequestHandler object at 0x10eb1e390>
<----- Request End ----->

I can see that there is something in the requests, the 28 bytes. I hoped to see it with print("Request payload:", self.rfile.read(length)) but the content is empty. Why? How? Any ideas? Somehow Content Length is doubled, 0 and 28 bytes. Why?

mapilarc commented 7 years ago

Answering own posts is not very polite but after a small fix:

Request payload: b'{"DeviceToken":"xxxxxxxxxxx"}'

So... that's the method. Enjoy further integration.

skavan commented 7 years ago

genius! will test this on my units soon...so the next question is armed with the token, can you command and query status? Would be awesome to get a code snipp that: a) Turns the thing on and off b) sets temp c) queries status. and for extreme points, does the server receive events from the AC when someone manually changes a setting with the remote.

Well done you!

miklosandras commented 7 years ago

This is excellent @mapilarc Thank you. Would you please describe this method in detailed?

mapilarc commented 7 years ago

Basically these are the steps, the flow is inspired by the dev docu from Samsung. I used python for scripting.

  1. Generate the pem file. I used the instruction from @skavan from 4th of June
  2. Start the server (script number 2)
  3. Start the first script
  4. Switch on the AC
  5. You can see the output on the server console.

Make sure the port 8889 is open for inbound traffic.

miklosandras commented 7 years ago

As I am quite new in Python3, would you please help me?

this should be the first script?

import requests
s = requests.Session()
headers = {'content-type': 'text/xml'}
payload = b'{"DeviceToken":"xxxxxxxxxxx"}'
resp = s.post("https://192.168.1.36:8888/devicetoken/request", data=payload, headers=headers, stream=True, verify=False, cert='cert.pem')

Output:

----- Request Start ----->

Request path: /devicetoken/response
Content Length: 0
Request headers: Host: 192.168.1.235:8889
Accept: */*

X-API-Version : v1.0.0
Content-Type: application/json
Content-Length: 28

Request payload: b''
Body: <__main__.RequestHandler object at 0x76a94830>
<----- Request End -----

192.168.1.36 - - [18/Aug/2017 15:10:28] "POST /devicetoken/response HTTP/1.1" 200 -
marko3dana commented 7 years ago

@mapilarc you didn't write what was the small fix in the server script that enabled you to see the token ?

mapilarc commented 7 years ago

I've just hardcoded the number of bytes that should be read

print("Request payload:", self.rfile.read(28))

For all my ACs it's always 28. Sorry guys that the scripts are not structured, I'll provide some more info when it's ready

marko3dana commented 7 years ago

Ok.. now it works for me also .. now its just a question on how to use that token to control the AC or read its state..

marko3dana commented 7 years ago

So I managed to send token via curl to the AC, but /capatibility get 404: curl -k -H "Content-Type: application/json" -H "Authorization: Bearer xxxxxxxxxxxx" --cert cert.pem --insecure -X GET https://10.10.90.1:8888/capatibility response is:

<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.2.7</center>
</body>
</html>
marko3dana commented 7 years ago

OK.. sorry for such many posts.. but want to share with you what I got so far.. this is nice..

curl -k -H "Content-Type: application/json" -H "Authorization: Bearer xxxxxx" --cert cert.pem --insecure -X GET https://10.10.90.1:8888/devices | json_pp


{
   "Devices" : [
      {
         "connected" : true,
         "Temperatures" : [
            {
               "desired" : 18,
               "current" : 26,
               "minimum" : 16,
               "unit" : "Celsius",
               "maximum" : 30,
               "id" : "0"
            }
         ],
         "Wind" : {
            "maxSpeedLevel" : 4,
            "speedLevel" : 2,
            "direction" : "Fix"
         },
         "Alarms" : [
            {
               "alarmType" : "Device",
               "code" : "FilterAlarm_OFF",
               "triggeredTime" : "1970-01-01T06:41:08",
               "id" : "0"
            }
         ],
         "ConfigurationLink" : {
            "href" : "/devices/0/configuration"
         },
         "id" : "0",
         "name" : "RAC",
         "Mode" : {
            "supportedModes" : [
               "Cool",
               "Dry",
               "Wind",
               "Auto"
            ],
            "modes" : [
               "Cool"
            ],
            "options" : [
               "Comode_Off",
               "Sleep_0",
               "Autoclean_Off",
               "FilterCleanAlarm_0",
               "OutdoorTemp_83",
               "CoolCapa_35",
               "WarmCapa_38",
               "UsagesDB_254",
               "FilterTime_420",
               "OptionCode_53432",
               "UpdateAllow_NotAllowed",
               "FilterAlarmTime_500",
               "Function_15",
               "Volume_100"
            ]
         },
         "EnergyConsumption" : {
            "saveLocation" : "/files/usage.db"
         },
         "Diagnosis" : {
            "diagnosisStart" : "Ready"
         },
         "description" : "TP6X_RAC_16K",
         "resources" : [
            "Alarms",
            "Configuration",
            "Diagnosis",
            "EnergyConsumption",
            "Information",
            "Mode",
            "Operation",
            "Temperatures",
            "Wind"
         ],
         "type" : "Air_Conditioner",
         "InformationLink" : {
            "href" : "/devices/0/information"
         },
         "uuid" : "XXXXXXXX-3D2E-0000-0000-000000000000",
         "Operation" : {
            "power" : "Off"
         }
      }
   ]
}
marko3dana commented 7 years ago

Ok.. enough for today I promise.. Just figured out how to control it .. for example..to turn it on: curl -k -H "Content-Type: application/json" -H "Authorization: Bearer xxxxxxxxxx" --cert cert.pem --insecure -X PUT -d '{"Operation" : {"power" : "On"}}' https://10.10.90.1:8888/devices/0

skavan commented 7 years ago

Keep it coming!!! After 3 months of being stalled, thanks to you (@marko3dana) and @mapilarc we have kicked into action...this is fantastic stuff! I'm going to build .net version when I learn all your tricks!

9037568 commented 7 years ago

Great to see all this interest. However, I'd like to suggest it's rather past time to move this discussion to the discussion forum.

skavan commented 7 years ago

Until that happens...a question...does autodiscovery still happen with a NOTIFY or with an M-SEARCH? and if so, how?

mog422 commented 7 years ago

I have found some control commands. curl -X PUT -d '{"desired": 25}' -v -k -H "Content-Type: application/json" -H "Authorization: Bearer XXXXX" --cert ac14k_m.p12 --insecure https://10.0.0.14:8888/devices/0/temperatures/0 curl -X PUT -d '{"speedLevel": 1}' -v -k -H "Content-Type: application/json" -H "Authorization: Bearer XXXXX" --cert ac14k_m.p12 --insecure https://10.0.0.14:8888/devices/0/wind curl -X PUT -d '{"modes": ["CoolClean"]}' -v -k -H "Content-Type: application/json" -H "Authorization: Bearer XXXXX" --cert ac14k_m.p12 --insecure https://10.0.0.14:8888/devices/0/mode curl -X PUT -d '{"options": ["Comode_Nano"]}' -v -k -H "Content-Type: application/json" -H "Authorization: Bearer XXXXX" --cert ac14k_m.p12 --insecure https://10.0.0.14:8888/devices/0/mode

9037568 commented 7 years ago

@skavan has started a new discussion thread.