{"id":7238,"date":"2024-07-28T15:22:37","date_gmt":"2024-07-28T15:22:37","guid":{"rendered":"http:\/\/172.30.70.55\/?p=7238"},"modified":"2024-08-09T01:25:40","modified_gmt":"2024-08-09T01:25:40","slug":"rtsp-camera-on-overlay-script","status":"publish","type":"post","link":"https:\/\/phixd.us\/?p=7238","title":{"rendered":"RTSP Camera on Overlay Script"},"content":{"rendered":"\n<p><\/p>\n\n\n\n<p>This script is designed to integrate RTSP compatible cameras into a designed for Home Assistant via MQTT discovery messages and manage media playback through a Kodi media center. The script is structured in Python and utilizes several libraries to handle HTTP requests, subprocess management, MQTT communication, and JSON data handling.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-medium-font-size\"><strong>Features<\/strong><\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li class=\"has-medium-font-size\"><strong>RTSP Stream Management<\/strong>: Handles multiple RTSP streams, allowing you to start and stop video feeds based on commands received over MQTT.<\/li>\n\n\n\n<li class=\"has-medium-font-size\"><strong>MQTT Integration<\/strong>: Connects to an MQTT broker to receive commands and publish the state of camera streams.<\/li>\n\n\n\n<li class=\"has-medium-font-size\"><strong>Kodi Control<\/strong>: Sends commands to a Kodi media center to pause and play media automatically when switching between streams.<\/li>\n\n\n\n<li class=\"has-medium-font-size\"><strong>Home Assistant Compatibility<\/strong>: Publishes MQTT discovery messages to integrate seamlessly with Home Assistant, making each camera stream appear as a switchable entity.<\/li>\n\n\n\n<li class=\"has-medium-font-size\"><strong>Error Handling<\/strong>: Includes basic error handling to manage exceptions and ensure the system remains operational in case of minor errors.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading has-medium-font-size\"><strong>Components of the Script<\/strong><\/h3>\n\n\n\n<h4 class=\"wp-block-heading has-medium-font-size\"><strong>Global Variables and Dictionaries<\/strong><\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"has-medium-font-size\"><code>current_stream<\/code>: A dictionary that tracks the state of the current stream, including whether Kodi was playing when the stream started.<\/li>\n\n\n\n<li class=\"has-medium-font-size\"><code>streams<\/code>: Contains the URLs for the RTSP streams, indexed by a friendly name. Each URL should be customized with the correct credentials and IP addresses for your cameras.<\/li>\n\n\n\n<li class=\"has-medium-font-size\"><code>broker_address<\/code>, <code>user<\/code>, <code>password<\/code>, <code>topic<\/code>, <code>mqtt_port<\/code>: Configuration settings for connecting to the MQTT broker.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading has-medium-font-size\"><strong>Functions<\/strong><\/h4>\n\n\n\n<ol class=\"wp-block-list\">\n<li class=\"has-medium-font-size\"><strong>control_kodi(action)<\/strong>:\n<ul class=\"wp-block-list\">\n<li><strong>Purpose<\/strong>: Sends a play\/pause command to Kodi.<\/li>\n\n\n\n<li><strong>Parameters<\/strong>: <code>action<\/code> (string) &#8211; either &#8220;pause&#8221; or &#8220;play&#8221;.<\/li>\n\n\n\n<li><strong>Process<\/strong>: Constructs an HTTP request to Kodi&#8217;s JSON-RPC API to control playback.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li class=\"has-medium-font-size\"><strong>check_kodi_play_state()<\/strong>:\n<ul class=\"wp-block-list\">\n<li><strong>Purpose<\/strong>: Checks whether Kodi is currently playing.<\/li>\n\n\n\n<li><strong>Returns<\/strong>: Playback speed (0 for paused, 1 for playing).<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li class=\"has-medium-font-size\"><strong>display_rtsp(stream_key, action)<\/strong>:\n<ul class=\"wp-block-list\">\n<li><strong>Purpose<\/strong>: Manages the display of RTSP streams based on MQTT commands.<\/li>\n\n\n\n<li><strong>Parameters<\/strong>: <code>stream_key<\/code> (the camera identifier), <code>action<\/code> (either &#8220;start&#8221; or &#8220;stop&#8221;).<\/li>\n\n\n\n<li><strong>Process<\/strong>: Starts or stops the video feed and manages the state of Kodi playback accordingly.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li class=\"has-medium-font-size\"><strong>on_message(client, userdata, message)<\/strong>:\n<ul class=\"wp-block-list\">\n<li><strong>Purpose<\/strong>: Callback function that triggers when an MQTT message is received.<\/li>\n\n\n\n<li><strong>Process<\/strong>: Parses the message and calls <code>display_rtsp<\/code> to show or hide the specified stream.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li class=\"has-medium-font-size\"><strong>publish_discovery_messages(client)<\/strong>:\n<ul class=\"wp-block-list\">\n<li><strong>Purpose<\/strong>: Publishes discovery messages to Home Assistant to automatically create entities for each camera.<\/li>\n\n\n\n<li><strong>Process<\/strong>: Iterates through each stream and sends a configuration payload over MQTT.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li class=\"has-medium-font-size\"><strong>publish_state(stream_key, state)<\/strong>:\n<ul class=\"wp-block-list\">\n<li><strong>Purpose<\/strong>: Publishes the current state of a stream to MQTT.<\/li>\n\n\n\n<li><strong>Parameters<\/strong>: <code>stream_key<\/code> (the camera identifier), <code>state<\/code> (either &#8220;on&#8221; or &#8220;off&#8221;).<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li class=\"has-medium-font-size\"><strong>initialize_camera_states()<\/strong>:\n<ul class=\"wp-block-list\">\n<li><strong>Purpose<\/strong>: Initializes the state of all camera streams to &#8220;off&#8221; upon script startup.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<h4 class=\"wp-block-heading has-medium-font-size\"><strong>MQTT Client Setup and Loop<\/strong><\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"has-medium-font-size\">Sets up an MQTT client, connects to the broker, subscribes to the relevant topic, and enters a loop to listen for messages indefinitely.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading has-medium-font-size\"><strong>Usage Instructions<\/strong><\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li class=\"has-medium-font-size\"><strong>Modify the Placeholder Values<\/strong>: Replace placeholders in the <code>streams<\/code> dictionary and MQTT settings with actual data from your environment.<\/li>\n\n\n\n<li class=\"has-medium-font-size\"><strong>Set Up Kodi and MQTT Broker<\/strong>: Ensure Kodi&#8217;s JSON-RPC API is accessible, and your MQTT broker is running.<\/li>\n\n\n\n<li class=\"has-medium-font-size\"><strong>Run the Script<\/strong>: Execute the script on a system within the same network as your cameras and MQTT broker. The system should also be able to communicate with Kodi.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Conclusion<\/strong><\/h3>\n\n\n\n<p class=\"has-medium-font-size\">This script provides a robust solution for integrating RTSP camera streams into a smart home environment using MQTT, with added functionality for controlling media playback through Kodi. It&#8217;s particularly useful in scenarios where real-time video monitoring is needed without interrupting media playback.<\/p>\n\n\n\n<p class=\"has-medium-font-size\">This guide can be adapted as necessary to fit the specific configuration and setup details of your home automation system.<\/p>\n\n\n\n<p class=\"has-medium-font-size\"><strong>Home Assistant:<\/strong><\/p>\n\n\n\n<p class=\"has-medium-font-size\">This script integrates seamlessly with Home Assistant, allowing you to leverage its powerful automation capabilities in conjunction with real-time video monitoring from your RTSP cameras. By utilizing MQTT for communication, the script automatically publishes camera streams as entities within Home Assistant, enabling you to trigger automations and control stream visibility directly from your Home Assistant dashboard. For instance I use in conjunction with Frigate, so when a specific object is detected, it can displayed on the screen.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"257\" height=\"359\" src=\"http:\/\/172.30.70.55\/wp-content\/uploads\/2024\/07\/image.png\" alt=\"\" class=\"wp-image-7239\" srcset=\"https:\/\/phixd.us\/wp-content\/uploads\/2024\/07\/image.png 257w, https:\/\/phixd.us\/wp-content\/uploads\/2024\/07\/image-215x300.png 215w\" sizes=\"auto, (max-width: 257px) 100vw, 257px\" \/><\/figure>\n\n\n\n<p class=\"has-medium-font-size\"><strong>Script:<\/strong><\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(3 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" data-code=\"import os\nimport subprocess\nimport requests\nimport json\nimport time\nimport threading\nimport paho.mqtt.client as mqtt\nimport paho.mqtt.publish as publish\n\n# Dictionary to keep track of the current stream process and the original play state\ncurrent_stream = {\n    'process': None,\n    'was_playing': False,\n    'active_stream': None,  # Track the currently active stream\n    'state': {}  # Added state dictionary to keep track of each stream's state\n}\n\n# RTSP URLs dictionary with generic credentials\nstreams = {\n    &quot;fdoorbell&quot;: &quot;rtsp:\/\/user:password@camera-ip:554\/h264Preview_01_main&quot;,\n    &quot;parking_area&quot;: &quot;rtsp:\/\/user:password@camera-ip:554\/cam\/realmonitor?channel=1&amp;subtype=0&quot;,\n    # Add additional streams as needed\n}\n\n# MQTT setup with generic credentials\nbroker_address = &quot;mqtt-broker-ip&quot;\nuser = &quot;mqtt-user&quot;\npassword = &quot;mqtt-password&quot;\ntopic = &quot;camera\/control\/#&quot;\nmqtt_port = 1883\n\n# Device Information\ndevice_id = &quot;device_id&quot;\ndevice_name = &quot;Device Name&quot;\ndevice_serial = &quot;serial_number&quot;\ndevice_manufacturer = &quot;Manufacturer&quot;\ndevice_model = &quot;Model&quot;\n\ndef control_kodi(action):\n    # Control Kodi functionality\n    kodi_url = 'http:\/\/kodi-ip:port\/jsonrpc'\n    headers = {'Content-Type': 'application\/json'}\n    payload = {\n        &quot;jsonrpc&quot;: &quot;2.0&quot;,\n        &quot;method&quot;: &quot;Player.PlayPause&quot;,\n        &quot;params&quot;: {\n            &quot;playerid&quot;: 1\n        },\n        &quot;id&quot;: 1\n    }\n    try:\n        response = requests.post(kodi_url, headers=headers, data=json.dumps(payload))\n        print(f&quot;Response from Kodi: {response.text}&quot;)\n    except Exception as e:\n        print(f&quot;Error controlling Kodi: {e}&quot;)\n\ndef check_kodi_play_state():\n    # Check the current play state of Kodi\n    kodi_url = 'http:\/\/kodi-ip:port\/jsonrpc'\n    headers = {'Content-Type': 'application\/json'}\n    payload = {\n        &quot;jsonrpc&quot;: &quot;2.0&quot;,\n        &quot;method&quot;: &quot;Player.GetProperties&quot;,\n        &quot;params&quot;: {\n            &quot;playerid&quot;: 1,\n            &quot;properties&quot;: [&quot;speed&quot;]\n        },\n        &quot;id&quot;: 1\n    }\n    try:\n        response = requests.post(kodi_url, headers=headers, data=json.dumps(payload))\n        response_data = response.json()\n        return response_data.get(&quot;result&quot;, {}).get(&quot;speed&quot;, 1)\n    except Exception as e:\n        print(f&quot;Error checking Kodi play state: {e}&quot;)\n        return 1\n\ndef display_rtsp(stream_key, action):\n    # Manage the display of RTSP streams\n    global current_stream\n    try:\n        if action == &quot;start&quot;:\n            # Example logic to handle stream starting\n            pass\n        elif action == &quot;stop&quot;:\n            # Example logic to handle stream stopping\n            pass\n    except Exception as e:\n        print(f&quot;Error in display_rtsp: {e}&quot;)\n\ndef on_message(client, userdata, message):\n    # Handle MQTT messages\n    msg = str(message.payload.decode(&quot;utf-8&quot;))\n    stream_key = message.topic.split('\/')[-1]\n    if msg == &quot;on&quot;:\n        display_rtsp(stream_key, &quot;start&quot;)\n    elif msg == &quot;off&quot;:\n        display_rtsp(stream_key, &quot;stop&quot;)\n\ndef publish_discovery_messages(client):\n    # Publish MQTT discovery messages for Home Assistant integration\n    global streams\n    for stream_key, _ in streams.items():\n        # Example discovery message\n        pass\n\ndef publish_state(stream_key, state):\n    # Publish state changes\n    state_topic = f&quot;homeassistant\/switch\/{device_id}\/camera_{stream_key}\/state&quot;\n    try:\n        publish.single(\n            state_topic,\n            payload=state,\n            hostname=broker_address,\n            port=mqtt_port,\n            auth={&quot;username&quot;: user, &quot;password&quot;: password},\n            retain=True\n        )\n    except Exception as e:\n        print(f&quot;Error publishing state for {stream_key}: {e}&quot;)\n\ndef initialize_camera_states():\n    # Initialize camera states\n    for stream_key in streams:\n        publish_state(stream_key, &quot;off&quot;)\n\nclient = mqtt.Client(&quot;CameraControl&quot;)\nclient.username_pw_set(user, password)\nclient.on_message = on_message\n\ntry:\n    client.connect(broker_address)\n    client.subscribe(topic)\n    initialize_camera_states()\n    publish_discovery_messages(client)\n    client.loop_forever()\nexcept Exception as e:\n    print(f&quot;Error connecting to MQTT broker: {e}&quot;)\n\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #C586C0\">import<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">os<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">import<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">subprocess<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">import<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">requests<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">import<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">json<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">import<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">time<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">import<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">threading<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">import<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">paho<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">mqtt<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">client<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">as<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">mqtt<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">import<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">paho<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">mqtt<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">publish<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">as<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">publish<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\"># <\/span><span style=\"color: #9CDCFE\">Dictionary<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">to<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">keep<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">track<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">of<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">the<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">current<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">stream<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">process<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">and<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">the<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">original<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">play<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">state<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">current_stream<\/span><span style=\"color: #D4D4D4\"> = {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    &#39;<\/span><span style=\"color: #9CDCFE\">process<\/span><span style=\"color: #D4D4D4\">&#39;: <\/span><span style=\"color: #9CDCFE\">None<\/span><span style=\"color: #D4D4D4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    &#39;<\/span><span style=\"color: #9CDCFE\">was_playing<\/span><span style=\"color: #D4D4D4\">&#39;: <\/span><span style=\"color: #9CDCFE\">False<\/span><span style=\"color: #D4D4D4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    &#39;<\/span><span style=\"color: #9CDCFE\">active_stream<\/span><span style=\"color: #D4D4D4\">&#39;: <\/span><span style=\"color: #9CDCFE\">None<\/span><span style=\"color: #D4D4D4\">,  # <\/span><span style=\"color: #9CDCFE\">Track<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">the<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">currently<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">active<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">stream<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    &#39;<\/span><span style=\"color: #9CDCFE\">state<\/span><span style=\"color: #D4D4D4\">&#39;: {}  # <\/span><span style=\"color: #9CDCFE\">Added<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">state<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">dictionary<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">to<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">keep<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">track<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">of<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">each<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">stream<\/span><span style=\"color: #CE9178\">&#39;s stat<\/span><span style=\"color: #F44747\">e<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\"># <\/span><span style=\"color: #9CDCFE\">RTSP<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">URLs<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">dictionary<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">with<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">generic<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">credentials<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">streams<\/span><span style=\"color: #D4D4D4\"> = {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    &quot;<\/span><span style=\"color: #9CDCFE\">fdoorbell<\/span><span style=\"color: #D4D4D4\">&quot;: &quot;<\/span><span style=\"color: #9CDCFE\">rtsp<\/span><span style=\"color: #D4D4D4\">:<\/span><span style=\"color: #6A9955\">\/\/user:password@camera-ip:554\/h264Preview_01_main&quot;,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    &quot;<\/span><span style=\"color: #9CDCFE\">parking_area<\/span><span style=\"color: #D4D4D4\">&quot;: &quot;<\/span><span style=\"color: #9CDCFE\">rtsp<\/span><span style=\"color: #D4D4D4\">:<\/span><span style=\"color: #6A9955\">\/\/user:password@camera-ip:554\/cam\/realmonitor?channel=1&amp;subtype=0&quot;,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    # <\/span><span style=\"color: #9CDCFE\">Add<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">additional<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">streams<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #C586C0\">as<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">needed<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\"># <\/span><span style=\"color: #9CDCFE\">MQTT<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">setup<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">with<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">generic<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">credentials<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">broker_address<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #CE9178\">&quot;mqtt-broker-ip&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">user<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #CE9178\">&quot;mqtt-user&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">password<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #CE9178\">&quot;mqtt-password&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">topic<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #CE9178\">&quot;camera\/control\/#&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">mqtt_port<\/span><span style=\"color: #D4D4D4\"> = 1883<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\"># <\/span><span style=\"color: #9CDCFE\">Device<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">Information<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">device_id<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #CE9178\">&quot;device_id&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">device_name<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #CE9178\">&quot;Device Name&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">device_serial<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #CE9178\">&quot;serial_number&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">device_manufacturer<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #CE9178\">&quot;Manufacturer&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">device_model<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #CE9178\">&quot;Model&quot;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">def<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">control_kodi<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">action<\/span><span style=\"color: #D4D4D4\">):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    # <\/span><span style=\"color: #9CDCFE\">Control<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">Kodi<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">functionality<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">kodi_url<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #CE9178\">&#39;http:\/\/kodi-ip:port\/jsonrpc&#39;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">headers<\/span><span style=\"color: #D4D4D4\"> = {&#39;<\/span><span style=\"color: #9CDCFE\">Content<\/span><span style=\"color: #D4D4D4\">-<\/span><span style=\"color: #9CDCFE\">Type<\/span><span style=\"color: #D4D4D4\">&#39;: &#39;<\/span><span style=\"color: #9CDCFE\">application<\/span><span style=\"color: #D4D4D4\">\/<\/span><span style=\"color: #9CDCFE\">json<\/span><span style=\"color: #D4D4D4\">&#39;}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">payload<\/span><span style=\"color: #D4D4D4\"> = {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        &quot;<\/span><span style=\"color: #9CDCFE\">jsonrpc<\/span><span style=\"color: #D4D4D4\">&quot;: &quot;2.0&quot;,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        &quot;<\/span><span style=\"color: #9CDCFE\">method<\/span><span style=\"color: #D4D4D4\">&quot;: &quot;<\/span><span style=\"color: #9CDCFE\">Player<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">PlayPause<\/span><span style=\"color: #D4D4D4\">&quot;,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        &quot;<\/span><span style=\"color: #9CDCFE\">params<\/span><span style=\"color: #D4D4D4\">&quot;: {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            &quot;<\/span><span style=\"color: #9CDCFE\">playerid<\/span><span style=\"color: #D4D4D4\">&quot;: 1<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        },<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #CE9178\">&quot;id&quot;<\/span><span style=\"color: #D4D4D4\">: 1<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">try<\/span><span style=\"color: #D4D4D4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">response<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">requests<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">post<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">kodi_url<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">headers<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #9CDCFE\">headers<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">data<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #9CDCFE\">json<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">dumps<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">payload<\/span><span style=\"color: #D4D4D4\">))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">print<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">f<\/span><span style=\"color: #CE9178\">&quot;Response from Kodi: {response.text}&quot;<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">except<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">Exception<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #C586C0\">as<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">e<\/span><span style=\"color: #D4D4D4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">print<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">f<\/span><span style=\"color: #CE9178\">&quot;Error controlling Kodi: {e}&quot;<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">def<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">check_kodi_play_state<\/span><span style=\"color: #D4D4D4\">():<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    # <\/span><span style=\"color: #9CDCFE\">Check<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">the<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">current<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">play<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">state<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">of<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">Kodi<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">kodi_url<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #CE9178\">&#39;http:\/\/kodi-ip:port\/jsonrpc&#39;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">headers<\/span><span style=\"color: #D4D4D4\"> = {&#39;<\/span><span style=\"color: #9CDCFE\">Content<\/span><span style=\"color: #D4D4D4\">-<\/span><span style=\"color: #9CDCFE\">Type<\/span><span style=\"color: #D4D4D4\">&#39;: &#39;<\/span><span style=\"color: #9CDCFE\">application<\/span><span style=\"color: #D4D4D4\">\/<\/span><span style=\"color: #9CDCFE\">json<\/span><span style=\"color: #D4D4D4\">&#39;}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">payload<\/span><span style=\"color: #D4D4D4\"> = {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        &quot;<\/span><span style=\"color: #9CDCFE\">jsonrpc<\/span><span style=\"color: #D4D4D4\">&quot;: &quot;2.0&quot;,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        &quot;<\/span><span style=\"color: #9CDCFE\">method<\/span><span style=\"color: #D4D4D4\">&quot;: &quot;<\/span><span style=\"color: #9CDCFE\">Player<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">GetProperties<\/span><span style=\"color: #D4D4D4\">&quot;,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        &quot;<\/span><span style=\"color: #9CDCFE\">params<\/span><span style=\"color: #D4D4D4\">&quot;: {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            &quot;<\/span><span style=\"color: #9CDCFE\">playerid<\/span><span style=\"color: #D4D4D4\">&quot;: 1,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            &quot;<\/span><span style=\"color: #9CDCFE\">properties<\/span><span style=\"color: #D4D4D4\">&quot;: [&quot;<\/span><span style=\"color: #9CDCFE\">speed<\/span><span style=\"color: #D4D4D4\">&quot;]<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        },<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #CE9178\">&quot;id&quot;<\/span><span style=\"color: #D4D4D4\">: 1<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">try<\/span><span style=\"color: #D4D4D4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">response<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">requests<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">post<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">kodi_url<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">headers<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #9CDCFE\">headers<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">data<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #9CDCFE\">json<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">dumps<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">payload<\/span><span style=\"color: #D4D4D4\">))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">response_data<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">response<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">json<\/span><span style=\"color: #D4D4D4\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">return<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">response_data<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">get<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">&quot;result&quot;<\/span><span style=\"color: #D4D4D4\">, {}).<\/span><span style=\"color: #9CDCFE\">get<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">&quot;speed&quot;<\/span><span style=\"color: #D4D4D4\">, 1)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">except<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">Exception<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #C586C0\">as<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">e<\/span><span style=\"color: #D4D4D4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">print<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">f<\/span><span style=\"color: #CE9178\">&quot;Error checking Kodi play state: {e}&quot;<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">return<\/span><span style=\"color: #D4D4D4\"> 1<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">def<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">display_rtsp<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">stream_key<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">action<\/span><span style=\"color: #D4D4D4\">):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    # <\/span><span style=\"color: #9CDCFE\">Manage<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">the<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">display<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">of<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">RTSP<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">streams<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">global<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">current_stream<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">try<\/span><span style=\"color: #D4D4D4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">if<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">action<\/span><span style=\"color: #D4D4D4\"> == <\/span><span style=\"color: #CE9178\">&quot;start&quot;<\/span><span style=\"color: #D4D4D4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            # <\/span><span style=\"color: #9CDCFE\">Example<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">logic<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">to<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">handle<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">stream<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">starting<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #9CDCFE\">pass<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">elif<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">action<\/span><span style=\"color: #D4D4D4\"> == <\/span><span style=\"color: #CE9178\">&quot;stop&quot;<\/span><span style=\"color: #D4D4D4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            # <\/span><span style=\"color: #9CDCFE\">Example<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">logic<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">to<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">handle<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">stream<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">stopping<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #9CDCFE\">pass<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">except<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">Exception<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #C586C0\">as<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">e<\/span><span style=\"color: #D4D4D4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">print<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">f<\/span><span style=\"color: #CE9178\">&quot;Error in display_rtsp: {e}&quot;<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">def<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">on_message<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">client<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">userdata<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">message<\/span><span style=\"color: #D4D4D4\">):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    # <\/span><span style=\"color: #9CDCFE\">Handle<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">MQTT<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">messages<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">msg<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">str<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">message<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">payload<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">decode<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">&quot;utf-8&quot;<\/span><span style=\"color: #D4D4D4\">))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">stream_key<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">message<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">topic<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">split<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">&#39;\/&#39;<\/span><span style=\"color: #D4D4D4\">)[-1]<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">if<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">msg<\/span><span style=\"color: #D4D4D4\"> == <\/span><span style=\"color: #CE9178\">&quot;on&quot;<\/span><span style=\"color: #D4D4D4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">display_rtsp<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">stream_key<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #CE9178\">&quot;start&quot;<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">elif<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">msg<\/span><span style=\"color: #D4D4D4\"> == <\/span><span style=\"color: #CE9178\">&quot;off&quot;<\/span><span style=\"color: #D4D4D4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">display_rtsp<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">stream_key<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #CE9178\">&quot;stop&quot;<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">def<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">publish_discovery_messages<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">client<\/span><span style=\"color: #D4D4D4\">):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    # <\/span><span style=\"color: #9CDCFE\">Publish<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">MQTT<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">discovery<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">messages<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">for<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">Home<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">Assistant<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">integration<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">global<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">streams<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">for<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">stream_key<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">_<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">in<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">streams<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">items<\/span><span style=\"color: #D4D4D4\">():<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        # <\/span><span style=\"color: #9CDCFE\">Example<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">discovery<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">message<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">pass<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">def<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">publish_state<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">stream_key<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">state<\/span><span style=\"color: #D4D4D4\">):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    # <\/span><span style=\"color: #9CDCFE\">Publish<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">state<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">changes<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">state_topic<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">f<\/span><span style=\"color: #CE9178\">&quot;homeassistant\/switch\/{device_id}\/camera_{stream_key}\/state&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">try<\/span><span style=\"color: #D4D4D4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">publish<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">single<\/span><span style=\"color: #D4D4D4\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #9CDCFE\">state_topic<\/span><span style=\"color: #D4D4D4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #9CDCFE\">payload<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #9CDCFE\">state<\/span><span style=\"color: #D4D4D4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #9CDCFE\">hostname<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #9CDCFE\">broker_address<\/span><span style=\"color: #D4D4D4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #9CDCFE\">port<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #9CDCFE\">mqtt_port<\/span><span style=\"color: #D4D4D4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #9CDCFE\">auth<\/span><span style=\"color: #D4D4D4\">={&quot;<\/span><span style=\"color: #9CDCFE\">username<\/span><span style=\"color: #D4D4D4\">&quot;: <\/span><span style=\"color: #9CDCFE\">user<\/span><span style=\"color: #D4D4D4\">, &quot;<\/span><span style=\"color: #9CDCFE\">password<\/span><span style=\"color: #D4D4D4\">&quot;: <\/span><span style=\"color: #9CDCFE\">password<\/span><span style=\"color: #D4D4D4\">},<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #9CDCFE\">retain<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #9CDCFE\">True<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        )<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">except<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">Exception<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #C586C0\">as<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">e<\/span><span style=\"color: #D4D4D4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">print<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">f<\/span><span style=\"color: #CE9178\">&quot;Error publishing state for {stream_key}: {e}&quot;<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">def<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">initialize_camera_states<\/span><span style=\"color: #D4D4D4\">():<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    # <\/span><span style=\"color: #9CDCFE\">Initialize<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">camera<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">states<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">for<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">stream_key<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">in<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">streams<\/span><span style=\"color: #D4D4D4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">publish_state<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">stream_key<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #CE9178\">&quot;off&quot;<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">client<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">mqtt<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Client<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">&quot;CameraControl&quot;<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">client<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">username_pw_set<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">user<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">password<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">client<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">on_message<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">on_message<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">try<\/span><span style=\"color: #D4D4D4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">client<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">connect<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">broker_address<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">client<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">subscribe<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">topic<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">initialize_camera_states<\/span><span style=\"color: #D4D4D4\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">publish_discovery_messages<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">client<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">client<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">loop_forever<\/span><span style=\"color: #D4D4D4\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">except<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">Exception<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #C586C0\">as<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">e<\/span><span style=\"color: #D4D4D4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">print<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">f<\/span><span style=\"color: #CE9178\">&quot;Error connecting to MQTT broker: {e}&quot;<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><\/span><\/code><\/pre><\/div>\n","protected":false},"excerpt":{"rendered":"<p>This script is designed to integrate RTSP compatible cameras into a designed for Home Assistant via MQTT discovery messages and manage media playback through a Kodi media center. The script &#8230;<\/p>\n","protected":false},"author":1,"featured_media":7241,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[352,355,354,353],"tags":[],"class_list":["post-7238","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-coding","category-home-assistant","category-linux","category-python"],"_links":{"self":[{"href":"https:\/\/phixd.us\/index.php?rest_route=\/wp\/v2\/posts\/7238","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/phixd.us\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/phixd.us\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/phixd.us\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/phixd.us\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=7238"}],"version-history":[{"count":3,"href":"https:\/\/phixd.us\/index.php?rest_route=\/wp\/v2\/posts\/7238\/revisions"}],"predecessor-version":[{"id":7254,"href":"https:\/\/phixd.us\/index.php?rest_route=\/wp\/v2\/posts\/7238\/revisions\/7254"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/phixd.us\/index.php?rest_route=\/wp\/v2\/media\/7241"}],"wp:attachment":[{"href":"https:\/\/phixd.us\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=7238"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/phixd.us\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=7238"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/phixd.us\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=7238"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}