New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Identifier rejected on 55U7KQ #14
Comments
i believe our issues my be linked however i cannot use the vidaa app only the remote now app. it is also only able to control volume now and wont do anything with a payload (changesource, launchapp etc) i am however abe to connect with mqtt explorer just check you have entered the certificates correctly for client cert and client key in mqtt explorer. other than that my knowledge is limited lol |
Having the same issue on a 55U8KQ. I'm pretty sure Hisense changed the way MQTT auth works with newer models (those supporting only the Vidaa App). I think this might be beyond the scope of what @Krazy998 is doing here, but here's what I could find so far:
This seems to indicate that the authentication process is much more complex now and I have no idea where to start with this. Anyways, I'll attach the database so you can have a look at them: |
Thank you for sharing @LeoKlaus - I was going to suggest wireshark however now that hisense is using TLS it would have been difficult. But worth a initial try. I think its important to establish if the new Vidaa app actually is using a local connect to the tv or is it using some broker or service on the internet. Wireshark should at least tell us that hopefully. The only other suggestion is to create a serial cable which can TTL directly to the TV and pull the config / app files for analysis. Im not sure how the new 55U8KQ TV's are, but my older 75" has a headphone jack (there is a service jack on the back of the tv) which effectively can connect to a usbTTL device. I haven't done this in a while however - When the TV is booting you can interrupt its boot and get local access. I found I could only a small portion of the filesystem is set to read only. There is a script that is run as root that kicks off the update check where you can modify it to also launch a telnet server with root access. This will give you ability to pull the code off the device (if it hasnt been patched already on newer devices). |
Just want to chime in... I battled with this for quite some time before giving up. I own a recently purchased (Oct 2023) 65E7KQ PRO which only connects via the VIDAA app. I can pretty much repeat everything listed above. I ended up buying a broadlink IR blaster as all I cared about was ON / OFF. I'll add another small (annoying) nugget : With this set, wifi / network connection is not established until after the TV has been powered on at least once, regardless of what you set in the menus etc. Pretty annoying for those of us who power socket the set OFF for the night (linked to my home alarm state etc...)... |
From what I recall when I was trying to overcome all of this after the initial handshake (eg: the TV was listed in the app), I was able to control it even when I firewalled it off from WAN access. 99% sure, but someone should verify, as I overcame most of what I wanted to do with a broadlink IR blaster and gave up...
|
The whole setup process and control does work without the TV having internet access. I've tried capturing and decrypting the traffic from the app to the TV, but haven't been able to do so for lack of a rootable Android device. On both Android and iOS, the app ignores the system proxy configuration and uses certificate pinning, so a simple mitm doesn't work. On Android, I've been able to at least capture the MQTT traffic (still on port 36669) using PCAPdroid. The app itself uses Baidu protect. This (among other things) prevents you from running with USB debugging enabled. If someone has a rooted Android device and a recent Hisense TV and wants to try getting this to work, the following might help: If that works and the TV can still be controlled, you should be able to capture and decrypt the traffic using PCAPdroid with the MITM add-on (as far as I could tell, all the commands are still sent on port 36669). It would be really cool to get this to work, but decompilation (and to some extent reverse engineering) is not exactly legal here in Germany, so even if I managed to break into the app, I couldn't post my findings. |
I have a brand new A6K 2023 model with the latest version of the Vidaa protocol. If you try a MITM (unpinning doesn't seem to work), the TV drops the connection. However, I was able to see what the initial connection message was including the username, password and client ID and I was able to connect for a limited amount of time using this information using the MQTT Explorer. The "multiscreen123" is the password for the keystore, so this is why I think this might be the case. However, it could be a key hardcoded into the java, which we can't see as it won't decompile. The "passNewword ===[C@d0eef3f" I guess is the "10121AEA780F5D61B6BD93C202F0A6AE", but they didn't convert the byte array to hex for debugging. Shame because it would make testing a lot easier than having to use a rooted phone with PCAPdroid |
Very interesting findings. I'm really surprised you were able to use Logcat, as I remember seeing something along the lines of
(don't remember the exact implementation but it was very clear what it was supposed to do) in the decompiled code. I didn't quite understand what information let you connect, though. The password you used to connect was The key takeway (for me) here is that, even if somebody managed to reverse engineer or find the algorithm/key they use to determine the password, the whole timestamp thing would make it a giant pain in the ass to integrate this into a smart home system. |
Yes, that's the plain text password, hex values. vs the older way |
The timestamp has been put in to stop it being just hardcoded and used on any TV. Hisense obviously don't want people to be able to control their own TV! |
Looks like they work for about 4 hours in each direction. I just captured these details with my clock set to 4 hours time. I can confirm these are working with my Hisense. If anybody wants to try them in the next 8 hours or so just to prove that the TV details aren't involved. You'll notice that even once it's expired that the error in MQTT explorer is no longer "invalid client ID" but "Not authorised" |
I keep getting "Disconnected from server" within MQTTExplorer. Tried your MAC, and both the "private" one and the real MAC of my iPhone. This is really interesting though. If what you assume is true, this implementation does not only suck ass for us users but also from a security-standpoint. |
What certs do you have setup? I get that error message of the certificate is added, |
None, actually. I just got a new Mac and didn't carry over any of the MQTTExplorer settings. I just installed it. |
Okay, I've used the certs from d3nd3/Hisense-mqtt-keyfiles.
|
Excellent. Saves me having to get out of bed, go to my local TV shop and loiter around in the carpark with my laptop! We now know that there's no "extra" info put into the password or the client ID from the TV itself. If we can correctly generate the password to match the clientID and username we can connect to any TV/ |
Hey @chimpzilla i also own the same model (A6K) and i would be happy to help as much as i can. |
No luck, sorry. It's pretty hardened to trying work out what's going on. |
I think only way would be to get root access to the tv via serial console (if that is still possible) and examine the code. Clearly hisense has gone out of its way to limit the ability to manage their latest TV's. On a sidenote - I have root access to my old 3 year hisense and I have examined their logs and various other things. I can tell you they send logs to unified-ter-na.hismarttv.com which include things like what is playing on the tv (using data from the tv guide) what source is selected. |
I have dug up a little bit more and by using logcat i found a lot of interesting details.
There is much more in the logcat, just not sure it is useful. i'll keep digging |
I've found out a lot more information including how to generate the password for pairing. However I am stuck on the last step of pairing, hopefully somebody can help with this, as I've ran out of ideas. So this is how the password and client ID are generated, they are a series of MD5 hashes based on some fixed data, the current unix timestamp and the connecting device's mac address . You can use any mac address, as the TV does not verify this. You can use this online tool to generate this info yourself: These are the series of hashes you need to use: These are the results How you get the hashes: username his$1701415028 (unix timestamp) Once you connect in using these login details, you send the following topics, subscribes. I could get most of it to work by either sending the raw TCP data or using the Android Paho MQTT client (this is what the Vidaa app uses). The topic contains the MQTT clientID publish topic :/remoteapp/tv/ui_service/C0:BD:D1:3D:6E:3E$his$44DE1F_vidaacommon_001/actions/vidaa_app_connect subscribe: /remoteapp/mobile/C0:BD:D1:3D:6E:3E$his$44DE1F_vidaacommon_001/ui_service/data/authentication publish topic :/remoteapp/tv/ui_service/C0:BD:D1:3D:6E:3E$his$44DE1F_vidaacommon_001/actions/authenticationcode subscribe: /remoteapp/mobile/C0:BD:D1:3D:6E:3E$his$44DE1F_vidaacommon_001/ui_service/data/authenticationcode or if incorrect pin publish topic:/remoteapp/tv/platform_service/C0:BD:D1:3D:6E:3E$his$44DE1F_vidaacommon_001/data/gettoken publish topic:/remoteapp/tv/ui_service/C0:BD:D1:3D:6E:3E$his$44DE1F_vidaacommon_001/actions/authenticationcodeclose subscribe: /remoteapp/mobile/C0:BD:D1:3D:6E:3E$his$44DE1F_vidaacommon_001/platform_service/data/tokenissuance I can get all of this to work, including the "{"result":1,"info":""} message back from the TV, which confirms the pin is correct. But....... the TV never replies with the access token. I can see from the logcat from the Vidaa app that should happen. |
You're an absolute madlad! I'll later try to reproduce this with my unit and see how it goes.
It's insane. Hisense could've literally spent less time just providing some official MQTT integration/documentation and turned this utter shitshow into a great unique selling point for their hardware (be it for a very small target audience). |
Update Here's a walkthrough of what was sent and received via the Windows MQTTX client. Received messages in bold Topic: /remoteapp/mobile/C0:BD:D1:3D:6E:3E$his$44DE1F_vidaacommon_001/ui_service/data/authentication Topic: /remoteapp/tv/ui_service/C0:BD:D1:3D:6E:3E$his$44DE1F_vidaacommon_001/actions/authenticationcode Topic: /remoteapp/mobile/C0:BD:D1:3D:6E:3E$his$44DE1F_vidaacommon_001/ui_service/data/authenticationcode Topic: /remoteapp/mobile/broadcast/ui_service/data/hotelmodechange Topic: /remoteapp/tv/platform_service/C0:BD:D1:3D:6E:3E$his$44DE1F_vidaacommon_001/data/gettoken Topic: /remoteapp/mobile/C0:BD:D1:3D:6E:3E$his$44DE1F_vidaacommon_001/platform_service/data/tokenissuance |
I just tried again in MQTTX and can't get it to respond. I've tried using fresh login details, but nothing comes back from the TV (the pin is displayed on the TV, so it's connected and the messages are sent correctly). |
you are amazing @chimpzilla! |
Did MQTT Explorer connect? |
it did yes but that was the only thing it was doing. no publishing, no subscribing. |
Mine won't connect unless it's TLS enabled. If you post your connection details I can check they are valid? |
I tried connecting now with your credentials and the connection keeps dropping in mqttx (although it connects). |
The refresh token does indeed refresh itself after a day or two. This is even when connected with the access token. I'm yet to see if you can still connect with the access token after 2 days of not connecting or if you need to connect with the refresh token. |
Interesting. Didn't expect that... |
I've found a bit more out. |
I'll start using this, haha. |
Thanks for this! I cannot get it to work yet with my 65U7KQ. I've done the following:
It remains in the waiting loop with disconnect_reason 5. I have tried the refresh and access tokens in the credentials.json file, neither seems to get it out of the loop. I did not modify the client_id. Should I get that from somewhere? What am I missing? Regards, Nika. |
You'll have to use either one of the scripts posted earlier or use the guide @chimpzilla posted. You will need the following from a matching set:
and the hi_keys, to connect. Client id, username and password depend on the MAC address of the device you used (though they aren't verified), so you won't be able to use the tokens from your android device without knowing its client id. |
Cool, thanks!! So here's a python script for that :) import getmac
import hashlib
import datetime
import time
mac_address = getmac.get_mac_address()
unix_time = str(int(time.time()))
unix_time_sum = sum(int(digit) for digit in unix_time)
unix_time_sum_lastdigit = str(unix_time_sum % 10)
decodedline1 = "&vidaa#^app"
hashline1 = hashlib.md5(decodedline1.encode()).hexdigest().upper()
decodedline2 = hashline1 + "$" + mac_address
hashline2 = hashlib.md5(decodedline2.encode()).hexdigest().upper()
decodedline3 = "his" + unix_time_sum_lastdigit + "h*i&s%e!r^v0i1c9"
hashline3 = hashlib.md5(decodedline3.encode()).hexdigest().upper()
decodedline4 = unix_time + "$" + hashline3[:6]
hashline4 = hashlib.md5(decodedline4.encode()).hexdigest().upper()
print(decodedline1)
print(decodedline2)
print(decodedline3)
print(decodedline4)
print(hashline1)
print(hashline2)
print(hashline3)
print(hashline4)
username = "his$" + unix_time
client_id = mac_address + "$his$" + hashline2[:6] + "_vidaacommon_001"
password = hashline4
print(username)
print(client_id)
print(password) |
I am planning to do the whole thing in python so it can be reused for other home automation and such. Initial authentication works with the PIN and such, I am stuck though getting the tokens... going to let it rest for a while, have been trying for hours but can't get it to work. So.... still a "big"work in progress: import paho.mqtt.client as mqtt
import json
import getmac
import hashlib
import time
import datetime
import sys
tv_ip = "192.168.178.152"
certfile = "./rcm_certchain_pem.cer"
keyfile = "./rcm_pem_privkey.pkcs8"
credfile = "./credentials.json"
mac_address = getmac.get_mac_address().upper()
unix_time = str(int(time.time()))
authjson = {
"client_id": None,
"username": None,
"refreshtoken": None,
"refreshtoken_time": None,
"refreshtoken_duration_day": None,
"accesstoken": None,
"accesstoken_time": None,
"accesstoken_duration_day": None
}
global mqtt_message
mqtt_message = "undefined"
def load_authjson(file):
loaded_json = json.load(file)
authjson["client_id"] = loaded_json["client_id"]
authjson["username"] = loaded_json["username"]
authjson["refreshtoken"] = loaded_json["refreshtoken"]
authjson["refreshtoken_time"] = loaded_json["refreshtoken_time"]
authjson["refreshtoken_duration_day"] = loaded_json["refreshtoken_duration_day"]
authjson["accesstoken"] = loaded_json["accesstoken"]
authjson["accesstoken_time"] = loaded_json["accesstoken_time"]
authjson["accesstoken_duration_day"] = loaded_json["accesstoken_duration_day"]
def initial_auth():
unix_time_sum = sum(int(digit) for digit in unix_time)
unix_time_sum_lastdigit = str(unix_time_sum % 10)
print("Connecting to Hisense TV...")
print("")
decodedline1 = "&vidaa#^app"
print("Decoded line 1: " + decodedline1)
hashline1 = hashlib.md5(decodedline1.encode()).hexdigest().upper()
print("Hashed line 1: " + hashline1)
print("")
decodedline2 = hashline1 + "$" + mac_address
print("Decoded line 2: " + decodedline2)
hashline2 = hashlib.md5(decodedline2.encode()).hexdigest().upper()
print("Hashed line 2: " + hashline2)
print("")
decodedline3 = "his" + unix_time_sum_lastdigit + "h*i&s%e!r^v0i1c9"
print("Decoded line 3: " + decodedline3)
hashline3 = hashlib.md5(decodedline3.encode()).hexdigest().upper()
print("Hashed line 3: " + hashline3)
print("")
decodedline4 = unix_time + "$" + hashline3[:6]
print("Decoded line 4: " + decodedline4)
hashline4 = hashlib.md5(decodedline4.encode()).hexdigest().upper()
print("Hashed line 4: " + hashline4)
authjson["client_id"] = mac_address + "$his$" + hashline2[:6] + "_vidaacommon_001"
authjson["username"] = "his$" + unix_time
authjson["refreshtoken"] = hashline4
def on_connect(client, userdata, flags, rc):
if rc == 0:
client.connected_flag=True #set flag
print("")
print("Connected!")
client.subscribe("#")
def on_message(client, userdata, message):
print("")
print("Message received: ", str(message.payload.decode("utf-8")))
global mqtt_message
mqtt_message = json.loads(message.payload.decode("utf-8"))
def on_publish(client, userdata, mid):
print("")
print("Published message: " + str(mid))
def on_disconnect(client, userdata, rc):
print("")
print("Disconnecting reason: " + str(rc))
print("Disconnecting client: ", client)
print("Disconnecting userdata: ", userdata)
def get_refresh_token():
global mqtt_message
print("")
print("Connecting to: " + tv_ip)
print("With client_id: " + authjson["client_id"])
print("Username: " + authjson["username"])
print("Refresh token: " + authjson["refreshtoken"])
client = mqtt.Client(client_id=authjson["client_id"], clean_session=True, userdata=None, protocol=mqtt.MQTTv311, transport="tcp")
client.tls_set(ca_certs=None, certfile=certfile, keyfile=keyfile, cert_reqs=mqtt.ssl.CERT_NONE, tls_version=mqtt.ssl.PROTOCOL_TLS, ciphers=None)
client.on_connect = on_connect
client.on_message = on_message
# client.on_subscribe = on_subscribe
client.on_publish = on_publish
client.on_disconnect = on_disconnect
client.connected_flag=False
client.tls_insecure_set(True)
client.username_pw_set(username=authjson["username"], password=authjson["refreshtoken"])
client.connect_async(tv_ip, 36669, 60)
client.loop_start()
print("")
while not client.connected_flag: #wait in loop
print("In wait loop...")
time.sleep(1)
# subscribe: /remoteapp/mobile/C0:BD:D1:3D:6E:3E$his$44DE1F_vidaacommon_001/ui_service/data/authentication
# receivedmsg:
subscription_topic = "/remoteapp/mobile/" + authjson["client_id"] + "/ui_service/data/authentication"
print("")
print("Subscription topic: " + subscription_topic)
client.subscribe(subscription_topic)
# subscribe: /remoteapp/mobile/C0:BD:D1:3D:6E:3E$his$44DE1F_vidaacommon_001/ui_service/data/authenticationcode
# receivedmsg: {"result":1,"info":""}
# or if incorrect pin
# receivedmsg: {"result":100,"info":"Wrong authNum!!"}
subscription_topic = "/remoteapp/mobile/" + authjson["client_id"] + "/ui_service/data/authenticationcode"
print("")
print("Subscription topic: " + subscription_topic)
client.subscribe(subscription_topic)
# publish topic: /remoteapp/tv/ui_service/C0:BD:D1:3D:6E:3E$his$44DE1F_vidaacommon_001/actions/vidaa_app_connect
# message: {"app_version":2,"connect_result":0,"device_type":"Mobile App"}
# Pin should show on screen.
publish_topic = "/remoteapp/tv/ui_service/" + authjson["client_id"] + "/actions/vidaa_app_connect"
publish_message = '{"app_version": 2, "connect_result": 0, "device_type": "Mobile App"}'
print("")
print("Publishing topic: " + publish_topic)
print("Publishing message: " + publish_message)
client.publish(publish_topic, publish_message)
print("")
while mqtt_message == "undefined":
print("Waiting for refreshed message...")
time.sleep(1)
if mqtt_message == "":
mqtt_message = { "result": 0}
if mqtt_message["result"] != 1:
mqtt_message = "undefined"
pin_code = input("Please enter the PIN: ")
# publish topic :/remoteapp/tv/ui_service/C0:BD:D1:3D:6E:3E$his$44DE1F_vidaacommon_001/actions/authenticationcode
# message: {"authNum":"5299"}
# 5299 is the pin that is shown on the TV, when this is sent the pin should vanish from the screen.
publish_topic = "/remoteapp/tv/ui_service/" + authjson["client_id"] + "/actions/authenticationcode"
publish_message = '{"authNum":"' + pin_code + '"}'
print("")
print("Publishing topic: " + publish_topic)
print("Publishing message: " + publish_message)
client.publish(publish_topic, publish_message)
else:
print("Already authenticated with PIN!")
print("")
while mqtt_message == "undefined":
print("Waiting for refreshed message...")
time.sleep(1)
if mqtt_message == "":
mqtt_message = { "result": 0}
if mqtt_message["result"] != 1:
print("Incorrect result received, please fix!")
sys.exit()
mqtt_message = "undefined"
# publish topic:/remoteapp/tv/platform_service/C0:BD:D1:3D:6E:3E$his$44DE1F_vidaacommon_001/data/gettoken
# message:{"refreshtoken":""}
# client.publish("/remoteapp/tv/platform_service/" + client_id + "/data/gettoken", '{"refreshtoken": "' + password + '"}')
# Publishing topic: /remoteapp/tv/platform_service/98:59:7A:5B:E6:09$his$0861D2_vidaacommon_001/data/gettoken
# Publishing message: {"refreshtoken":""}
# subscribe: /remoteapp/mobile/C0:BD:D1:3D:6E:3E$his$44DE1F_vidaacommon_001/platform_service/data/tokenissuance
# receivedmsg:{
# "accesstoken": "_ZN5aYxg0DNMirIAXJ5OxWZ8mJaUcdR4GzkHXBofpx0a84hQAcbIaHhMxMhO/4hLf",
# "accesstoken_time": "1702028255",
# "accesstoken_duration_day": 2,
# "refreshtoken": "#ZN5aYxg0DNMirIAXJ5OxWZ8mJaUcdR4GzkHXBofpx0Z5nuorpAkQ9hUjZT8JfRRp",
# "refreshtoken_time": "1702028255",
# "refreshtoken_duration_day": 30
# }
# client.subscribe("/remoteapp/mobile/" + client_id + "/platform_service/data/tokenissuance")
# Subscription topic: /remoteapp/mobile/98:59:7A:5B:E6:09$his$0861D2_vidaacommon_001/platform_service/data/tokenissuance
subscription_topic = "/remoteapp/mobile/" + authjson["client_id"] + "/platform_service/data/tokenissuance"
print("")
print("Subscription topic: " + subscription_topic)
client.subscribe(subscription_topic)
publish_topic = "/remoteapp/tv/platform_service/" + authjson["client_id"] + "/data/gettoken"
publish_message = '{"refreshtoken": "' + authjson["refreshtoken"] + '"}'
print("")
print("Publishing topic: " + publish_topic)
print("Publishing message: " + publish_message)
client.publish(publish_topic, publish_message)
# print("")
# while mqtt_message == "undefined":
# print("Waiting for refreshed message...")
# time.sleep(1)
# if mqtt_message == "":
# mqtt_message = { "result": 0}
# if mqtt_message["result"] != 1:
# print("Incorrect result received, please fix!")
# sys.exit()
# # publish topic:/remoteapp/tv/ui_service/C0:BD:D1:3D:6E:3E$his$44DE1F_vidaacommon_001/actions/authenticationcodeclose
# # message:
# publish_topic = "/remoteapp/tv/ui_service/" + authjson["client_id"] + "/actions/authenticationcodeclose"
# publish_message = ''
# print("")
# print("Publishing topic: " + publish_topic)
# print("Publishing message: " + publish_message)
# client.publish(publish_topic, publish_message)
print("")
while mqtt_message == "undefined":
print("Waiting for refreshed message...")
time.sleep(1)
if mqtt_message == "":
mqtt_message = { "refreshtoken": "" }
if mqtt_message["refreshtoken"] != "":
print("Refreshtoken received: " + mqtt_message["refreshtoken"])
authjson["refreshtoken"] = mqtt_message["refreshtoken"]
# with open(credfile, "w") as file:
# json.dump(credentials, file, indent= 4)
#print("Got new credentials" + mqtt_message + ", terminating...")
client.loop_stop()
client.disconnect()
def days_difference(timestamp1, timestamp2):
# Convert Unix timestamps to datetime objects
date1 = datetime.utcfromtimestamp(timestamp1)
date2 = datetime.utcfromtimestamp(timestamp2)
# Calculate the difference in seconds
time_difference = abs(date2 - date1).total_seconds()
# Calculate the difference in days
days_difference = time_difference / (24 * 3600)
return days_difference
try:
file = open(credfile)
except FileNotFoundError:
print("")
print('No stored credentials found, starting auth with TV...')
initial_auth()
else:
with file:
load_authjson(file)
print("Current time: " + unix_time)
print("Refresh token: ", authjson["refreshtoken"])
print("Refresh time: ", authjson["refreshtoken_time"])
print("Refresh duration:", authjson["refreshtoken_duration_day"])
if authjson["refreshtoken"] == "":
print('No stored credentials found, starting auth with TV...')
initial_auth()
if authjson["refreshtoken"] and authjson["refreshtoken_time"] and authjson["refreshtoken_duration_day"]:
print('Stored credentials found, checking expiration...')
if days_difference(unix_time, authjson["refreshtoken_time"]) < authjson["refreshtoken_duration_day"]:
print('Stored credentials expired, starting auth with TV...')
initial_auth()
get_refresh_token()
with open(credfile, "w") as file:
json.dump(authjson, file, indent= 4) |
@nikagersonlohman We've already managed to produce a working Python script to both obtain the token and refresh it: Refreshing token (second part) Keep in mind that the TV does have some sort of flooding protection/blacklist, so you might have to use a different MAC address if you've experimented a lot with one device/MAC. This isn't really a problem though as the TV doesn't verify the MAC address. If you want to spend some more time on this, I'm sure it could be streamlined a lot. I think the best approach for building a HASS integration would be working with sehaas/ha_hisense_tv and building a custom MQTT bridge that handles the token authentication on its own. |
Wow, this is great again. For some reason it didn't show that entire post with the Obtaining a new token script! All that time wasted trying to create what you guys had created already! We should publish it on a github account to make collaboration easier... I can do that if you guys want? |
Note that I have OpenHAB and not HASS and will therefore convert it to javascript (ecmascript) as soon as it works... |
I think all that was missing in my script was "hotelmodechange"... |
Sure, just keep in mind that some of of this may be considered intellectual property of Hisense (especially the certificates). I can't say whether I'll take the time to contribute to this, as I've got everything I wanted working, but I'd surely appreciate the effort. |
Do you have it perfectly up and running now? What platform are you using, I use HA and NodeRED, can run on this? |
I'm using Node-RED with the FakeRoku adapter to be able to set inputs with my Harmony remote, nothing more. This repo contains some more commands to control the TV, they seem to still work with newer TVs. You can use the scripts I posted earlier to connect via Node-RED. From there on, you'll have to build your own integration for whatever you need. I guess with HASS, it should be fairly easy to send webhooks to trigger actions through Node-RED, but I'm not familiar with either, so I can't help you there. |
I'm in the same boat after (foolishly) letting my 43A6BGTUK update its firmware. |
That's a 2021 model I think. I wonder if all models are getting this "update"? |
Transport protocol is 3160 I contacted Hisense and they sent me a downgrade firmware. I applied it but still no joy in getting access back. It looks like the downgrade firmware is still running VIDAA 7 as the RemoteNOW app still does not work. |
Just to add some more context to this. I am not sure what the firmware was before the OTA update. The OTA update was V0007.06.30F.N1027 I contacted Hisense UK and told them MQTT is now locked down. They sent me V0007.06.30O.N0829. The RemoteNOW still does not work just the VIDAA app. I cannot get MQTT Explorer to connect to the TV. I have contacted Hisense again to see what they suggest. I also contacted VIDAA who came back with the following: "Thank you for contacting Vidaa Customer Support. We're so sorry for the late response, Regrettably, it is not publicly available for anyone who wants to integrate with our TV but for the partners we sign with to enhance our platform. We sincerely apologize for any inconvenience that this might cause you. If you are interested in becoming a partner with Vidaa you can contact our team at https://www.vidaa.com/partners/ " Sounds like this will be a no go unless Hisense can provide the right downgrade software. |
On the TVs I've looked at, >3000 is the new authentication and under 3000 is the older way |
Hisense sent another firmware file. I'm now running V0007.06.12R.N0508. MQTT Explorer works as does the RemoteNOW app. I am now having trouble getting the https://github.com/sehaas/ha_hisense_tv integration to work. I just get the spinning wheel and the tv does not bring up a code. This is so frustrating. |
Could you share the firmware file? |
The link will be valid for 7 days: |
Has anybody had any luck getting the picture setting info or channel list from the new firmware? and you would get a list of the current TV channels the TV has. Also older versions would have the following: Publish But I can't get this to work or figure out what the subscribe would be for it to work? |
EDIT: RESOLVED Hey there, I encountered this thread two weeks ago, and managed to control the TV through Home Assistant sensors, switches and number entities using some rudimentary manual YAML configuration. But, as of a few days ago the sendkey topic seems to have stopped working, can anyone confirm that? The topic is this one (excluding my client id): Tested with payloads Note that every other topic in the spreadsheet posted by LeoKlaus still works, the sendkey topic used to work until a few days ago and that I'm using two different clients (with different client ids) to control the TV (which also worked fine). |
Did your TV perform a firmware update by chance? Maybe they changed something. |
It's working again, but I can only guess what the problem was. Maybe I was just stupid somewhere. One of my two access tokens expired a few hours ago, that might be related. Interestingly though, all the other topics kept working on both clients and the sendkey topic stopped working on both clients. |
Hi, Ive tried the above python routine to generate a access token and at first I was joyed as it connected from MQTT Explorer. However, if I publish events they dont seem to action on the TV. I have also tried the same configuration on Home Assistant, and it seems to like the configuration ( doesnt error ) but again it does not appear to be receiving any events or allowing me to publish simple events to the TV. Ive also tried in MQTTX which connects fine but gives the following error when I try to subscribe to anything "Failed to Subscribe TOPIC, Error: Not authorized(Code: 135). Make sure the permissions are correct, and check MQTT broker ACL configuration". I am running a brand new 2023 model - Hisense 55U8KQTUK. However, the scripts that were written on this thread appear to work fine and they are publishing and subscribing with no problem. Thanks Steve |
I have a Hisense 65E7KQ PRO, bought via Amazon DE in Dec 2023 with software V0002.07.50B.N0715. Thanks to this thread I can connect to it via MQTT. I slightly modified the two scripts provided, call refresh_token.py and it will either create a new set of credentials or refresh an existing one. The credentials will be saved in credentials.json. You will be asked for the PIN code shown on the TV if a fresh set is created. I confirmed I can control and view the TV state via MQTT Explorer. Some notes, in case they help someone:
Next steps (in the medium term) for me are to pretty up this code and try my hand at creating my first HASS integration or modifying an existing one. Not sure how to handle the client certs yet. Sadly you can't easily extract them from the APK. generate_token.py: import re, uuid
import hashlib
import time
import paho.mqtt.client as mqtt
import json
from pprint import pprint
reply = None
authentication_payload = None
authentication_code_payload = None
tokenissuance = None
topicTVUIBasepath = None
topicTVPlatformBasepath = None
topicMobileBasepath = None
def cross_sum(n):
r = 0
while n:
r, n = r + n % 10, n // 10
return r
def stringToHash(input: str):
result = hashlib.md5(input.encode("utf-8"))
return result.hexdigest().upper()
def on_connect(client, userdata, flags, rc):
global topicTVUIBasepath
global topicTVPlatformBasepath
global topicMobileBasepath
if rc == 0:
client.connected_flag=True #set flag
print("connected ok")
# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
client.subscribe("#")
client.subscribe(topicTVUIBasepath + "actions/vidaa_app_connect")
client.subscribe(topicMobileBasepath + 'ui_service/data/authentication')
client.subscribe(topicMobileBasepath + 'ui_service/data/authenticationcode')
client.subscribe("/remoteapp/mobile/broadcast/ui_service/data/hotelmodechange")
client.subscribe(topicMobileBasepath + 'platform_service/data/tokenissuance')
else:
print("Bad connection Returned code=",rc)
client.bad_connection_flag=True
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, message):
print("i'm on message")
global reply
print("message received " ,str(message.payload.decode("utf-8")))
print("message topic=",message.topic)
print("message qos=",message.qos)
print("message retain flag=",message.retain)
reply = message
def on_subscribe(client, userdata, mid, granted_qos):
print("Subscribed: "+str(mid)+" "+str(granted_qos))
def on_publish(client, userdata, mid):
print("Published message " + str(mid))
def on_disconnect(client, userdata, rc):
print("disconnecting reason " +str(rc))
def on_log(client, userdata, level, buf):
print("log: ",buf)
def on_message_msgs(mosq, obj, msg):
print("MESSAGES: " + msg.topic + " " + str(msg.qos) + " " + str(msg.payload))
def on_authentication(mosq, obj, msg):
global authentication_payload
authentication_payload = msg
def on_authentication_code(mosq, obj, msg):
global authentication_code_payload
authentication_code_payload = msg
def on_tokenissuance(mosq, obj, msg):
global tokenissuance
tokenissuance = msg
def write_token_to_creds_file():
global topicTVUIBasepath
global topicTVPlatformBasepath
global topicMobileBasepath
timestamp = int(time.time())
#timestamp = 1702583685
firstHash = stringToHash("&vidaa#^app")
mac = ':'.join(re.findall('..', '%012x' % uuid.getnode())).upper()
#mac = "C1:BD:D1:3D:6E:3E"
print(f'mac {mac}')
secondHash = stringToHash("38D65DC30F45109A369A86FCE866A85B$" + mac)
lastDigitOfCrossSum = cross_sum(timestamp)%10
thirdHash = stringToHash("his"+ str(lastDigitOfCrossSum) +"h*i&s%e!r^v0i1c9")
fourthHash = stringToHash(str(timestamp) + "$" + thirdHash[:6])
print(firstHash)
print(secondHash)
print(thirdHash)
print(fourthHash)
clientID = mac + "$his$" + secondHash[:6] + "_vidaacommon_001"
client = mqtt.Client(client_id=clientID, clean_session=True, userdata=None, protocol=mqtt.MQTTv311, transport="tcp")
client.tls_set(ca_certs=None, certfile="./rcm_certchain_pem.cer", keyfile="./rcm_pem_privkey.pkcs8", cert_reqs=mqtt.ssl.CERT_NONE,
tls_version=mqtt.ssl.PROTOCOL_TLS, ciphers=None)
client.on_connect = on_connect
client.on_message = on_message
# client.on_subscribe = on_subscribe
client.on_publish = on_publish
client.on_disconnect = on_disconnect
# client.on_log = on_log
client.connected_flag=False
client.tls_insecure_set(True)
username = "his$" + str(timestamp)
print(f'username: {username}')
print(f'password: {fourthHash}')
print(f'client_id: {clientID}')
client.username_pw_set(username=username, password=fourthHash)
topicTVUIBasepath = "/remoteapp/tv/ui_service/" + clientID + "/"
topicTVPlatformBasepath = "/remoteapp/tv/platform_service/" + clientID + "/"
topicMobileBasepath = "/remoteapp/mobile/" + clientID + "/"
client.message_callback_add(topicMobileBasepath + 'ui_service/data/authentication' , on_authentication)
client.message_callback_add(topicMobileBasepath + 'ui_service/data/authenticationcode' , on_authentication_code)
client.message_callback_add('/remoteapp/mobile/broadcast/ui_service/data/hotelmodechange' , on_message_msgs)
client.message_callback_add(topicMobileBasepath + 'platform_service/data/tokenissuance' , on_tokenissuance)
client.connect_async("192.168.65.61", 36669, 60)
client.loop_start()
while not client.connected_flag: #wait in loop
print("In wait loop")
time.sleep(1)
print('publishing message to actions/vidaa_app_connect ...')
client.publish( topicTVUIBasepath + "actions/vidaa_app_connect", '{"app_version":2,"connect_result":0,"device_type":"Mobile App"}')
print(f'subscribing to {topicMobileBasepath}ui_service/data/authentication ...')
while authentication_payload is None:
time.sleep(0.1)
if authentication_payload.payload.decode() != '""' :
print('Problems with the authentication message!')
print(authentication_payload.payload)
print('Exiting...')
exit()
authNum = input("Enter the four digits displayed on your TV: ")
print(f'publishing message to {topicTVUIBasepath}actions/authenticationcode ...')
client.publish( topicTVUIBasepath + "actions/authenticationcode", '{"authNum":' + authNum + '}')
print(f'subscribing to {topicMobileBasepath}ui_service/data/authenticationcode ...')
client.subscribe(topicMobileBasepath + 'ui_service/data/authenticationcode')
while authentication_code_payload is None:
time.sleep(0.1)
print(authentication_code_payload.payload.decode())
if json.loads(authentication_code_payload.payload.decode()) != json.loads('{"result": 1,"info": ""}') :
print('Problems with the authentication message!')
print(authentication_code_payload.payload)
print('Exiting...')
exit()
print("Success! Getting access token...")
print(f'publishing message to {topicTVPlatformBasepath}data/gettoken ...')
client.publish( topicTVPlatformBasepath + "data/gettoken", '{"refreshtoken": ""}')
print(f'publishing message to {topicTVUIBasepath}actions/authenticationcodeclose ...')
client.publish( topicTVUIBasepath + "actions/authenticationcodeclose")
print(f'subscribing to /remoteapp/mobile/broadcast/ui_service/data/hotelmodechange ...')
client.subscribe('/remoteapp/mobile/broadcast/ui_service/data/hotelmodechange')
print(f'subscribing to {topicMobileBasepath}platform_service/data/tokenissuance ...')
client.subscribe(topicMobileBasepath + 'platform_service/data/tokenissuance')
while tokenissuance is None:
time.sleep(0.1)
t = tokenissuance.payload.decode()
t2 = json.loads(t)
t2['client_id'] = clientID
t2['username'] = username
t2['password'] = fourthHash
pprint(t2)
print('token issued...well done!')
json.dump(t2, open('credentials.json', 'w'))
print('credentials saved to credentials.json')
client.loop_stop()
client.disconnect()
if __name__ == "__main__":
write_token_to_creds_file() refresh_token.py: import paho.mqtt.client as mqtt
import time
import json
from generate_token import write_token_to_creds_file
tv_ip = "192.168.65.61"
certfile = "./rcm_certchain_pem.cer"
keyfile = "./rcm_pem_privkey.pkcs8"
def load_or_generate_creds(rec=False):
global oldCreds
try:
file = open('credentials.json')
except FileNotFoundError:
if not rec:
print('No stored credentials found, starting auth with TV...')
write_token_to_creds_file()
load_or_generate_creds(True)
else:
print('Unable to generate credentials.')
else:
with file:
oldCreds = json.load(file)
load_or_generate_creds()
refreshtoken = oldCreds["refreshtoken"]
client_id = oldCreds['client_id']
username = oldCreds['username']
password = oldCreds['password']
credentials = ""
def on_connect(client, userdata, flags, rc):
if rc == 0:
client.connected_flag=True #set flag
print("connected ok")
# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
client.subscribe("#")
def on_message(client, userdata, message):
print("message received " ,str(message.payload.decode("utf-8")))
global credentials
credentials = json.loads(message.payload.decode("utf-8"))
def on_publish(client, userdata, mid):
print("Published message " + str(mid))
def on_disconnect(client, userdata, rc):
print("disconnecting reason " +str(rc))
def refresh_token():
client = mqtt.Client(client_id=client_id, clean_session=True, userdata=None, protocol=mqtt.MQTTv311, transport="tcp")
client.tls_set(ca_certs=None, certfile=certfile, keyfile=keyfile, cert_reqs=mqtt.ssl.CERT_NONE,
tls_version=mqtt.ssl.PROTOCOL_TLS, ciphers=None)
client.on_connect = on_connect
client.on_message = on_message
# client.on_subscribe = on_subscribe
client.on_publish = on_publish
client.on_disconnect = on_disconnect
client.connected_flag=False
client.tls_insecure_set(True)
client.username_pw_set(username=username, password=password)
client.connect_async(tv_ip, 36669, 60)
client.loop_start()
while not client.connected_flag: #wait in loop
print("In wait loop")
time.sleep(1)
client.subscribe("/remoteapp/mobile/" + client_id + "/platform_service/data/tokenissuance")
client.publish("/remoteapp/tv/platform_service/" + client_id + "/data/gettoken", '{"refreshtoken": "' + refreshtoken + '"}')
while credentials == "":
print("waiting for refreshed credentials...")
time.sleep(1)
credentials['client_id'] = client_id
credentials['username'] = username
credentials['password'] = password
with open("credentials.json", "w") as file:
json.dump(credentials, file, indent= 4)
print("got new credentials, terminating...")
client.loop_stop()
client.disconnect()
return credentials["accesstoken"]
#exit()
if __name__ == "__main__":
refresh_token() |
Thats great thanks. I worked out what my problem was. I had been sending the password instead of the access key in the password field. Now got the same issue you mention in that I cannot subscribe using a wildcard #. Thats fine though as the spreadsheet you mention has all of the useful ones in. I found some more in the source code of this plugin. https://github.com/sehaas/ha_hisense_tv Thanks Steve |
I bought a 55U7KQ that came out this year. It looks like Hisense did some changes to MQTT again, I am getting “Connection refused: Identifier rejected” over MQTT Explorer as well as within the mqtt logs on my raspberry although I am using my phone’s MAC address as the client ID.
Also on this newer model I can not use the Remotenow app, only the VIDAA app. Any help would be highy appreciated.
The text was updated successfully, but these errors were encountered: