newAM / hisensetv

Python API to control Hisense brand TVs via their internal MQTT broker.
MIT License
37 stars 17 forks source link

RuntimeError: Message publish failed: The client is not currently connected. #27

Open fresh-avocado opened 6 months ago

fresh-avocado commented 6 months ago

Steps to Reproduce

My computer and my Hisense are connected to the same network. When I run python hisense.py <tv_ip> --authorize I get the following error:

Traceback (most recent call last):
  File "hisense.py", line 143, in <module>
    main()
  File "hisense.py", line 134, in main
    func()
  File "/Users/gabrielspranger/.pyenv/versions/3.8.18/lib/python3.8/site-packages/hisensetv/__init__.py", line 53, in wrapper
    return func(self, *args, **kwargs)
  File "/Users/gabrielspranger/.pyenv/versions/3.8.18/lib/python3.8/site-packages/hisensetv/__init__.py", line 551, in send_key_down
    self.send_key("KEY_DOWN")
  File "/Users/gabrielspranger/.pyenv/versions/3.8.18/lib/python3.8/site-packages/hisensetv/__init__.py", line 240, in send_key
    self._call_service(service="remote_service", action="sendkey", payload=keyname)
  File "/Users/gabrielspranger/.pyenv/versions/3.8.18/lib/python3.8/site-packages/hisensetv/__init__.py", line 231, in _call_service
    msg.wait_for_publish()
  File "/Users/gabrielspranger/.pyenv/versions/3.8.18/lib/python3.8/site-packages/paho/mqtt/client.py", line 362, in wait_for_publish
    raise RuntimeError('Message publish failed: %s' % (error_string(self.rc)))
RuntimeError: Message publish failed: The client is not currently connected.

hisense.py:

import ssl
import argparse
import json
import logging
import ssl
import os
import sys
# from . import HisenseTv
from hisensetv import HisenseTv

script_path = os.path.abspath(sys.argv[0])
script_directory = os.path.dirname(script_path)

def main():
    parser = argparse.ArgumentParser(description="Hisense TV control.")
    parser.add_argument("hostname", type=str, help="Hostname or IP for the TV.")
    parser.add_argument(
        "--authorize",
        action="store_true",
        help="Authorize this API to access the TV.",
    )
    parser.add_argument(
        "--ifname",
        type=str,
        help="Name of the network interface to use",
        default=""
    )

    parser.add_argument(
        "--get",
        action="append",
        default=[],
        choices=["sources", "volume", "state"],
        help="Gets a value from the TV.",
    )
    parser.add_argument(
        "--key",
        action="append",
        default=[],
        choices=[
        "power",
        "up",
        "down",
        "left",
        "right",
        "menu",
        "back",
        "exit",
        "ok",
        "volume_up",
        "volume_down",
        "channel_up",
        "channel_down",
        "fast_forward",
        "rewind",
        "stop",
        "play",
        "pause",
        "mute",
        "home",
        "subtitle",
        "netflix",
        "google",
        "haystack",
        "youtube",
        "amazon",
        "0",
        "1",
        "2",
        "3",
        "4",
        "5",
        "6",
        "7",
        "8",
        "9",
        "source_0",
        "source_1",
        "source_2",
        "source_3",
        "source_4",
        "source_5",
        "source_6",
        "source_7",
        ],
        help="Sends a keypress to the TV.",
    )
    parser.add_argument(
        "--no-ssl",
        action="store_true",
        help="Do not connect with SSL (required for some models).",
    )
    parser.add_argument(
        "-v", "--verbose", action="count", default=0, help="Logging verbosity."
    )

    args = parser.parse_args()

    if args.verbose:
        level = logging.DEBUG
    else:
        level = logging.INFO

    root_logger = logging.getLogger()
    stream_handler = logging.StreamHandler()
    formatter = logging.Formatter(
        fmt="[{asctime}] [{levelname:<8}] {message}", style="{"
    )
    stream_handler.setFormatter(formatter)
    root_logger.addHandler(stream_handler)
    root_logger.setLevel(level)
    logger = logging.getLogger(__name__)

    if args.no_ssl:
        ssl_context = None
    else:
        ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
        certfile = script_directory + '/hi_keys/rcm_certchain_pem.cer'
        keyfile = script_directory + '/hi_keys/rcm_pem_privkey.pkcs8'
        ssl_context.load_cert_chain(certfile=certfile, keyfile=keyfile)

    tv = HisenseTv(
        args.hostname, enable_client_logger=args.verbose >= 2, ssl_context=ssl_context
    )
    with tv:
        if args.authorize:
            tv.start_authorization()
            code = input("Please enter the 4-digit code: ")
            tv.send_authorization_code(code)

        for key in args.key:
            func = getattr(tv, f"send_key_{key}")
            logger.info(f"sending keypress: {key}")
            func()

        for getter in args.get:
            func = getattr(tv, f"get_{getter}")
            output = func()
            if isinstance(output, dict) or isinstance(output, list):
                output = json.dumps(output, indent=4)
            print(output)

main()